summaryrefslogtreecommitdiff
path: root/posix
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2002-02-26 19:06:03 +0000
committerUlrich Drepper <drepper@redhat.com>2002-02-26 19:06:03 +0000
commit3b0bdc723579a7c6df2cace0115a6ca0977d73f9 (patch)
tree8b6d7f9ab35be46faadc9e778abc1ce632fe98d0 /posix
parent73f1b06797637163b8529f4c7fa4b02b90c0154c (diff)
Update.
* posix/Makefile (distribute): Add regcomp.c, regexec.c, regex_internal.c, and regex_internal.h. (CFLAGS-regex.c): Replace -DMBS_SUPPORT with -DRE_ENABLE_I18N. * posix/regex.c: Complete rewrite. * posix/regexec.c: New file. * posix/regcomp.c: New file. * posix/regex_internal.c: New file. * posix/regex_internal.h: New file. * posix/regex.h (RE_ICASE): New macro. Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
Diffstat (limited to 'posix')
-rw-r--r--posix/Makefile7
-rw-r--r--posix/regcomp.c3092
-rw-r--r--posix/regex.h12
-rw-r--r--posix/regex_internal.c1095
-rw-r--r--posix/regex_internal.h542
-rw-r--r--posix/regexec.c2076
6 files changed, 6817 insertions, 7 deletions
diff --git a/posix/Makefile b/posix/Makefile
index 785c227ebb..008154de9a 100644
--- a/posix/Makefile
+++ b/posix/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 1991-1999, 2000, 2001 Free Software Foundation, Inc.
+# Copyright (C) 1991-1999, 2000, 2001, 2002 Free Software Foundation, Inc.
# This file is part of the GNU C Library.
# The GNU C Library is free software; you can redistribute it and/or
@@ -32,7 +32,8 @@ headers := sys/utsname.h sys/times.h sys/wait.h sys/types.h unistd.h \
distribute := confstr.h TESTS TESTS2C.sed testcases.h \
PTESTS PTESTS2C.sed ptestcases.h \
globtest.c globtest.sh wordexp-tst.sh annexc.c fnmatch_loop.c \
- spawn_int.h tst-getconf.sh
+ spawn_int.h tst-getconf.sh regcomp.c regexec.c regex_internal.c \
+ regex_internal.h
routines := \
uname \
@@ -109,7 +110,7 @@ $(objpfx)wordexp-tst.out: wordexp-tst.sh $(objpfx)wordexp-test
endif
endif
-CFLAGS-regex.c = -Wno-strict-prototypes -DMBS_SUPPORT
+CFLAGS-regex.c = -Wno-strict-prototypes -DRE_ENABLE_I18N
CFLAGS-getaddrinfo.c = -DRESOLVER
tstgetopt-ARGS = -a -b -cfoobar --required foobar --optional=bazbug \
--none random --col --color --colour
diff --git a/posix/regcomp.c b/posix/regcomp.c
new file mode 100644
index 0000000000..12da043062
--- /dev/null
+++ b/posix/regcomp.c
@@ -0,0 +1,3092 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <assert.h>
+#include <ctype.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#ifdef _LIBC
+# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
+# define _RE_DEFINE_LOCALE_FUNCTIONS 1
+# include <locale/localeinfo.h>
+# include <locale/elem-hash.h>
+# include <locale/coll-lookup.h>
+# endif
+#endif
+
+/* This is for other GNU distributions with internationalized messages. */
+#if HAVE_LIBINTL_H || defined _LIBC
+# include <libintl.h>
+# ifdef _LIBC
+# undef gettext
+# define gettext(msgid) __dcgettext ("libc", msgid, LC_MESSAGES)
+# endif
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef gettext_noop
+/* This define is so xgettext can find the internationalizable
+ strings. */
+# define gettext_noop(String) String
+#endif
+
+#include "regex.h"
+#include "regex_internal.h"
+
+static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
+ int length, reg_syntax_t syntax);
+static void re_compile_fastmap_iter (regex_t *bufp,
+ const re_dfastate_t *init_state,
+ char *fastmap);
+static reg_errcode_t init_dfa (re_dfa_t *dfa, int pat_len);
+static void init_word_char (re_dfa_t *dfa);
+static void free_charset (re_charset_t *cset);
+static void free_workarea_compile (regex_t *preg);
+static reg_errcode_t create_initial_state (re_dfa_t *dfa);
+static reg_errcode_t analyze (re_dfa_t *dfa);
+static reg_errcode_t analyze_tree (re_dfa_t *dfa, bin_tree_t *node);
+static void calc_first (re_dfa_t *dfa, bin_tree_t *node);
+static void calc_next (re_dfa_t *dfa, bin_tree_t *node);
+static void calc_epsdest (re_dfa_t *dfa, bin_tree_t *node);
+static int duplicate_node (re_dfa_t *dfa, int org_idx,
+ unsigned int constraint);
+static reg_errcode_t calc_eclosure (re_dfa_t *dfa);
+static re_node_set calc_eclosure_iter (re_dfa_t *dfa, int node, int root);
+static void calc_inveclosure (re_dfa_t *dfa);
+static int fetch_number (re_string_t *input, re_token_t *token,
+ reg_syntax_t syntax);
+static re_token_t fetch_token (re_string_t *input, reg_syntax_t syntax);
+static int peek_token (re_token_t *token, re_string_t *input,
+ reg_syntax_t syntax);
+static int peek_token_bracket (re_token_t *token, re_string_t *input,
+ reg_syntax_t syntax);
+static bin_tree_t *parse (re_string_t *regexp, regex_t *preg,
+ reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp,
+ re_dfa_t *dfa, re_token_t *token,
+ reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa,
+ re_token_t *token, reg_syntax_t syntax,
+ reg_errcode_t *err);
+static reg_errcode_t parse_bracket_element (bracket_elem_t *elem,
+ re_string_t *regexp,
+ re_token_t *token, int token_len,
+ re_dfa_t *dfa,
+ reg_syntax_t syntax);
+static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem,
+ re_string_t *regexp,
+ re_token_t *token);
+static reg_errcode_t build_equiv_class (re_charset_t *mbcset,
+ re_bitset_ptr_t sbcset,
+ int *equiv_class_alloc,
+ const unsigned char *name);
+static reg_errcode_t build_charclass (re_charset_t *mbcset,
+ re_bitset_ptr_t sbcset,
+ int *char_class_alloc,
+ const unsigned char *name);
+static bin_tree_t *build_word_op (re_dfa_t *dfa, int not, reg_errcode_t *err);
+static void free_bin_tree (bin_tree_t *tree);
+static bin_tree_t *create_tree (bin_tree_t *left, bin_tree_t *right,
+ re_token_type_t type, int index);
+static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa);
+
+/* This table gives an error message for each of the error codes listed
+ in regex.h. Obviously the order here has to be same as there.
+ POSIX doesn't require that we do anything for REG_NOERROR,
+ but why not be nice? */
+
+const char re_error_msgid[] =
+ {
+#define REG_NOERROR_IDX 0
+ gettext_noop ("Success") /* REG_NOERROR */
+ "\0"
+#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success")
+ gettext_noop ("No match") /* REG_NOMATCH */
+ "\0"
+#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match")
+ gettext_noop ("Invalid regular expression") /* REG_BADPAT */
+ "\0"
+#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression")
+ gettext_noop ("Invalid collation character") /* REG_ECOLLATE */
+ "\0"
+#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character")
+ gettext_noop ("Invalid character class name") /* REG_ECTYPE */
+ "\0"
+#define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name")
+ gettext_noop ("Trailing backslash") /* REG_EESCAPE */
+ "\0"
+#define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash")
+ gettext_noop ("Invalid back reference") /* REG_ESUBREG */
+ "\0"
+#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference")
+ gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */
+ "\0"
+#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^")
+ gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */
+ "\0"
+#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(")
+ gettext_noop ("Unmatched \\{") /* REG_EBRACE */
+ "\0"
+#define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{")
+ gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */
+ "\0"
+#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}")
+ gettext_noop ("Invalid range end") /* REG_ERANGE */
+ "\0"
+#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end")
+ gettext_noop ("Memory exhausted") /* REG_ESPACE */
+ "\0"
+#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted")
+ gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */
+ "\0"
+#define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression")
+ gettext_noop ("Premature end of regular expression") /* REG_EEND */
+ "\0"
+#define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression")
+ gettext_noop ("Regular expression too big") /* REG_ESIZE */
+ "\0"
+#define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big")
+ gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */
+ };
+
+const size_t re_error_msgid_idx[] =
+ {
+ REG_NOERROR_IDX,
+ REG_NOMATCH_IDX,
+ REG_BADPAT_IDX,
+ REG_ECOLLATE_IDX,
+ REG_ECTYPE_IDX,
+ REG_EESCAPE_IDX,
+ REG_ESUBREG_IDX,
+ REG_EBRACK_IDX,
+ REG_EPAREN_IDX,
+ REG_EBRACE_IDX,
+ REG_BADBR_IDX,
+ REG_ERANGE_IDX,
+ REG_ESPACE_IDX,
+ REG_BADRPT_IDX,
+ REG_EEND_IDX,
+ REG_ESIZE_IDX,
+ REG_ERPAREN_IDX
+ };
+
+/* Entry points for GNU code. */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+ compiles PATTERN (of length SIZE) and puts the result in BUFP.
+ Returns 0 if the pattern was valid, otherwise an error string.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+ are set in BUFP on entry. */
+
+const char *
+re_compile_pattern (pattern, length, bufp)
+ const char *pattern;
+ size_t length;
+ struct re_pattern_buffer *bufp;
+{
+ reg_errcode_t ret;
+
+ /* GNU code is written to assume at least RE_NREGS registers will be set
+ (and at least one extra will be -1). */
+ bufp->regs_allocated = REGS_UNALLOCATED;
+
+ /* And GNU code determines whether or not to get register information
+ by passing null for the REGS argument to re_match, etc., not by
+ setting no_sub. */
+ bufp->no_sub = 0;
+
+ /* Match anchors at newline. */
+ bufp->newline_anchor = 1;
+
+ ret = re_compile_internal (bufp, (const unsigned char *) pattern, length,
+ re_syntax_options);
+
+ if (!ret)
+ return NULL;
+ return gettext (re_error_msgid + re_error_msgid_idx[(int) ret]);
+}
+#ifdef _LIBC
+weak_alias (__re_compile_pattern, re_compile_pattern)
+#endif
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can
+ also be assigned to arbitrarily: each pattern buffer stores its own
+ syntax, so it can be changed between regex compilations. */
+/* This has no initializer because initialized variables in Emacs
+ become read-only after dumping. */
+reg_syntax_t re_syntax_options;
+
+
+/* Specify the precise syntax of regexps for compilation. This provides
+ for compatibility for various utilities which historically have
+ different, incompatible syntaxes.
+
+ The argument SYNTAX is a bit mask comprised of the various bits
+ defined in regex.h. We return the old syntax. */
+
+reg_syntax_t
+re_set_syntax (syntax)
+ reg_syntax_t syntax;
+{
+ reg_syntax_t ret = re_syntax_options;
+
+ re_syntax_options = syntax;
+ return ret;
+}
+#ifdef _LIBC
+weak_alias (__re_set_syntax, re_set_syntax)
+#endif
+
+int
+re_compile_fastmap (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+ char *fastmap = bufp->fastmap;
+
+ memset (fastmap, '\0', sizeof (char) * SBC_MAX);
+ re_compile_fastmap_iter (bufp, dfa->init_state, fastmap);
+ if (dfa->init_state != dfa->init_state_word)
+ re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap);
+ if (dfa->init_state != dfa->init_state_nl)
+ re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap);
+ if (dfa->init_state != dfa->init_state_begbuf)
+ re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap);
+ bufp->fastmap_accurate = 1;
+ return 0;
+}
+#ifdef _LIBC
+weak_alias (__re_compile_fastmap, re_compile_fastmap)
+#endif
+
+/* Helper function for re_compile_fastmap.
+ Compile fastmap for the initial_state INIT_STATE. */
+
+static void
+re_compile_fastmap_iter (bufp, init_state, fastmap)
+ regex_t *bufp;
+ const re_dfastate_t *init_state;
+ char *fastmap;
+{
+ re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+ int node_cnt;
+ for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt)
+ {
+ int node = init_state->nodes.elems[node_cnt];
+ re_token_type_t type = dfa->nodes[node].type;
+ if (type == OP_CONTEXT_NODE)
+ {
+ node = dfa->nodes[node].opr.ctx_info->entity;
+ type = dfa->nodes[node].type;
+ }
+
+ if (type == CHARACTER)
+ fastmap[dfa->nodes[node].opr.c] = 1;
+ else if (type == SIMPLE_BRACKET)
+ {
+ int i, j, ch;
+ for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+ for (j = 0; j < UINT_BITS; ++j, ++ch)
+ if (dfa->nodes[node].opr.sbcset[i] & (1 << j))
+ fastmap[ch] = 1;
+ }
+ else if (type == COMPLEX_BRACKET)
+ {
+ int i;
+ re_charset_t *cset = dfa->nodes[node].opr.mbcset;
+ if (cset->non_match || cset->ncoll_syms || cset->nequiv_classes
+ || cset->nranges || cset->nchar_classes)
+ {
+ if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0)
+ {
+ /* In this case we want to catch the bytes which are
+ the first byte of any collation elements.
+ e.g. In da_DK, we want to catch 'a' since "aa"
+ is a valid collation element, and don't catch
+ 'b' since 'b' is the only collation element
+ which starts from 'b'. */
+ int j, ch;
+ const int32_t *table = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+ for (j = 0; j < UINT_BITS; ++j, ++ch)
+ if (table[ch] < 0)
+ fastmap[ch] = 1;
+ }
+ }
+ for (i = 0; i < cset->nmbchars; ++i)
+ {
+ unsigned char buf[256];
+ wctomb (buf, cset->mbchars[i]);
+ fastmap[buf[0]] = 1;
+ }
+ }
+ else if (type == END_OF_RE || type == COMPLEX_BRACKET
+ || type == OP_PERIOD)
+ {
+ memset (fastmap, '\1', sizeof (char) * SBC_MAX);
+ if (type == END_OF_RE)
+ bufp->can_be_null = 1;
+ return;
+ }
+ }
+}
+
+/* Entry point for POSIX code. */
+/* regcomp takes a regular expression as a string and compiles it.
+
+ PREG is a regex_t *. We do not expect any fields to be initialized,
+ since POSIX says we shouldn't. Thus, we set
+
+ `buffer' to the compiled pattern;
+ `used' to the length of the compiled pattern;
+ `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+ REG_EXTENDED bit in CFLAGS is set; otherwise, to
+ RE_SYNTAX_POSIX_BASIC;
+ `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+ `fastmap' to an allocated space for the fastmap;
+ `fastmap_accurate' to zero;
+ `re_nsub' to the number of subexpressions in PATTERN.
+
+ PATTERN is the address of the pattern string.
+
+ CFLAGS is a series of bits which affect compilation.
+
+ If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+ use POSIX basic syntax.
+
+ If REG_NEWLINE is set, then . and [^...] don't match newline.
+ Also, regexec will try a match beginning after every newline.
+
+ If REG_ICASE is set, then we considers upper- and lowercase
+ versions of letters to be equivalent when matching.
+
+ If REG_NOSUB is set, then when PREG is passed to regexec, that
+ routine will report only success or failure, and nothing about the
+ registers.
+
+ It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
+ the return codes and their meanings.) */
+
+int
+regcomp (preg, pattern, cflags)
+ regex_t *preg;
+ const char *pattern;
+ int cflags;
+{
+ reg_errcode_t ret;
+ reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED
+ : RE_SYNTAX_POSIX_BASIC);
+
+ preg->buffer = NULL;
+ preg->allocated = 0;
+ preg->used = 0;
+
+ /* Try to allocate space for the fastmap. */
+ preg->fastmap = re_malloc (char, SBC_MAX);
+ if (preg->fastmap == NULL)
+ return REG_ESPACE;
+
+ syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0;
+
+ /* If REG_NEWLINE is set, newlines are treated differently. */
+ if (cflags & REG_NEWLINE)
+ { /* REG_NEWLINE implies neither . nor [^...] match newline. */
+ syntax &= ~RE_DOT_NEWLINE;
+ syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+ /* It also changes the matching behavior. */
+ preg->newline_anchor = 1;
+ }
+ else
+ preg->newline_anchor = 0;
+ preg->no_sub = !!(cflags & REG_NOSUB);
+ preg->translate = NULL;
+
+ ret = re_compile_internal (preg, pattern, strlen (pattern), syntax);
+
+ /* POSIX doesn't distinguish between an unmatched open-group and an
+ unmatched close-group: both are REG_EPAREN. */
+ if (ret == REG_ERPAREN)
+ ret = REG_EPAREN;
+
+ /* XXX Why the test for preg->fastmap != NULL? */
+ if (ret == REG_NOERROR && preg->fastmap != NULL)
+ {
+ /* Compute the fastmap now, since regexec cannot modify the pattern
+ buffer. */
+ if (re_compile_fastmap (preg) == -2)
+ {
+ /* Some error occurred while computing the fastmap, just forget
+ about it. */
+ re_free (preg->fastmap);
+ preg->fastmap = NULL;
+ }
+ }
+
+ return (int) ret;
+}
+#ifdef _LIBC
+weak_alias (__regcomp, regcomp)
+#endif
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+ from either regcomp or regexec. We don't use PREG here. */
+
+size_t
+regerror (errcode, preg, errbuf, errbuf_size)
+ int errcode;
+ const regex_t *preg;
+ char *errbuf;
+ size_t errbuf_size;
+{
+ const char *msg;
+ size_t msg_size;
+
+ if (errcode < 0
+ || errcode >= (int) (sizeof (re_error_msgid_idx)
+ / sizeof (re_error_msgid_idx[0])))
+ /* Only error codes returned by the rest of the code should be passed
+ to this routine. If we are given anything else, or if other regex
+ code generates an invalid error code, then the program has a bug.
+ Dump core so we can fix it. */
+ abort ();
+
+ msg = gettext (re_error_msgid + re_error_msgid_idx[errcode]);
+
+ msg_size = strlen (msg) + 1; /* Includes the null. */
+
+ if (errbuf_size != 0)
+ {
+ if (msg_size > errbuf_size)
+ {
+#if defined HAVE_MEMPCPY || defined _LIBC
+ *((char *) __mempcpy (errbuf, msg, errbuf_size - 1)) = '\0';
+#else
+ memcpy (errbuf, msg, errbuf_size - 1);
+ errbuf[errbuf_size - 1] = 0;
+#endif
+ }
+ else
+ memcpy (errbuf, msg, msg_size);
+ }
+
+ return msg_size;
+}
+#ifdef _LIBC
+weak_alias (__regerror, regerror)
+#endif
+
+/* Free dynamically allocated space used by PREG. */
+
+void
+regfree (preg)
+ regex_t *preg;
+{
+ int i, j;
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ if (dfa != NULL)
+ {
+ re_free (dfa->subexps);
+
+ for (i = 0; i < dfa->nodes_len; ++i)
+ {
+ re_token_t *node = dfa->nodes + i;
+ if (node->type == COMPLEX_BRACKET && node->duplicated == 0)
+ free_charset (node->opr.mbcset);
+ else if (node->type == SIMPLE_BRACKET && node->duplicated == 0)
+ re_free (node->opr.sbcset);
+ else if (node->type == OP_CONTEXT_NODE)
+ {
+ if (dfa->nodes[node->opr.ctx_info->entity].type == OP_BACK_REF)
+ {
+ if (node->opr.ctx_info->bkref_eclosure != NULL)
+ re_node_set_free (node->opr.ctx_info->bkref_eclosure);
+ re_free (node->opr.ctx_info->bkref_eclosure);
+ }
+ re_free (node->opr.ctx_info);
+ }
+ }
+ re_free (dfa->firsts);
+ re_free (dfa->nexts);
+ for (i = 0; i < dfa->nodes_len; ++i)
+ {
+ if (dfa->eclosures != NULL)
+ re_node_set_free (dfa->eclosures + i);
+ if (dfa->inveclosures != NULL)
+ re_node_set_free (dfa->inveclosures + i);
+ if (dfa->edests != NULL)
+ re_node_set_free (dfa->edests + i);
+ }
+ re_free (dfa->edests);
+ re_free (dfa->eclosures);
+ re_free (dfa->inveclosures);
+ re_free (dfa->nodes);
+
+ for (i = 0; i <= dfa->state_hash_mask; ++i)
+ {
+ struct re_state_table_entry *entry = dfa->state_table + i;
+ if (entry->alloc == 0)
+ re_free (entry->entry.state);
+ else
+ {
+ for (j = 0; j < entry->num; ++j)
+ {
+ re_dfastate_t *state = entry->entry.array[j];
+ if (state->entrance_nodes != &state->nodes)
+ {
+ re_node_set_free (state->entrance_nodes);
+ re_free (state->entrance_nodes);
+ }
+ re_node_set_free (&state->nodes);
+ re_free (state->trtable);
+ re_free (state->trtable_search);
+ re_free (state);
+ }
+ re_free (entry->entry.array);
+ }
+ }
+ re_free (dfa->state_table);
+
+ if (dfa->word_char != NULL)
+ re_free (dfa->word_char);
+ re_free (dfa);
+ }
+ re_free (preg->fastmap);
+}
+#ifdef _LIBC
+weak_alias (__regfree, regfree)
+#endif
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them unless specifically requested. */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+
+/* BSD has one and only one pattern buffer. */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+# ifdef _LIBC
+/* Make these definitions weak in libc, so POSIX programs can redefine
+ these names if they don't use our functions, and still use
+ regcomp/regexec above without link errors. */
+weak_function
+# endif
+re_comp (s)
+ const char *s;
+{
+ reg_errcode_t ret;
+
+ if (!s)
+ {
+ if (!re_comp_buf.buffer)
+ return gettext ("No previous regular expression");
+ return 0;
+ }
+
+ if (!re_comp_buf.buffer)
+ {
+ re_comp_buf.fastmap = (char *) malloc (SBC_MAX);
+ if (re_comp_buf.fastmap == NULL)
+ return (char *) gettext (re_error_msgid
+ + re_error_msgid_idx[(int) REG_ESPACE]);
+ }
+
+ /* Since `re_exec' always passes NULL for the `regs' argument, we
+ don't need to initialize the pattern buffer fields which affect it. */
+
+ /* Match anchors at newlines. */
+ re_comp_buf.newline_anchor = 1;
+
+ ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options);
+
+ if (!ret)
+ return NULL;
+
+ /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */
+ return (char *) gettext (re_error_msgid + re_error_msgid_idx[(int) ret]);
+}
+#endif /* _REGEX_RE_COMP */
+
+/* Internal entry point.
+ Compile the regular expression PATTERN, whose length is LENGTH.
+ SYNTAX indicate regular expression's syntax. */
+
+static reg_errcode_t
+re_compile_internal (preg, pattern, length, syntax)
+ regex_t *preg;
+ const char * pattern;
+ int length;
+ reg_syntax_t syntax;
+{
+ reg_errcode_t err = REG_NOERROR;
+ re_dfa_t *dfa;
+ re_string_t regexp;
+
+ /* Initialize the pattern buffer. */
+ preg->fastmap_accurate = 0;
+ preg->syntax = syntax;
+ preg->not_bol = preg->not_eol = 0;
+ preg->used = 0;
+ preg->re_nsub = 0;
+
+ /* Initialize the dfa. */
+ dfa = (re_dfa_t *) preg->buffer;
+ if (preg->allocated < sizeof (re_dfa_t))
+ {
+ /* If zero allocated, but buffer is non-null, try to realloc
+ enough space. This loses if buffer's address is bogus, but
+ that is the user's responsibility. If ->buffer is NULL this
+ is a simple allocation. */
+ dfa = re_realloc (preg->buffer, re_dfa_t, 1);
+ if (dfa == NULL)
+ return REG_ESPACE;
+ memset (dfa, '\0', sizeof (re_dfa_t));
+ preg->allocated = sizeof (re_dfa_t);
+ }
+ preg->buffer = (unsigned char *) dfa;
+ preg->used = sizeof (re_dfa_t);
+
+ err = init_dfa (dfa, length);
+ if (err != REG_NOERROR)
+ {
+ re_free (dfa);
+ preg->buffer = NULL;
+ return err;
+ }
+
+ if (syntax & RE_ICASE)
+ err = re_string_construct_toupper (&regexp, pattern, length,
+ preg->translate);
+ else
+ err = re_string_construct (&regexp, pattern, length, preg->translate);
+
+ if (err != REG_NOERROR)
+ {
+ re_free (dfa);
+ preg->buffer = NULL;
+ return err;
+ }
+
+ /* Parse the regular expression, and build a structure tree. */
+ preg->re_nsub = 0;
+ dfa->str_tree = parse (&regexp, preg, syntax, &err);
+ if (dfa->str_tree == NULL)
+ goto re_compile_internal_free_return;
+
+ /* Analyze the tree and collect information which is necessary to
+ create the dfa. */
+ err = analyze (dfa);
+ if (err != REG_NOERROR)
+ goto re_compile_internal_free_return;
+
+ /* Then create the initial state of the dfa. */
+ err = create_initial_state (dfa);
+ if (err != REG_NOERROR)
+ goto re_compile_internal_free_return;
+
+ re_compile_internal_free_return:
+ /* Release work areas. */
+ free_workarea_compile (preg);
+ re_string_destruct (&regexp);
+
+ return err;
+}
+
+/* Initialize DFA. We use the length of the regular expression PAT_LEN
+ as the initial length of some arrays. */
+
+static reg_errcode_t
+init_dfa (dfa, pat_len)
+ re_dfa_t *dfa;
+ int pat_len;
+{
+ int table_size;
+ dfa->nodes_alloc = pat_len + 1;
+ dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc);
+
+ dfa->states_alloc = pat_len + 1;
+
+ /* table_size = 2 ^ ceil(log pat_len) */
+ for (table_size = 1; table_size > 0; table_size <<= 1)
+ if (table_size > pat_len)
+ break;
+
+ dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size);
+ dfa->state_hash_mask = table_size - 1;
+
+ dfa->subexps_alloc = 1;
+ dfa->subexps = re_malloc (re_subexp_t, dfa->subexps_alloc);
+ dfa->word_char = NULL;
+
+ if (dfa->nodes == NULL || dfa->state_table == NULL || dfa->subexps == NULL)
+ {
+ /* We don't bother to free anything which was allocated. Very
+ soon the process will go down anyway. */
+ dfa->subexps = NULL;
+ dfa->state_table = NULL;
+ dfa->nodes = NULL;
+ return REG_ESPACE;
+ }
+ return REG_NOERROR;
+}
+
+/* Initialize WORD_CHAR table, which indicate which character is
+ "word". In this case "word" means that it is the word construction
+ character used by some operators like "\<", "\>", etc. */
+
+static void
+init_word_char (dfa)
+ re_dfa_t *dfa;
+{
+ int i, j, ch;
+ dfa->word_char = (re_bitset_ptr_t) calloc (sizeof (bitset), 1);
+ for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+ for (j = 0; j < UINT_BITS; ++j, ++ch)
+ if (isalnum (ch) || ch == '_')
+ dfa->word_char[i] |= 1 << j;
+}
+
+/* Free the work area which are only used while compiling. */
+
+static void
+free_workarea_compile (preg)
+ regex_t *preg;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ free_bin_tree (dfa->str_tree);
+ dfa->str_tree = NULL;
+}
+
+/* Create initial states for all contexts. */
+
+static reg_errcode_t
+create_initial_state (dfa)
+ re_dfa_t *dfa;
+{
+ int first, i;
+ reg_errcode_t err;
+ re_node_set init_nodes;
+
+ /* Initial states have the epsilon closure of the node which is
+ the first node of the regular expression. */
+ first = dfa->str_tree->first;
+ dfa->init_node = first;
+ err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first);
+ if (err != REG_NOERROR)
+ return err;
+
+ /* The back-references which are in initial states can epsilon transit,
+ since in this case all of the subexpressions can be null.
+ Then we add epsilon closures of the nodes which are the next nodes of
+ the back-references. */
+ if (dfa->nbackref > 0)
+ for (i = 0; i < init_nodes.nelem; ++i)
+ {
+ int node_idx = init_nodes.elems[i];
+ re_token_type_t type = dfa->nodes[node_idx].type;
+ if (type == OP_CONTEXT_NODE
+ && (dfa->nodes[dfa->nodes[node_idx].opr.ctx_info->entity].type
+ == OP_BACK_REF))
+ {
+ int prev_nelem = init_nodes.nelem;
+ re_node_set_merge (&init_nodes,
+ dfa->nodes[node_idx].opr.ctx_info->bkref_eclosure);
+ if (prev_nelem < init_nodes.nelem)
+ i = 0;
+ }
+ else if (type == OP_BACK_REF)
+ {
+ int next_idx = dfa->nexts[node_idx];
+ if (!re_node_set_contains (&init_nodes, next_idx))
+ {
+ re_node_set_merge (&init_nodes, dfa->eclosures + next_idx);
+ i = 0;
+ }
+ }
+ }
+
+ /* It must be the first time to invoke acquire_state. */
+ dfa->init_state = re_acquire_state_context (dfa, &init_nodes, 0);
+ if (dfa->init_state->has_constraint)
+ {
+ dfa->init_state_word = re_acquire_state_context (dfa, &init_nodes,
+ CONTEXT_WORD);
+ dfa->init_state_nl = re_acquire_state_context (dfa, &init_nodes,
+ CONTEXT_NEWLINE);
+ dfa->init_state_begbuf = re_acquire_state_context (dfa, &init_nodes,
+ CONTEXT_NEWLINE
+ | CONTEXT_BEGBUF);
+ }
+ else
+ dfa->init_state_word = dfa->init_state_nl
+ = dfa->init_state_begbuf = dfa->init_state;
+
+ if (dfa->init_state == NULL || dfa->init_state_word == NULL
+ || dfa->init_state_nl == NULL || dfa->init_state_begbuf == NULL )
+ return REG_ESPACE;
+ re_node_set_free (&init_nodes);
+ return REG_NOERROR;
+}
+
+/* Analyze the structure tree, and calculate "first", "next", "edest",
+ "eclosure", and "inveclosure". */
+
+static reg_errcode_t
+analyze (dfa)
+ re_dfa_t *dfa;
+{
+ int i;
+ reg_errcode_t ret;
+
+ /* Allocate arrays. */
+ dfa->firsts = re_malloc (int, dfa->nodes_alloc);
+ dfa->nexts = re_malloc (int, dfa->nodes_alloc);
+ dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc);
+ dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc);
+ dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_alloc);
+ if (dfa->firsts == NULL || dfa->nexts == NULL || dfa->edests == NULL
+ || dfa->eclosures == NULL || dfa->inveclosures == NULL)
+ return REG_ESPACE;
+ /* Initialize them. */
+ for (i = 0; i < dfa->nodes_len; ++i)
+ {
+ dfa->firsts[i] = -1;
+ dfa->nexts[i] = -1;
+ re_node_set_init_empty (dfa->edests + i);
+ re_node_set_init_empty (dfa->eclosures + i);
+ re_node_set_init_empty (dfa->inveclosures + i);
+ }
+
+ ret = analyze_tree (dfa, dfa->str_tree);
+ if (ret == REG_NOERROR)
+ {
+ ret = calc_eclosure (dfa);
+ if (ret == REG_NOERROR)
+ calc_inveclosure (dfa);
+ }
+ return ret;
+}
+
+/* Helper functions for analyze.
+ This function calculate "first", "next", and "edest" for the subtree
+ whose root is NODE. */
+
+static reg_errcode_t
+analyze_tree (dfa, node)
+ re_dfa_t *dfa;
+ bin_tree_t *node;
+{
+ reg_errcode_t ret;
+ if (node->first == -1)
+ calc_first (dfa, node);
+ if (node->next == -1)
+ calc_next (dfa, node);
+ if (node->eclosure.nelem == 0)
+ calc_epsdest (dfa, node);
+ /* Calculate "first" etc. for the left child. */
+ if (node->left != NULL)
+ {
+ ret = analyze_tree (dfa, node->left);
+ if (ret != REG_NOERROR)
+ return ret;
+ }
+ /* Calculate "first" etc. for the right child. */
+ if (node->right != NULL)
+ {
+ ret = analyze_tree (dfa, node->right);
+ if (ret != REG_NOERROR)
+ return ret;
+ }
+ return REG_NOERROR;
+}
+
+/* Calculate "first" for the node NODE. */
+static void
+calc_first (dfa, node)
+ re_dfa_t *dfa;
+ bin_tree_t *node;
+{
+ int idx, type;
+ idx = node->node_idx;
+ type = (node->type == 0) ? dfa->nodes[idx].type : node->type;
+
+ switch (type)
+ {
+#ifdef DEBUG
+ case OP_OPEN_SUBEXP:
+ case OP_CLOSE_SUBEXP:
+ case OP_OPEN_BRACKET:
+ case OP_CLOSE_BRACKET:
+ case OP_OPEN_DUP_NUM:
+ case OP_CLOSE_DUP_NUM:
+ case OP_NON_MATCH_LIST:
+ case OP_OPEN_COLL_ELEM:
+ case OP_CLOSE_COLL_ELEM:
+ case OP_OPEN_EQUIV_CLASS:
+ case OP_CLOSE_EQUIV_CLASS:
+ case OP_OPEN_CHAR_CLASS:
+ case OP_CLOSE_CHAR_CLASS:
+ /* These must not be appeared here. */
+ assert (0);
+#endif
+ case END_OF_RE:
+ case CHARACTER:
+ case OP_PERIOD:
+ case OP_DUP_ASTERISK:
+ case OP_DUP_QUESTION:
+ case COMPLEX_BRACKET:
+ case SIMPLE_BRACKET:
+ case OP_BACK_REF:
+ case ANCHOR:
+ node->first = idx;
+ break;
+ case OP_DUP_PLUS:
+#ifdef DEBUG
+ assert (node->left != NULL);
+#endif
+ if (node->left->first == -1)
+ calc_first (dfa, node->left);
+ node->first = node->left->first;
+ break;
+ case OP_ALT:
+ node->first = idx;
+ break;
+ case SUBEXP:
+ if (node->left == NULL)
+ {
+ if (node->next == -1)
+ calc_next (dfa, node);
+ node->first = node->next;
+ break;
+ }
+ /* else fall through */
+ default:
+#ifdef DEBUG
+ assert (node->left != NULL);
+#endif
+ if (node->left->first == -1)
+ calc_first (dfa, node->left);
+ node->first = node->left->first;
+ break;
+ }
+ if (node->type == 0)
+ dfa->firsts[idx] = node->first;
+}
+
+/* Calculate "next" for the node NODE. */
+
+static void
+calc_next (dfa, node)
+ re_dfa_t *dfa;
+ bin_tree_t *node;
+{
+ int idx, type;
+ bin_tree_t *parent = node->parent;
+ if (parent == NULL)
+ {
+ node->next = -1;
+ idx = node->node_idx;
+ if (node->type == 0)
+ dfa->nexts[idx] = node->next;
+ return;
+ }
+
+ idx = parent->node_idx;
+ type = (parent->type == 0) ? dfa->nodes[idx].type : parent->type;
+
+ switch (type)
+ {
+ case OP_DUP_ASTERISK:
+ case OP_DUP_PLUS:
+ node->next = idx;
+ break;
+ case CONCAT:
+ if (parent->left == node)
+ {
+ if (parent->right->first == -1)
+ calc_first (dfa, parent->right);
+ node->next = parent->right->first;
+ break;
+ }
+ /* else fall through */
+ default:
+ if (parent->next == -1)
+ calc_next (dfa, parent);
+ node->next = parent->next;
+ break;
+ }
+ idx = node->node_idx;
+ if (node->type == 0)
+ dfa->nexts[idx] = node->next;
+}
+
+/* Calculate "edest" for the node NODE. */
+
+static void
+calc_epsdest (dfa, node)
+ re_dfa_t *dfa;
+ bin_tree_t *node;
+{
+ int idx;
+ idx = node->node_idx;
+ if (node->type == 0)
+ {
+ if (dfa->nodes[idx].type == OP_DUP_ASTERISK
+ || dfa->nodes[idx].type == OP_DUP_PLUS
+ || dfa->nodes[idx].type == OP_DUP_QUESTION)
+ {
+ if (node->left->first == -1)
+ calc_first (dfa, node->left);
+ if (node->next == -1)
+ calc_next (dfa, node);
+ re_node_set_init_2 (dfa->edests + idx, node->left->first,
+ node->next);
+ }
+ else if (dfa->nodes[idx].type == OP_ALT)
+ {
+ int left, right;
+ if (node->left != NULL)
+ {
+ if (node->left->first == -1)
+ calc_first (dfa, node->left);
+ left = node->left->first;
+ }
+ else
+ {
+ if (node->next == -1)
+ calc_next (dfa, node);
+ left = node->next;
+ }
+ if (node->right != NULL)
+ {
+ if (node->right->first == -1)
+ calc_first (dfa, node->right);
+ right = node->right->first;
+ }
+ else
+ {
+ if (node->next == -1)
+ calc_next (dfa, node);
+ right = node->next;
+ }
+ re_node_set_init_2 (dfa->edests + idx, left, right);
+ }
+ else if (dfa->nodes[idx].type == ANCHOR)
+ re_node_set_init_1 (dfa->edests + idx, node->next);
+ }
+}
+
+static int
+duplicate_node (dfa, org_idx, constraint)
+ re_dfa_t *dfa;
+ int org_idx;
+ unsigned int constraint;
+{
+ re_token_t dup;
+ int dup_idx;
+
+ dup.type = OP_CONTEXT_NODE;
+ if (dfa->nodes[org_idx].type == OP_CONTEXT_NODE)
+ {
+ if (dfa->nodes[org_idx].constraint == constraint)
+ return org_idx;
+ dup.constraint = constraint |
+ dfa->nodes[org_idx].constraint;
+ }
+ else
+ dup.constraint = constraint;
+
+ /* In case that `entity' points OP_CONTEXT_NODE,
+ we correct `entity' to real entity in calc_inveclosures(). */
+ dup.opr.ctx_info = malloc (sizeof (*dup.opr.ctx_info));
+ dup.opr.ctx_info->entity = org_idx;
+ dup.opr.ctx_info->bkref_eclosure = NULL;
+ dup_idx = re_dfa_add_node (dfa, dup, 1);
+ dfa->nodes[dup_idx].duplicated = 1;
+
+ dfa->firsts[dup_idx] = dfa->firsts[org_idx];
+ dfa->nexts[dup_idx] = dfa->nexts[org_idx];
+ re_node_set_init_copy (dfa->edests + dup_idx, dfa->edests + org_idx);
+ /* Since we don't duplicate epsilon nodes, epsilon closure have
+ only itself. */
+ re_node_set_init_1 (dfa->eclosures + dup_idx, dup_idx);
+ re_node_set_init_1 (dfa->inveclosures + dup_idx, dup_idx);
+ /* Then we must update inveclosure for this node.
+ We process them at last part of calc_eclosure(),
+ since we don't complete to calculate them here. */
+
+ return dup_idx;
+}
+
+static void
+calc_inveclosure (dfa)
+ re_dfa_t *dfa;
+{
+ int src, idx, dest, entity;
+ for (src = 0; src < dfa->nodes_len; ++src)
+ {
+ for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx)
+ {
+ dest = dfa->eclosures[src].elems[idx];
+ re_node_set_insert (dfa->inveclosures + dest, src);
+ }
+
+ entity = src;
+ while (dfa->nodes[entity].type == OP_CONTEXT_NODE)
+ {
+ entity = dfa->nodes[entity].opr.ctx_info->entity;
+ re_node_set_merge (dfa->inveclosures + src,
+ dfa->inveclosures + entity);
+ dfa->nodes[src].opr.ctx_info->entity = entity;
+ }
+ }
+}
+
+/* Calculate "eclosure" for all the node in DFA. */
+
+static reg_errcode_t
+calc_eclosure (dfa)
+ re_dfa_t *dfa;
+{
+ int idx, node_idx, max, incomplete = 0;
+#ifdef DEBUG
+ assert (dfa->nodes_len > 0);
+#endif
+ /* For each nodes, calculate epsilon closure. */
+ for (node_idx = 0, max = dfa->nodes_len; ; ++node_idx)
+ {
+ re_node_set eclosure_elem;
+ if (node_idx == max)
+ {
+ if (!incomplete)
+ break;
+ incomplete = 0;
+ node_idx = 0;
+ }
+
+#ifdef DEBUG
+ assert (dfa->nodes[node_idx].type != OP_CONTEXT_NODE);
+ assert (dfa->eclosures[node_idx].nelem != -1);
+#endif
+ /* If we have already calculated, skip it. */
+ if (dfa->eclosures[node_idx].nelem != 0)
+ continue;
+ /* Calculate epsilon closure of `node_idx'. */
+ eclosure_elem = calc_eclosure_iter (dfa, node_idx, 1);
+
+ if (dfa->eclosures[node_idx].nelem == 0)
+ {
+ incomplete = 1;
+ re_node_set_free (&eclosure_elem);
+ }
+ }
+
+ /* for duplicated nodes. */
+ for (idx = max; idx < dfa->nodes_len; ++idx)
+ {
+ int entity, i, constraint;
+ re_node_set *bkref_eclosure;
+ entity = dfa->nodes[idx].opr.ctx_info->entity;
+ re_node_set_merge (dfa->inveclosures + idx, dfa->inveclosures + entity);
+ if (dfa->nodes[entity].type != OP_BACK_REF)
+ continue;
+
+ /* If the node is backreference, duplicate the epsilon closure of
+ the next node. Since it may epsilon transit. */
+ /* Note: duplicate_node() may realloc dfa->eclosures, etc. */
+ bkref_eclosure = re_malloc (re_node_set, 1);
+ if (bkref_eclosure == NULL)
+ return REG_ESPACE;
+ re_node_set_init_empty (bkref_eclosure);
+ constraint = dfa->nodes[idx].constraint;
+ for (i = 0; i < dfa->eclosures[dfa->nexts[idx]].nelem; ++i)
+ {
+ int dest_node_idx = dfa->eclosures[dfa->nexts[idx]].elems[i];
+ if (!IS_EPSILON_NODE (dfa->nodes[dest_node_idx].type))
+ dest_node_idx = duplicate_node (dfa, dest_node_idx, constraint);
+ re_node_set_insert (bkref_eclosure, dest_node_idx);
+ }
+ dfa->nodes[idx].opr.ctx_info->bkref_eclosure = bkref_eclosure;
+ }
+
+ return REG_NOERROR;
+}
+
+/* Calculate epsilon closure of NODE. */
+
+static re_node_set
+calc_eclosure_iter (dfa, node, root)
+ re_dfa_t *dfa;
+ int node, root;
+{
+ unsigned int constraint;
+ int i, max, incomplete = 0;
+ re_node_set eclosure;
+ re_node_set_alloc (&eclosure, 1);
+
+ /* This indicates that we are calculating this node now.
+ We reference this value to avoid infinite loop. */
+ dfa->eclosures[node].nelem = -1;
+
+ constraint = ((dfa->nodes[node].type == ANCHOR)
+ ? dfa->nodes[node].opr.ctx_type : 0);
+
+ /* Expand each epsilon destination nodes. */
+ if (dfa->edests[node].nelem != 0)
+ for (i = 0; i < dfa->edests[node].nelem; ++i)
+ {
+ re_node_set eclosure_elem;
+ int edest = dfa->edests[node].elems[i];
+ /* If calculating the epsilon closure of `edest' is in progress,
+ return intermediate result. */
+ if (dfa->eclosures[edest].nelem == -1)
+ {
+ incomplete = 1;
+ continue;
+ }
+ /* If we haven't calculated the epsilon closure of `edest' yet,
+ calculate now. Otherwise use calculated epsilon closure. */
+ if (dfa->eclosures[edest].nelem == 0)
+ eclosure_elem = calc_eclosure_iter (dfa, edest, 0);
+ else
+ eclosure_elem = dfa->eclosures[edest];
+ /* Merge the epsilon closure of `edest'. */
+ re_node_set_merge (&eclosure, &eclosure_elem);
+ /* If the epsilon closure of `edest' is incomplete,
+ the epsilon closure of this node is also incomplete. */
+ if (dfa->eclosures[edest].nelem == 0)
+ {
+ incomplete = 1;
+ re_node_set_free (&eclosure_elem);
+ }
+ }
+
+ /* If the current node has constraints, duplicate all non-epsilon nodes.
+ Since they must inherit the constraints. */
+ if (constraint)
+ for (i = 0, max = eclosure.nelem; i < max; ++i)
+ {
+ int dest = eclosure.elems[i];
+ if (!IS_EPSILON_NODE (dfa->nodes[dest].type))
+ {
+ int dup_dest = duplicate_node (dfa, dest, constraint);
+ if (dest != dup_dest)
+ {
+ re_node_set_remove_at (&eclosure, i--);
+ re_node_set_insert (&eclosure, dup_dest);
+ --max;
+ }
+ }
+ }
+
+ /* Epsilon closures include itself. */
+ re_node_set_insert (&eclosure, node);
+ if (incomplete && !root)
+ dfa->eclosures[node].nelem = 0;
+ else
+ dfa->eclosures[node] = eclosure;
+ return eclosure;
+}
+
+/* Functions for token which are used in the parser. */
+
+/* Fetch a token from INPUT.
+ We must not use this function inside bracket expressions. */
+
+static re_token_t
+fetch_token (input, syntax)
+ re_string_t *input;
+ reg_syntax_t syntax;
+{
+ re_token_t token;
+ int consumed_byte;
+ consumed_byte = peek_token (&token, input, syntax);
+ re_string_skip_bytes (input, consumed_byte);
+ return token;
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+ We must not use this function inside bracket expressions. */
+
+static int
+peek_token (token, input, syntax)
+ re_token_t *token;
+ re_string_t *input;
+ reg_syntax_t syntax;
+{
+ unsigned char c;
+
+ if (re_string_eoi (input))
+ {
+ token->type = END_OF_RE;
+ return 0;
+ }
+
+ c = re_string_peek_byte (input, 0);
+ token->opr.c = c;
+
+#ifdef RE_ENABLE_I18N
+ token->mb_partial = 0;
+ if (MB_CUR_MAX > 1 &&
+ !re_string_first_byte (input, re_string_cur_idx (input)))
+ {
+ token->type = CHARACTER;
+ token->mb_partial = 1;
+ return 1;
+ }
+#endif
+ if (c == '\\')
+ {
+ unsigned char c2;
+ if (re_string_cur_idx (input) + 1 >= re_string_length (input))
+ {
+ token->type = BACK_SLASH;
+ return 1;
+ }
+
+ c2 = re_string_peek_byte_case (input, 1);
+ token->opr.c = c2;
+ token->type = CHARACTER;
+ switch (c2)
+ {
+ case '|':
+ if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR))
+ token->type = OP_ALT;
+ break;
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ if (!(syntax & RE_NO_BK_REFS))
+ {
+ token->type = OP_BACK_REF;
+ token->opr.idx = c2 - '0';
+ }
+ break;
+ case '<':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.idx = WORD_FIRST;
+ }
+ break;
+ case '>':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.idx = WORD_LAST;
+ }
+ break;
+ case 'b':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.idx = WORD_DELIM;
+ }
+ break;
+ case 'B':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.idx = INSIDE_WORD;
+ }
+ break;
+ case 'w':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_WORD;
+ break;
+ case 'W':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_NOTWORD;
+ break;
+ case '`':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.idx = BUF_FIRST;
+ }
+ break;
+ case '\'':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.idx = BUF_LAST;
+ }
+ break;
+ case '(':
+ if (!(syntax & RE_NO_BK_PARENS))
+ token->type = OP_OPEN_SUBEXP;
+ break;
+ case ')':
+ if (!(syntax & RE_NO_BK_PARENS))
+ token->type = OP_CLOSE_SUBEXP;
+ break;
+ case '+':
+ if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_PLUS;
+ break;
+ case '?':
+ if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_QUESTION;
+ break;
+ case '{':
+ if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+ token->type = OP_OPEN_DUP_NUM;
+ break;
+ case '}':
+ if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+ token->type = OP_CLOSE_DUP_NUM;
+ break;
+ default:
+ break;
+ }
+ return 2;
+ }
+
+ token->type = CHARACTER;
+ switch (c)
+ {
+ case '\n':
+ if (syntax & RE_NEWLINE_ALT)
+ token->type = OP_ALT;
+ break;
+ case '|':
+ if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR))
+ token->type = OP_ALT;
+ break;
+ case '*':
+ token->type = OP_DUP_ASTERISK;
+ break;
+ case '+':
+ if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_PLUS;
+ break;
+ case '?':
+ if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_QUESTION;
+ break;
+ case '{':
+ if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ token->type = OP_OPEN_DUP_NUM;
+ break;
+ case '}':
+ if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ token->type = OP_CLOSE_DUP_NUM;
+ break;
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ token->type = OP_OPEN_SUBEXP;
+ break;
+ case ')':
+ if (syntax & RE_NO_BK_PARENS)
+ token->type = OP_CLOSE_SUBEXP;
+ break;
+ case '[':
+ token->type = OP_OPEN_BRACKET;
+ break;
+ case '.':
+ token->type = OP_PERIOD;
+ break;
+ case '^':
+ if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
+ re_string_cur_idx (input) != 0)
+ {
+ char prev = re_string_peek_byte (input, -1);
+ if (prev != '|' && prev != '(' &&
+ (!(syntax & RE_NEWLINE_ALT) || prev != '\n'))
+ break;
+ }
+ token->type = ANCHOR;
+ token->opr.idx = LINE_FIRST;
+ break;
+ case '$':
+ if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
+ re_string_cur_idx (input) + 1 != re_string_length (input))
+ {
+ re_token_t next;
+ re_string_skip_bytes (input, 1);
+ peek_token (&next, input, syntax);
+ re_string_skip_bytes (input, -1);
+ if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP)
+ break;
+ }
+ token->type = ANCHOR;
+ token->opr.idx = LINE_LAST;
+ break;
+ default:
+ break;
+ }
+ return 1;
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+ We must not use this function out of bracket expressions. */
+
+static int
+peek_token_bracket (token, input, syntax)
+ re_token_t *token;
+ re_string_t *input;
+ reg_syntax_t syntax;
+{
+ unsigned char c;
+ if (re_string_eoi (input))
+ {
+ token->type = END_OF_RE;
+ return 0;
+ }
+ c = re_string_peek_byte (input, 0);
+ token->opr.c = c;
+
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1 &&
+ !re_string_first_byte (input, re_string_cur_idx (input)))
+ {
+ token->type = CHARACTER;
+ return 1;
+ }
+#endif /* RE_ENABLE_I18N */
+
+ if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS))
+ {
+ /* In this case, '\' escape a character. */
+ unsigned char c2;
+ c2 = re_string_peek_byte (input, 1);
+ token->opr.c = c2;
+ token->type = CHARACTER;
+ return 1;
+ }
+ if (c == '[') /* '[' is a special char in a bracket exps. */
+ {
+ unsigned char c2;
+ int token_len;
+ c2 = re_string_peek_byte (input, 1);
+ token->opr.c = c2;
+ token_len = 2;
+ switch (c2)
+ {
+ case '.':
+ token->type = OP_OPEN_COLL_ELEM;
+ break;
+ case '=':
+ token->type = OP_OPEN_EQUIV_CLASS;
+ break;
+ case ':':
+ if (syntax & RE_CHAR_CLASSES)
+ {
+ token->type = OP_OPEN_CHAR_CLASS;
+ break;
+ }
+ /* else fall through. */
+ default:
+ token->type = CHARACTER;
+ token->opr.c = c;
+ token_len = 1;
+ break;
+ }
+ return token_len;
+ }
+ switch (c)
+ {
+ case '-':
+ token->type = OP_CHARSET_RANGE;
+ break;
+ case ']':
+ token->type = OP_CLOSE_BRACKET;
+ break;
+ case '^':
+ token->type = OP_NON_MATCH_LIST;
+ break;
+ default:
+ token->type = CHARACTER;
+ }
+ return 1;
+}
+
+/* Functions for parser. */
+
+/* Entry point of the parser.
+ Parse the regular expression REGEXP and return the structure tree.
+ If an error is occured, ERR is set by error code, and return NULL.
+ This function build the following tree, from regular expression <reg_exp>:
+ CAT
+ / \
+ / \
+ <reg_exp> EOR
+
+ CAT means concatenation.
+ EOR means end of regular expression. */
+
+static bin_tree_t *
+parse (regexp, preg, syntax, err)
+ re_string_t *regexp;
+ regex_t *preg;
+ reg_syntax_t syntax;
+ reg_errcode_t *err;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree, *eor, *root;
+ re_token_t current_token;
+ int new_idx;
+ current_token = fetch_token (regexp, syntax);
+ tree = parse_reg_exp (regexp, preg, &current_token, syntax, 0, err);
+ if (*err != REG_NOERROR && tree == NULL)
+ return NULL;
+ new_idx = re_dfa_add_node (dfa, current_token, 0);
+ eor = create_tree (NULL, NULL, 0, new_idx);
+ if (tree != NULL)
+ root = create_tree (tree, eor, CONCAT, 0);
+ else
+ root = eor;
+ if (new_idx == -1 || eor == NULL || root == NULL)
+ return *err = REG_ESPACE, NULL;
+ return root;
+}
+
+/* This function build the following tree, from regular expression
+ <branch1>|<branch2>:
+ ALT
+ / \
+ / \
+ <branch1> <branch2>
+
+ ALT means alternative, which represents the operator `|'. */
+
+static bin_tree_t *
+parse_reg_exp (regexp, preg, token, syntax, nest, err)
+ re_string_t *regexp;
+ regex_t *preg;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ int nest;
+ reg_errcode_t *err;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree, *branch = NULL;
+ int new_idx;
+ tree = parse_branch (regexp, preg, token, syntax, nest, err);
+ if (*err != REG_NOERROR && tree == NULL)
+ return NULL;
+
+ while (token->type == OP_ALT)
+ {
+ re_token_t alt_token = *token;
+ new_idx = re_dfa_add_node (dfa, alt_token, 0);
+ *token = fetch_token (regexp, syntax);
+ if (token->type != OP_ALT && token->type != END_OF_RE
+ && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+ {
+ branch = parse_branch (regexp, preg, token, syntax, nest, err);
+ if (*err != REG_NOERROR && branch == NULL)
+ {
+ free_bin_tree (tree);
+ return NULL;
+ }
+ }
+ tree = create_tree (tree, branch, 0, new_idx);
+ if (new_idx == -1 || tree == NULL)
+ return *err = REG_ESPACE, NULL;
+ }
+ return tree;
+}
+
+/* This function build the following tree, from regular expression
+ <exp1><exp2>:
+ CAT
+ / \
+ / \
+ <exp1> <exp2>
+
+ CAT means concatenation. */
+
+static bin_tree_t *
+parse_branch (regexp, preg, token, syntax, nest, err)
+ re_string_t *regexp;
+ regex_t *preg;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ int nest;
+ reg_errcode_t *err;
+{
+ bin_tree_t *tree, *exp;
+ tree = parse_expression (regexp, preg, token, syntax, nest, err);
+ if (*err != REG_NOERROR && tree == NULL)
+ return NULL;
+
+ while (token->type != OP_ALT && token->type != END_OF_RE
+ && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+ {
+ exp = parse_expression (regexp, preg, token, syntax, nest, err);
+ if (*err != REG_NOERROR && exp == NULL)
+ {
+ free_bin_tree (tree);
+ return NULL;
+ }
+ if (tree != NULL && exp != NULL)
+ {
+ tree = create_tree (tree, exp, CONCAT, 0);
+ if (tree == NULL)
+ return *err = REG_ESPACE, NULL;
+ }
+ else if (tree == NULL)
+ tree = exp;
+ /* Otherwise exp == NULL, we don't need to create new tree. */
+ }
+ return tree;
+}
+
+/* This function build the following tree, from regular expression a*:
+ *
+ |
+ a
+*/
+
+static bin_tree_t *
+parse_expression (regexp, preg, token, syntax, nest, err)
+ re_string_t *regexp;
+ regex_t *preg;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ int nest;
+ reg_errcode_t *err;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree;
+ int new_idx;
+ switch (token->type)
+ {
+ case CHARACTER:
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ tree = create_tree (NULL, NULL, 0, new_idx);
+ if (new_idx == -1 || tree == NULL)
+ return *err = REG_ESPACE, NULL;
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1)
+ {
+ while (!re_string_eoi (regexp)
+ && !re_string_first_byte (regexp, re_string_cur_idx (regexp)))
+ {
+ bin_tree_t *mbc_remain;
+ *token = fetch_token (regexp, syntax);
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ mbc_remain = create_tree (NULL, NULL, 0, new_idx);
+ tree = create_tree (tree, mbc_remain, CONCAT, 0);
+ if (new_idx == -1 || mbc_remain == NULL || tree == NULL)
+ return *err = REG_ESPACE, NULL;
+ }
+ }
+#endif
+ break;
+ case OP_OPEN_SUBEXP:
+ tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err);
+ if (*err != REG_NOERROR && tree == NULL)
+ return NULL;
+ break;
+ case OP_OPEN_BRACKET:
+ tree = parse_bracket_exp (regexp, dfa, token, syntax, err);
+ if (*err != REG_NOERROR && tree == NULL)
+ return NULL;
+ break;
+ case OP_BACK_REF:
+ if (preg->re_nsub < token->opr.idx
+ || dfa->subexps[token->opr.idx - 1].end == -1)
+ {
+ *err = REG_ESUBREG;
+ return NULL;
+ }
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ tree = create_tree (NULL, NULL, 0, new_idx);
+ if (new_idx == -1 || tree == NULL)
+ return *err = REG_ESPACE, NULL;
+ ++dfa->nbackref;
+ dfa->has_mb_node = 1;
+ break;
+ case OP_DUP_ASTERISK:
+ case OP_DUP_PLUS:
+ case OP_DUP_QUESTION:
+ case OP_OPEN_DUP_NUM:
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ return *err = REG_BADRPT, NULL;
+ else if (syntax & RE_CONTEXT_INDEP_OPS)
+ {
+ *token = fetch_token (regexp, syntax);
+ return parse_expression (regexp, preg, token, syntax, nest, err);
+ }
+ /* else fall through */
+ case OP_CLOSE_SUBEXP:
+ if ((token->type == OP_CLOSE_SUBEXP) &&
+ !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD))
+ return *err = REG_ERPAREN, NULL;
+ /* else fall through */
+ case OP_CLOSE_DUP_NUM:
+ /* We treat it as a normal character. */
+
+ /* Then we can these characters as normal characters. */
+ token->type = CHARACTER;
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ tree = create_tree (NULL, NULL, 0, new_idx);
+ if (new_idx == -1 || tree == NULL)
+ return *err = REG_ESPACE, NULL;
+ break;
+ case ANCHOR:
+ if (dfa->word_char == NULL)
+ init_word_char (dfa);
+ if (token->opr.ctx_type == WORD_DELIM)
+ {
+ bin_tree_t *tree_first, *tree_last;
+ int idx_first, idx_last;
+ token->opr.ctx_type = WORD_FIRST;
+ idx_first = re_dfa_add_node (dfa, *token, 0);
+ tree_first = create_tree (NULL, NULL, 0, idx_first);
+ token->opr.ctx_type = WORD_LAST;
+ idx_last = re_dfa_add_node (dfa, *token, 0);
+ tree_last = create_tree (NULL, NULL, 0, idx_last);
+ token->type = OP_ALT;
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ tree = create_tree (tree_first, tree_last, 0, new_idx);
+ if (idx_first == -1 || idx_last == -1 || new_idx == -1
+ || tree_first == NULL || tree_last == NULL || tree == NULL)
+ return *err = REG_ESPACE, NULL;
+ }
+ else
+ {
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ tree = create_tree (NULL, NULL, 0, new_idx);
+ if (new_idx == -1 || tree == NULL)
+ return *err = REG_ESPACE, NULL;
+ }
+ /* We must return here, since ANCHORs can't be followed
+ by repetition operators.
+ eg. RE"^*" is invalid or "<ANCHOR(^)><CHAR(*)>",
+ it must not be "<ANCHOR(^)><REPEAT(*)>". */
+ *token = fetch_token (regexp, syntax);
+ return tree;
+ case OP_PERIOD:
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ tree = create_tree (NULL, NULL, 0, new_idx);
+ if (new_idx == -1 || tree == NULL)
+ return *err = REG_ESPACE, NULL;
+ if (MB_CUR_MAX > 1)
+ dfa->has_mb_node = 1;
+ break;
+ case OP_WORD:
+ tree = build_word_op (dfa, 0, err);
+ if (*err != REG_NOERROR && tree == NULL)
+ return NULL;
+ break;
+ case OP_NOTWORD:
+ tree = build_word_op (dfa, 1, err);
+ if (*err != REG_NOERROR && tree == NULL)
+ return NULL;
+ break;
+ case OP_ALT:
+ case END_OF_RE:
+ return NULL;
+ case BACK_SLASH:
+ *err = REG_EESCAPE;
+ return NULL;
+ default:
+ /* Must not happen? */
+#ifdef DEBUG
+ assert (0);
+#endif
+ return NULL;
+ }
+ *token = fetch_token (regexp, syntax);
+
+ while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS
+ || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM)
+ {
+ tree = parse_dup_op (tree, regexp, dfa, token, syntax, err);
+ if (*err != REG_NOERROR && tree == NULL)
+ return *err = REG_ESPACE, NULL;
+ }
+
+ return tree;
+}
+
+/* This function build the following tree, from regular expression
+ (<reg_exp>):
+ SUBEXP
+ |
+ <reg_exp>
+*/
+
+static bin_tree_t *
+parse_sub_exp (regexp, preg, token, syntax, nest, err)
+ re_string_t *regexp;
+ regex_t *preg;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ int nest;
+ reg_errcode_t *err;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree;
+ size_t cur_nsub;
+ cur_nsub = preg->re_nsub++;
+ if (dfa->subexps_alloc < preg->re_nsub)
+ {
+ re_subexp_t *new_array;
+ dfa->subexps_alloc *= 2;
+ new_array = re_realloc (dfa->subexps, re_subexp_t, dfa->subexps_alloc);
+ if (new_array == NULL)
+ {
+ dfa->subexps_alloc /= 2;
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ dfa->subexps = new_array;
+ }
+ dfa->subexps[cur_nsub].start = dfa->nodes_len;
+ dfa->subexps[cur_nsub].end = -1;
+ *token = fetch_token (regexp, syntax);
+
+ /* The subexpression may be a null string. */
+ if (token->type == OP_CLOSE_SUBEXP)
+ {
+ tree = create_tree (NULL, NULL, SUBEXP, 0);
+ if (tree == NULL)
+ return *err = REG_ESPACE, NULL;
+ dfa->subexps[cur_nsub].end = dfa->nodes_len;
+ }
+ else
+ {
+ tree = parse_reg_exp (regexp, preg, token, syntax, nest, err);
+ if (*err != REG_NOERROR && tree == NULL)
+ return NULL;
+ dfa->subexps[cur_nsub].end = dfa->nodes_len;
+ if (token->type != OP_CLOSE_SUBEXP)
+ {
+ free_bin_tree (tree);
+ *err = REG_BADPAT;
+ return NULL;
+ }
+ tree = create_tree (tree, NULL, SUBEXP, 0);
+ }
+ return tree;
+}
+
+/* This function parse repetition operators like "*", "+", "{1,3}" etc. */
+
+static bin_tree_t *
+parse_dup_op (dup_elem, regexp, dfa, token, syntax, err)
+ bin_tree_t *dup_elem;
+ re_string_t *regexp;
+ re_dfa_t *dfa;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ reg_errcode_t *err;
+{
+ re_token_t dup_token;
+ bin_tree_t *tree = dup_elem, *work_tree;
+ int new_idx, start_idx = re_string_cur_idx (regexp);
+ re_token_t start_token = *token;
+ if (token->type == OP_OPEN_DUP_NUM)
+ {
+ int i, end, start = fetch_number (regexp, token, syntax);
+ bin_tree_t *elem;
+ if (start == -1)
+ start = 0; /* We treat "{,m}" as "{0,m}". */
+ if (start != -2 && token->type == OP_CLOSE_DUP_NUM)
+ {
+ if (start == 0)
+ {
+ /* We treat "<re>{0}" as null string. */
+ *token = fetch_token (regexp, syntax);
+ free_bin_tree (dup_elem);
+ return NULL;
+ }
+ end = start; /* We treat "{n}" as "{n,n}". */
+ }
+ else if (start == -2 || token->type != CHARACTER || token->opr.c != ',')
+ /* Invalid sequence. */
+ goto parse_dup_op_invalid_interval;
+ else
+ {
+ end = fetch_number (regexp, token, syntax);
+ if (end == -2 || token->type != OP_CLOSE_DUP_NUM)
+ /* Invalid sequence. */
+ goto parse_dup_op_invalid_interval;
+ }
+ /* Extract "<re>{n,m}" to "<re><re>...<re><re>{0,<m-n>}". */
+ elem = tree;
+ for (i = 0; i < start; ++i)
+ if (i != 0)
+ {
+ work_tree = duplicate_tree (elem, dfa);
+ tree = create_tree (tree, work_tree, CONCAT, 0);
+ if (work_tree == NULL || tree == NULL)
+ goto parse_dup_op_espace;
+ }
+
+ if (end == -1)
+ {
+ /* We treat "<re>{0,}" as "<re>*". */
+ dup_token.type = OP_DUP_ASTERISK;
+ if (start > 0)
+ {
+ elem = duplicate_tree (elem, dfa);
+ new_idx = re_dfa_add_node (dfa, dup_token, 0);
+ work_tree = create_tree (elem, NULL, 0, new_idx);
+ tree = create_tree (tree, work_tree, CONCAT, 0);
+ if (elem == NULL || new_idx == -1 || work_tree == NULL
+ || tree == NULL)
+ goto parse_dup_op_espace;
+ }
+ else
+ {
+ new_idx = re_dfa_add_node (dfa, dup_token, 0);
+ tree = create_tree (elem, NULL, 0, new_idx);
+ if (new_idx == -1 || tree == NULL)
+ goto parse_dup_op_espace;
+ }
+ }
+ else if (end - start > 0)
+ {
+ /* Then extract "<re>{0,m}" to "<re>?<re>?...<re>?". */
+ dup_token.type = OP_DUP_QUESTION;
+ if (start > 0)
+ {
+ elem = duplicate_tree (elem, dfa);
+ new_idx = re_dfa_add_node (dfa, dup_token, 0);
+ elem = create_tree (elem, NULL, 0, new_idx);
+ tree = create_tree (tree, elem, CONCAT, 0);
+ if (elem == NULL || new_idx == -1 || tree == NULL)
+ goto parse_dup_op_espace;
+ }
+ else
+ {
+ new_idx = re_dfa_add_node (dfa, dup_token, 0);
+ tree = elem = create_tree (elem, NULL, 0, new_idx);
+ if (new_idx == -1 || tree == NULL)
+ goto parse_dup_op_espace;
+ }
+ for (i = 1; i < end - start; ++i)
+ {
+ work_tree = duplicate_tree (elem, dfa);
+ tree = create_tree (tree, work_tree, CONCAT, 0);
+ if (work_tree == NULL || tree == NULL)
+ return *err = REG_ESPACE, NULL;
+ }
+ }
+ }
+ else
+ {
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ tree = create_tree (tree, NULL, 0, new_idx);
+ if (new_idx == -1 || tree == NULL)
+ return *err = REG_ESPACE, NULL;
+ }
+ *token = fetch_token (regexp, syntax);
+ return tree;
+
+ parse_dup_op_espace:
+ free_bin_tree (tree);
+ *err = REG_ESPACE;
+ return NULL;
+
+ parse_dup_op_invalid_interval:
+ if (!(syntax & RE_INVALID_INTERVAL_ORD))
+ {
+ *err = REG_EBRACE;
+ return NULL;
+ }
+ re_string_set_index (regexp, start_idx);
+ *token = start_token;
+ token->type = CHARACTER;
+ return dup_elem;
+}
+
+/* Size of the names for collating symbol/equivalence_class/character_class.
+ I'm not sure, but maybe enough. */
+#define BRACKET_NAME_BUF_SIZE 32
+
+static inline void *
+extend_array_for_cset (array, num, alloc, type_size)
+ void *array;
+ int num, *alloc, type_size;
+{
+ void *new_array = array;
+ if (*alloc == num)
+ {
+ if (*alloc == 0)
+ {
+ new_array = malloc (type_size);
+ *alloc = 1;
+ }
+ else
+ {
+ new_array = realloc (array, type_size * num * 2);
+ *alloc = 2 * num;
+ }
+ }
+ return new_array;
+}
+
+/* This function parse bracket expression like "[abc]", "[a-c]",
+ "[[.a-a.]]" etc. */
+
+static bin_tree_t *
+parse_bracket_exp (regexp, dfa, token, syntax, err)
+ re_string_t *regexp;
+ re_dfa_t *dfa;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ reg_errcode_t *err;
+{
+#ifdef _LIBC
+ const unsigned char *collseqmb, *collseqwc;
+ uint32_t nrules;
+ int32_t table_size;
+ const int32_t *symb_table;
+ const unsigned char *extra;
+
+ /* Local function for parse_bracket_exp.
+ Seek the collating symbol entry correspondings to NAME.
+ Return the index of the symbol in the SYMB_TABLE. */
+
+ static inline int32_t
+ seek_collating_symbol_entry (name, name_len)
+ unsigned char *name;
+ size_t name_len;
+ {
+ int32_t hash = elem_hash (name, name_len);
+ int32_t elem = hash % table_size;
+ int32_t second = hash % (table_size - 2);
+ while (symb_table[2 * elem] != 0)
+ {
+ /* First compare the hashing value. */
+ if (symb_table[2 * elem] == hash
+ /* Compare the length of the name. */
+ && name_len == extra[symb_table[2 * elem + 1]]
+ /* Compare the name. */
+ && memcmp (name, &extra[symb_table[2 * elem + 1] + 1],
+ name_len) == 0)
+ {
+ /* Yep, this is the entry. */
+ break;
+ }
+
+ /* Next entry. */
+ elem += second;
+ }
+ return elem;
+ }
+
+ /* Local function for parse_bracket_exp.
+ Look up the collation sequence value of BR_ELEM.
+ Return the value if succeeded, UINT_MAX otherwise. */
+
+ static inline unsigned int
+ lookup_collation_sequence_value (br_elem)
+ bracket_elem_t *br_elem;
+ {
+ if (br_elem->type == SB_CHAR)
+ {
+ /*
+ if (MB_CUR_MAX == 1)
+ */
+ if (nrules == 0)
+ return collseqmb[br_elem->opr.ch];
+ else
+ {
+ wint_t wc = __btowc (br_elem->opr.ch);
+ return collseq_table_lookup (collseqwc, wc);
+ }
+ }
+ else if (br_elem->type == MB_CHAR)
+ {
+ return collseq_table_lookup (collseqwc, br_elem->opr.wch);
+ }
+ else if (br_elem->type == COLL_SYM)
+ {
+ if (nrules != 0)
+ {
+ int32_t elem, idx;
+ elem = seek_collating_symbol_entry (br_elem->opr.name,
+ strlen (br_elem->opr.name));
+ if (symb_table[2 * elem] != 0)
+ {
+ /* We found the entry. */
+ idx = symb_table[2 * elem + 1];
+ /* Skip the name of collating element name. */
+ idx += 1 + extra[idx];
+ /* Skip the byte sequence of the collating element. */
+ idx += 1 + extra[idx];
+ /* Adjust for the alignment. */
+ idx = (idx + 3) & ~3;
+ /* Skip the multibyte collation sequence value. */
+ idx += sizeof (unsigned int);
+ /* Skip the wide char sequence of the collating element. */
+ idx += sizeof (unsigned int) *
+ (1 + *(unsigned int *) (extra + idx));
+ /* Return the collation sequence value. */
+ return *(unsigned int *) (extra + idx);
+ }
+ else if (symb_table[2 * elem] == 0 &&
+ strlen (br_elem->opr.name) == 1)
+ {
+ /* No valid character. Match it as a single byte
+ character. */
+ return collseqmb[br_elem->opr.name[0]];
+ }
+ }
+ else if (strlen (br_elem->opr.name) == 1)
+ return collseqmb[br_elem->opr.name[0]];
+ }
+ return UINT_MAX;
+ }
+
+ /* Local function for parse_bracket_exp.
+ Build the range expression which starts from START_ELEM, and ends
+ at END_ELEM. The result are written to MBCSET and SBCSET.
+ RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+ mbcset->range_ends, is a pointer argument sinse we may
+ update it. */
+
+ static inline reg_errcode_t
+ build_range_exp (mbcset, sbcset, range_alloc, start_elem, end_elem)
+ re_charset_t *mbcset;
+ re_bitset_ptr_t sbcset;
+ int *range_alloc;
+ bracket_elem_t *start_elem, *end_elem;
+ {
+ unsigned int ch;
+ uint32_t start_collseq;
+ uint32_t end_collseq;
+
+ /* Check the space of the arrays. */
+ if (*range_alloc == mbcset->nranges)
+ {
+ /* There are not enough space, need realloc. */
+ uint32_t *new_array_start;
+ uint32_t *new_array_end;
+ int new_nranges;
+
+ /* XXX If mbcset->range_starts and mbcset->range_ends are NULL
+ if *range_alloc == 0 then we do not need the if. */
+ if (*range_alloc == 0)
+ {
+ new_nranges = 1;
+ new_array_start = re_malloc (uint32_t, 1);
+ new_array_end = re_malloc (uint32_t, 1);
+ }
+ else
+ {
+ new_nranges = 2 * mbcset->nranges;
+ new_array_start = re_realloc (mbcset->range_starts, uint32_t,
+ new_nranges);
+ new_array_end = re_realloc (mbcset->range_ends, uint32_t,
+ new_nranges);
+ }
+ if (new_array_start == NULL || new_array_end == NULL)
+ return REG_ESPACE;
+
+ mbcset->range_starts = new_array_start;
+ mbcset->range_ends = new_array_end;
+ *range_alloc = new_nranges;
+ }
+
+ if (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+ || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS)
+ return REG_ERANGE;
+
+ start_collseq = lookup_collation_sequence_value (start_elem);
+ end_collseq = lookup_collation_sequence_value (end_elem);
+ /* Check start/end collation sequence values. */
+ if (start_collseq == UINT_MAX || end_collseq == UINT_MAX)
+ return REG_ECOLLATE;
+ if ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq)
+ return REG_ERANGE;
+
+ /* Got valid collation sequence values, add them as a new entry. */
+ mbcset->range_starts[mbcset->nranges] = start_collseq;
+ mbcset->range_ends[mbcset->nranges++] = end_collseq;
+
+ /* Build the table for single byte characters. */
+ for (ch = 0; ch <= SBC_MAX; ch++)
+ {
+ uint32_t ch_collseq;
+ /*
+ if (MB_CUR_MAX == 1)
+ */
+ if (nrules == 0)
+ ch_collseq = collseqmb[ch];
+ else
+ ch_collseq = collseq_table_lookup (collseqwc, __btowc (ch));
+ if (start_collseq <= ch_collseq && ch_collseq <= end_collseq)
+ bitset_set (sbcset, ch);
+ }
+ return REG_NOERROR;
+ }
+#endif
+
+ /* Local function for parse_bracket_exp.
+ Build the collating element which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+ pointer argument sinse we may update it. */
+
+ static inline reg_errcode_t
+ build_collating_symbol (mbcset, sbcset, coll_sym_alloc, name)
+ re_charset_t *mbcset;
+ re_bitset_ptr_t sbcset;
+ int *coll_sym_alloc;
+ unsigned char *name;
+ {
+#ifdef _LIBC
+ int32_t elem, idx;
+ if (nrules != 0)
+ {
+ elem = seek_collating_symbol_entry (name, strlen (name));
+ if (symb_table[2 * elem] != 0)
+ {
+ /* We found the entry. */
+ idx = symb_table[2 * elem + 1];
+ /* Skip the name of collating element name. */
+ idx += 1 + extra[idx];
+ }
+ else if (symb_table[2 * elem] == 0 && strlen (name) == 1)
+ {
+ /* No valid character, treat it as a normal
+ character. */
+ bitset_set (sbcset, name[0]);
+ return REG_NOERROR;
+ }
+ else
+ return REG_ECOLLATE;
+
+ /* Got valid collation sequence, add it as a new entry. */
+ /* Check the space of the arrays. */
+ mbcset->coll_syms = extend_array_for_cset (mbcset->coll_syms,
+ mbcset->ncoll_syms,
+ coll_sym_alloc,
+ sizeof (int32_t));
+ if (mbcset->coll_syms == NULL)
+ return REG_ESPACE;
+
+ mbcset->coll_syms[mbcset->ncoll_syms++] = idx;
+ return REG_NOERROR;
+ }
+ else
+#endif
+ {
+ if (strlen (name) != 1)
+ return REG_ECOLLATE;
+ else
+ {
+ bitset_set (sbcset, name[0]);
+ return REG_NOERROR;
+ }
+ }
+ }
+ re_token_t br_token;
+ re_bitset_ptr_t sbcset;
+ re_charset_t *mbcset;
+ bin_tree_t *work_tree;
+ int token_len, new_idx;
+ int coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0;
+ int equiv_class_alloc = 0, char_class_alloc = 0;
+#ifdef _LIBC
+ collseqmb = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+ nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules)
+ {
+ /*
+ if (MB_CUR_MAX > 1)
+ */
+ collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+ table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB);
+ symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_SYMB_TABLEMB);
+ extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_SYMB_EXTRAMB);
+ }
+#endif
+ sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS);
+ mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+ if (sbcset == NULL || mbcset == NULL)
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+
+ token_len = peek_token_bracket (token, regexp, syntax);
+ if (token->type == END_OF_RE)
+ {
+ re_free (sbcset);
+ free_charset (mbcset);
+ *err = REG_BADPAT;
+ return NULL;
+ }
+ if (token->type == OP_NON_MATCH_LIST)
+ {
+ int i;
+ mbcset->non_match = 1;
+ if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
+ bitset_set (sbcset, '\0');
+ re_string_skip_bytes (regexp, token_len); /* Skip a token. */
+ token_len = peek_token_bracket (token, regexp, syntax);
+ if (token->type == END_OF_RE)
+ {
+ re_free (sbcset);
+ free_charset (mbcset);
+ *err = REG_BADPAT;
+ return NULL;
+ }
+ if (MB_CUR_MAX > 1)
+ for (i = 0; i < SBC_MAX; ++i)
+ if (__btowc (i) == WEOF)
+ bitset_set (sbcset, i);
+ }
+
+ /* We treat the first ']' as a normal character. */
+ if (token->type == OP_CLOSE_BRACKET)
+ token->type = CHARACTER;
+
+ while (1)
+ {
+ bracket_elem_t start_elem, end_elem;
+ unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE];
+ unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE];
+ reg_errcode_t ret;
+ int token_len2 = 0, is_range_exp = 0;
+ re_token_t token2;
+
+ start_elem.opr.name = start_name_buf;
+ ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa,
+ syntax);
+ if (ret != REG_NOERROR)
+ goto parse_bracket_exp_espace;
+
+ token_len = peek_token_bracket (token, regexp, syntax);
+ if (token->type == END_OF_RE)
+ {
+ re_free (sbcset);
+ free_charset (mbcset);
+ *err = REG_BADPAT;
+ return NULL;
+ }
+ if (token->type == OP_CHARSET_RANGE)
+ {
+ re_string_skip_bytes (regexp, token_len); /* Skip '-'. */
+ token_len2 = peek_token_bracket (&token2, regexp, syntax);
+ if (token->type == END_OF_RE)
+ {
+ re_free (sbcset);
+ free_charset (mbcset);
+ *err = REG_BADPAT;
+ return NULL;
+ }
+ if (token2.type == OP_CLOSE_BRACKET)
+ {
+ /* We treat the last '-' as a normal character. */
+ re_string_skip_bytes (regexp, -token_len);
+ token->type = CHARACTER;
+ }
+ else
+ is_range_exp = 1;
+ }
+
+ if (is_range_exp == 1)
+ {
+ end_elem.opr.name = end_name_buf;
+ ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2,
+ dfa, syntax);
+ if (ret != REG_NOERROR)
+ goto parse_bracket_exp_espace;
+
+ token_len = peek_token_bracket (token, regexp, syntax);
+ if (token->type == END_OF_RE)
+ {
+ re_free (sbcset);
+ free_charset (mbcset);
+ *err = REG_BADPAT;
+ return NULL;
+ }
+ *err = build_range_exp (mbcset, sbcset, &range_alloc, &start_elem,
+ &end_elem);
+ if (*err != REG_NOERROR)
+ {
+ re_free (sbcset);
+ free_charset (mbcset);
+ return NULL;
+ }
+ }
+ else
+ {
+ switch (start_elem.type)
+ {
+ case SB_CHAR:
+ bitset_set (sbcset, start_elem.opr.ch);
+ break;
+ case MB_CHAR:
+ mbcset->mbchars = extend_array_for_cset (mbcset->mbchars,
+ mbcset->nmbchars,
+ &mbchar_alloc,
+ sizeof (wchar_t));
+ if (mbcset->mbchars == NULL)
+ goto parse_bracket_exp_espace;
+ mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch;
+ break;
+ case EQUIV_CLASS:
+ *err = build_equiv_class (mbcset, sbcset, &equiv_class_alloc,
+ start_elem.opr.name);
+ if (*err != REG_NOERROR)
+ {
+ re_free (sbcset);
+ free_charset (mbcset);
+ return NULL;
+ }
+ break;
+ case COLL_SYM:
+ *err = build_collating_symbol (mbcset, sbcset, &coll_sym_alloc,
+ start_elem.opr.name);
+ if (*err != REG_NOERROR)
+ {
+ re_free (sbcset);
+ free_charset (mbcset);
+ return NULL;
+ }
+ break;
+ case CHAR_CLASS:
+ ret = build_charclass (mbcset, sbcset, &char_class_alloc,
+ start_elem.opr.name);
+ if (ret != REG_NOERROR)
+ goto parse_bracket_exp_espace;
+ break;
+ default:
+ assert (0);
+ break;
+ }
+ }
+ if (token->type == OP_CLOSE_BRACKET)
+ break;
+ }
+
+ re_string_skip_bytes (regexp, token_len); /* Skip a token. */
+
+ /* If it is non-matching list. */
+ if (mbcset->non_match)
+ bitset_not (sbcset);
+
+ /* Build a tree for simple bracket. */
+ br_token.type = SIMPLE_BRACKET;
+ br_token.opr.sbcset = sbcset;
+ new_idx = re_dfa_add_node (dfa, br_token, 0);
+ work_tree = create_tree (NULL, NULL, 0, new_idx);
+ if (new_idx == -1 || work_tree == NULL)
+ goto parse_bracket_exp_espace;
+
+ if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes
+ || mbcset->nranges || (mbcset->nchar_classes && MB_CUR_MAX > 1))
+ {
+ re_token_t alt_token;
+ bin_tree_t *mbc_tree;
+ /* Build a tree for complex bracket. */
+ br_token.type = COMPLEX_BRACKET;
+ br_token.opr.mbcset = mbcset;
+ dfa->has_mb_node = 1;
+ new_idx = re_dfa_add_node (dfa, br_token, 0);
+ mbc_tree = create_tree (NULL, NULL, 0, new_idx);
+ if (new_idx == -1 || mbc_tree == NULL)
+ goto parse_bracket_exp_espace;
+ /* Then join them by ALT node. */
+ alt_token.type = OP_ALT;
+ new_idx = re_dfa_add_node (dfa, alt_token, 0);
+ work_tree = create_tree (work_tree, mbc_tree, 0, new_idx);
+ if (new_idx != -1 && mbc_tree != NULL)
+ return work_tree;
+ }
+ else
+ {
+ free_charset (mbcset);
+ return work_tree;
+ }
+
+ parse_bracket_exp_espace:
+ free_charset (mbcset);
+ *err = REG_ESPACE;
+ return NULL;
+}
+
+static reg_errcode_t
+parse_bracket_element (elem, regexp, token, token_len, dfa, syntax)
+ bracket_elem_t *elem;
+ re_string_t *regexp;
+ re_token_t *token;
+ int token_len;
+ re_dfa_t *dfa;
+ reg_syntax_t syntax;
+{
+#ifdef RE_ENABLE_I18N
+ int cur_char_size;
+ cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp));
+ if (cur_char_size > 1)
+ {
+ elem->type = MB_CHAR;
+ elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp));
+ re_string_skip_bytes (regexp, cur_char_size);
+ return REG_NOERROR;
+ }
+#endif /* RE_ENABLE_I18N */
+ re_string_skip_bytes (regexp, token_len); /* Skip a token. */
+ if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS
+ || token->type == OP_OPEN_EQUIV_CLASS)
+ return parse_bracket_symbol (elem, regexp, token);
+ elem->type = SB_CHAR;
+ elem->opr.ch = token->opr.c;
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+parse_bracket_symbol (elem, regexp, token)
+ bracket_elem_t *elem;
+ re_string_t *regexp;
+ re_token_t *token;
+{
+ unsigned char ch, delim = token->opr.c;
+ int i = 0;
+ for (;; i++)
+ {
+#ifdef DEBUG
+ assert (i < BRACKET_NAME_BUF_SIZE);
+#endif
+ if (token->type == OP_OPEN_CHAR_CLASS)
+ ch = re_string_fetch_byte_case (regexp);
+ else
+ ch = re_string_fetch_byte (regexp);
+ if (ch == delim && re_string_peek_byte (regexp, 0) == ']')
+ break;
+ elem->opr.name[i] = ch;
+ }
+ re_string_skip_bytes (regexp, 1);
+ elem->opr.name[i] = '\0';
+ switch (token->type)
+ {
+ case OP_OPEN_COLL_ELEM:
+ elem->type = COLL_SYM;
+ break;
+ case OP_OPEN_EQUIV_CLASS:
+ elem->type = EQUIV_CLASS;
+ break;
+ case OP_OPEN_CHAR_CLASS:
+ elem->type = CHAR_CLASS;
+ break;
+ default:
+ break;
+ }
+ return REG_NOERROR;
+}
+
+ /* Helper function for parse_bracket_exp.
+ Build the equivalence class which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes,
+ is a pointer argument sinse we may update it. */
+
+static reg_errcode_t
+build_equiv_class (mbcset, sbcset, equiv_class_alloc, name)
+ re_charset_t *mbcset;
+ re_bitset_ptr_t sbcset;
+ int *equiv_class_alloc;
+ const unsigned char *name;
+{
+#ifdef _LIBC
+ uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules != 0)
+ {
+ const int32_t *table, *indirect;
+ const unsigned char *weights, *extra, *cp;
+ unsigned char char_buf[2];
+ int32_t idx1, idx2;
+ unsigned int ch;
+ size_t len;
+ /* This #include defines a local function! */
+# include <locale/weight.h>
+ /* Calculate the index for equivalence class. */
+ cp = name;
+ table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_WEIGHTMB);
+ extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_INDIRECTMB);
+ idx1 = findidx (&cp);
+ if (idx1 == 0 || cp < name + strlen (name))
+ /* This isn't a valid character. */
+ return REG_ECOLLATE;
+
+ /* Build single byte matcing table for this equivalence class. */
+ char_buf[1] = '\0';
+ len = weights[idx1];
+ for (ch = 0; ch < SBC_MAX; ++ch)
+ {
+ char_buf[0] = ch;
+ cp = char_buf;
+ idx2 = findidx (&cp);
+/*
+ idx2 = table[ch];
+*/
+ if (idx2 == 0)
+ /* This isn't a valid character. */
+ continue;
+ if (len == weights[idx2])
+ {
+ int cnt = 0;
+ while (cnt <= len &&
+ weights[idx1 + 1 + cnt] == weights[idx2 + 1 + cnt])
+ ++cnt;
+
+ if (cnt > len)
+ bitset_set (sbcset, ch);
+ }
+ }
+ /* Check the space of the arrays, and extend if we need. */
+ mbcset->equiv_classes = extend_array_for_cset (mbcset->equiv_classes,
+ mbcset->nequiv_classes,
+ equiv_class_alloc,
+ sizeof (int32_t));
+ if (mbcset->equiv_classes == NULL)
+ return REG_ESPACE;
+
+ mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1;
+ }
+ else
+#endif
+ {
+ if (strlen (name) != 1)
+ return REG_ECOLLATE;
+ bitset_set (sbcset, name[0]);
+ }
+ return REG_NOERROR;
+}
+
+ /* Helper function for parse_bracket_exp.
+ Build the character class which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes,
+ is a pointer argument sinse we may update it. */
+
+static reg_errcode_t
+build_charclass (mbcset, sbcset, char_class_alloc, name)
+ re_charset_t *mbcset;
+ re_bitset_ptr_t sbcset;
+ int *char_class_alloc;
+ const unsigned char *name;
+{
+ int i;
+
+ /* Check the space of the arrays. */
+ mbcset->char_classes = extend_array_for_cset (mbcset->char_classes,
+ mbcset->nchar_classes,
+ char_class_alloc,
+ sizeof (wctype_t));
+ if (mbcset->char_classes == NULL)
+ return REG_ESPACE;
+
+ mbcset->char_classes[mbcset->nchar_classes++] = __wctype (name);
+
+#define BUILD_CHARCLASS_LOOP(ctype_func)\
+ for (i = 0; i < SBC_MAX; ++i) \
+ { \
+ if (ctype_func (i)) \
+ bitset_set (sbcset, i); \
+ }
+
+ if (strcmp (name, "alnum") == 0)
+ BUILD_CHARCLASS_LOOP (isalnum)
+ else if (strcmp (name, "cntrl") == 0)
+ BUILD_CHARCLASS_LOOP (iscntrl)
+ else if (strcmp (name, "lower") == 0)
+ BUILD_CHARCLASS_LOOP (islower)
+ else if (strcmp (name, "space") == 0)
+ BUILD_CHARCLASS_LOOP (isspace)
+ else if (strcmp (name, "alpha") == 0)
+ BUILD_CHARCLASS_LOOP (isalpha)
+ else if (strcmp (name, "digit") == 0)
+ BUILD_CHARCLASS_LOOP (isdigit)
+ else if (strcmp (name, "print") == 0)
+ BUILD_CHARCLASS_LOOP (isprint)
+ else if (strcmp (name, "upper") == 0)
+ BUILD_CHARCLASS_LOOP (isupper)
+ else if (strcmp (name, "blank") == 0)
+ BUILD_CHARCLASS_LOOP (isblank)
+ else if (strcmp (name, "graph") == 0)
+ BUILD_CHARCLASS_LOOP (isgraph)
+ else if (strcmp (name, "punct") == 0)
+ BUILD_CHARCLASS_LOOP (ispunct)
+ else if (strcmp (name, "xdigit") == 0)
+ BUILD_CHARCLASS_LOOP (isxdigit)
+ else
+ return REG_ECTYPE;
+
+ return REG_NOERROR;
+}
+
+static bin_tree_t *
+build_word_op (dfa, not, err)
+ re_dfa_t *dfa;
+ int not;
+ reg_errcode_t *err;
+{
+ re_bitset_ptr_t sbcset;
+ re_charset_t *mbcset;
+ reg_errcode_t ret;
+ re_token_t br_token;
+ bin_tree_t *tree;
+ int new_idx, alloc = 0;
+
+ sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS);
+ mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+ if (sbcset == NULL || mbcset == NULL)
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+
+ if (not)
+ {
+ int i;
+ mbcset->non_match = 1;
+ /*
+ if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
+ bitset_set(cset->sbcset, '\0');
+ */
+ if (MB_CUR_MAX > 1)
+ for (i = 0; i < SBC_MAX; ++i)
+ if (__btowc (i) == WEOF)
+ bitset_set (sbcset, i);
+ }
+
+ ret = build_charclass (mbcset, sbcset, &alloc, "alpha");
+ if (ret != REG_NOERROR)
+ {
+ re_free (sbcset);
+ free_charset (mbcset);
+ *err = REG_ESPACE;
+ return NULL;
+ }
+
+ /* If it is non-matching list. */
+ if (mbcset->non_match)
+ bitset_not (sbcset);
+
+ /* Build a tree for simple bracket. */
+ br_token.type = SIMPLE_BRACKET;
+ br_token.opr.sbcset = sbcset;
+ new_idx = re_dfa_add_node (dfa, br_token, 0);
+ tree = create_tree (NULL, NULL, 0, new_idx);
+ if (new_idx == -1 || tree == NULL)
+ goto build_word_op_espace;
+
+ if (MB_CUR_MAX > 1)
+ {
+ re_token_t alt_token;
+ bin_tree_t *mbc_tree;
+ /* Build a tree for complex bracket. */
+ br_token.type = COMPLEX_BRACKET;
+ br_token.opr.mbcset = mbcset;
+ dfa->has_mb_node = 1;
+ new_idx = re_dfa_add_node (dfa, br_token, 0);
+ mbc_tree = create_tree (NULL, NULL, 0, new_idx);
+ if (new_idx == -1 || mbc_tree == NULL)
+ goto build_word_op_espace;
+ /* Then join them by ALT node. */
+ alt_token.type = OP_ALT;
+ new_idx = re_dfa_add_node (dfa, alt_token, 0);
+ tree = create_tree (tree, mbc_tree, 0, new_idx);
+ if (new_idx != -1 && mbc_tree != NULL)
+ return tree;
+ }
+ else
+ {
+ free_charset (mbcset);
+ return tree;
+ }
+ build_word_op_espace:
+ re_free (sbcset);
+ free_charset (mbcset);
+ *err = REG_ESPACE;
+ return NULL;
+}
+
+/* This is intended for the expressions like "a{1,3}".
+ Fetch a number from `input', and return the number.
+ Return -1, if the number field is empty like "{,1}".
+ Return -2, If an error is occured. */
+
+static int
+fetch_number (input, token, syntax)
+ re_string_t *input;
+ re_token_t *token;
+ reg_syntax_t syntax;
+{
+ int num = -1;
+ unsigned char c;
+ while (1)
+ {
+ *token = fetch_token (input, syntax);
+ c = token->opr.c;
+ if (token->type == OP_CLOSE_DUP_NUM || c == ',')
+ break;
+ if (token->type != CHARACTER || c < '0' || '9' < c)
+ return -2;
+ num = (num == -1) ? c - '0' : num * 10 + c - '0';
+ }
+ if (num > RE_DUP_MAX)
+ return -2;
+ return num;
+}
+
+static void
+free_charset (re_charset_t *cset)
+{
+ re_free (cset->mbchars);
+ re_free (cset->coll_syms);
+ re_free (cset->equiv_classes);
+ re_free (cset->range_starts);
+ re_free (cset->range_ends);
+ re_free (cset->char_classes);
+ re_free (cset);
+}
+
+/* Functions for binary tree operation. */
+
+/* Create a node of tree.
+ Note: This function automatically free left and right if malloc fails. */
+
+static bin_tree_t *
+create_tree (left, right, type, index)
+ bin_tree_t *left;
+ bin_tree_t *right;
+ re_token_type_t type;
+ int index;
+{
+ bin_tree_t *tree;
+ tree = re_malloc (bin_tree_t, 1);
+ if (tree == NULL)
+ {
+ free_bin_tree (left);
+ free_bin_tree (right);
+ return NULL;
+ }
+ tree->parent = NULL;
+ tree->left = left;
+ tree->right = right;
+ tree->type = type;
+ tree->node_idx = index;
+ tree->first = -1;
+ tree->next = -1;
+ re_node_set_init_empty (&tree->eclosure);
+
+ if (left != NULL)
+ left->parent = tree;
+ if (right != NULL)
+ right->parent = tree;
+ return tree;
+}
+
+/* Free the sub tree pointed by TREE. */
+
+static void
+free_bin_tree (tree)
+ bin_tree_t *tree;
+{
+ if (tree == NULL)
+ return;
+ /*re_node_set_free (&tree->eclosure);*/
+ free_bin_tree (tree->left);
+ free_bin_tree (tree->right);
+ re_free (tree);
+}
+
+/* Duplicate the node SRC, and return new node. */
+
+static bin_tree_t *
+duplicate_tree (src, dfa)
+ const bin_tree_t *src;
+ re_dfa_t *dfa;
+{
+ bin_tree_t *left = NULL, *right = NULL, *new_tree;
+ int new_node_idx;
+ /* Since node indies must be according to Post-order of the tree,
+ we must duplicate the left at first. */
+ if (src->left != NULL)
+ {
+ left = duplicate_tree (src->left, dfa);
+ if (left == NULL)
+ return NULL;
+ }
+
+ /* Secondaly, duplicate the right. */
+ if (src->right != NULL)
+ {
+ right = duplicate_tree (src->right, dfa);
+ if (right == NULL)
+ {
+ free_bin_tree (left);
+ return NULL;
+ }
+ }
+
+ /* At last, duplicate itself. */
+ if (src->type == NON_TYPE)
+ {
+ new_node_idx = re_dfa_add_node (dfa, dfa->nodes[src->node_idx], 0);
+ dfa->nodes[new_node_idx].duplicated = 1;
+ if (new_node_idx == -1)
+ {
+ free_bin_tree (left);
+ free_bin_tree (right);
+ return NULL;
+ }
+ }
+ else
+ new_node_idx = src->type;
+
+ new_tree = create_tree (left, right, src->type, new_node_idx);
+ if (new_tree == NULL)
+ {
+ free_bin_tree (left);
+ free_bin_tree (right);
+ }
+ return new_tree;
+}
diff --git a/posix/regex.h b/posix/regex.h
index 8b8bb9d5a0..d1e4b6841a 100644
--- a/posix/regex.h
+++ b/posix/regex.h
@@ -1,8 +1,8 @@
/* Definitions for data structures and routines for the regular
- expression library, version 0.12.
- Copyright (C) 1985,1989-93,1995-98,2000,2001 Free Software Foundation, Inc.
- This file is part of the GNU C Library. Its master source is NOT part of
- the C library, however. The master source lives in /gd/gnu/lib.
+ expression library.
+ Copyright (C) 1985,1989-93,1995-98,2000,2001,2002
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -164,6 +164,10 @@ typedef unsigned long int reg_syntax_t;
treated as 'a\{1'. */
#define RE_INVALID_INTERVAL_ORD (RE_DEBUG << 1)
+/* If this bit is set, then ignore case when matching.
+ If not set, then case is significant. */
+#define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1)
+
/* This global variable defines the particular regexp syntax to use (for
some interfaces). When a regexp is compiled, the syntax used is
stored in the pattern buffer, so changing this does not affect
diff --git a/posix/regex_internal.c b/posix/regex_internal.c
new file mode 100644
index 0000000000..63bed420cd
--- /dev/null
+++ b/posix/regex_internal.c
@@ -0,0 +1,1095 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <assert.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#ifdef _LIBC
+# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
+# define _RE_DEFINE_LOCALE_FUNCTIONS 1
+# include <locale/localeinfo.h>
+# include <locale/elem-hash.h>
+# include <locale/coll-lookup.h>
+# endif
+#endif
+
+/* This is for other GNU distributions with internationalized messages. */
+#if HAVE_LIBINTL_H || defined _LIBC
+# include <libintl.h>
+# ifdef _LIBC
+# undef gettext
+# define gettext(msgid) __dcgettext ("libc", msgid, LC_MESSAGES)
+# endif
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef gettext_noop
+/* This define is so xgettext can find the internationalizable
+ strings. */
+# define gettext_noop(String) String
+#endif
+
+#include "regex.h"
+#include "regex_internal.h"
+
+static void re_string_construct_common (const unsigned char *str,
+ int len, re_string_t *pstr);
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t build_wcs_buffer (re_string_t *pstr);
+static reg_errcode_t build_wcs_upper_buffer (re_string_t *pstr);
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t build_upper_buffer (re_string_t *pstr);
+static reg_errcode_t re_string_translate_buffer (re_string_t *pstr,
+ RE_TRANSLATE_TYPE trans);
+static re_dfastate_t *create_newstate_common (re_dfa_t *dfa,
+ const re_node_set *nodes,
+ unsigned int hash);
+static re_dfastate_t *create_ci_newstate (re_dfa_t *dfa,
+ const re_node_set *nodes,
+ unsigned int hash);
+static re_dfastate_t *create_cd_newstate (re_dfa_t *dfa,
+ const re_node_set *nodes,
+ unsigned int context,
+ unsigned int hash);
+static unsigned int inline calc_state_hash (const re_node_set *nodes,
+ unsigned int context);
+
+/* Functions for string operation. */
+
+/* Construct string object. */
+static reg_errcode_t
+re_string_construct (pstr, str, len, trans)
+ re_string_t *pstr;
+ const unsigned char *str;
+ int len;
+ RE_TRANSLATE_TYPE trans;
+{
+ reg_errcode_t ret;
+ re_string_construct_common (str, len, pstr);
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX >1 && pstr->len > 0)
+ {
+ ret = build_wcs_buffer (pstr);
+ if (ret != REG_NOERROR)
+ return ret;
+ }
+#endif /* RE_ENABLE_I18N */
+ pstr->mbs_case = str;
+ if (trans != NULL)
+ {
+ ret = re_string_translate_buffer (pstr, trans);
+ if (ret != REG_NOERROR)
+ return ret;
+ }
+ return REG_NOERROR;
+}
+
+/* Construct string object. We use this function instead of
+ re_string_construct for case insensitive mode. */
+
+static reg_errcode_t
+re_string_construct_toupper (pstr, str, len, trans)
+ re_string_t *pstr;
+ const unsigned char *str;
+ int len;
+ RE_TRANSLATE_TYPE trans;
+{
+ reg_errcode_t ret;
+ /* Set case sensitive buffer. */
+ re_string_construct_common (str, len, pstr);
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX >1)
+ {
+ if (pstr->len > 0)
+ {
+ ret = build_wcs_upper_buffer (pstr);
+ if (ret != REG_NOERROR)
+ return ret;
+ }
+ }
+ else
+#endif /* RE_ENABLE_I18N */
+ {
+ if (pstr->len > 0)
+ {
+ ret = build_upper_buffer (pstr);
+ if (ret != REG_NOERROR)
+ return ret;
+ }
+ }
+ pstr->mbs_case = str;
+ if (trans != NULL)
+ {
+ ret = re_string_translate_buffer (pstr, trans);
+ if (ret != REG_NOERROR)
+ return ret;
+ }
+ return REG_NOERROR;
+}
+
+/* Helper functions for re_string_construct_*. */
+static void
+re_string_construct_common (str, len, pstr)
+ const unsigned char *str;
+ int len;
+ re_string_t *pstr;
+{
+ pstr->mbs = str;
+ pstr->cur_idx = 0;
+ pstr->len = len;
+#ifdef RE_ENABLE_I18N
+ pstr->wcs = NULL;
+#endif
+ pstr->mbs_case = NULL;
+ pstr->mbs_alloc = 0;
+ pstr->mbs_case_alloc = 0;
+}
+
+#ifdef RE_ENABLE_I18N
+
+/* Build wide character buffer for `pstr'.
+ If the byte sequence of the string are:
+ <mb1>(0), <mb1>(1), <mb2>(0), <mb2>(1), <sb3>
+ Then wide character buffer will be:
+ <wc1> , WEOF , <wc2> , WEOF , <wc3>
+ We use WEOF for padding, they indicate that the position isn't
+ a first byte of a multibyte character. */
+
+static reg_errcode_t
+build_wcs_buffer (pstr)
+ re_string_t *pstr;
+{
+ mbstate_t state, prev_st;
+ wchar_t wc;
+ int char_idx, char_len, mbclen;
+
+ pstr->wcs = re_malloc (wchar_t, pstr->len + 1);
+ if (pstr->wcs == NULL)
+ return REG_ESPACE;
+
+ memset (&state, '\0', sizeof (mbstate_t));
+ char_len = pstr->len;
+ for (char_idx = 0; char_idx < char_len ;)
+ {
+ int next_idx, remain_len = char_len - char_idx;
+ prev_st = state;
+ mbclen = mbrtowc (&wc, pstr->mbs + char_idx, remain_len, &state);
+ if (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0)
+ /* We treat these cases as a singlebyte character. */
+ {
+ mbclen = 1;
+ wc = (wchar_t) pstr->mbs[char_idx++];
+ state = prev_st;
+ }
+ /* Write wide character and padding. */
+ pstr->wcs[char_idx++] = wc;
+ for (next_idx = char_idx + mbclen - 1; char_idx < next_idx ;)
+ pstr->wcs[char_idx++] = WEOF;
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+build_wcs_upper_buffer (pstr)
+ re_string_t *pstr;
+{
+ mbstate_t state, prev_st;
+ wchar_t wc;
+ unsigned char *mbs_upper;
+ int char_idx, char_len, mbclen;
+
+ pstr->wcs = re_malloc (wchar_t, pstr->len + 1);
+ mbs_upper = re_malloc (unsigned char, pstr->len + 1);
+ if (pstr->wcs == NULL || mbs_upper == NULL)
+ {
+ pstr->wcs = NULL;
+ return REG_ESPACE;
+ }
+
+ memset (&state, '\0', sizeof (mbstate_t));
+ char_len = pstr->len;
+ for (char_idx = 0 ; char_idx < char_len ; char_idx += mbclen)
+ {
+ int byte_idx, remain_len = char_len - char_idx;
+ prev_st = state;
+ mbclen = mbrtowc (&wc, pstr->mbs + char_idx, remain_len, &state);
+ if (mbclen == 1)
+ {
+ pstr->wcs[char_idx] = wc;
+ if (islower (pstr->mbs[char_idx]))
+ mbs_upper[char_idx] = toupper (pstr->mbs[char_idx]);
+ else
+ mbs_upper[char_idx] = pstr->mbs[char_idx];
+ }
+ else if (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0)
+ /* We treat these cases as a singlebyte character. */
+ {
+ mbclen = 1;
+ pstr->wcs[char_idx] = (wchar_t) pstr->mbs[char_idx];
+ mbs_upper[char_idx] = pstr->mbs[char_idx];
+ state = prev_st;
+ }
+ else /* mbclen > 1 */
+ {
+ pstr->wcs[char_idx] = wc;
+ if (iswlower (wc))
+ wcrtomb (mbs_upper + char_idx, towupper (wc), &prev_st);
+ else
+ memcpy (mbs_upper + char_idx, pstr->mbs + char_idx, mbclen);
+ for (byte_idx = 1 ; byte_idx < mbclen ; byte_idx++)
+ pstr->wcs[char_idx + byte_idx] = WEOF;
+ }
+ }
+ pstr->mbs = mbs_upper;
+ pstr->mbs_alloc = 1;
+ return REG_NOERROR;
+}
+#endif /* RE_ENABLE_I18N */
+
+static reg_errcode_t
+build_upper_buffer (pstr)
+ re_string_t *pstr;
+{
+ unsigned char *mbs_upper;
+ int char_idx, char_len;
+
+ mbs_upper = re_malloc (unsigned char, pstr->len + 1);
+ if (mbs_upper == NULL)
+ return REG_ESPACE;
+
+ char_len = pstr->len;
+ for (char_idx = 0 ; char_idx < char_len ; char_idx ++)
+ {
+ if (islower (pstr->mbs[char_idx]))
+ mbs_upper[char_idx] = toupper (pstr->mbs[char_idx]);
+ else
+ mbs_upper[char_idx] = pstr->mbs[char_idx];
+ }
+ pstr->mbs = mbs_upper;
+ pstr->mbs_alloc = 1;
+ return REG_NOERROR;
+}
+
+/* Apply TRANS to the buffer in PSTR. We assume that wide char buffer
+ is already constructed if MB_CUR_MAX > 1. */
+
+static reg_errcode_t
+re_string_translate_buffer (pstr, trans)
+ re_string_t *pstr;
+ RE_TRANSLATE_TYPE trans;
+{
+ int buf_idx;
+ unsigned char *transed_buf, *transed_case_buf;
+#ifdef DEBUG
+ assert (trans != NULL);
+#endif
+ if (pstr->mbs_alloc)
+ {
+ transed_buf = (unsigned char *) pstr->mbs;
+ transed_case_buf = re_malloc (unsigned char, pstr->len + 1);
+ if (transed_case_buf == NULL)
+ return REG_ESPACE;
+ pstr->mbs_case_alloc = 1;
+ }
+ else
+ {
+ transed_buf = re_malloc (unsigned char, pstr->len + 1);
+ if (transed_buf == NULL)
+ return REG_ESPACE;
+ transed_case_buf = NULL;
+ pstr->mbs_alloc = 1;
+ }
+ for (buf_idx = 0 ; buf_idx < pstr->len ; buf_idx++)
+ {
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1 && !re_string_is_single_byte_char (pstr, buf_idx))
+ transed_buf[buf_idx] = pstr->mbs[buf_idx];
+ else
+#endif
+ transed_buf[buf_idx] = trans[pstr->mbs[buf_idx]];
+ if (transed_case_buf)
+ {
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1 && !re_string_is_single_byte_char (pstr, buf_idx))
+ transed_case_buf[buf_idx] = pstr->mbs_case[buf_idx];
+ else
+#endif
+ transed_case_buf[buf_idx] = trans[pstr->mbs_case[buf_idx]];
+ }
+ }
+ if (pstr->mbs_case_alloc == 1)
+ {
+ pstr->mbs = transed_buf;
+ pstr->mbs_case = transed_case_buf;
+ }
+ else
+ {
+ pstr->mbs = transed_buf;
+ pstr->mbs_case = transed_buf;
+ }
+ return REG_NOERROR;
+}
+
+static void
+re_string_destruct (pstr)
+ re_string_t *pstr;
+{
+#ifdef RE_ENABLE_I18N
+ re_free (pstr->wcs);
+#endif /* RE_ENABLE_I18N */
+ if (pstr->mbs_alloc)
+ re_free ((void *) pstr->mbs);
+ if (pstr->mbs_case_alloc)
+ re_free ((void *) pstr->mbs_case);
+}
+
+/* Return the context at IDX in INPUT. */
+static unsigned int
+re_string_context_at (input, idx, eflags, newline_anchor)
+ const re_string_t *input;
+ int idx, eflags, newline_anchor;
+{
+ int c;
+ if (idx < 0 || idx == input->len)
+ {
+ unsigned int context = 0;
+ if (idx < 0)
+ context = CONTEXT_BEGBUF;
+ else if (idx == input->len)
+ context = CONTEXT_ENDBUF;
+
+ if ((idx < 0 && !(eflags & REG_NOTBOL))
+ || (idx == input->len && !(eflags & REG_NOTEOL)))
+ return CONTEXT_NEWLINE | context;
+ else
+ return context;
+ }
+ c = re_string_byte_at (input, idx);
+ if (IS_WORD_CHAR (c))
+ return CONTEXT_WORD;
+ return (newline_anchor && IS_NEWLINE (c)) ? CONTEXT_NEWLINE : 0;
+}
+
+/* Functions for set operation. */
+
+static reg_errcode_t
+re_node_set_alloc (set, size)
+ re_node_set *set;
+ int size;
+{
+ set->alloc = size;
+ set->nelem = 0;
+ set->elems = re_malloc (int, size);
+ if (set->elems == NULL)
+ return REG_ESPACE;
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_init_1 (set, elem)
+ re_node_set *set;
+ int elem;
+{
+ set->alloc = 1;
+ set->nelem = 1;
+ set->elems = re_malloc (int, 1);
+ if (set->elems == NULL)
+ return REG_ESPACE;
+ set->elems[0] = elem;
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_init_2 (set, elem1, elem2)
+ re_node_set *set;
+ int elem1, elem2;
+{
+ set->alloc = 2;
+ set->elems = re_malloc (int, 2);
+ if (set->elems == NULL)
+ return REG_ESPACE;
+ if (elem1 == elem2)
+ {
+ set->nelem = 1;
+ set->elems[0] = elem1;
+ }
+ else
+ {
+ set->nelem = 2;
+ if (elem1 < elem2)
+ {
+ set->elems[0] = elem1;
+ set->elems[1] = elem2;
+ }
+ else
+ {
+ set->elems[0] = elem2;
+ set->elems[1] = elem1;
+ }
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_init_copy (dest, src)
+ re_node_set *dest;
+ const re_node_set *src;
+{
+ dest->nelem = src->nelem;
+ if (src->nelem > 0)
+ {
+ dest->alloc = dest->nelem;
+ dest->elems = re_malloc (int, dest->alloc);
+ if (dest->elems == NULL)
+ return REG_ESPACE;
+ memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
+ }
+ else
+ re_node_set_init_empty (dest);
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_intersect (dest, src1, src2)
+ re_node_set *dest;
+ const re_node_set *src1, *src2;
+{
+ int i1, i2, id;
+ if (src1->nelem > 0 && src2->nelem > 0)
+ {
+ if (src1->nelem + src2->nelem > dest->alloc)
+ {
+ int *new_array;
+ if (dest->alloc == 0)
+ new_array = re_malloc (int, src1->nelem + src2->nelem);
+ else
+ new_array = re_realloc (dest->elems, int,
+ src1->nelem + src2->nelem);
+ dest->alloc = src1->nelem + src2->nelem;
+ if (new_array == NULL)
+ return REG_ESPACE;
+ dest->elems = new_array;
+ }
+ }
+ else
+ {
+ dest->nelem = 0;
+ return REG_NOERROR;
+ }
+
+ for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
+ {
+ if (src1->elems[i1] > src2->elems[i2])
+ {
+ ++i2;
+ continue;
+ }
+ if (src1->elems[i1] == src2->elems[i2])
+ dest->elems[id++] = src2->elems[i2++];
+ ++i1;
+ }
+ dest->nelem = id;
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_add_intersect (dest, src1, src2)
+ re_node_set *dest;
+ const re_node_set *src1, *src2;
+{
+ int i1, i2, id;
+ if (src1->nelem > 0 && src2->nelem > 0)
+ {
+ if (src1->nelem + src2->nelem + dest->nelem > dest->alloc)
+ {
+ int *new_array;
+ if (dest->alloc == 0)
+ new_array = re_malloc (int, src1->nelem + src2->nelem);
+ else
+ new_array = re_realloc (dest->elems, int,
+ src1->nelem + src2->nelem + dest->nelem);
+ dest->alloc = src1->nelem + src2->nelem + dest->nelem;
+ if (new_array == NULL)
+ return REG_ESPACE;
+ dest->elems = new_array;
+ }
+ }
+ else
+ return REG_NOERROR;
+
+ for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
+ {
+ if (src1->elems[i1] > src2->elems[i2])
+ {
+ ++i2;
+ continue;
+ }
+ if (src1->elems[i1] == src2->elems[i2])
+ {
+ while (id < dest->nelem && dest->elems[id] < src2->elems[i2])
+ ++id;
+ if (id < dest->nelem && dest->elems[id] == src2->elems[i2])
+ ++id;
+ else
+ {
+ memmove (dest->elems + id + 1, dest->elems + id,
+ sizeof (int) * (dest->nelem - id));
+ dest->elems[id++] = src2->elems[i2++];
+ ++dest->nelem;
+ }
+ }
+ ++i1;
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_init_union (dest, src1, src2)
+ re_node_set *dest;
+ const re_node_set *src1, *src2;
+{
+ int i1, i2, id;
+ if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0)
+ {
+ dest->alloc = src1->nelem + src2->nelem;
+ dest->elems = re_malloc (int, dest->alloc);
+ if (dest->elems == NULL)
+ return REG_ESPACE;
+ }
+ else
+ {
+ if (src1 != NULL && src1->nelem > 0)
+ return re_node_set_init_copy (dest, src1);
+ else if (src2 != NULL && src2->nelem > 0)
+ return re_node_set_init_copy (dest, src2);
+ else
+ re_node_set_init_empty (dest);
+ return REG_NOERROR;
+ }
+ for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
+ {
+ if (src1->elems[i1] > src2->elems[i2])
+ {
+ dest->elems[id++] = src2->elems[i2++];
+ continue;
+ }
+ if (src1->elems[i1] == src2->elems[i2])
+ ++i2;
+ dest->elems[id++] = src1->elems[i1++];
+ }
+ if (i1 < src1->nelem)
+ {
+ memcpy (dest->elems + id, src1->elems + i1,
+ (src1->nelem - i1) * sizeof (int));
+ id += src1->nelem - i1;
+ }
+ else if (i2 < src2->nelem)
+ {
+ memcpy (dest->elems + id, src2->elems + i2,
+ (src2->nelem - i2) * sizeof (int));
+ id += src2->nelem - i2;
+ }
+ dest->nelem = id;
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_merge (dest, src)
+ re_node_set *dest;
+ const re_node_set *src;
+{
+ int si, di;
+ if (src == NULL || src->nelem == 0)
+ return REG_NOERROR;
+ else if (dest == NULL)
+ {
+ dest = re_malloc (re_node_set, 1);
+ return re_node_set_init_copy (dest, src);
+ }
+ if (dest->alloc < src->nelem + dest->nelem)
+ {
+ dest->alloc = 2 * (src->nelem + dest->alloc);
+ dest->elems = re_realloc (dest->elems, int, dest->alloc);
+ }
+
+ for (si = 0, di = 0 ; si < src->nelem && di < dest->nelem ;)
+ {
+ int cp_from, ncp, mid, right, src_elem = src->elems[si];
+ /* Binary search the spot we will add the new element. */
+ right = dest->nelem;
+ while (di < right)
+ {
+ mid = (di + right) / 2;
+ if (dest->elems[mid] < src_elem)
+ di = mid + 1;
+ else
+ right = mid;
+ }
+ if (di >= dest->nelem)
+ break;
+
+ if (dest->elems[di] == src_elem)
+ {
+ /* Skip since, DEST already has the element. */
+ ++di;
+ ++si;
+ continue;
+ }
+
+ /* Skip the src elements which are less than dest->elems[di]. */
+ cp_from = si;
+ while (si < src->nelem && src->elems[si] < dest->elems[di])
+ ++si;
+ /* Copy these src elements. */
+ ncp = si - cp_from;
+ memmove (dest->elems + di + ncp, dest->elems + di,
+ sizeof (int) * (dest->nelem - di));
+ memcpy (dest->elems + di, src->elems + cp_from,
+ sizeof (int) * ncp);
+ /* Update counters. */
+ di += ncp;
+ dest->nelem += ncp;
+ }
+
+ /* Copy remaining src elements. */
+ if (si < src->nelem)
+ {
+ memcpy (dest->elems + di, src->elems + si,
+ sizeof (int) * (src->nelem - si));
+ dest->nelem += src->nelem - si;
+ }
+ return REG_NOERROR;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+ return 0 if SET already has ELEM,
+ return -1 if an error is occured, return 1 otherwise. */
+
+static int
+re_node_set_insert (set, elem)
+ re_node_set *set;
+ int elem;
+{
+ int idx, right, mid;
+ /* In case of the set is empty. */
+ if (set->elems == NULL || set->alloc == 0)
+ {
+ if (re_node_set_init_1 (set, elem) == REG_NOERROR)
+ return 1;
+ else
+ return -1;
+ }
+
+ /* Binary search the spot we will add the new element. */
+ idx = 0;
+ right = set->nelem;
+ while (idx < right)
+ {
+ mid = (idx + right) / 2;
+ if (set->elems[mid] < elem)
+ idx = mid + 1;
+ else
+ right = mid;
+ }
+
+ /* Realloc if we need. */
+ if (set->alloc < set->nelem + 1)
+ {
+ int *new_array;
+ set->alloc = set->alloc * 2;
+ new_array = re_malloc (int, set->alloc);
+ if (new_array == NULL)
+ return -1;
+ /* Copy the elements they are followed by the new element. */
+ if (idx > 0)
+ memcpy (new_array, set->elems, sizeof (int) * (idx));
+ /* Copy the elements which follows the new element. */
+ if (set->nelem - idx > 0)
+ memcpy (new_array + idx + 1, set->elems + idx,
+ sizeof (int) * (set->nelem - idx));
+ set->elems = new_array;
+ }
+ else
+ {
+ /* Move the elements which follows the new element. */
+ if (set->nelem - idx > 0)
+ memmove (set->elems + idx + 1, set->elems + idx,
+ sizeof (int) * (set->nelem - idx));
+ }
+ /* Insert the new element. */
+ set->elems[idx] = elem;
+ ++set->nelem;
+ return 1;
+}
+
+/* Compare two node sets SET1 and SET2.
+ return 1 if SET1 and SET2 are equivalent, retrun 0 otherwise. */
+
+static int
+re_node_set_compare (set1, set2)
+ const re_node_set *set1, *set2;
+{
+ int i;
+ if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem)
+ return 0;
+ for (i = 0 ; i < set1->nelem ; i++)
+ if (set1->elems[i] != set2->elems[i])
+ return 0;
+ return 1;
+}
+
+/* Return 1 if SET contains the element ELEM, return 0 otherwise. */
+
+static int
+re_node_set_contains (set, elem)
+ const re_node_set *set;
+ int elem;
+{
+ int idx, right, mid;
+ if (set->nelem <= 0)
+ return 0;
+
+ /* Binary search the element. */
+ idx = 0;
+ right = set->nelem - 1;
+ while (idx < right)
+ {
+ mid = (idx + right) / 2;
+ if (set->elems[mid] < elem)
+ idx = mid + 1;
+ else
+ right = mid;
+ }
+ return set->elems[idx] == elem;
+}
+
+static void
+re_node_set_remove_at (set, idx)
+ re_node_set *set;
+ int idx;
+{
+ if (idx < 0 || idx >= set->nelem)
+ return;
+ if (idx < set->nelem - 1)
+ memmove (set->elems + idx, set->elems + idx + 1,
+ sizeof (int) * (set->nelem - idx - 1));
+ --set->nelem;
+}
+
+
+/* Add the token TOKEN to dfa->nodes, and return the index of the token.
+ Or return -1, if an error will be occured. */
+
+static int
+re_dfa_add_node (dfa, token, mode)
+ re_dfa_t *dfa;
+ re_token_t token;
+ int mode;
+{
+ if (dfa->nodes_len >= dfa->nodes_alloc)
+ {
+ re_token_t *new_array;
+ dfa->nodes_alloc *= 2;
+ new_array = re_realloc (dfa->nodes, re_token_t, dfa->nodes_alloc);
+ if (new_array == NULL)
+ return -1;
+ else
+ dfa->nodes = new_array;
+ if (mode)
+ {
+ int *new_firsts, *new_nexts;
+ re_node_set *new_edests, *new_eclosures, *new_inveclosures;
+
+ new_firsts = re_realloc (dfa->firsts, int, dfa->nodes_alloc);
+ new_nexts = re_realloc (dfa->nexts, int, dfa->nodes_alloc);
+ new_edests = re_realloc (dfa->edests, re_node_set, dfa->nodes_alloc);
+ new_eclosures = re_realloc (dfa->eclosures, re_node_set,
+ dfa->nodes_alloc);
+ new_inveclosures = re_realloc (dfa->inveclosures, re_node_set,
+ dfa->nodes_alloc);
+ if (new_firsts == NULL || new_nexts == NULL || new_edests == NULL
+ || new_eclosures == NULL || new_inveclosures == NULL)
+ return -1;
+ dfa->firsts = new_firsts;
+ dfa->nexts = new_nexts;
+ dfa->edests = new_edests;
+ dfa->eclosures = new_eclosures;
+ dfa->inveclosures = new_inveclosures;
+ }
+ }
+ dfa->nodes[dfa->nodes_len] = token;
+ dfa->nodes[dfa->nodes_len].duplicated = 0;
+ return dfa->nodes_len++;
+}
+
+static unsigned int inline
+calc_state_hash (nodes, context)
+ const re_node_set *nodes;
+ unsigned int context;
+{
+ unsigned int hash = nodes->nelem + context;
+ int i;
+ for (i = 0 ; i < nodes->nelem ; i++)
+ hash += nodes->elems[i];
+ return hash;
+}
+
+/* Search for the state whose node_set is equivalent to NODES.
+ Return the pointer to the state, if we found it in the DFA.
+ Otherwise create the new one and return it. */
+
+static re_dfastate_t *
+re_acquire_state (dfa, nodes)
+ re_dfa_t *dfa;
+ const re_node_set *nodes;
+{
+ unsigned int hash;
+ struct re_state_table_entry *spot;
+ int i;
+ if (nodes->nelem == 0)
+ return NULL;
+ hash = calc_state_hash (nodes, 0);
+ spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+ if (spot->alloc == 0)
+ {
+ /* Currently there are only one state in this spot. */
+ if (spot->entry.state != NULL && hash == spot->entry.state->hash
+ && re_node_set_compare (&spot->entry.state->nodes, nodes))
+ return spot->entry.state;
+ }
+ else
+ for (i = 0 ; i < spot->num ; i++)
+ {
+ re_dfastate_t *state = spot->entry.array[i];
+ if (hash != state->hash)
+ continue;
+ if (re_node_set_compare (&state->nodes, nodes))
+ return state;
+ }
+
+ /* There are no appropriate state in the dfa, create the new one. */
+ return create_ci_newstate (dfa, nodes, hash);
+}
+
+/* Search for the state whose node_set is equivalent to NODES and
+ whose context is equivalent to CONTEXT.
+ Return the pointer to the state, if we found it in the DFA.
+ Otherwise create the new one and return it. */
+
+static re_dfastate_t *
+re_acquire_state_context (dfa, nodes, context)
+ re_dfa_t *dfa;
+ const re_node_set *nodes;
+ unsigned int context;
+{
+ unsigned int hash;
+ struct re_state_table_entry *spot;
+ int i;
+ if (nodes->nelem == 0)
+ return NULL;
+ hash = calc_state_hash (nodes, context);
+ spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+ if (spot->alloc == 0)
+ {
+ /* Currently there are only one state in this spot. */
+ if (spot->entry.state != NULL && hash == spot->entry.state->hash
+ && re_node_set_compare (&spot->entry.state->nodes, nodes)
+ && spot->entry.state->context == context)
+ return spot->entry.state;
+ }
+ else
+ for (i = 0 ; i < spot->num ; i++)
+ {
+ re_dfastate_t *state = spot->entry.array[i];
+ if (hash != state->hash)
+ continue;
+ if (re_node_set_compare (state->entrance_nodes, nodes)
+ && state->context == context)
+ return state;
+ }
+ /* There are no appropriate state in `dfa', create the new one. */
+ return create_cd_newstate (dfa, nodes, context, hash);
+}
+
+static re_dfastate_t *
+create_newstate_common (dfa, nodes, hash)
+ re_dfa_t *dfa;
+ const re_node_set *nodes;
+ unsigned int hash;
+{
+ re_dfastate_t *newstate;
+ newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+ re_node_set_init_copy (&newstate->nodes, nodes);
+ newstate->trtable = NULL;
+ newstate->trtable_search = NULL;
+ newstate->hash = hash;
+ return newstate;
+}
+
+static void
+register_state (dfa, newstate, hash)
+ re_dfa_t *dfa;
+ re_dfastate_t *newstate;
+ unsigned int hash;
+{
+ struct re_state_table_entry *spot;
+ spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+ if (spot->alloc <= spot->num)
+ {
+ re_dfastate_t **new_array;
+
+ /* XXX Is spot->entry.array == NULL if spot->alloc == 0? If yes
+ the if can go away and only realloc is needed. */
+ if (spot->alloc == 0)
+ {
+ spot->alloc = 4;
+ new_array = re_malloc (re_dfastate_t *, spot->alloc);
+ if (new_array == NULL)
+ /* XXX return value */
+ return;
+ new_array[0] = spot->entry.state;
+ }
+ else
+ {
+ spot->alloc = 2 * spot->num;
+ new_array = re_realloc (spot->entry.array, re_dfastate_t *,
+ spot->alloc);
+ }
+ spot->entry.array = new_array;
+ }
+ spot->entry.array[spot->num++] = newstate;
+}
+
+static re_dfastate_t *
+create_ci_newstate (dfa, nodes, hash)
+ re_dfa_t *dfa;
+ const re_node_set *nodes;
+ unsigned int hash;
+{
+ int i;
+ re_dfastate_t *newstate;
+ newstate = create_newstate_common (dfa, nodes, hash);
+ newstate->entrance_nodes = &newstate->nodes;
+
+ for (i = 0 ; i < nodes->nelem ; i++)
+ {
+ re_token_t *node = dfa->nodes + nodes->elems[i];
+ re_token_type_t type = node->type;
+ if (type == CHARACTER)
+ continue;
+
+ /* If the state has the halt node, the state is a halt state. */
+ else if (type == END_OF_RE)
+ newstate->halt = 1;
+ else if (type == COMPLEX_BRACKET
+ || (type == OP_PERIOD && MB_CUR_MAX > 1))
+ newstate->accept_mb = 1;
+ else if (type == OP_BACK_REF)
+ newstate->has_backref = 1;
+ else if (type == ANCHOR || OP_CONTEXT_NODE)
+ {
+ newstate->has_constraint = 1;
+ if (type == OP_CONTEXT_NODE
+ && dfa->nodes[node->opr.ctx_info->entity].type == END_OF_RE)
+ newstate->halt = 1;
+ }
+ }
+
+ register_state (dfa, newstate, hash);
+ return newstate;
+}
+
+static re_dfastate_t *
+create_cd_newstate (dfa, nodes, context, hash)
+ re_dfa_t *dfa;
+ const re_node_set *nodes;
+ unsigned int context, hash;
+{
+ int i, nctx_nodes = 0;
+ re_dfastate_t *newstate;
+
+ newstate = create_newstate_common (dfa, nodes, hash);
+ newstate->context = context;
+ newstate->entrance_nodes = &newstate->nodes;
+
+ for (i = 0 ; i < nodes->nelem ; i++)
+ {
+ unsigned int constraint = 0;
+ re_token_t *node = dfa->nodes + nodes->elems[i];
+ re_token_type_t type = node->type;
+ if (type == CHARACTER)
+ continue;
+
+ /* If the state has the halt node, the state is a halt state. */
+ else if (type == END_OF_RE)
+ newstate->halt = 1;
+ else if (type == COMPLEX_BRACKET
+ || (type == OP_PERIOD && MB_CUR_MAX > 1))
+ newstate->accept_mb = 1;
+ else if (type == OP_BACK_REF)
+ newstate->has_backref = 1;
+ else if (type == ANCHOR)
+ constraint = node->opr.ctx_type;
+ else if (type == OP_CONTEXT_NODE)
+ {
+ re_token_type_t ctype = dfa->nodes[node->opr.ctx_info->entity].type;
+ constraint = node->constraint;
+ if (ctype == END_OF_RE)
+ newstate->halt = 1;
+ else if (ctype == OP_BACK_REF)
+ newstate->has_backref = 1;
+ else if (ctype == COMPLEX_BRACKET
+ || (type == OP_PERIOD && MB_CUR_MAX > 1))
+ newstate->accept_mb = 1;
+ }
+
+ if (constraint)
+ {
+ if (newstate->entrance_nodes == &newstate->nodes)
+ {
+ newstate->entrance_nodes = re_malloc (re_node_set, 1);
+ if (newstate->entrance_nodes == NULL)
+ /* XXX Return which value? */
+ return NULL;
+ re_node_set_init_copy (newstate->entrance_nodes, nodes);
+ nctx_nodes = 0;
+ newstate->has_constraint = 1;
+ }
+
+ if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context))
+ {
+ re_node_set_remove_at (&newstate->nodes, i - nctx_nodes);
+ ++nctx_nodes;
+ }
+ }
+ }
+ register_state (dfa, newstate, hash);
+ return newstate;
+}
diff --git a/posix/regex_internal.h b/posix/regex_internal.h
new file mode 100644
index 0000000000..35f9f4a868
--- /dev/null
+++ b/posix/regex_internal.h
@@ -0,0 +1,542 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _REGEX_INTERNAL_H
+#define _REGEX_INTERNAL_H 1
+
+/* Number of bits in a byte. */
+#define BYTE_BITS 8
+/* Number of single byte character. */
+#define SBC_MAX 256
+
+#define COLL_ELEM_LEN_MAX 8
+
+/* The character which represents newline. */
+#define NEWLINE_CHAR '\n'
+
+/* Rename to standard API for using out of glibc. */
+#ifndef _LIBC
+# define __wctype wctype
+# define __iswctype iswctype
+# define __btowc btowc
+# define __mempcpy memcpy
+#endif
+
+extern const char re_error_msgid[];
+extern const size_t re_error_msgid_idx[];
+
+/* Number of bits in an unsinged int. */
+#define UINT_BITS (sizeof (unsigned int) * BYTE_BITS)
+/* Number of unsigned int in an bit_set. */
+#define BITSET_UINTS ((SBC_MAX + UINT_BITS - 1) / UINT_BITS)
+typedef unsigned int bitset[BITSET_UINTS];
+typedef unsigned int *re_bitset_ptr_t;
+
+#define bitset_set(set,i) (set[i / UINT_BITS] |= 1 << i % UINT_BITS)
+#define bitset_clear(set,i) (set[i / UINT_BITS] &= ~(1 << i % UINT_BITS))
+#define bitset_contain(set,i) (set[i / UINT_BITS] & (1 << i % UINT_BITS))
+#define bitset_empty(set) memset (set, 0, sizeof (unsigned int) * BITSET_UINTS)
+#define bitset_set_all(set) \
+ memset (set, 255, sizeof (unsigned int) * BITSET_UINTS)
+#define bitset_copy(dest,src) \
+ memcpy (dest, src, sizeof (unsigned int) * BITSET_UINTS)
+static inline void bitset_not (bitset set);
+static inline void bitset_merge (bitset dest, const bitset src);
+static inline void bitset_not_merge (bitset dest, const bitset src);
+
+#define PREV_WORD_CONSTRAINT 0x0001
+#define PREV_NOTWORD_CONSTRAINT 0x0002
+#define NEXT_WORD_CONSTRAINT 0x0004
+#define NEXT_NOTWORD_CONSTRAINT 0x0008
+#define PREV_NEWLINE_CONSTRAINT 0x0010
+#define NEXT_NEWLINE_CONSTRAINT 0x0020
+#define PREV_BEGBUF_CONSTRAINT 0x0040
+#define NEXT_ENDBUF_CONSTRAINT 0x0080
+#define DUMMY_CONSTRAINT 0x0100
+
+typedef enum
+{
+ INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+ WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+ WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
+ LINE_FIRST = PREV_NEWLINE_CONSTRAINT,
+ LINE_LAST = NEXT_NEWLINE_CONSTRAINT,
+ BUF_FIRST = PREV_BEGBUF_CONSTRAINT,
+ BUF_LAST = NEXT_ENDBUF_CONSTRAINT,
+ WORD_DELIM = DUMMY_CONSTRAINT
+} re_context_type;
+
+typedef struct
+{
+ int alloc;
+ int nelem;
+ int *elems;
+} re_node_set;
+
+typedef enum
+{
+ NON_TYPE = 0,
+
+ /* Token type, these are used only by token. */
+ OP_OPEN_SUBEXP,
+ OP_CLOSE_SUBEXP,
+ OP_OPEN_BRACKET,
+ OP_CLOSE_BRACKET,
+ OP_CHARSET_RANGE,
+ OP_OPEN_DUP_NUM,
+ OP_CLOSE_DUP_NUM,
+ OP_NON_MATCH_LIST,
+ OP_OPEN_COLL_ELEM,
+ OP_CLOSE_COLL_ELEM,
+ OP_OPEN_EQUIV_CLASS,
+ OP_CLOSE_EQUIV_CLASS,
+ OP_OPEN_CHAR_CLASS,
+ OP_CLOSE_CHAR_CLASS,
+ OP_WORD,
+ OP_NOTWORD,
+ BACK_SLASH,
+
+ /* Tree type, these are used only by tree. */
+ CONCAT,
+ ALT,
+ SUBEXP,
+ SIMPLE_BRACKET,
+ COMPLEX_BRACKET,
+
+ /* Node type, These are used by token, node, tree. */
+ OP_PERIOD,
+ CHARACTER,
+ END_OF_RE,
+ OP_ALT,
+ OP_DUP_ASTERISK,
+ OP_DUP_PLUS,
+ OP_DUP_QUESTION,
+ OP_BACK_REF,
+ ANCHOR,
+ OP_CONTEXT_NODE,
+
+ /* Dummy marker. */
+ END_OF_RE_TOKEN_T
+} re_token_type_t;
+
+typedef struct
+{
+ /* If this character set is the non-matching list. */
+ unsigned int non_match : 1;
+
+ /* Multibyte characters. */
+ wchar_t *mbchars;
+ int nmbchars;
+
+ /* Collating symbols. */
+#ifdef _LIBC
+ int32_t *coll_syms;
+#endif
+ int ncoll_syms;
+
+ /* Equivalence classes. */
+#ifdef _LIBC
+ int32_t *equiv_classes;
+#endif
+ int nequiv_classes;
+
+ /* Range expressions. */
+#ifdef _LIBC
+ uint32_t *range_starts;
+ uint32_t *range_ends;
+#endif
+ int nranges;
+
+ /* Character classes. */
+ wctype_t *char_classes;
+ int nchar_classes;
+} re_charset_t;
+
+typedef struct
+{
+ re_token_type_t type;
+ union
+ {
+ unsigned char c; /* for CHARACTER */
+ re_bitset_ptr_t sbcset; /* for SIMPLE_BRACKET */
+ re_charset_t *mbcset; /* for COMPLEX_BRACKET */
+ int idx; /* for BACK_REF */
+ re_context_type ctx_type; /* for ANCHOR */
+ struct
+ {
+ int entity; /* for OP_CONTEXT_NODE, index of the entity */
+ re_node_set *bkref_eclosure;
+ } *ctx_info;
+ } opr;
+ unsigned int constraint : 10; /* context constraint */
+ unsigned int duplicated : 1;
+#ifdef RE_ENABLE_I18N
+ unsigned int mb_partial : 1;
+#endif
+} re_token_t;
+
+#define IS_EPSILON_NODE(type) \
+ ((type) == OP_ALT || (type) == OP_DUP_ASTERISK || (type) == OP_DUP_PLUS || \
+ (type) == OP_DUP_QUESTION || (type) == ANCHOR)
+
+#define ACCEPT_MB_NODE(type) \
+ ((type) == COMPLEX_BRACKET || (type) == OP_PERIOD)
+
+struct re_string_t
+{
+ /* Store the multibyte string. In case of "case insensitive mode" like
+ REG_ICASE, upper cases of the string are stored. */
+ const unsigned char *mbs;
+ /* Store the case sensitive multibyte string. In case of
+ "case insensitive mode", the original string are stored,
+ otherwise MBS_CASE points the same address that MBS points. */
+ const unsigned char *mbs_case;
+ int cur_idx;
+ int len;
+#ifdef RE_ENABLE_I18N
+ /* Store the wide character string which is corresponding to MBS. */
+ wchar_t *wcs;
+#endif
+ /* 1 if mbs is allocated by regex library. */
+ unsigned int mbs_alloc : 1;
+ /* 1 if mbs_case is allocated by regex library. */
+ unsigned int mbs_case_alloc : 1;
+};
+typedef struct re_string_t re_string_t;
+
+static reg_errcode_t re_string_construct (re_string_t *pstr,
+ const unsigned char *str, int len,
+ RE_TRANSLATE_TYPE trans);
+static reg_errcode_t re_string_construct_toupper (re_string_t *pstr,
+ const unsigned char *str,
+ int len,
+ RE_TRANSLATE_TYPE trans);
+static void re_string_destruct (re_string_t *pstr);
+#ifdef RE_ENABLE_I18N
+static int re_string_elem_size_at (const re_string_t *pstr, int idx);
+static inline int re_string_char_size_at (const re_string_t *pstr, int idx);
+static inline wint_t re_string_wchar_at (const re_string_t *pstr, int idx);
+#endif /* RE_ENABLE_I18N */
+static unsigned int re_string_context_at (const re_string_t *input, int idx,
+ int eflags, int newline_anchor);
+#define re_string_peek_byte(pstr, offset) \
+ ((pstr)->mbs[(pstr)->cur_idx + offset])
+#define re_string_peek_byte_case(pstr, offset) \
+ ((pstr)->mbs_case[(pstr)->cur_idx + offset])
+#define re_string_fetch_byte(pstr) \
+ ((pstr)->mbs[(pstr)->cur_idx++])
+#define re_string_fetch_byte_case(pstr) \
+ ((pstr)->mbs_case[(pstr)->cur_idx++])
+#define re_string_first_byte(pstr, idx) \
+ ((idx) == (pstr)->len || (pstr)->wcs[idx] != WEOF)
+#define re_string_is_single_byte_char(pstr, idx) \
+ ((pstr)->wcs[idx] != WEOF && ((pstr)->len == (idx) \
+ || (pstr)->wcs[(idx) + 1] != WEOF))
+#define re_string_eoi(pstr) ((pstr)->len == (pstr)->cur_idx)
+#define re_string_cur_idx(pstr) ((pstr)->cur_idx)
+#define re_string_get_buffer(pstr) ((pstr)->mbs)
+#define re_string_length(pstr) ((pstr)->len)
+#define re_string_byte_at(pstr,idx) \
+ ((pstr)->mbs[idx])
+#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx))
+#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx))
+
+#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t)))
+#define re_realloc(p,t,n) ((t *) realloc (p, (n) * sizeof (t)))
+#define re_free(p) free (p)
+
+struct bin_tree_t
+{
+ struct bin_tree_t *parent;
+ struct bin_tree_t *left;
+ struct bin_tree_t *right;
+
+ /* `node_idx' is the index in dfa->nodes, if `type' == 0.
+ Otherwise `type' indicate the type of this node. */
+ re_token_type_t type;
+ int node_idx;
+
+ int first;
+ int next;
+ re_node_set eclosure;
+};
+typedef struct bin_tree_t bin_tree_t;
+
+struct re_backref_cache_entry
+{
+ int node;
+ int from;
+ int to;
+ int flag;
+};
+
+typedef struct
+{
+ int eflags;
+ int match_first;
+ int match_last;
+ int state_log_top;
+ /* Back reference cache. */
+ int nbkref_ents;
+ int abkref_ents;
+ struct re_backref_cache_entry *bkref_ents;
+ int max_bkref_len;
+} re_match_context_t;
+
+
+#define CONTEXT_WORD 1
+#define CONTEXT_NEWLINE (CONTEXT_WORD << 1)
+#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1)
+#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1)
+
+#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD)
+#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE)
+#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF)
+#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF)
+#define IS_ORDINARY_CONTEXT(c) ((c) == 0)
+
+#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_')
+#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR)
+
+#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \
+ ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+ || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+ || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\
+ || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context)))
+
+#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \
+ ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+ || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+ || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \
+ || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context)))
+
+struct re_dfastate_t
+{
+ unsigned int hash;
+ re_node_set nodes;
+ re_node_set *entrance_nodes;
+ struct re_dfastate_t **trtable;
+ struct re_dfastate_t **trtable_search;
+ /* If this state is a special state.
+ A state is a special state if the state is the halt state, or
+ a anchor. */
+ unsigned int context : 2;
+ unsigned int halt : 1;
+ /* If this state can accept `multi byte'.
+ Note that we refer to multibyte characters, and multi character
+ collating elements as `multi byte'. */
+ unsigned int accept_mb : 1;
+ /* If this state has backreference node(s). */
+ unsigned int has_backref : 1;
+ unsigned int has_constraint : 1;
+};
+typedef struct re_dfastate_t re_dfastate_t;
+
+typedef struct
+{
+ /* start <= node < end */
+ int start;
+ int end;
+} re_subexp_t;
+
+struct re_state_table_entry
+{
+ int num;
+ int alloc;
+ union
+ {
+ re_dfastate_t *state;
+ re_dfastate_t **array;
+ } entry;
+};
+
+struct re_dfa_t
+{
+ re_bitset_ptr_t word_char;
+
+ /* number of subexpressions `re_nsub' is in regex_t. */
+ int subexps_alloc;
+ re_subexp_t *subexps;
+
+ re_token_t *nodes;
+ int nodes_alloc;
+ int nodes_len;
+ bin_tree_t *str_tree;
+ int *firsts;
+ int *nexts;
+ re_node_set *edests;
+ re_node_set *eclosures;
+ re_node_set *inveclosures;
+ struct re_state_table_entry *state_table;
+ unsigned int state_hash_mask;
+ re_dfastate_t *init_state;
+ re_dfastate_t *init_state_word;
+ re_dfastate_t *init_state_nl;
+ re_dfastate_t *init_state_begbuf;
+ int states_alloc;
+ int init_node;
+ int nbackref; /* The number of backreference in this dfa. */
+ /* If this dfa has "multibyte node", which is a backreference or
+ a node which can accept multibyte character or multi character
+ collating element. */
+ unsigned int has_mb_node : 1;
+};
+typedef struct re_dfa_t re_dfa_t;
+
+static reg_errcode_t re_node_set_alloc (re_node_set *set, int size);
+static reg_errcode_t re_node_set_init_1 (re_node_set *set, int elem);
+static reg_errcode_t re_node_set_init_2 (re_node_set *set, int elem1,
+ int elem2);
+#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set))
+static reg_errcode_t re_node_set_init_copy (re_node_set *dest,
+ const re_node_set *src);
+static reg_errcode_t re_node_set_intersect (re_node_set *dest,
+ const re_node_set *src1,
+ const re_node_set *src2);
+static reg_errcode_t re_node_set_add_intersect (re_node_set *dest,
+ const re_node_set *src1,
+ const re_node_set *src2);
+static reg_errcode_t re_node_set_init_union (re_node_set *dest,
+ const re_node_set *src1,
+ const re_node_set *src2);
+static reg_errcode_t re_node_set_merge (re_node_set *dest,
+ const re_node_set *src);
+static int re_node_set_insert (re_node_set *set, int elem);
+static int re_node_set_compare (const re_node_set *set1,
+ const re_node_set *set2);
+static int re_node_set_contains (const re_node_set *set, int elem);
+static void re_node_set_remove_at (re_node_set *set, int idx);
+#define re_node_set_empty(p) ((p)->nelem = 0)
+#define re_node_set_free(set) re_free ((set)->elems)
+static int re_dfa_add_node (re_dfa_t *dfa, re_token_t token, int mode);
+static re_dfastate_t *re_acquire_state (re_dfa_t *dfa,
+ const re_node_set *nodes);
+static re_dfastate_t *re_acquire_state_context (re_dfa_t *dfa,
+ const re_node_set *nodes,
+ unsigned int context);
+
+
+typedef enum
+{
+ SB_CHAR,
+ MB_CHAR,
+ EQUIV_CLASS,
+ COLL_SYM,
+ CHAR_CLASS
+} bracket_elem_type;
+
+typedef struct
+{
+ bracket_elem_type type;
+ union
+ {
+ unsigned char ch;
+ unsigned char *name;
+ wchar_t wch;
+ } opr;
+} bracket_elem_t;
+
+
+/* Inline functions for bitset operation. */
+static inline void
+bitset_not (set)
+ bitset set;
+{
+ int bitset_i;
+ for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i)
+ set[bitset_i] = ~set[bitset_i];
+}
+
+static inline void
+bitset_merge (dest, src)
+ bitset dest;
+ const bitset src;
+{
+ int bitset_i;
+ for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i)
+ dest[bitset_i] |= src[bitset_i];
+}
+
+static inline void
+bitset_not_merge (dest, src)
+ bitset dest;
+ const bitset src;
+{
+ int i;
+ for (i = 0; i < BITSET_UINTS; ++i)
+ dest[i] |= ~src[i];
+}
+
+#ifdef RE_ENABLE_I18N
+/* Inline functions for re_string. */
+static inline int
+re_string_char_size_at (pstr, idx)
+ const re_string_t *pstr;
+ int idx;
+{
+ int byte_idx;
+ if (MB_CUR_MAX == 1)
+ return 1;
+ for (byte_idx = 1; idx + byte_idx < pstr->len; ++byte_idx)
+ if (pstr->wcs[idx + byte_idx] != WEOF)
+ break;
+ return byte_idx;
+}
+
+static inline wint_t
+re_string_wchar_at (pstr, idx)
+ const re_string_t *pstr;
+ int idx;
+{
+ if (MB_CUR_MAX == 1)
+ return (wint_t) pstr->mbs[idx];
+ return (wint_t) pstr->wcs[idx];
+}
+
+static int
+re_string_elem_size_at (pstr, idx)
+ const re_string_t *pstr;
+ int idx;
+{
+#ifdef _LIBC
+ const unsigned char *p;
+ const char *extra;
+ const int32_t *table, *indirect;
+ int32_t tmp;
+# include <locale/weight.h>
+ uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+
+ if (nrules != 0)
+ {
+ table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ extra = (const char *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_INDIRECTMB);
+ p = pstr->mbs + idx;
+ tmp = findidx (&p);
+ return p - (const unsigned char *) pstr->mbs - idx;
+ }
+ else
+#endif /* _LIBC */
+ return 1;
+}
+#endif /* RE_ENABLE_I18N */
+
+#endif /* _REGEX_INTERNAL_H */
diff --git a/posix/regexec.c b/posix/regexec.c
new file mode 100644
index 0000000000..cf8f304b48
--- /dev/null
+++ b/posix/regexec.c
@@ -0,0 +1,2076 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#ifdef _LIBC
+# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
+# define _RE_DEFINE_LOCALE_FUNCTIONS 1
+# include <locale/localeinfo.h>
+# include <locale/elem-hash.h>
+# include <locale/coll-lookup.h>
+# endif
+#endif
+
+#include "regex.h"
+#include "regex_internal.h"
+
+static void match_ctx_init (re_match_context_t *cache, int eflags, int n);
+static void match_ctx_free (re_match_context_t *cache);
+static void match_ctx_add_entry (re_match_context_t *cache, int node, int from,
+ int to);
+static int re_search_internal (const regex_t *preg, const char *string,
+ int length, int start, int range, size_t nmatch,
+ regmatch_t pmatch[], int eflags);
+static inline re_dfastate_t *acquire_init_state_context (const regex_t *preg,
+ const re_string_t *input, int idx, int eflags);
+static int check_matching (const regex_t *preg, re_string_t *input,
+ re_match_context_t *mctx, re_dfastate_t **state_log,
+ int start_idx, int fl_search, int fl_longest_match);
+static int check_halt_node_context (const re_dfa_t *dfa, int node,
+ unsigned int context);
+static int check_halt_state_context (const regex_t *preg,
+ const re_dfastate_t *state,
+ const re_string_t *input, int idx,
+ int eflags);
+static int proceed_next_node (const regex_t *preg,
+ re_dfastate_t **state_log,
+ const re_match_context_t *mctx,
+ const re_string_t *input,
+ int *pidx, int node, re_node_set *eps_via_nodes);
+static void set_regs (const regex_t *preg, re_dfastate_t **state_log,
+ const re_match_context_t *mctx, const re_string_t *input,
+ size_t nmatch, regmatch_t *pmatch, int last);
+static int sift_states_iter_mb (const regex_t *preg, re_dfastate_t **state_log,
+ const re_match_context_t *mctx,
+ const re_string_t *input, int node_idx,
+ int str_idx, int max_str_idx);
+static int sift_states_iter_bkref (const re_dfa_t *dfa,
+ re_dfastate_t **state_log,
+ struct re_backref_cache_entry *mctx_entry,
+ int node_idx, int idx, int match_first,
+ int match_last);
+static void sift_states_backward (const regex_t *preg,
+ re_dfastate_t **state_log,
+ const re_match_context_t *mctx,
+ const re_string_t *input, int last_node);
+static void add_epsilon_backreference (const re_dfa_t *dfa,
+ const re_match_context_t *mctx,
+ const re_node_set *plog, int idx,
+ re_node_set *state_buf);
+static re_dfastate_t *transit_state (const regex_t *preg, re_dfastate_t *state,
+ re_string_t *input, int fl_search,
+ re_dfastate_t **state_log,
+ re_match_context_t *mctx);
+static re_dfastate_t *transit_state_sb (const regex_t *preg,
+ re_dfastate_t *pstate,
+ re_string_t *input, int fl_search,
+ re_match_context_t *mctx);
+static void transit_state_mb (const regex_t *preg, re_dfastate_t *pstate,
+ const re_string_t *input,
+ re_dfastate_t **state_log,
+ re_match_context_t *mctx);
+static void transit_state_bkref (const regex_t *preg, re_dfastate_t *pstate,
+ const re_string_t *input,
+ re_dfastate_t **state_log,
+ re_match_context_t *mctx);
+static void transit_state_bkref_loop (const regex_t *preg,
+ const re_string_t *input,
+ re_node_set *nodes,
+ re_dfastate_t **work_state_log,
+ re_dfastate_t **state_log,
+ re_match_context_t *mctx);
+static re_dfastate_t **build_trtable (const regex_t *dfa,
+ const re_dfastate_t *state,
+ int fl_search);
+static int check_node_accept_bytes (const regex_t *preg, int node_idx,
+ const re_string_t *input, int idx);
+static unsigned int find_collation_sequence_value (const unsigned char *mbs,
+ size_t name_len);
+static int group_nodes_into_DFAstates (const regex_t *dfa,
+ const re_dfastate_t *state,
+ re_node_set *states_node,
+ bitset *states_ch);
+static int check_node_accept (const regex_t *preg, const re_token_t *node,
+ const re_string_t *input, int idx, int eflags);
+
+/* Entry point for POSIX code. */
+
+/* regexec searches for a given pattern, specified by PREG, in the
+ string STRING.
+
+ If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+ `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
+ least NMATCH elements, and we set them to the offsets of the
+ corresponding matched substrings.
+
+ EFLAGS specifies `execution flags' which affect matching: if
+ REG_NOTBOL is set, then ^ does not match at the beginning of the
+ string; if REG_NOTEOL is set, then $ does not match at the end.
+
+ We return 0 if we find a match and REG_NOMATCH if not. */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+ const regex_t *preg;
+ const char *string;
+ size_t nmatch;
+ regmatch_t pmatch[];
+ int eflags;
+{
+ int length = strlen (string);
+ if (preg->no_sub)
+ return re_search_internal (preg, string, length, 0, length, 0,
+ NULL, eflags);
+ else
+ return re_search_internal (preg, string, length, 0, length, nmatch,
+ pmatch, eflags);
+}
+#ifdef _LIBC
+weak_alias (__regexec, regexec)
+#endif
+
+/* Entry points for GNU code. */
+
+/* re_match is like re_match_2 except it takes only a single string. */
+
+int
+re_match (buffer, string, length, start, regs)
+ struct re_pattern_buffer *buffer;
+ const char *string;
+ int length, start;
+ struct re_registers *regs;
+{
+ int i, nregs, result, rval, eflags = 0;
+ regmatch_t *pmatch;
+
+ eflags |= (buffer->not_bol) ? REG_NOTBOL : 0;
+ eflags |= (buffer->not_eol) ? REG_NOTEOL : 0;
+
+ /* We need at least 1 register. */
+ nregs = ((regs == NULL) ? 1
+ : ((regs->num_regs > buffer->re_nsub) ? buffer->re_nsub + 1
+ : regs->num_regs + 1));
+ pmatch = re_malloc (regmatch_t, nregs);
+ if (pmatch == NULL)
+ return -2;
+ result = re_search_internal (buffer, string, length, start, 0,
+ nregs, pmatch, eflags);
+
+ /* If caller wants register contents data back, do it. */
+ if (regs && !buffer->no_sub)
+ {
+ /* Have the register data arrays been allocated? */
+ if (buffer->regs_allocated == REGS_UNALLOCATED)
+ { /* No. So allocate them with malloc. We need one
+ extra element beyond `num_regs' for the `-1' marker
+ GNU code uses. */
+ regs->num_regs = ((RE_NREGS > buffer->re_nsub + 1) ? RE_NREGS
+ : buffer->re_nsub + 1);
+ regs->start = re_malloc (regoff_t, regs->num_regs);
+ regs->end = re_malloc (regoff_t, regs->num_regs);
+ if (regs->start == NULL || regs->end == NULL)
+ {
+ re_free (pmatch);
+ return -2;
+ }
+ buffer->regs_allocated = REGS_REALLOCATE;
+ }
+ else if (buffer->regs_allocated == REGS_REALLOCATE)
+ { /* Yes. If we need more elements than were already
+ allocated, reallocate them. If we need fewer, just
+ leave it alone. */
+ if (regs->num_regs < buffer->re_nsub + 1)
+ {
+ regs->num_regs = buffer->re_nsub + 1;
+ regs->start = re_realloc (regs->start, regoff_t, regs->num_regs);
+ regs->end = re_realloc (regs->end, regoff_t, regs->num_regs);
+ if (regs->start == NULL || regs->end == NULL)
+ {
+ re_free (pmatch);
+ return -2;
+ }
+ }
+ }
+ else
+ {
+ /* These braces fend off a "empty body in an else-statement"
+ warning under GCC when assert expands to nothing. */
+ assert (buffer->regs_allocated == REGS_FIXED);
+ }
+ }
+
+ /* Restore registers. */
+ if (regs != NULL)
+ {
+ for (i = 0; i <= nregs; ++i)
+ {
+ regs->start[i] = pmatch[i].rm_so;
+ regs->end[i] = pmatch[i].rm_eo;
+ }
+ for ( ; i < regs->num_regs; ++i)
+ {
+ regs->start[i] = -1;
+ regs->end[i] = -1;
+ }
+ }
+ /* Return value is -1 if not match, the length of mathing otherwise. */
+ rval = (result) ? -1 : pmatch[0].rm_eo - pmatch[0].rm_so;
+ re_free (pmatch);
+ return rval;
+}
+#ifdef _LIBC
+weak_alias (__re_match, re_match)
+#endif
+
+/* re_match_2 matches the compiled pattern in BUFP against the
+ the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
+ and SIZE2, respectively). We start matching at POS, and stop
+ matching at STOP.
+
+ If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
+ store offsets for the substring each group matched in REGS. See the
+ documentation for exactly how many groups we fill.
+
+ We return -1 if no match, -2 if an internal error.
+ Otherwise, we return the length of the matched substring. */
+
+int
+re_match_2 (buffer, string1, length1, string2, length2, start, regs, stop)
+ struct re_pattern_buffer *buffer;
+ const char *string1, *string2;
+ int length1, length2, start, stop;
+ struct re_registers *regs;
+{
+ int len, ret;
+ char *str = re_malloc (char, length1 + length2);
+ if (str == NULL)
+ return -2;
+ memcpy (str, string1, length1);
+ memcpy (str + length1, string2, length2);
+ len = (length1 + length2 < stop) ? length1 + length2 : stop;
+ ret = re_match (buffer, str, len, start, regs);
+ re_free (str);
+ return ret;
+}
+#ifdef _LIBC
+weak_alias (__re_match_2, re_match_2)
+#endif
+
+/* Like re_search_2, below, but only one string is specified, and
+ doesn't let you say where to stop matching. */
+
+int
+re_search (bufp, string, size, startpos, range, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int size, startpos, range;
+ struct re_registers *regs;
+{
+ int i, nregs, result, real_range, rval, eflags = 0;
+ regmatch_t *pmatch;
+
+ eflags |= (bufp->not_bol) ? REG_NOTBOL : 0;
+ eflags |= (bufp->not_eol) ? REG_NOTEOL : 0;
+
+ /* Check for out-of-range. */
+ if (startpos < 0 || startpos > size)
+ return -1;
+
+ /* We need at least 1 register. */
+ nregs = ((regs == NULL) ? 1
+ : ((regs->num_regs > bufp->re_nsub) ? bufp->re_nsub + 1
+ : regs->num_regs + 1));
+ pmatch = re_malloc (regmatch_t, nregs);
+
+ /* Correct range if we need. */
+ real_range = ((startpos + range > size) ? size - startpos
+ : ((startpos + range < 0) ? -startpos : range));
+
+ /* Compile fastmap if we haven't yet. */
+ if (bufp->fastmap != NULL && !bufp->fastmap_accurate)
+ re_compile_fastmap (bufp);
+
+ result = re_search_internal (bufp, string, size, startpos, real_range,
+ nregs, pmatch, eflags);
+
+ /* If caller wants register contents data back, do it. */
+ if (regs && !bufp->no_sub)
+ {
+ /* Have the register data arrays been allocated? */
+ if (bufp->regs_allocated == REGS_UNALLOCATED)
+ { /* No. So allocate them with malloc. We need one
+ extra element beyond `num_regs' for the `-1' marker
+ GNU code uses. */
+ regs->num_regs = ((RE_NREGS > bufp->re_nsub + 1) ? RE_NREGS
+ : bufp->re_nsub + 1);
+ regs->start = re_malloc (regoff_t, regs->num_regs);
+ regs->end = re_malloc (regoff_t, regs->num_regs);
+ if (regs->start == NULL || regs->end == NULL)
+ {
+ re_free (pmatch);
+ return -2;
+ }
+ bufp->regs_allocated = REGS_REALLOCATE;
+ }
+ else if (bufp->regs_allocated == REGS_REALLOCATE)
+ { /* Yes. If we need more elements than were already
+ allocated, reallocate them. If we need fewer, just
+ leave it alone. */
+ if (regs->num_regs < bufp->re_nsub + 1)
+ {
+ regs->num_regs = bufp->re_nsub + 1;
+ regs->start = re_realloc (regs->start, regoff_t, regs->num_regs);
+ regs->end = re_realloc (regs->end, regoff_t, regs->num_regs);
+ if (regs->start == NULL || regs->end == NULL)
+ {
+ re_free (pmatch);
+ return -2;
+ }
+ }
+ }
+ else
+ {
+ /* These braces fend off a "empty body in an else-statement"
+ warning under GCC when assert expands to nothing. */
+ assert (bufp->regs_allocated == REGS_FIXED);
+ }
+ }
+
+ /* Restore registers. */
+ if (regs != NULL)
+ {
+ for (i = 0; i <= bufp->re_nsub; ++i)
+ {
+ regs->start[i] = pmatch[i].rm_so;
+ regs->end[i] = pmatch[i].rm_eo;
+ }
+ for ( ; i < regs->num_regs; ++i)
+ {
+ regs->start[i] = -1;
+ regs->end[i] = -1;
+ }
+ }
+ /* Return value is -1 if not match, the position where the mathing starts
+ otherwise. */
+ rval = (result) ? -1 : pmatch[0].rm_so;
+ re_free (pmatch);
+ return rval;
+}
+#ifdef _LIBC
+weak_alias (__re_search, re_search)
+#endif
+
+/* Using the compiled pattern in BUFP, first tries to match the virtual
+ concatenation of STRING1 and STRING2, starting first at index
+ STARTPOS, then at STARTPOS + 1, and so on.
+
+ STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
+
+ RANGE is how far to scan while trying to match. RANGE = 0 means try
+ only at STARTPOS; in general, the last start tried is STARTPOS +
+ RANGE.
+
+ In REGS, return the indices of the virtual concatenation of STRING1
+ and STRING2 that matched the entire BUFP->buffer and its contained
+ subexpressions.
+
+ Do not consider matching one past the index STOP in the virtual
+ concatenation of STRING1 and STRING2.
+
+ We return either the position in the strings at which the match was
+ found, -1 if no match, or -2 if error. */
+
+int
+re_search_2 (bufp, string1, length1, string2, length2, start, range, regs,
+ stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int length1, length2, start, range, stop;
+ struct re_registers *regs;
+{
+ int len, ret;
+ char *str = re_malloc (char, length1 + length2);
+ memcpy (str, string1, length1);
+ memcpy (str + length1, string2, length2);
+ len = (length1 + length2 < stop) ? length1 + length2 : stop;
+ ret = re_search (bufp, str, len, start, range, regs);
+ re_free (str);
+ return ret;
+}
+#ifdef _LIBC
+weak_alias (__re_search_2, re_search_2)
+#endif
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use
+ this memory for recording register information. STARTS and ENDS
+ must be allocated using the malloc library routine, and must each
+ be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+ struct re_pattern_buffer *bufp;
+ struct re_registers *regs;
+ unsigned num_regs;
+ regoff_t *starts, *ends;
+{
+ if (num_regs)
+ {
+ bufp->regs_allocated = REGS_REALLOCATE;
+ regs->num_regs = num_regs;
+ regs->start = starts;
+ regs->end = ends;
+ }
+ else
+ {
+ bufp->regs_allocated = REGS_UNALLOCATED;
+ regs->num_regs = 0;
+ regs->start = regs->end = (regoff_t *) 0;
+ }
+}
+#ifdef _LIBC
+weak_alias (__re_set_registers, re_set_registers)
+#endif
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them unless specifically requested. */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+int
+# ifdef _LIBC
+weak_function
+# endif
+re_exec (s)
+ const char *s;
+{
+ return 0 == regexec (&re_comp_buf, s, 0, NULL, 0);
+}
+#endif /* _REGEX_RE_COMP */
+
+static re_node_set empty_set;
+
+/* Internal entry point. */
+
+/* Searches for a compiled pattern PREG in the string STRING, whose
+ length is LENGTH. NMATCH, PMATCH, and EFLAGS have the same
+ mingings with regexec. START, and RANGE have the same meanings
+ with re_search.
+ Return 0 if we find a match and REG_NOMATCH if not.
+ Note: We assume front end functions already check ranges.
+ (START + RANGE >= 0 && START + RANGE <= LENGTH) */
+
+static int
+re_search_internal (preg, string, length, start, range, nmatch, pmatch, eflags)
+ const regex_t *preg;
+ const char *string;
+ int length, start, range, eflags;
+ size_t nmatch;
+ regmatch_t pmatch[];
+{
+ re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+ re_string_t input;
+ re_dfastate_t **state_log;
+ int fl_longest_match, match_first, match_last = -1;
+ re_match_context_t mctx;
+ char *fastmap = ((preg->fastmap != NULL && preg->fastmap_accurate)
+ ? preg->fastmap : NULL);
+
+ /* Check if the DFA haven't been compiled. */
+ if (preg->used == 0 || dfa->init_state == NULL
+ || dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+ || dfa->init_state_begbuf == NULL)
+ return 1;
+
+ re_node_set_init_empty (&empty_set);
+
+ /* We must check the longest matching, if nmatch > 0. */
+ fl_longest_match = (nmatch != 0);
+
+ /* We will log all the DFA states through which the dfa pass,
+ if nmatch > 1, or this dfa has "multibyte node", which is a
+ back-reference or a node which can accept multibyte character or
+ multi character collating element. */
+ if (nmatch > 1 || dfa->has_mb_node)
+ state_log = re_malloc (re_dfastate_t *, length + 1);
+ else
+ state_log = NULL;
+
+ if (preg->syntax & RE_ICASE)
+ re_string_construct_toupper (&input, string, length, preg->translate);
+ else
+ re_string_construct (&input, string, length, preg->translate);
+
+ match_ctx_init (&mctx, eflags, dfa->nbackref * 2);
+
+#ifdef DEBUG
+ /* We assume front-end functions already check them. */
+ assert (start + range >= 0 && start + range <= length);
+#endif
+
+ /* Check incrementally whether of not the input string match. */
+ for (match_first = start; ;)
+ {
+ if ((match_first < length
+ && (fastmap == NULL
+ || fastmap[re_string_byte_at (&input, match_first)]))
+ || preg->can_be_null)
+ {
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX == 1 || re_string_first_byte (&input, match_first))
+#endif
+ {
+ /* We assume that the matching starts from `match_first'. */
+ re_string_set_index (&input, match_first);
+ mctx.match_first = mctx.state_log_top = match_first;
+ mctx.nbkref_ents = mctx.max_bkref_len = 0;
+ match_last = check_matching (preg, &input, &mctx, state_log,
+ match_first, 0, fl_longest_match);
+ if (match_last != -1)
+ break;
+ }
+ }
+ /* Update counter. */
+ if (range < 0)
+ {
+ --match_first;
+ if (match_first < start + range)
+ break;
+ }
+ else
+ {
+ ++match_first;
+ if (match_first > start + range)
+ break;
+ }
+ }
+
+ /* Set pmatch[] if we need. */
+ if (match_last != -1 && nmatch > 0)
+ {
+ int reg_idx;
+
+ /* Initialize registers. */
+ for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+ pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1;
+
+ /* Set the points where matching start/end. */
+ pmatch[0].rm_so = mctx.match_first;
+ mctx.match_last = pmatch[0].rm_eo = match_last;
+
+ if (!preg->no_sub && nmatch > 1)
+ {
+ /* We need the ranges of all the subexpressions. */
+ int halt_node;
+ re_dfastate_t *pstate = state_log[match_last];
+#ifdef DEBUG
+ assert (state_log != NULL);
+#endif
+ halt_node = check_halt_state_context (preg, pstate, &input,
+ match_last, eflags);
+ sift_states_backward (preg, state_log, &mctx, &input, halt_node);
+ set_regs (preg, state_log, &mctx, &input, nmatch, pmatch, halt_node);
+ }
+ }
+
+ re_free (state_log);
+ if (dfa->nbackref)
+ match_ctx_free (&mctx);
+ re_string_destruct (&input);
+ return match_last == -1;
+}
+
+/* Acquire an initial state.
+ We must select appropriate initial state depending on the context,
+ since initial states may have constraints like "\<", "^", etc.. */
+
+static inline re_dfastate_t *
+acquire_init_state_context (preg, input, idx, eflags)
+ const regex_t *preg;
+ const re_string_t *input;
+ int idx, eflags;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+
+ if (dfa->init_state->has_constraint)
+ {
+ unsigned int context;
+ context = re_string_context_at (input, idx - 1, eflags,
+ preg->newline_anchor);
+ if (IS_WORD_CONTEXT (context))
+ return dfa->init_state_word;
+ else if (IS_ORDINARY_CONTEXT (context))
+ return dfa->init_state;
+ else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context))
+ return dfa->init_state_begbuf;
+ else if (IS_NEWLINE_CONTEXT (context))
+ return dfa->init_state_nl;
+ else if (IS_BEGBUF_CONTEXT (context))
+ /* It is relatively rare case, then calculate on demand. */
+ return re_acquire_state_context (dfa, dfa->init_state->entrance_nodes,
+ context);
+ else
+ /* Must not happen? */
+ return dfa->init_state;
+ }
+ else
+ return dfa->init_state;
+}
+
+/* Check whether the regular expression match input string INPUT or not,
+ and return the index where the matching end, or return -1 if not match.
+ FL_SEARCH means we must search where the matching starts,
+ FL_LONGEST_MATCH means we want the POSIX longest matching. */
+
+static int
+check_matching (preg, input, mctx, state_log, start_idx, fl_search,
+ fl_longest_match)
+ const regex_t *preg;
+ re_string_t *input;
+ re_match_context_t *mctx;
+ re_dfastate_t **state_log;
+ int start_idx, fl_search, fl_longest_match;
+{
+ int match = 0, match_last = -1;
+ re_dfastate_t *cur_state;
+
+ cur_state = acquire_init_state_context (preg, input, start_idx,
+ mctx->eflags);
+ if (state_log != NULL)
+ state_log[start_idx] = cur_state;
+ /* If the RE accepts NULL string. */
+ if (cur_state->halt)
+ {
+ if (!cur_state->has_constraint
+ || check_halt_state_context (preg, cur_state, input, start_idx,
+ mctx->eflags))
+ {
+ if (!fl_longest_match)
+ return start_idx;
+ else
+ {
+ match_last = start_idx;
+ match = 1;
+ }
+ }
+ }
+
+ while (!re_string_eoi (input))
+ {
+ cur_state = transit_state (preg, cur_state, input, fl_search && !match,
+ state_log, mctx);
+ if (cur_state == NULL) /* Reached at the invalid state. */
+ {
+ int cur_str_idx = re_string_cur_idx (input);
+ if (fl_search && !match)
+ {
+ /* Restart from initial state, since we are searching
+ the point from where matching start. */
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX == 1 || re_string_first_byte (input, cur_str_idx))
+#endif /* RE_ENABLE_I18N */
+ cur_state = acquire_init_state_context (preg, input,
+ cur_str_idx,
+ mctx->eflags);
+ if (state_log != NULL)
+ state_log[cur_str_idx] = cur_state;
+ }
+ else if (!fl_longest_match && match)
+ break;
+ else /* (fl_longest_match && match) || (!fl_search && !match) */
+ {
+ if (state_log == NULL)
+ break;
+ else
+ {
+ int max = mctx->state_log_top;
+ for (; cur_str_idx <= max; ++cur_str_idx)
+ if (state_log[cur_str_idx] != NULL)
+ break;
+ if (cur_str_idx > max)
+ break;
+ }
+ }
+ }
+
+ if (cur_state != NULL && cur_state->halt)
+ {
+ /* Reached at a halt state.
+ Check the halt state can satisfy the current context. */
+ if (!cur_state->has_constraint
+ || check_halt_state_context (preg, cur_state, input,
+ re_string_cur_idx (input),
+ mctx->eflags))
+ {
+ /* We found an appropriate halt state. */
+ match_last = re_string_cur_idx (input);
+ match = 1;
+ if (!fl_longest_match)
+ break;
+ }
+ }
+ }
+ return match_last;
+}
+
+/* Check NODE match the current context. */
+
+static int check_halt_node_context (dfa, node, context)
+ const re_dfa_t *dfa;
+ int node;
+ unsigned int context;
+{
+ int entity;
+ re_token_type_t type = dfa->nodes[node].type;
+ if (type == END_OF_RE)
+ return 1;
+ if (type != OP_CONTEXT_NODE)
+ return 0;
+ entity = dfa->nodes[node].opr.ctx_info->entity;
+ if (dfa->nodes[entity].type != END_OF_RE
+ || NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[node].constraint, context))
+ return 0;
+ return 1;
+}
+
+/* Check the halt state STATE match the current context.
+ Return 0 if not match, if the node, STATE has, is a halt node and
+ match the context, return the node. */
+
+static int
+check_halt_state_context (preg, state, input, idx, eflags)
+ const regex_t *preg;
+ const re_dfastate_t *state;
+ const re_string_t *input;
+ int idx, eflags;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ int i;
+ unsigned int context;
+#ifdef DEBUG
+ assert (state->halt);
+#endif
+ context = re_string_context_at (input, idx, eflags, preg->newline_anchor);
+ for (i = 0; i < state->nodes.nelem; ++i)
+ if (check_halt_node_context (dfa, state->nodes.elems[i], context))
+ return state->nodes.elems[i];
+ return 0;
+}
+
+/* Compute the next node to which "NFA" transit from NODE.
+ Return the destination node, and update EPS_VIA_NODES.
+ ("NFA" is a NFA corresponding to the DFA. */
+
+static int
+proceed_next_node (preg, state_log, mctx, input, pidx, node, eps_via_nodes)
+ const regex_t *preg;
+ re_dfastate_t **state_log;
+ const re_match_context_t *mctx;
+ const re_string_t *input;
+ int *pidx, node;
+ re_node_set *eps_via_nodes;
+{
+ re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+ int i, dest_node = -1;
+ if (IS_EPSILON_NODE (dfa->nodes[node].type))
+ {
+ re_node_set_insert (eps_via_nodes, node);
+ for (i = 0; i < state_log[*pidx]->nodes.nelem; ++i)
+ {
+ int candidate = state_log[*pidx]->nodes.elems[i];
+ if (!re_node_set_contains (dfa->edests + node, candidate)
+ && !(dfa->nodes[candidate].type == OP_CONTEXT_NODE
+ && re_node_set_contains (dfa->edests + node,
+ dfa->nodes[candidate].opr.ctx_info->entity)))
+ continue;
+ dest_node = candidate;
+ /* In order to avoid infinite loop like "(a*)*". */
+ if (!re_node_set_contains (eps_via_nodes, dest_node))
+ break;
+ }
+#ifdef DEBUG
+ assert (dest_node != -1);
+#endif
+ return dest_node;
+ }
+ else
+ {
+ int naccepted = 0, entity = node;
+ re_token_type_t type = dfa->nodes[node].type;
+ if (type == OP_CONTEXT_NODE)
+ {
+ entity = dfa->nodes[node].opr.ctx_info->entity;
+ type = dfa->nodes[entity].type;
+ }
+
+ if (ACCEPT_MB_NODE (type))
+ naccepted = check_node_accept_bytes (preg, entity, input, *pidx);
+ else if (type == OP_BACK_REF)
+ {
+ for (i = 0; i < mctx->nbkref_ents; ++i)
+ {
+ if (mctx->bkref_ents[i].node == node
+ && mctx->bkref_ents[i].from == *pidx)
+ naccepted = mctx->bkref_ents[i].to - *pidx;
+ }
+ if (naccepted == 0)
+ {
+ re_node_set_insert (eps_via_nodes, node);
+ dest_node = dfa->nexts[node];
+ if (re_node_set_contains (&state_log[*pidx]->nodes, dest_node))
+ return dest_node;
+ for (i = 0; i < state_log[*pidx]->nodes.nelem; ++i)
+ {
+ dest_node = state_log[*pidx]->nodes.elems[i];
+ if ((dfa->nodes[dest_node].type == OP_CONTEXT_NODE
+ && (dfa->nexts[node]
+ == dfa->nodes[dest_node].opr.ctx_info->entity)))
+ return dest_node;
+ }
+ }
+ }
+
+ if (naccepted != 0
+ || check_node_accept (preg, dfa->nodes + node, input, *pidx,
+ mctx->eflags))
+ {
+ dest_node = dfa->nexts[node];
+ *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted;
+#ifdef DEBUG
+ assert (state_log[*pidx] != NULL);
+#endif
+ re_node_set_empty (eps_via_nodes);
+ return dest_node;
+ }
+ }
+ /* Must not reach here. */
+#ifdef DEBUG
+ assert (0);
+#endif
+ return 0;
+}
+
+/* Set the positions where the subexpressions are starts/ends to registers
+ PMATCH.
+ Note: We assume that pmatch[0] is already set, and
+ pmatch[i].rm_so == pmatch[i].rm_eo == -1 (i > 1). */
+
+static void
+set_regs (preg, state_log, mctx, input, nmatch, pmatch, last_node)
+ const regex_t *preg;
+ re_dfastate_t **state_log;
+ const re_match_context_t *mctx;
+ const re_string_t *input;
+ size_t nmatch;
+ regmatch_t *pmatch;
+ int last_node;
+{
+ re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+ int idx, cur_node, node_entity, real_nmatch;
+ re_node_set eps_via_nodes;
+ int i;
+#ifdef DEBUG
+ assert (nmatch > 1);
+ assert (state_log != NULL);
+#endif
+ cur_node = dfa->init_node;
+ real_nmatch = (nmatch <= preg->re_nsub) ? nmatch : preg->re_nsub + 1;
+ re_node_set_init_empty (&eps_via_nodes);
+ for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;)
+ {
+ node_entity = ((dfa->nodes[cur_node].type == OP_CONTEXT_NODE)
+ ? dfa->nodes[cur_node].opr.ctx_info->entity : cur_node);
+ for (i = 1; i < real_nmatch; ++i)
+ {
+ if (dfa->subexps[i - 1].start == dfa->subexps[i - 1].end)
+ {
+ /* In case of the null subexpression like '()'. */
+ if (dfa->subexps[i - 1].start == node_entity)
+ {
+ pmatch[i].rm_so = idx;
+ pmatch[i].rm_eo = idx;
+ }
+ }
+ else if (dfa->subexps[i - 1].start <= node_entity
+ && node_entity < dfa->subexps[i - 1].end)
+ {
+ if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo != -1)
+ /* We are at the first node of this sub expression. */
+ {
+ pmatch[i].rm_so = idx;
+ pmatch[i].rm_eo = -1;
+ }
+ }
+ else
+ {
+ if (pmatch[i].rm_so != -1 && pmatch[i].rm_eo == -1)
+ /* We are at the last node of this sub expression. */
+ pmatch[i].rm_eo = idx;
+ }
+ }
+ if (idx == pmatch[0].rm_eo && cur_node == last_node)
+ break;
+
+ /* Proceed to next node. */
+ cur_node = proceed_next_node (preg, state_log, mctx, input, &idx,
+ cur_node, &eps_via_nodes);
+ }
+ re_node_set_free (&eps_via_nodes);
+ return;
+}
+
+#define NUMBER_OF_STATE 1
+
+/* This function checks the STATE_LOG from the MCTX->match_last
+ to MCTX->match_first and sift the nodes in each states according to
+ the following rules. Updated state_log will be wrote to STATE_LOG.
+
+ Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if...
+ 1. When STR_IDX == MATCH_LAST(the last index in the state_log):
+ If `a' isn't the LAST_NODE and `a' can't epsilon transit to
+ the LAST_NODE, we throw away the node `a'.
+ 2. When MATCH_FIRST <= STR_IDX < MATCH_LAST and `a' accepts
+ string `s' and transit to `b':
+ i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw
+ away the node `a'.
+ ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is
+ throwed away, we throw away the node `a'.
+ 3. When 0 <= STR_IDX < n and 'a' epsilon transit to 'b':
+ i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the
+ node `a'.
+ ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is throwed away,
+ we throw away the node `a'. */
+
+#define STATE_NODE_CONTAINS(state,node) \
+ ((state) != NULL && re_node_set_contains (&(state)->nodes, node))
+
+static void
+sift_states_backward (preg, state_log, mctx, input, last_node)
+ const regex_t *preg;
+ re_dfastate_t **state_log;
+ const re_match_context_t *mctx;
+ const re_string_t *input;
+ int last_node;
+{
+ re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+ re_node_set state_buf;
+ int str_idx = mctx->match_last;
+ re_node_set *plog; /* Points the state_log[str_idx]->nodes */
+
+#ifdef DEBUG
+ assert (state_log != NULL && state_log[str_idx] != NULL);
+#endif
+ re_node_set_alloc (&state_buf, NUMBER_OF_STATE);
+ plog = &state_log[str_idx]->nodes;
+
+ /* Build sifted state_log[str_idx]. It has the nodes which can epsilon
+ transit to the last_node and the last_node itself. */
+ re_node_set_intersect (&state_buf, plog, dfa->inveclosures + last_node);
+
+ if (state_log[str_idx] != NULL && state_log[str_idx]->has_backref)
+ add_epsilon_backreference (dfa, mctx, plog, str_idx, &state_buf);
+
+ /* Update state log. */
+ state_log[str_idx] = re_acquire_state (dfa, &state_buf);
+
+ /* Then check each states in the state_log. */
+ while (str_idx > mctx->match_first)
+ {
+ int i, j;
+ /* Update counters. */
+ re_node_set_empty (&state_buf);
+ --str_idx;
+ plog = ((state_log[str_idx] == NULL) ? &empty_set
+ : &state_log[str_idx]->nodes);
+
+ /* Then build the next sifted state.
+ We build the next sifted state on `state_buf', and update
+ `state_log[str_idx]' with `state_buf'.
+ Note:
+ `state_buf' is the sifted state from `state_log[str_idx + 1]'.
+ `plog' points the node_set of the old `state_log[str_idx]'. */
+ for (i = 0; i < plog->nelem; i++)
+ {
+ int prev_node = plog->elems[i];
+ int entity = prev_node;
+ int naccepted = 0;
+ re_token_type_t type = dfa->nodes[prev_node].type;
+ if (type == OP_CONTEXT_NODE)
+ {
+ entity = dfa->nodes[prev_node].opr.ctx_info->entity;
+ type = dfa->nodes[entity].type;
+ }
+
+ /* If the node may accept `multi byte'. */
+ if (ACCEPT_MB_NODE (type))
+ naccepted = sift_states_iter_mb (preg, state_log, mctx, input,
+ entity, str_idx,
+ mctx->match_last);
+
+ /* If the node is a back reference. */
+ else if (type == OP_BACK_REF)
+ for (j = 0; j < mctx->nbkref_ents; ++j)
+ {
+ naccepted = sift_states_iter_bkref (dfa, state_log,
+ mctx->bkref_ents + j,
+ prev_node, str_idx,
+ mctx->match_first,
+ mctx->match_last);
+ if (naccepted)
+ break;
+ }
+
+ if (!naccepted
+ && check_node_accept (preg, dfa->nodes + prev_node, input,
+ str_idx, mctx->eflags)
+ && STATE_NODE_CONTAINS (state_log[str_idx + 1],
+ dfa->nexts[prev_node]))
+ naccepted = 1;
+
+ if (naccepted == 0)
+ continue;
+
+ /* `prev_node' may point the entity of the OP_CONTEXT_NODE,
+ then we use plog->elems[i] instead. */
+ re_node_set_add_intersect (&state_buf, plog,
+ dfa->inveclosures + prev_node);
+ }
+ if (state_log[str_idx] != NULL && state_log[str_idx]->has_backref)
+ add_epsilon_backreference (dfa, mctx, plog, str_idx, &state_buf);
+
+ /* Update state_log. */
+ state_log[str_idx] = re_acquire_state (dfa, &state_buf);
+ }
+
+ re_node_set_free (&state_buf);
+}
+
+/* Helper functions. */
+
+static inline void
+clean_state_log_if_need (state_log, mctx, next_state_log_idx)
+ re_dfastate_t **state_log;
+ re_match_context_t *mctx;
+ int next_state_log_idx;
+{
+ int top = mctx->state_log_top;
+ if (top < next_state_log_idx)
+ {
+ memset (state_log + top + 1, '\0',
+ sizeof (re_dfastate_t *) * (next_state_log_idx - top));
+ mctx->state_log_top = next_state_log_idx;
+ }
+}
+
+static int
+sift_states_iter_mb (preg, state_log, mctx, input, node_idx, str_idx,
+ max_str_idx)
+ const regex_t *preg;
+ re_dfastate_t **state_log;
+ const re_match_context_t *mctx;
+ const re_string_t *input;
+ int node_idx, str_idx, max_str_idx;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ int naccepted;
+ /* Check the node can accept `multi byte'. */
+ naccepted = check_node_accept_bytes (preg, node_idx, input, str_idx);
+ if (naccepted > 0 && str_idx + naccepted <= max_str_idx &&
+ !STATE_NODE_CONTAINS (state_log[str_idx + naccepted],
+ dfa->nexts[node_idx]))
+ /* The node can't accept the `multi byte', or the
+ destination was already throwed away, then the node
+ could't accept the current input `multi byte'. */
+ naccepted = 0;
+ /* Otherwise, it is sure that the node could accept
+ `naccepted' bytes input. */
+ return naccepted;
+}
+
+static int
+sift_states_iter_bkref (dfa, state_log, mctx_entry, node_idx, idx, match_first,
+ match_last)
+ const re_dfa_t *dfa;
+ re_dfastate_t **state_log;
+ struct re_backref_cache_entry *mctx_entry;
+ int node_idx, idx, match_first, match_last;
+{
+ int naccepted = 0;
+ int from_idx, to_idx;
+ from_idx = mctx_entry->from;
+ to_idx = mctx_entry->to;
+ if (mctx_entry->node == node_idx
+ && from_idx == idx && to_idx <= match_last
+ && STATE_NODE_CONTAINS (state_log[to_idx], dfa->nexts[node_idx]))
+ naccepted = to_idx - from_idx;
+ return naccepted;
+}
+
+static void
+add_epsilon_backreference (dfa, mctx, plog, idx, state_buf)
+ const re_dfa_t *dfa;
+ const re_match_context_t *mctx;
+ const re_node_set *plog;
+ int idx;
+ re_node_set *state_buf;
+{
+ int i, j;
+ for (i = 0; i < plog->nelem; ++i)
+ {
+ int node_idx = plog->elems[i];
+ re_token_type_t type = dfa->nodes[node_idx].type;
+ if (type == OP_CONTEXT_NODE)
+ type = dfa->nodes[dfa->nodes[node_idx].opr.ctx_info->entity].type;
+
+ if (type == OP_BACK_REF &&
+ !re_node_set_contains (state_buf, node_idx))
+ {
+ for (j = 0; j < mctx->nbkref_ents; ++j)
+ {
+ struct re_backref_cache_entry *entry;
+ entry = mctx->bkref_ents + j;
+ if (entry->from == entry->to && entry->from == idx)
+ break;
+ }
+ if (j < mctx->nbkref_ents || idx == mctx->match_first)
+ {
+ re_node_set_add_intersect (state_buf, plog,
+ dfa->inveclosures + node_idx);
+ i = 0;
+ }
+ }
+ }
+}
+
+/* Functions for state transition. */
+
+/* Return the next state to which the current state STATE will transit by
+ accepting the current input byte, and update STATE_LOG if necessary.
+ If STATE can accept a multibyte char/collating element/back reference
+ update the destination of STATE_LOG. */
+
+static re_dfastate_t *
+transit_state (preg, state, input, fl_search, state_log, mctx)
+ const regex_t *preg;
+ re_dfastate_t *state, **state_log;
+ re_string_t *input;
+ int fl_search;
+ re_match_context_t *mctx;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ re_dfastate_t **trtable, *next_state;
+ unsigned char ch;
+
+ if (state == NULL)
+ {
+ next_state = state;
+ re_string_skip_bytes (input, 1);
+ }
+ else
+ {
+ /* If the current state can accept multibyte. */
+ if (state->accept_mb)
+ transit_state_mb (preg, state, input, state_log, mctx);
+
+ /* Then decide the next state with the single byte. */
+ if (1)
+ {
+ /* Use transition table */
+ ch = re_string_fetch_byte (input);
+ trtable = fl_search ? state->trtable_search : state->trtable;
+ if (trtable == NULL)
+ {
+ trtable = build_trtable (preg, state, fl_search);
+ if (fl_search)
+ state->trtable_search = trtable;
+ else
+ state->trtable = trtable;
+ }
+ next_state = trtable[ch];
+ }
+ else
+ {
+ /* don't use transition table */
+ next_state = transit_state_sb (preg, state, input, fl_search, mctx);
+ }
+ }
+
+ /* Update the state_log if we need. */
+ if (state_log != NULL)
+ {
+ int cur_idx = re_string_cur_idx (input);
+ if (cur_idx > mctx->state_log_top)
+ {
+ state_log[cur_idx] = next_state;
+ mctx->state_log_top = cur_idx;
+ }
+ else if (state_log[cur_idx] == 0)
+ {
+ state_log[cur_idx] = next_state;
+ }
+ else
+ {
+ re_dfastate_t *pstate;
+ unsigned int context;
+ re_node_set next_nodes, *log_nodes, *table_nodes = NULL;
+ /* If (state_log[cur_idx] != 0), it implies that cur_idx is
+ the destination of a multibyte char/collating element/
+ back reference. Then the next state is the union set of
+ these destinations and the results of the transition table. */
+ pstate = state_log[cur_idx];
+ log_nodes = pstate->entrance_nodes;
+ if (next_state != NULL)
+ {
+ table_nodes = next_state->entrance_nodes;
+ re_node_set_init_union (&next_nodes, table_nodes, log_nodes);
+ }
+ else
+ next_nodes = *log_nodes;
+ /* Note: We already add the nodes of the initial state,
+ then we don't need to add them here. */
+
+ context = re_string_context_at (input, re_string_cur_idx (input) - 1,
+ mctx->eflags, preg->newline_anchor);
+ next_state = state_log[cur_idx]
+ = re_acquire_state_context (dfa, &next_nodes, context);
+ if (table_nodes != NULL)
+ re_node_set_free (&next_nodes);
+ }
+ /* If the next state has back references. */
+ if (next_state != NULL && next_state->has_backref)
+ {
+ transit_state_bkref (preg, next_state, input, state_log, mctx);
+ next_state = state_log[cur_idx];
+ }
+ }
+ return next_state;
+}
+
+/* Helper functions for transit_state. */
+
+/* Return the next state to which the current state STATE will transit by
+ accepting the current input byte. */
+
+static re_dfastate_t *
+transit_state_sb (preg, state, input, fl_search, mctx)
+ const regex_t *preg;
+ re_dfastate_t *state;
+ re_string_t *input;
+ int fl_search;
+ re_match_context_t *mctx;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ re_node_set next_nodes;
+ re_dfastate_t *next_state;
+ int node_cnt, cur_str_idx = re_string_cur_idx (input);
+ unsigned int context;
+
+ re_node_set_alloc (&next_nodes, state->nodes.nelem + 1);
+ for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt)
+ {
+ int cur_node = state->nodes.elems[node_cnt];
+ if (check_node_accept (preg, dfa->nodes + cur_node, input,
+ cur_str_idx, mctx->eflags))
+ re_node_set_merge (&next_nodes,
+ dfa->eclosures + dfa->nexts[cur_node]);
+ }
+ if (fl_search)
+ {
+#ifdef RE_ENABLE_I18N
+ int not_initial = 0;
+ if (MB_CUR_MAX > 1)
+ for (node_cnt = 0; node_cnt < next_nodes.nelem; ++node_cnt)
+ if (dfa->nodes[next_nodes.elems[node_cnt]].type == CHARACTER)
+ {
+ not_initial = dfa->nodes[next_nodes.elems[node_cnt]].mb_partial;
+ break;
+ }
+ if (!not_initial)
+#endif
+ re_node_set_merge (&next_nodes, dfa->init_state->entrance_nodes);
+ }
+ context = re_string_context_at (input, cur_str_idx, mctx->eflags,
+ preg->newline_anchor);
+ next_state = re_acquire_state_context (dfa, &next_nodes, context);
+ re_node_set_free (&next_nodes);
+ re_string_skip_bytes (input, 1);
+ return next_state;
+}
+
+static void
+transit_state_mb (preg, pstate, input, state_log, mctx)
+ const regex_t *preg;
+ re_dfastate_t *pstate, **state_log;
+ const re_string_t *input;
+ re_match_context_t *mctx;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ int i;
+
+ for (i = 0; i < pstate->nodes.nelem; ++i)
+ {
+ re_node_set dest_nodes, *new_nodes;
+ int cur_node_idx = pstate->nodes.elems[i];
+ int naccepted = 0, dest_idx;
+ unsigned int context;
+ re_dfastate_t *dest_state;
+
+ if (dfa->nodes[cur_node_idx].type == OP_CONTEXT_NODE)
+ {
+ context = re_string_context_at (input, re_string_cur_idx (input),
+ mctx->eflags, preg->newline_anchor);
+ if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint,
+ context))
+ continue;
+ cur_node_idx = dfa->nodes[cur_node_idx].opr.ctx_info->entity;
+ }
+
+ /* How many bytes the node can accepts? */
+ if (ACCEPT_MB_NODE (dfa->nodes[cur_node_idx].type))
+ naccepted = check_node_accept_bytes (preg, cur_node_idx, input,
+ re_string_cur_idx (input));
+ if (naccepted == 0)
+ continue;
+
+ /* The node can accepts `naccepted' bytes. */
+ dest_idx = re_string_cur_idx (input) + naccepted;
+ clean_state_log_if_need (state_log, mctx, dest_idx);
+#ifdef DEBUG
+ assert (dfa->nexts[cur_node_idx] != -1);
+#endif
+ /* `cur_node_idx' may point the entity of the OP_CONTEXT_NODE,
+ then we use pstate->nodes.elems[i] instead. */
+ new_nodes = dfa->eclosures + dfa->nexts[pstate->nodes.elems[i]];
+
+ dest_state = state_log[dest_idx];
+ if (dest_state == NULL)
+ dest_nodes = *new_nodes;
+ else
+ re_node_set_init_union (&dest_nodes, dest_state->entrance_nodes,
+ new_nodes);
+ context = re_string_context_at (input, dest_idx - 1, mctx->eflags,
+ preg->newline_anchor);
+ state_log[dest_idx] = re_acquire_state_context (dfa, &dest_nodes, context);
+ if (dest_state != NULL)
+ re_node_set_free (&dest_nodes);
+ }
+}
+
+static void
+transit_state_bkref (preg, pstate, input, state_log, mctx)
+ const regex_t *preg;
+ re_dfastate_t *pstate, **state_log;
+ const re_string_t *input;
+ re_match_context_t *mctx;
+{
+ re_dfastate_t **work_state_log;
+
+#ifdef DEBUG
+ assert (mctx->match_first != -1);
+#endif
+ work_state_log = re_malloc (re_dfastate_t *, re_string_cur_idx (input) + 1);
+
+ transit_state_bkref_loop (preg, input, &pstate->nodes, work_state_log,
+ state_log, mctx);
+
+ re_free (work_state_log);
+}
+
+/* Caller must allocate `work_state_log'. */
+
+static void
+transit_state_bkref_loop (preg, input, nodes, work_state_log, state_log, mctx)
+ const regex_t *preg;
+ const re_string_t *input;
+ re_node_set *nodes;
+ re_dfastate_t **work_state_log, **state_log;
+ re_match_context_t *mctx;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ int i, j;
+ regmatch_t *cur_regs = re_malloc (regmatch_t, preg->re_nsub + 1);
+ int cur_str_idx = re_string_cur_idx (input);
+
+ for (i = 0; i < nodes->nelem; ++i)
+ {
+ int dest_str_idx, subexp_idx, prev_nelem, subexp_len;
+ int node_idx = nodes->elems[i];
+ unsigned int context;
+ re_token_t *node = dfa->nodes + node_idx;
+ re_dfastate_t *dest_state;
+ re_node_set *new_dest_nodes;
+
+ /* Check whether `node' is a backreference or not. */
+ if (node->type == OP_BACK_REF)
+ subexp_idx = node->opr.idx;
+ else if (node->type == OP_CONTEXT_NODE &&
+ dfa->nodes[node->opr.ctx_info->entity].type == OP_BACK_REF)
+ {
+ context = re_string_context_at (input, cur_str_idx, mctx->eflags,
+ preg->newline_anchor);
+ if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+ continue;
+ subexp_idx = dfa->nodes[node->opr.ctx_info->entity].opr.idx;
+ }
+ else
+ continue;
+
+ /* `node' is a backreference.
+ At first, set registers to check the backreference. */
+ cur_regs[0].rm_so = mctx->match_first;
+ cur_regs[0].rm_eo = cur_str_idx;
+ memcpy (work_state_log + mctx->match_first,
+ state_log + mctx->match_first,
+ sizeof (re_dfastate_t *)
+ * (cur_str_idx - mctx->match_first + 1));
+ mctx->match_last = cur_str_idx;
+ sift_states_backward (preg, work_state_log, mctx, input, node_idx);
+ if (!STATE_NODE_CONTAINS (work_state_log[mctx->match_first],
+ dfa->init_node))
+ continue;
+ for (j = 1; j <= preg->re_nsub; ++j)
+ cur_regs[j].rm_so = cur_regs[j].rm_eo = -1;
+ set_regs (preg, work_state_log, mctx, input,
+ subexp_idx + 1, cur_regs, node_idx);
+
+ /* Then check that the backreference can match the input string. */
+ subexp_len = cur_regs[subexp_idx].rm_eo - cur_regs[subexp_idx].rm_so;
+ if (subexp_len < 0
+ || (strncmp ((re_string_get_buffer (input)
+ + cur_regs[subexp_idx].rm_so),
+ re_string_get_buffer (input) + cur_str_idx, subexp_len)
+ != 0))
+ continue;
+
+ /* Successfully matched, add a new cache entry. */
+ dest_str_idx = cur_str_idx + subexp_len;
+ match_ctx_add_entry (mctx, node_idx, cur_str_idx, dest_str_idx);
+ clean_state_log_if_need (state_log, mctx, dest_str_idx);
+
+ /* And add the epsilon closures (which is `new_dest_nodes') of
+ the backreference to appropriate state_log. */
+#ifdef DEBUG
+ assert (dfa->nexts[node_idx] != -1);
+#endif
+ if (node->type == OP_CONTEXT_NODE && subexp_len == 0)
+ new_dest_nodes = dfa->nodes[node_idx].opr.ctx_info->bkref_eclosure;
+ else
+ new_dest_nodes = dfa->eclosures + dfa->nexts[node_idx];
+ context = (IS_WORD_CHAR (re_string_byte_at (input, dest_str_idx - 1))
+ ? CONTEXT_WORD : 0);
+ dest_state = state_log[dest_str_idx];
+
+ prev_nelem = ((state_log[cur_str_idx] == NULL) ? 0
+ : state_log[cur_str_idx]->nodes.nelem);
+ /* Add `new_dest_node' to state_log. */
+ if (dest_state == NULL)
+ state_log[dest_str_idx] = re_acquire_state_context (dfa,
+ new_dest_nodes,
+ context);
+ else
+ {
+ re_node_set dest_nodes;
+ re_node_set_init_union (&dest_nodes, dest_state->entrance_nodes,
+ new_dest_nodes);
+ state_log[dest_str_idx] = re_acquire_state_context (dfa, &dest_nodes,
+ context);
+ re_node_set_free (&dest_nodes);
+ }
+
+ /* We need to check recursively if the backreference can epsilon
+ transit. */
+ if (subexp_len == 0 && state_log[cur_str_idx]->nodes.nelem > prev_nelem)
+ transit_state_bkref_loop (preg, input, new_dest_nodes, work_state_log,
+ state_log, mctx);
+ }
+ re_free (cur_regs);
+}
+
+/* Build transition table for the state. */
+
+static re_dfastate_t **
+build_trtable (preg, state, fl_search)
+ const regex_t *preg;
+ const re_dfastate_t *state;
+ int fl_search;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ int i, j, k, ch;
+ int ndests; /* Number of the destination states from `state'. */
+ re_dfastate_t **trtable, **dest_states, **dest_states_word, **dest_states_nl;
+ re_node_set follows, *dests_node;
+ bitset *dests_ch;
+ bitset acceptable;
+
+ /* We build DFA states which corresponds to the destination nodes
+ from `state'. `dests_node[i]' represents the nodes which i-th
+ destination state contains, and `dests_ch[i]' represents the
+ characters which i-th destination state accepts. */
+ dests_node = re_malloc (re_node_set, SBC_MAX);
+ dests_ch = re_malloc (bitset, SBC_MAX);
+
+ /* Initialize transiton table. */
+ trtable = (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX);
+
+ /* At first, group all nodes belonging to `state' into several
+ destinations. */
+ ndests = group_nodes_into_DFAstates (preg, state, dests_node, dests_ch);
+ if (ndests == 0)
+ {
+ re_free (dests_node);
+ re_free (dests_ch);
+ return trtable;
+ }
+
+ dest_states = re_malloc (re_dfastate_t *, ndests);
+ dest_states_word = re_malloc (re_dfastate_t *, ndests);
+ dest_states_nl = re_malloc (re_dfastate_t *, ndests);
+ bitset_empty (acceptable);
+
+ re_node_set_alloc (&follows, ndests + 1);
+ /* Then build the states for all destinations. */
+ for (i = 0; i < ndests; ++i)
+ {
+ int next_node;
+ re_node_set_empty (&follows);
+ /* Merge the follows of this destination states. */
+ for (j = 0; j < dests_node[i].nelem; ++j)
+ {
+ next_node = dfa->nexts[dests_node[i].elems[j]];
+ if (next_node != -1)
+ {
+ re_node_set_merge (&follows, dfa->eclosures + next_node);
+ }
+ }
+ /* If search flag is set, merge the initial state. */
+ if (fl_search)
+ {
+#ifdef RE_ENABLE_I18N
+ int not_initial = 0;
+ for (j = 0; j < follows.nelem; ++j)
+ if (dfa->nodes[follows.elems[j]].type == CHARACTER)
+ {
+ not_initial = dfa->nodes[follows.elems[j]].mb_partial;
+ break;
+ }
+ if (!not_initial)
+#endif
+ re_node_set_merge (&follows, dfa->init_state->entrance_nodes);
+ }
+ dest_states[i] = re_acquire_state_context (dfa, &follows, 0);
+ /* If the new state has context constraint,
+ build appropriate states for these contexts. */
+ if (dest_states[i]->has_constraint)
+ {
+ dest_states_word[i] = re_acquire_state_context (dfa, &follows,
+ CONTEXT_WORD);
+ dest_states_nl[i] = re_acquire_state_context (dfa, &follows,
+ CONTEXT_NEWLINE);
+ }
+ else
+ {
+ dest_states_word[i] = dest_states[i];
+ dest_states_nl[i] = dest_states[i];
+ }
+ bitset_merge (acceptable, dests_ch[i]);
+ }
+
+ /* Update the transition table. */
+ for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+ for (j = 0; j < UINT_BITS; ++j, ++ch)
+ if ((acceptable[i] >> j) & 1)
+ {
+ if (IS_WORD_CHAR (ch))
+ {
+ for (k = 0; k < ndests; ++k)
+ if ((dests_ch[k][i] >> j) & 1)
+ trtable[ch] = dest_states_word[k];
+ }
+ else /* not WORD_CHAR */
+ {
+ for (k = 0; k < ndests; ++k)
+ if ((dests_ch[k][i] >> j) & 1)
+ trtable[ch] = dest_states[k];
+ }
+ }
+ /* new line */
+ for (k = 0; k < ndests; ++k)
+ if (bitset_contain (acceptable, NEWLINE_CHAR))
+ trtable[NEWLINE_CHAR] = dest_states_nl[k];
+
+ re_free (dest_states_nl);
+ re_free (dest_states_word);
+ re_free (dest_states);
+
+ re_node_set_free (&follows);
+ for (i = 0; i < ndests; ++i)
+ re_node_set_free (dests_node + i);
+
+ re_free (dests_ch);
+ re_free (dests_node);
+
+ return trtable;
+}
+
+/* Group all nodes belonging to STATE into several destinations.
+ Then for all destinations, set the nodes belonging to the destination
+ to DESTS_NODE[i] and set the characters accepted by the destination
+ to DEST_CH[i]. This function return the number of destinations. */
+
+static int
+group_nodes_into_DFAstates (preg, state, dests_node, dests_ch)
+ const regex_t *preg;
+ const re_dfastate_t *state;
+ re_node_set *dests_node;
+ bitset *dests_ch;
+{
+ const re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ int i, j, k;
+ int ndests; /* Number of the destinations from `state'. */
+ bitset accepts; /* Characters a node can accept. */
+ const re_node_set *cur_nodes = &state->nodes;
+ bitset_empty (accepts);
+ ndests = 0;
+
+ /* For all the nodes belonging to `state', */
+ for (i = 0; i < cur_nodes->nelem; ++i)
+ {
+ unsigned int constraint = 0;
+ re_token_t *node = &dfa->nodes[cur_nodes->elems[i]];
+ re_token_type_t type = node->type;
+
+ if (type == OP_CONTEXT_NODE)
+ {
+ constraint = node->constraint;
+ node = dfa->nodes + node->opr.ctx_info->entity;
+ type = node->type;
+ }
+
+ /* Enumerate all single byte character this node can accept. */
+ if (type == CHARACTER)
+ bitset_set (accepts, node->opr.c);
+ else if (type == SIMPLE_BRACKET)
+ {
+ bitset_merge (accepts, node->opr.sbcset);
+ }
+ else if (type == OP_PERIOD)
+ {
+ bitset_set_all (accepts);
+ if (!(preg->syntax & RE_DOT_NEWLINE))
+ bitset_clear (accepts, '\n');
+ if (preg->syntax & RE_DOT_NOT_NULL)
+ bitset_clear (accepts, '\0');
+ }
+ else
+ continue;
+
+ /* Check the `accepts' and sift the characters which are not
+ match it the context. */
+ if (constraint)
+ {
+ if (constraint & NEXT_WORD_CONSTRAINT)
+ for (j = 0; j < BITSET_UINTS; ++j)
+ accepts[j] &= dfa->word_char[j];
+ else if (constraint & NEXT_NOTWORD_CONSTRAINT)
+ for (j = 0; j < BITSET_UINTS; ++j)
+ accepts[j] &= ~dfa->word_char[j];
+ else if (constraint & NEXT_NEWLINE_CONSTRAINT)
+ {
+ int accepts_newline = bitset_contain (accepts, NEWLINE_CHAR);
+ bitset_empty (accepts);
+ if (accepts_newline)
+ bitset_set (accepts, NEWLINE_CHAR);
+ else
+ continue;
+ }
+ }
+
+ /* Then divide `accepts' into DFA states, or create a new
+ state. */
+ for (j = 0; j < ndests; ++j)
+ {
+ bitset intersec; /* Intersection sets, see below. */
+ bitset remains;
+ /* Flags, see below. */
+ int has_intersec, not_subset, not_consumed;
+
+ /* Optimization, skip if this state doesn't accept the character. */
+ if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c))
+ continue;
+
+ /* Enumerate the intersection set of this state and `accepts'. */
+ has_intersec = 0;
+ for (k = 0; k < BITSET_UINTS; ++k)
+ has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k];
+ /* And skip if the intersection set is empty. */
+ if (!has_intersec)
+ continue;
+
+ /* Then check if this state is a subset of `accepts'. */
+ not_subset = not_consumed = 0;
+ for (k = 0; k < BITSET_UINTS; ++k)
+ {
+ not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k];
+ not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k];
+ }
+
+ /* If this state isn't a subset of `accepts', create a
+ new group state, which has the `remains'. */
+ if (not_subset)
+ {
+ bitset_copy (dests_ch[ndests], remains);
+ bitset_copy (dests_ch[j], intersec);
+ re_node_set_init_copy (dests_node + ndests, &dests_node[j]);
+ ++ndests;
+ }
+
+ /* Put the position in the current group. */
+ re_node_set_insert (&dests_node[j], cur_nodes->elems[i]);
+
+ /* If all characters are consumed, go to next node. */
+ if (!not_consumed)
+ break;
+ }
+ /* Some characters remain, create a new group. */
+ if (j == ndests)
+ {
+ bitset_copy (dests_ch[ndests], accepts);
+ re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]);
+ ++ndests;
+ bitset_empty (accepts);
+ }
+ }
+ return ndests;
+}
+
+/* Check how many bytes the node `dfa->nodes[node_idx]' accepts. */
+
+static int
+check_node_accept_bytes (preg, node_idx, input, str_idx)
+ const regex_t *preg;
+ int node_idx, str_idx;
+ const re_string_t *input;
+{
+ const re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ const re_token_t *node = dfa->nodes + node_idx;
+ int elem_len = re_string_elem_size_at (input, str_idx);
+ int char_len = re_string_char_size_at (input, str_idx);
+ int i, j;
+#ifdef _LIBC
+ uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+#endif /* _LIBC */
+ if (elem_len <= 1 && char_len <= 1)
+ return 0;
+ if (node->type == OP_PERIOD)
+ {
+ if ((!(preg->syntax & RE_DOT_NEWLINE) &&
+ re_string_byte_at (input, str_idx) == '\n') ||
+ ((preg->syntax & RE_DOT_NOT_NULL) &&
+ re_string_byte_at (input, str_idx) == '\0'))
+ return 0;
+ return char_len;
+ }
+ else if (node->type == COMPLEX_BRACKET)
+ {
+ const re_charset_t *cset = node->opr.mbcset;
+ const unsigned char *pin = re_string_get_buffer (input) + str_idx;
+#ifdef _LIBC
+ if (nrules != 0)
+ {
+ int match_len = 0;
+ unsigned int in_collseq = 0;
+ const int32_t *table, *indirect;
+ const unsigned char *weights, *extra, *collseqwc;
+ int32_t idx;
+ wchar_t wc = 0;
+ /* This #include defines a local function! */
+# include <locale/weight.h>
+
+ /* match with collating_symbol? */
+ if (cset->ncoll_syms)
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+ for (i = 0; i < cset->ncoll_syms; ++i)
+ {
+ const unsigned char *coll_sym = extra + cset->coll_syms[i];
+ /* Compare the length of input collating element and
+ the length of current collating element. */
+ if (*coll_sym != elem_len)
+ continue;
+ /* Compare each bytes. */
+ for (j = 0; j < *coll_sym; j++)
+ if (pin[j] != coll_sym[1 + j])
+ break;
+ if (j == *coll_sym)
+ {
+ /* Match if every bytes is equal. */
+ match_len = j;
+ goto check_node_accept_bytes_match;
+ }
+ }
+
+ if (cset->nranges || cset->nchar_classes || cset->nmbchars)
+ wc = re_string_wchar_at (input, str_idx);
+
+ if (cset->nranges)
+ {
+ if (elem_len <= char_len)
+ {
+ collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+ in_collseq = collseq_table_lookup (collseqwc, wc);
+ }
+ else
+ in_collseq = find_collation_sequence_value (pin, elem_len);
+ }
+ /* match with range expression? */
+ for (i = 0; i < cset->nranges; ++i)
+ if (cset->range_starts[i] <= in_collseq
+ && in_collseq <= cset->range_ends[i])
+ {
+ match_len = elem_len;
+ goto check_node_accept_bytes_match;
+ }
+
+ /* match with equivalence_class? */
+ if (cset->nequiv_classes)
+ {
+ const unsigned char *cp = pin;
+ table = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ weights = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB);
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);
+ idx = findidx (&cp);
+ if (idx > 0)
+ for (i = 0; i < cset->nequiv_classes; ++i)
+ {
+ int32_t equiv_class_idx = cset->equiv_classes[i];
+ size_t weight_len = weights[idx];
+ if (weight_len == weights[equiv_class_idx])
+ {
+ int cnt = 0;
+ while (cnt <= weight_len
+ && (weights[equiv_class_idx + 1 + cnt]
+ == weights[idx + 1 + cnt]))
+ ++cnt;
+ if (cnt > weight_len)
+ {
+ match_len = elem_len;
+ goto check_node_accept_bytes_match;
+ }
+ }
+ }
+ }
+
+ /* match with multibyte character? */
+ for (i = 0; i < cset->nmbchars; ++i)
+ if (wc == cset->mbchars[i])
+ {
+ match_len = char_len;
+ goto check_node_accept_bytes_match;
+ }
+
+ /* match with character_class? */
+ for (i = 0; i < cset->nchar_classes; ++i)
+ {
+ wctype_t wt = cset->char_classes[i];
+ if (__iswctype (wc, wt))
+ {
+ match_len = char_len;
+ goto check_node_accept_bytes_match;
+ }
+ }
+
+ check_node_accept_bytes_match:
+ if (!cset->non_match)
+ return match_len;
+ else
+ {
+ if (match_len > 0)
+ return 0;
+ else
+ return re_string_elem_size_at (input, str_idx);
+ }
+ }
+#endif
+ }
+ return 0;
+}
+
+#ifdef _LIBC
+static unsigned int
+find_collation_sequence_value (mbs, mbs_len)
+ const unsigned char *mbs;
+ size_t mbs_len;
+{
+ uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules == 0)
+ {
+ if (mbs_len == 1)
+ {
+ /* No valid character. Match it as a single byte character. */
+ const unsigned char *collseq = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+ return collseq[mbs[0]];
+ }
+ return UINT_MAX;
+ }
+ else
+ {
+ int32_t idx;
+ const unsigned char *extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+
+ for (idx = 0; ;)
+ {
+ int mbs_cnt, found = 0;
+ int32_t elem_mbs_len;
+ /* Skip the name of collating element name. */
+ idx = idx + extra[idx] + 1;
+ elem_mbs_len = extra[idx++];
+ if (mbs_len == elem_mbs_len)
+ {
+ for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt)
+ if (extra[idx + mbs_cnt] != mbs[mbs_cnt])
+ break;
+ if (mbs_cnt == elem_mbs_len)
+ /* Found the entry. */
+ found = 1;
+ }
+ /* Skip the byte sequence of the collating element. */
+ idx += elem_mbs_len;
+ /* Adjust for the alignment. */
+ idx = (idx + 3) & ~3;
+ /* Skip the collation sequence value. */
+ idx += sizeof (uint32_t);
+ /* Skip the wide char sequence of the collating element. */
+ idx = idx + sizeof (uint32_t) * (extra[idx] + 1);
+ /* If we found the entry, return the sequence value. */
+ if (found)
+ return *(uint32_t *) (extra + idx);
+ /* Skip the collation sequence value. */
+ idx += sizeof (uint32_t);
+ }
+ }
+}
+#endif
+
+/* Check whether the node accepts the byte which is IDX-th
+ byte of the INPUT. */
+
+static int
+check_node_accept (preg, node, input, idx, eflags)
+ const regex_t *preg;
+ const re_token_t *node;
+ const re_string_t *input;
+ int idx, eflags;
+{
+ const re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ const re_token_t *cur_node;
+ unsigned char ch;
+ if (node->type == OP_CONTEXT_NODE)
+ {
+ /* The node has constraints. Check whether the current context
+ satisfies the constraints. */
+ unsigned int context = re_string_context_at (input, idx, eflags,
+ preg->newline_anchor);
+ if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+ return 0;
+ cur_node = dfa->nodes + node->opr.ctx_info->entity;
+ }
+ else
+ cur_node = node;
+
+ ch = re_string_byte_at (input, idx);
+ if (cur_node->type == CHARACTER)
+ return cur_node->opr.c == ch;
+ else if (cur_node->type == SIMPLE_BRACKET)
+ return bitset_contain (cur_node->opr.sbcset, ch);
+ else if (cur_node->type == OP_PERIOD)
+ return !((ch == '\n' && !(preg->syntax & RE_DOT_NEWLINE))
+ || (ch == '\0' && (preg->syntax & RE_DOT_NOT_NULL)));
+ else
+ return 0;
+}
+
+/* Functions for matching context. */
+
+static void
+match_ctx_init (mctx, eflags, n)
+ re_match_context_t *mctx;
+ int eflags;
+ int n;
+{
+ mctx->eflags = eflags;
+ mctx->match_first = mctx->match_last = -1;
+ if (n > 0)
+ mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n);
+ else
+ mctx->bkref_ents = NULL;
+ mctx->nbkref_ents = 0;
+ mctx->abkref_ents = n;
+ mctx->max_bkref_len = 0;
+}
+
+static void
+match_ctx_free (mctx)
+ re_match_context_t *mctx;
+{
+ re_free (mctx->bkref_ents);
+}
+
+/* Add a new backreference entry to the cache. */
+
+static void
+match_ctx_add_entry (mctx, node, from, to)
+ re_match_context_t *mctx;
+ int node, from, to;
+{
+ if (mctx->nbkref_ents >= mctx->abkref_ents)
+ {
+ mctx->bkref_ents = re_realloc (mctx->bkref_ents,
+ struct re_backref_cache_entry,
+ mctx->abkref_ents * 2);
+ memset (mctx->bkref_ents + mctx->nbkref_ents, '\0',
+ sizeof (struct re_backref_cache_entry) * mctx->abkref_ents);
+ mctx->abkref_ents *= 2;
+ }
+ mctx->bkref_ents[mctx->nbkref_ents].node = node;
+ mctx->bkref_ents[mctx->nbkref_ents].from = from;
+ mctx->bkref_ents[mctx->nbkref_ents++].to = to;
+ if (mctx->max_bkref_len < to - from)
+ mctx->max_bkref_len = to - from;
+}