]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - posix/regcomp.c
Fix regex wctype namespace (bug 18495).
[thirdparty/glibc.git] / posix / regcomp.c
index c94a44012dd3695493323bec367ce663df5ba060..728c48239f0efceec280916a144160a7035b870e 100644 (file)
@@ -1,5 +1,5 @@
 /* Extended regular expression matching and search library.
-   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 2002-2015 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
 
    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.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stdint.h>
+
+#ifdef _LIBC
+# include <locale/weight.h>
+#endif
 
 static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
-                                         int length, reg_syntax_t syntax);
+                                         size_t 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 reg_errcode_t init_word_char (re_dfa_t *dfa);
+static reg_errcode_t init_dfa (re_dfa_t *dfa, size_t pat_len);
 #ifdef RE_ENABLE_I18N
 static void free_charset (re_charset_t *cset);
 #endif /* RE_ENABLE_I18N */
@@ -33,29 +37,31 @@ static reg_errcode_t create_initial_state (re_dfa_t *dfa);
 #ifdef RE_ENABLE_I18N
 static void optimize_utf8 (re_dfa_t *dfa);
 #endif
-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 reg_errcode_t duplicate_node_closure (re_dfa_t *dfa, int top_org_node,
-                                            int top_clone_node, int root_node,
-                                            unsigned int constraint);
-static reg_errcode_t duplicate_node (int *new_idx, re_dfa_t *dfa, int org_idx,
-                                    unsigned int constraint);
-static int search_duplicated_node (re_dfa_t *dfa, int org_node,
+static reg_errcode_t analyze (regex_t *preg);
+static reg_errcode_t preorder (bin_tree_t *root,
+                              reg_errcode_t (fn (void *, bin_tree_t *)),
+                              void *extra);
+static reg_errcode_t postorder (bin_tree_t *root,
+                               reg_errcode_t (fn (void *, bin_tree_t *)),
+                               void *extra);
+static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node);
+static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node);
+static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg,
+                                bin_tree_t *node);
+static reg_errcode_t calc_first (void *extra, bin_tree_t *node);
+static reg_errcode_t calc_next (void *extra, bin_tree_t *node);
+static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node);
+static int duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint);
+static int search_duplicated_node (const re_dfa_t *dfa, int org_node,
                                   unsigned int constraint);
 static reg_errcode_t calc_eclosure (re_dfa_t *dfa);
 static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa,
                                         int node, int root);
-static void calc_inveclosure (re_dfa_t *dfa);
+static reg_errcode_t 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);
+                       reg_syntax_t syntax) internal_function;
 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,
@@ -85,55 +91,40 @@ static reg_errcode_t parse_bracket_element (bracket_elem_t *elem,
 static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem,
                                          re_string_t *regexp,
                                          re_token_t *token);
-#ifndef _LIBC
-# ifdef RE_ENABLE_I18N
-static reg_errcode_t build_range_exp (re_bitset_ptr_t sbcset,
-                                     re_charset_t *mbcset, int *range_alloc,
-                                     bracket_elem_t *start_elem,
-                                     bracket_elem_t *end_elem);
-static reg_errcode_t build_collating_symbol (re_bitset_ptr_t sbcset,
-                                            re_charset_t *mbcset,
-                                            int *coll_sym_alloc,
-                                            const unsigned char *name);
-# else /* not RE_ENABLE_I18N */
-static reg_errcode_t build_range_exp (re_bitset_ptr_t sbcset,
-                                     bracket_elem_t *start_elem,
-                                     bracket_elem_t *end_elem);
-static reg_errcode_t build_collating_symbol (re_bitset_ptr_t sbcset,
-                                            const unsigned char *name);
-# endif /* not RE_ENABLE_I18N */
-#endif /* not _LIBC */
 #ifdef RE_ENABLE_I18N
-static reg_errcode_t build_equiv_class (re_bitset_ptr_t sbcset,
+static reg_errcode_t build_equiv_class (bitset_t sbcset,
                                        re_charset_t *mbcset,
                                        int *equiv_class_alloc,
                                        const unsigned char *name);
 static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
-                                     re_bitset_ptr_t sbcset,
+                                     bitset_t sbcset,
                                      re_charset_t *mbcset,
                                      int *char_class_alloc,
                                      const unsigned char *class_name,
                                      reg_syntax_t syntax);
 #else  /* not RE_ENABLE_I18N */
-static reg_errcode_t build_equiv_class (re_bitset_ptr_t sbcset,
+static reg_errcode_t build_equiv_class (bitset_t sbcset,
                                        const unsigned char *name);
 static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
-                                     re_bitset_ptr_t sbcset,
+                                     bitset_t sbcset,
                                      const unsigned char *class_name,
                                      reg_syntax_t syntax);
 #endif /* not RE_ENABLE_I18N */
-static bin_tree_t *build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans,
+static bin_tree_t *build_charclass_op (re_dfa_t *dfa,
+                                      RE_TRANSLATE_TYPE trans,
                                       const unsigned char *class_name,
-                                      const unsigned char *extra, int not,
-                                      reg_errcode_t *err);
+                                      const unsigned char *extra,
+                                      int non_match, reg_errcode_t *err);
 static bin_tree_t *create_tree (re_dfa_t *dfa,
                                bin_tree_t *left, bin_tree_t *right,
-                               re_token_type_t type, int index);
-static bin_tree_t *re_dfa_add_tree_node (re_dfa_t *dfa,
-                                        bin_tree_t *left, bin_tree_t *right,
-                                        re_token_t)
-  __attribute ((noinline));
+                               re_token_type_t type);
+static bin_tree_t *create_token_tree (re_dfa_t *dfa,
+                                     bin_tree_t *left, bin_tree_t *right,
+                                     const re_token_t *token);
 static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa);
+static void free_token (re_token_t *node);
+static reg_errcode_t free_tree (void *extra, bin_tree_t *node);
+static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node);
 \f
 /* 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.
@@ -221,7 +212,7 @@ const size_t __re_error_msgid_idx[] attribute_hidden =
    compiles PATTERN (of length LENGTH) 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
+   Assumes the 'allocated' (and perhaps 'buffer') and 'translate' fields
    are set in BUFP on entry.  */
 
 const char *
@@ -234,8 +225,8 @@ re_compile_pattern (pattern, length, bufp)
 
   /* 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;
+     setting no_sub, unless RE_NO_SUB is set.  */
+  bufp->no_sub = !!(re_syntax_options & RE_NO_SUB);
 
   /* Match anchors at newline.  */
   bufp->newline_anchor = 1;
@@ -250,7 +241,7 @@ re_compile_pattern (pattern, length, bufp)
 weak_alias (__re_compile_pattern, re_compile_pattern)
 #endif
 
-/* Set by `re_set_syntax' to the current regexp syntax to recognize.  Can
+/* 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
@@ -301,8 +292,8 @@ weak_alias (__re_compile_fastmap, re_compile_fastmap)
 #endif
 
 static inline void
-__attribute ((always_inline))
-re_set_fastmap (char *fastmap, int icase, int ch)
+__attribute__ ((always_inline))
+re_set_fastmap (char *fastmap, bool icase, int ch)
 {
   fastmap[ch] = 1;
   if (icase)
@@ -313,10 +304,8 @@ re_set_fastmap (char *fastmap, int icase, int ch)
    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_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state,
+                        char *fastmap)
 {
   re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
   int node_cnt;
@@ -342,71 +331,98 @@ re_compile_fastmap_iter (bufp, init_state, fastmap)
                     && dfa->nodes[node].type == CHARACTER
                     && dfa->nodes[node].mb_partial)
                *p++ = dfa->nodes[node].opr.c;
-             memset (&state, 0, sizeof (state));
-             if (mbrtowc (&wc, (const char *) buf, p - buf,
-                          &state) == p - buf
-                 && __wcrtomb ((char *) buf, towlower (wc), &state) > 0)
+             memset (&state, '\0', sizeof (state));
+             if (__mbrtowc (&wc, (const char *) buf, p - buf,
+                            &state) == p - buf
+                 && (__wcrtomb ((char *) buf, __towlower (wc), &state)
+                     != (size_t) -1))
                re_set_fastmap (fastmap, 0, buf[0]);
            }
 #endif
        }
       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))
-               re_set_fastmap (fastmap, icase, ch);
+         int i, ch;
+         for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+           {
+             int j;
+             bitset_word_t w = dfa->nodes[node].opr.sbcset[i];
+             for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+               if (w & ((bitset_word_t) 1 << j))
+                 re_set_fastmap (fastmap, icase, ch);
+           }
        }
 #ifdef RE_ENABLE_I18N
       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)
-           {
+         int i;
+
 # ifdef _LIBC
-             if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0)
+         /* See if we have to try all bytes which start multiple 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' (and
+                 it is caught by SIMPLE_BRACKET).  */
+             if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0
+                 && (cset->ncoll_syms || cset->nranges))
                {
-                 /* 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)
-                       re_set_fastmap (fastmap, icase, ch);
+                 for (i = 0; i < SBC_MAX; ++i)
+                   if (table[i] < 0)
+                     re_set_fastmap (fastmap, icase, i);
                }
-# else
-             if (dfa->mb_cur_max > 1)
-               for (i = 0; i < SBC_MAX; ++i)
-                 if (__btowc (i) == WEOF)
-                   re_set_fastmap (fastmap, icase, i);
-# endif /* not _LIBC */
+# endif /* _LIBC */
+
+         /* See if we have to start the match at all multibyte characters,
+            i.e. where we would not find an invalid sequence.  This only
+            applies to multibyte character sets; for single byte character
+            sets, the SIMPLE_BRACKET again suffices.  */
+         if (dfa->mb_cur_max > 1
+             && (cset->nchar_classes || cset->non_match || cset->nranges
+# ifdef _LIBC
+                 || cset->nequiv_classes
+# endif /* _LIBC */
+                ))
+           {
+             unsigned char c = 0;
+             do
+               {
+                 mbstate_t mbs;
+                 memset (&mbs, 0, sizeof (mbs));
+                 if (__mbrtowc (NULL, (char *) &c, 1, &mbs) == (size_t) -2)
+                   re_set_fastmap (fastmap, false, (int) c);
+               }
+             while (++c != 0);
            }
-         for (i = 0; i < cset->nmbchars; ++i)
+
+         else
            {
-             char buf[256];
-             mbstate_t state;
-             memset (&state, '\0', sizeof (state));
-             __wcrtomb (buf, cset->mbchars[i], &state);
-             re_set_fastmap (fastmap, icase, *(unsigned char *) buf);
-             if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+             /* ... Else catch all bytes which can start the mbchars.  */
+             for (i = 0; i < cset->nmbchars; ++i)
                {
-                 __wcrtomb (buf, towlower (cset->mbchars[i]), &state);
-                 re_set_fastmap (fastmap, 0, *(unsigned char *) buf);
+                 char buf[256];
+                 mbstate_t state;
+                 memset (&state, '\0', sizeof (state));
+                 if (__wcrtomb (buf, cset->mbchars[i], &state) != (size_t) -1)
+                   re_set_fastmap (fastmap, icase, *(unsigned char *) buf);
+                 if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+                   {
+                     if (__wcrtomb (buf, __towlower (cset->mbchars[i]), &state)
+                         != (size_t) -1)
+                       re_set_fastmap (fastmap, false, *(unsigned char *) buf);
+                   }
                }
            }
        }
 #endif /* RE_ENABLE_I18N */
-      else if (type == END_OF_RE || type == OP_PERIOD
-              || type == OP_UTF8_PERIOD)
+      else if (type == OP_PERIOD
+#ifdef RE_ENABLE_I18N
+              || type == OP_UTF8_PERIOD
+#endif /* RE_ENABLE_I18N */
+              || type == END_OF_RE)
        {
          memset (fastmap, '\1', sizeof (char) * SBC_MAX);
          if (type == END_OF_RE)
@@ -422,15 +438,15 @@ re_compile_fastmap_iter (bufp, init_state, fastmap)
    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
+     '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.
+     '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.
 
@@ -496,7 +512,7 @@ regcomp (preg, pattern, cflags)
   /* We have already checked preg->fastmap != NULL.  */
   if (BE (ret == REG_NOERROR, 1))
     /* Compute the fastmap now, since regexec cannot modify the pattern
-       buffer.  This function nevers fails in this implementation.  */
+       buffer.  This function never fails in this implementation.  */
     (void) re_compile_fastmap (preg);
   else
     {
@@ -517,8 +533,8 @@ weak_alias (__regcomp, regcomp)
 size_t
 regerror (errcode, preg, errbuf, errbuf_size)
     int errcode;
-    const regex_t *preg;
-    char *errbuf;
+    const regex_t *__restrict preg;
+    char *__restrict errbuf;
     size_t errbuf_size;
 {
   const char *msg;
@@ -559,25 +575,27 @@ weak_alias (__regerror, regerror)
 #endif
 
 
+#ifdef RE_ENABLE_I18N
+/* This static array is used for the map to single-byte characters when
+   UTF-8 is used.  Otherwise we would allocate memory just to initialize
+   it the same all the time.  UTF-8 is the preferred encoding so this is
+   a worthwhile optimization.  */
+static const bitset_t utf8_sb_map =
+{
+  /* Set the first 128 bits.  */
+  [0 ... 0x80 / BITSET_WORD_BITS - 1] = BITSET_WORD_MAX
+};
+#endif
+
+
 static void
 free_dfa_content (re_dfa_t *dfa)
 {
   int i, j;
 
-  re_free (dfa->subexps);
-
   if (dfa->nodes)
     for (i = 0; i < dfa->nodes_len; ++i)
-      {
-       re_token_t *node = dfa->nodes + i;
-#ifdef RE_ENABLE_I18N
-       if (node->type == COMPLEX_BRACKET && node->duplicated == 0)
-         free_charset (node->opr.mbcset);
-       else
-#endif /* RE_ENABLE_I18N */
-         if (node->type == SIMPLE_BRACKET && node->duplicated == 0)
-           re_free (node->opr.sbcset);
-      }
+      free_token (dfa->nodes + i);
   re_free (dfa->nexts);
   for (i = 0; i < dfa->nodes_len; ++i)
     {
@@ -602,10 +620,14 @@ free_dfa_content (re_dfa_t *dfa)
            re_dfastate_t *state = entry->array[j];
            free_state (state);
          }
-        re_free (entry->array);
+       re_free (entry->array);
       }
   re_free (dfa->state_table);
-  re_free (dfa->word_char);
+#ifdef RE_ENABLE_I18N
+  if (dfa->sb_char != utf8_sb_map)
+    re_free (dfa->sb_char);
+#endif
+  re_free (dfa->subexp_map);
 #ifdef DEBUG
   re_free (dfa->re_str);
 #endif
@@ -623,8 +645,14 @@ regfree (preg)
   re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
   if (BE (dfa != NULL, 1))
     free_dfa_content (dfa);
+  preg->buffer = NULL;
+  preg->allocated = 0;
 
   re_free (preg->fastmap);
+  preg->fastmap = NULL;
+
+  re_free (preg->translate);
+  preg->translate = NULL;
 }
 #ifdef _LIBC
 weak_alias (__regfree, regfree)
@@ -675,7 +703,7 @@ re_comp (s)
                                 + __re_error_msgid_idx[(int) REG_ESPACE]);
     }
 
-  /* Since `re_exec' always passes NULL for the `regs' argument, we
+  /* 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.  */
@@ -704,11 +732,8 @@ libc_freeres_fn (free_mem)
    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;
+re_compile_internal (regex_t *preg, const char * pattern, size_t length,
+                    reg_syntax_t syntax)
 {
   reg_errcode_t err = REG_NOERROR;
   re_dfa_t *dfa;
@@ -725,7 +750,7 @@ re_compile_internal (preg, pattern, length, syntax)
 
   /* Initialize the dfa.  */
   dfa = (re_dfa_t *) preg->buffer;
-  if (preg->allocated < sizeof (re_dfa_t))
+  if (BE (preg->allocated < sizeof (re_dfa_t), 0))
     {
       /* If zero allocated, but buffer is non-null, try to realloc
         enough space.  This loses if buffer's address is bogus, but
@@ -735,8 +760,8 @@ re_compile_internal (preg, pattern, length, syntax)
       if (dfa == NULL)
        return REG_ESPACE;
       preg->allocated = sizeof (re_dfa_t);
+      preg->buffer = (unsigned char *) dfa;
     }
-  preg->buffer = (unsigned char *) dfa;
   preg->used = sizeof (re_dfa_t);
 
   err = init_dfa (dfa, length);
@@ -748,10 +773,13 @@ re_compile_internal (preg, pattern, length, syntax)
       return err;
     }
 #ifdef DEBUG
+  /* Note: length+1 will not overflow since it is checked in init_dfa.  */
   dfa->re_str = re_malloc (char, length + 1);
   strncpy (dfa->re_str, pattern, length + 1);
 #endif
 
+  __libc_lock_init (dfa->lock);
+
   err = re_string_construct (&regexp, pattern, length, preg->translate,
                             syntax & RE_ICASE, dfa);
   if (BE (err != REG_NOERROR, 0))
@@ -771,18 +799,17 @@ re_compile_internal (preg, pattern, length, syntax)
   if (BE (dfa->str_tree == NULL, 0))
     goto re_compile_internal_free_return;
 
+  /* Analyze the tree and create the nfa.  */
+  err = analyze (preg);
+  if (BE (err != REG_NOERROR, 0))
+    goto re_compile_internal_free_return;
+
 #ifdef RE_ENABLE_I18N
   /* If possible, do searching in single byte encoding to speed things up.  */
   if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL)
     optimize_utf8 (dfa);
 #endif
 
-  /* Analyze the tree and collect information which is necessary to
-     create the dfa.  */
-  err = analyze (dfa);
-  if (BE (err != REG_NOERROR, 0))
-    goto re_compile_internal_free_return;
-
   /* Then create the initial state of the dfa.  */
   err = create_initial_state (dfa);
 
@@ -804,45 +831,94 @@ re_compile_internal (preg, pattern, length, syntax)
    as the initial length of some arrays.  */
 
 static reg_errcode_t
-init_dfa (dfa, pat_len)
-     re_dfa_t *dfa;
-     int pat_len;
+init_dfa (re_dfa_t *dfa, size_t pat_len)
 {
-  int table_size;
+  unsigned int table_size;
+#ifndef _LIBC
+  char *codeset_name;
+#endif
 
   memset (dfa, '\0', sizeof (re_dfa_t));
 
   /* Force allocation of str_tree_storage the first time.  */
   dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
 
+  /* Avoid overflows.  */
+  if (pat_len == SIZE_MAX)
+    return REG_ESPACE;
+
   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)
+  for (table_size = 1; ; 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;
-
   dfa->mb_cur_max = MB_CUR_MAX;
 #ifdef _LIBC
-  if (dfa->mb_cur_max > 1
+  if (dfa->mb_cur_max == 6
       && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0)
     dfa->is_utf8 = 1;
   dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII)
                       != 0);
+#else
+# ifdef HAVE_LANGINFO_CODESET
+  codeset_name = nl_langinfo (CODESET);
+# else
+  codeset_name = getenv ("LC_ALL");
+  if (codeset_name == NULL || codeset_name[0] == '\0')
+    codeset_name = getenv ("LC_CTYPE");
+  if (codeset_name == NULL || codeset_name[0] == '\0')
+    codeset_name = getenv ("LANG");
+  if (codeset_name == NULL)
+    codeset_name = "";
+  else if (strchr (codeset_name, '.') !=  NULL)
+    codeset_name = strchr (codeset_name, '.') + 1;
+# endif
+
+  if (strcasecmp (codeset_name, "UTF-8") == 0
+      || strcasecmp (codeset_name, "UTF8") == 0)
+    dfa->is_utf8 = 1;
+
+  /* We check exhaustively in the loop below if this charset is a
+     superset of ASCII.  */
+  dfa->map_notascii = 0;
+#endif
+
+#ifdef RE_ENABLE_I18N
+  if (dfa->mb_cur_max > 1)
+    {
+      if (dfa->is_utf8)
+       dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map;
+      else
+       {
+         int i, j, ch;
+
+         dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+         if (BE (dfa->sb_char == NULL, 0))
+           return REG_ESPACE;
+
+         /* Set the bits corresponding to single byte chars.  */
+         for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+           for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+             {
+               wint_t wch = __btowc (ch);
+               if (wch != WEOF)
+                 dfa->sb_char[i] |= (bitset_word_t) 1 << j;
+# ifndef _LIBC
+               if (isascii (ch) && wch != ch)
+                 dfa->map_notascii = 1;
+# endif
+             }
+       }
+    }
 #endif
 
-  if (BE (dfa->nodes == NULL || dfa->state_table == NULL
-         || dfa->subexps == NULL, 0))
+  if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0))
     return REG_ESPACE;
   return REG_NOERROR;
 }
@@ -851,26 +927,54 @@ init_dfa (dfa, pat_len)
    "word".  In this case "word" means that it is the word construction
    character used by some operators like "\<", "\>", etc.  */
 
-static reg_errcode_t
-init_word_char (dfa)
-     re_dfa_t *dfa;
+static void
+internal_function
+init_word_char (re_dfa_t *dfa)
 {
-  int i, j, ch;
-  dfa->word_char = (re_bitset_ptr_t) calloc (sizeof (bitset), 1);
-  if (BE (dfa->word_char == NULL, 0))
-    return REG_ESPACE;
-  for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
-    for (j = 0; j < UINT_BITS; ++j, ++ch)
+  dfa->word_ops_used = 1;
+  int i = 0;
+  int ch = 0;
+  if (BE (dfa->map_notascii == 0, 1))
+    {
+      if (sizeof (dfa->word_char[0]) == 8)
+       {
+          /* The extra temporaries here avoid "implicitly truncated"
+             warnings in the case when this is dead code, i.e. 32-bit.  */
+          const uint64_t wc0 = UINT64_C (0x03ff000000000000);
+          const uint64_t wc1 = UINT64_C (0x07fffffe87fffffe);
+         dfa->word_char[0] = wc0;
+         dfa->word_char[1] = wc1;
+         i = 2;
+       }
+      else if (sizeof (dfa->word_char[0]) == 4)
+       {
+         dfa->word_char[0] = UINT32_C (0x00000000);
+         dfa->word_char[1] = UINT32_C (0x03ff0000);
+         dfa->word_char[2] = UINT32_C (0x87fffffe);
+         dfa->word_char[3] = UINT32_C (0x07fffffe);
+         i = 4;
+       }
+      else
+       abort ();
+      ch = 128;
+
+      if (BE (dfa->is_utf8, 1))
+       {
+         memset (&dfa->word_char[i], '\0', (SBC_MAX - ch) / 8);
+         return;
+       }
+    }
+
+  for (; i < BITSET_WORDS; ++i)
+    for (int j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
       if (isalnum (ch) || ch == '_')
-       dfa->word_char[i] |= 1 << j;
-  return REG_NOERROR;
+       dfa->word_char[i] |= (bitset_word_t) 1 << j;
 }
 
 /* Free the work area which are only used while compiling.  */
 
 static void
-free_workarea_compile (preg)
-     regex_t *preg;
+free_workarea_compile (regex_t *preg)
 {
   re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
   bin_tree_storage_t *storage, *next;
@@ -889,8 +993,7 @@ free_workarea_compile (preg)
 /* Create initial states for all contexts.  */
 
 static reg_errcode_t
-create_initial_state (dfa)
-     re_dfa_t *dfa;
+create_initial_state (re_dfa_t *dfa)
 {
   int first, i;
   reg_errcode_t err;
@@ -898,7 +1001,7 @@ create_initial_state (dfa)
 
   /* Initial states have the epsilon closure of the node which is
      the first node of the regular expression.  */
-  first = dfa->str_tree->first;
+  first = dfa->str_tree->first->node_idx;
   dfa->init_node = first;
   err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first);
   if (BE (err != REG_NOERROR, 0))
@@ -922,7 +1025,7 @@ create_initial_state (dfa)
            re_token_t *clexp_node;
            clexp_node = dfa->nodes + init_nodes.elems[clexp_idx];
            if (clexp_node->type == OP_CLOSE_SUBEXP
-               && clexp_node->opr.idx + 1 == dfa->nodes[node_idx].opr.idx)
+               && clexp_node->opr.idx == dfa->nodes[node_idx].opr.idx)
              break;
          }
        if (clexp_idx == init_nodes.nelem)
@@ -933,7 +1036,11 @@ create_initial_state (dfa)
            int dest_idx = dfa->edests[node_idx].elems[0];
            if (!re_node_set_contains (&init_nodes, dest_idx))
              {
-               re_node_set_merge (&init_nodes, dfa->eclosures + dest_idx);
+               reg_errcode_t err = re_node_set_merge (&init_nodes,
+                                                      dfa->eclosures
+                                                      + dest_idx);
+               if (err != REG_NOERROR)
+                 return err;
                i = 0;
              }
          }
@@ -972,8 +1079,7 @@ create_initial_state (dfa)
    DFA nodes where needed.  */
 
 static void
-optimize_utf8 (dfa)
-     re_dfa_t *dfa;
+optimize_utf8 (re_dfa_t *dfa)
 {
   int node, i, mb_chars = 0, has_period = 0;
 
@@ -985,7 +1091,7 @@ optimize_utf8 (dfa)
          mb_chars = 1;
        break;
       case ANCHOR:
-       switch (dfa->nodes[node].opr.idx)
+       switch (dfa->nodes[node].opr.ctx_type)
          {
          case LINE_FIRST:
          case LINE_LAST:
@@ -993,30 +1099,33 @@ optimize_utf8 (dfa)
          case BUF_LAST:
            break;
          default:
-           /* Word anchors etc. cannot be handled.  */
+           /* Word anchors etc. cannot be handled.  It's okay to test
+              opr.ctx_type since constraints (for all DFA nodes) are
+              created by ORing one or more opr.ctx_type values.  */
            return;
          }
        break;
       case OP_PERIOD:
-        has_period = 1;
-        break;
+       has_period = 1;
+       break;
       case OP_BACK_REF:
       case OP_ALT:
       case END_OF_RE:
       case OP_DUP_ASTERISK:
-      case OP_DUP_QUESTION:
-      case OP_DUP_PLUS:
       case OP_OPEN_SUBEXP:
       case OP_CLOSE_SUBEXP:
        break;
+      case COMPLEX_BRACKET:
+       return;
       case SIMPLE_BRACKET:
-       /* Just double check.  */
-        for (i = 0x80 / UINT_BITS; i < BITSET_UINTS; ++i)
+       /* Just double check.  The non-ASCII range starts at 0x80.  */
+       assert (0x80 % BITSET_WORD_BITS == 0);
+       for (i = 0x80 / BITSET_WORD_BITS; i < BITSET_WORDS; ++i)
          if (dfa->nodes[node].opr.sbcset[i])
            return;
        break;
       default:
-       return;
+       abort ();
       }
 
   if (mb_chars || has_period)
@@ -1040,10 +1149,9 @@ optimize_utf8 (dfa)
    "eclosure", and "inveclosure".  */
 
 static reg_errcode_t
-analyze (dfa)
-     re_dfa_t *dfa;
+analyze (regex_t *preg)
 {
-  int i;
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
   reg_errcode_t ret;
 
   /* Allocate arrays.  */
@@ -1051,234 +1159,312 @@ analyze (dfa)
   dfa->org_indices = 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 (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL
-         || dfa->eclosures == NULL || dfa->inveclosures == NULL, 0))
+         || dfa->eclosures == NULL, 0))
     return REG_ESPACE;
-  /* Initialize them.  */
-  for (i = 0; i < dfa->nodes_len; ++i)
+
+  dfa->subexp_map = re_malloc (int, preg->re_nsub);
+  if (dfa->subexp_map != NULL)
     {
-      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);
+      int i;
+      for (i = 0; i < preg->re_nsub; i++)
+       dfa->subexp_map[i] = i;
+      preorder (dfa->str_tree, optimize_subexps, dfa);
+      for (i = 0; i < preg->re_nsub; i++)
+       if (dfa->subexp_map[i] != i)
+         break;
+      if (i == preg->re_nsub)
+       {
+         free (dfa->subexp_map);
+         dfa->subexp_map = NULL;
+       }
     }
 
-  ret = analyze_tree (dfa, dfa->str_tree);
-  if (BE (ret == REG_NOERROR, 1))
+  ret = postorder (dfa->str_tree, lower_subexps, preg);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+  ret = postorder (dfa->str_tree, calc_first, dfa);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+  preorder (dfa->str_tree, calc_next, dfa);
+  ret = preorder (dfa->str_tree, link_nfa_nodes, dfa);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+  ret = calc_eclosure (dfa);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+
+  /* We only need this during the prune_impossible_nodes pass in regexec.c;
+     skip it if p_i_n will not run, as calc_inveclosure can be quadratic.  */
+  if ((!preg->no_sub && preg->re_nsub > 0 && dfa->has_plural_match)
+      || dfa->nbackref)
     {
-      ret = calc_eclosure (dfa);
-      if (ret == REG_NOERROR)
-       calc_inveclosure (dfa);
+      dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len);
+      if (BE (dfa->inveclosures == NULL, 0))
+       return REG_ESPACE;
+      ret = calc_inveclosure (dfa);
     }
+
   return ret;
 }
 
-/* Helper functions for analyze.
-   This function calculate "first", "next", and "edest" for the subtree
-   whose root is NODE.  */
+/* Our parse trees are very unbalanced, so we cannot use a stack to
+   implement parse tree visits.  Instead, we use parent pointers and
+   some hairy code in these two functions.  */
+static reg_errcode_t
+postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
+          void *extra)
+{
+  bin_tree_t *node, *prev;
+
+  for (node = root; ; )
+    {
+      /* Descend down the tree, preferably to the left (or to the right
+        if that's the only child).  */
+      while (node->left || node->right)
+       if (node->left)
+         node = node->left;
+       else
+         node = node->right;
+
+      do
+       {
+         reg_errcode_t err = fn (extra, node);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+         if (node->parent == NULL)
+           return REG_NOERROR;
+         prev = node;
+         node = node->parent;
+       }
+      /* Go up while we have a node that is reached from the right.  */
+      while (node->right == prev || node->right == NULL);
+      node = node->right;
+    }
+}
+
+static reg_errcode_t
+preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
+         void *extra)
+{
+  bin_tree_t *node;
+
+  for (node = root; ; )
+    {
+      reg_errcode_t err = fn (extra, node);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+
+      /* Go to the left node, or up and to the right.  */
+      if (node->left)
+       node = node->left;
+      else
+       {
+         bin_tree_t *prev = NULL;
+         while (node->right == prev || node->right == NULL)
+           {
+             prev = node;
+             node = node->parent;
+             if (!node)
+               return REG_NOERROR;
+           }
+         node = node->right;
+       }
+    }
+}
 
+/* Optimization pass: if a SUBEXP is entirely contained, strip it and tell
+   re_search_internal to map the inner one's opr.idx to this one's.  Adjust
+   backreferences as well.  Requires a preorder visit.  */
 static reg_errcode_t
-analyze_tree (dfa, node)
-     re_dfa_t *dfa;
-     bin_tree_t *node;
+optimize_subexps (void *extra, 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 (BE (ret != REG_NOERROR, 0))
-       return ret;
+  re_dfa_t *dfa = (re_dfa_t *) extra;
+
+  if (node->token.type == OP_BACK_REF && dfa->subexp_map)
+    {
+      int idx = node->token.opr.idx;
+      node->token.opr.idx = dfa->subexp_map[idx];
+      dfa->used_bkref_map |= 1 << node->token.opr.idx;
     }
-  /* Calculate "first" etc. for the right child.  */
-  if (node->right != NULL)
+
+  else if (node->token.type == SUBEXP
+          && node->left && node->left->token.type == SUBEXP)
     {
-      ret = analyze_tree (dfa, node->right);
-      if (BE (ret != REG_NOERROR, 0))
-       return ret;
+      int other_idx = node->left->token.opr.idx;
+
+      node->left = node->left->left;
+      if (node->left)
+       node->left->parent = node;
+
+      dfa->subexp_map[other_idx] = dfa->subexp_map[node->token.opr.idx];
+      if (other_idx < BITSET_WORD_BITS)
+         dfa->used_bkref_map &= ~((bitset_word_t) 1 << other_idx);
     }
+
   return REG_NOERROR;
 }
 
-/* Calculate "first" for the node NODE.  */
-static void
-calc_first (dfa, node)
-     re_dfa_t *dfa;
-     bin_tree_t *node;
+/* Lowering pass: Turn each SUBEXP node into the appropriate concatenation
+   of OP_OPEN_SUBEXP, the body of the SUBEXP (if any) and OP_CLOSE_SUBEXP.  */
+static reg_errcode_t
+lower_subexps (void *extra, bin_tree_t *node)
 {
-  int idx, type;
-  idx = node->node_idx;
-  type = (node->type == 0) ? dfa->nodes[idx].type : node->type;
+  regex_t *preg = (regex_t *) extra;
+  reg_errcode_t err = REG_NOERROR;
 
-  switch (type)
+  if (node->left && node->left->token.type == SUBEXP)
     {
-#ifdef DEBUG
-    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:
-#ifdef RE_ENABLE_I18N
-    case OP_UTF8_PERIOD:
-    case COMPLEX_BRACKET:
-#endif /* RE_ENABLE_I18N */
-    case SIMPLE_BRACKET:
-    case OP_BACK_REF:
-    case ANCHOR:
-    case OP_OPEN_SUBEXP:
-    case OP_CLOSE_SUBEXP:
-      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;
-      /* 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;
+      node->left = lower_subexp (&err, preg, node->left);
+      if (node->left)
+       node->left->parent = node;
+    }
+  if (node->right && node->right->token.type == SUBEXP)
+    {
+      node->right = lower_subexp (&err, preg, node->right);
+      if (node->right)
+       node->right->parent = node;
     }
-}
 
-/* Calculate "next" for the node NODE.  */
+  return err;
+}
 
-static void
-calc_next (dfa, node)
-     re_dfa_t *dfa;
-     bin_tree_t *node;
+static bin_tree_t *
+lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node)
 {
-  int idx, type;
-  bin_tree_t *parent = node->parent;
-  if (parent == NULL)
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *body = node->left;
+  bin_tree_t *op, *cls, *tree1, *tree;
+
+  if (preg->no_sub
+      /* We do not optimize empty subexpressions, because otherwise we may
+        have bad CONCAT nodes with NULL children.  This is obviously not
+        very common, so we do not lose much.  An example that triggers
+        this case is the sed "script" /\(\)/x.  */
+      && node->left != NULL
+      && (node->token.opr.idx >= BITSET_WORD_BITS
+         || !(dfa->used_bkref_map
+              & ((bitset_word_t) 1 << node->token.opr.idx))))
+    return node->left;
+
+  /* Convert the SUBEXP node to the concatenation of an
+     OP_OPEN_SUBEXP, the contents, and an OP_CLOSE_SUBEXP.  */
+  op = create_tree (dfa, NULL, NULL, OP_OPEN_SUBEXP);
+  cls = create_tree (dfa, NULL, NULL, OP_CLOSE_SUBEXP);
+  tree1 = body ? create_tree (dfa, body, cls, CONCAT) : cls;
+  tree = create_tree (dfa, op, tree1, CONCAT);
+  if (BE (tree == NULL || tree1 == NULL || op == NULL || cls == NULL, 0))
     {
-      node->next = -1;
-      idx = node->node_idx;
-      if (node->type == 0)
-       dfa->nexts[idx] = node->next;
-      return;
+      *err = REG_ESPACE;
+      return NULL;
     }
 
-  idx = parent->node_idx;
-  type = (parent->type == 0) ? dfa->nodes[idx].type : parent->type;
+  op->token.opr.idx = cls->token.opr.idx = node->token.opr.idx;
+  op->token.opt_subexp = cls->token.opt_subexp = node->token.opt_subexp;
+  return tree;
+}
 
-  switch (type)
+/* Pass 1 in building the NFA: compute FIRST and create unlinked automaton
+   nodes.  Requires a postorder visit.  */
+static reg_errcode_t
+calc_first (void *extra, bin_tree_t *node)
+{
+  re_dfa_t *dfa = (re_dfa_t *) extra;
+  if (node->token.type == CONCAT)
+    {
+      node->first = node->left->first;
+      node->node_idx = node->left->node_idx;
+    }
+  else
+    {
+      node->first = node;
+      node->node_idx = re_dfa_add_node (dfa, node->token);
+      if (BE (node->node_idx == -1, 0))
+       return REG_ESPACE;
+      if (node->token.type == ANCHOR)
+       dfa->nodes[node->node_idx].constraint = node->token.opr.ctx_type;
+    }
+  return REG_NOERROR;
+}
+
+/* Pass 2: compute NEXT on the tree.  Preorder visit.  */
+static reg_errcode_t
+calc_next (void *extra, bin_tree_t *node)
+{
+  switch (node->token.type)
     {
     case OP_DUP_ASTERISK:
-    case OP_DUP_PLUS:
-      node->next = idx;
+      node->left->next = node;
       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 */
+      node->left->next = node->right->first;
+      node->right->next = node->next;
+      break;
     default:
-      if (parent->next == -1)
-       calc_next (dfa, parent);
-      node->next = parent->next;
+      if (node->left)
+       node->left->next = node->next;
+      if (node->right)
+       node->right->next = node->next;
       break;
     }
-  idx = node->node_idx;
-  if (node->type == 0)
-    dfa->nexts[idx] = node->next;
+  return REG_NOERROR;
 }
 
-/* Calculate "edest" for the node NODE.  */
-
-static void
-calc_epsdest (dfa, node)
-     re_dfa_t *dfa;
-     bin_tree_t *node;
+/* Pass 3: link all DFA nodes to their NEXT node (any order will do).  */
+static reg_errcode_t
+link_nfa_nodes (void *extra, bin_tree_t *node)
 {
-  int idx;
-  idx = node->node_idx;
-  if (node->type == 0)
+  re_dfa_t *dfa = (re_dfa_t *) extra;
+  int idx = node->node_idx;
+  reg_errcode_t err = REG_NOERROR;
+
+  switch (node->token.type)
     {
-      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
-              || dfa->nodes[idx].type == OP_OPEN_SUBEXP
-              || dfa->nodes[idx].type == OP_CLOSE_SUBEXP
-              || dfa->nodes[idx].type == OP_BACK_REF)
-       re_node_set_init_1 (dfa->edests + idx, node->next);
-      else
-        assert (!IS_EPSILON_NODE (dfa->nodes[idx].type));
+    case CONCAT:
+      break;
+
+    case END_OF_RE:
+      assert (node->next == NULL);
+      break;
+
+    case OP_DUP_ASTERISK:
+    case OP_ALT:
+      {
+       int left, right;
+       dfa->has_plural_match = 1;
+       if (node->left != NULL)
+         left = node->left->first->node_idx;
+       else
+         left = node->next->node_idx;
+       if (node->right != NULL)
+         right = node->right->first->node_idx;
+       else
+         right = node->next->node_idx;
+       assert (left > -1);
+       assert (right > -1);
+       err = re_node_set_init_2 (dfa->edests + idx, left, right);
+      }
+      break;
+
+    case ANCHOR:
+    case OP_OPEN_SUBEXP:
+    case OP_CLOSE_SUBEXP:
+      err = re_node_set_init_1 (dfa->edests + idx, node->next->node_idx);
+      break;
+
+    case OP_BACK_REF:
+      dfa->nexts[idx] = node->next->node_idx;
+      if (node->token.type == OP_BACK_REF)
+       err = re_node_set_init_1 (dfa->edests + idx, dfa->nexts[idx]);
+      break;
+
+    default:
+      assert (!IS_EPSILON_NODE (node->token.type));
+      dfa->nexts[idx] = node->next->node_idx;
+      break;
     }
+
+  return err;
 }
 
 /* Duplicate the epsilon closure of the node ROOT_NODE.
@@ -1286,13 +1472,10 @@ calc_epsdest (dfa, node)
    to their own constraint.  */
 
 static reg_errcode_t
-duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node,
-                       init_constraint)
-     re_dfa_t *dfa;
-     int top_org_node, top_clone_node, root_node;
-     unsigned int init_constraint;
+internal_function
+duplicate_node_closure (re_dfa_t *dfa, int top_org_node, int top_clone_node,
+                       int root_node, unsigned int init_constraint)
 {
-  reg_errcode_t err;
   int org_node, clone_node, ret;
   unsigned int constraint = init_constraint;
   for (org_node = top_org_node, clone_node = top_clone_node;;)
@@ -1306,9 +1489,9 @@ duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node,
             edests of the back reference.  */
          org_dest = dfa->nexts[org_node];
          re_node_set_empty (dfa->edests + clone_node);
-         err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
-         if (BE (err != REG_NOERROR, 0))
-           return err;
+         clone_dest = duplicate_node (dfa, org_dest, constraint);
+         if (BE (clone_dest == -1, 0))
+           return REG_ESPACE;
          dfa->nexts[clone_node] = dfa->nexts[org_node];
          ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
          if (BE (ret < 0, 0))
@@ -1328,25 +1511,20 @@ duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node,
             destination.  */
          org_dest = dfa->edests[org_node].elems[0];
          re_node_set_empty (dfa->edests + clone_node);
-         if (dfa->nodes[org_node].type == ANCHOR)
+         /* If the node is root_node itself, it means the epsilon closure
+            has a loop.   Then tie it to the destination of the root_node.  */
+         if (org_node == root_node && clone_node != org_node)
            {
-             /* In case of the node has another constraint, append it.  */
-             if (org_node == root_node && clone_node != org_node)
-               {
-                 /* ...but if the node is root_node itself, it means the
-                    epsilon closure have a loop, then tie it to the
-                    destination of the root_node.  */
-                 ret = re_node_set_insert (dfa->edests + clone_node,
-                                           org_dest);
-                 if (BE (ret < 0, 0))
-                   return REG_ESPACE;
-                 break;
-               }
-             constraint |= dfa->nodes[org_node].opr.ctx_type;
+             ret = re_node_set_insert (dfa->edests + clone_node, org_dest);
+             if (BE (ret < 0, 0))
+               return REG_ESPACE;
+             break;
            }
-         err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
-         if (BE (err != REG_NOERROR, 0))
-           return err;
+         /* In case the node has another constraint, append it.  */
+         constraint |= dfa->nodes[org_node].constraint;
+         clone_dest = duplicate_node (dfa, org_dest, constraint);
+         if (BE (clone_dest == -1, 0))
+           return REG_ESPACE;
          ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
          if (BE (ret < 0, 0))
            return REG_ESPACE;
@@ -1354,17 +1532,18 @@ duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node,
       else /* dfa->edests[org_node].nelem == 2 */
        {
          /* In case of the node can epsilon-transit, and it has two
-            destinations. E.g. '|', '*', '+', '?'.   */
+            destinations. In the bin_tree_t and DFA, that's '|' and '*'.   */
          org_dest = dfa->edests[org_node].elems[0];
          re_node_set_empty (dfa->edests + clone_node);
          /* Search for a duplicated node which satisfies the constraint.  */
          clone_dest = search_duplicated_node (dfa, org_dest, constraint);
          if (clone_dest == -1)
            {
-             /* There are no such a duplicated node, create a new one.  */
-             err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
-             if (BE (err != REG_NOERROR, 0))
-               return err;
+             /* There is no such duplicated node, create a new one.  */
+             reg_errcode_t err;
+             clone_dest = duplicate_node (dfa, org_dest, constraint);
+             if (BE (clone_dest == -1, 0))
+               return REG_ESPACE;
              ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
              if (BE (ret < 0, 0))
                return REG_ESPACE;
@@ -1375,7 +1554,7 @@ duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node,
            }
          else
            {
-             /* There are a duplicated node which satisfy the constraint,
+             /* There is a duplicated node which satisfies the constraint,
                 use it to avoid infinite loop.  */
              ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
              if (BE (ret < 0, 0))
@@ -1383,9 +1562,9 @@ duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node,
            }
 
          org_dest = dfa->edests[org_node].elems[1];
-         err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
-         if (BE (err != REG_NOERROR, 0))
-           return err;
+         clone_dest = duplicate_node (dfa, org_dest, constraint);
+         if (BE (clone_dest == -1, 0))
+           return REG_ESPACE;
          ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
          if (BE (ret < 0, 0))
            return REG_ESPACE;
@@ -1400,10 +1579,8 @@ duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node,
    satisfies the constraint CONSTRAINT.  */
 
 static int
-search_duplicated_node (dfa, org_node, constraint)
-     re_dfa_t *dfa;
-     int org_node;
-     unsigned int constraint;
+search_duplicated_node (const re_dfa_t *dfa, int org_node,
+                       unsigned int constraint)
 {
   int idx;
   for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx)
@@ -1416,56 +1593,50 @@ search_duplicated_node (dfa, org_node, constraint)
 }
 
 /* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT.
-   The new index will be stored in NEW_IDX and return REG_NOERROR if succeeded,
-   otherwise return the error code.  */
+   Return the index of the new node, or -1 if insufficient storage is
+   available.  */
 
-static reg_errcode_t
-duplicate_node (new_idx, dfa, org_idx, constraint)
-     re_dfa_t *dfa;
-     int *new_idx, org_idx;
-     unsigned int constraint;
+static int
+duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint)
 {
-  re_token_t dup;
-  int dup_idx;
+  int dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx]);
+  if (BE (dup_idx != -1, 1))
+    {
+      dfa->nodes[dup_idx].constraint = constraint;
+      dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].constraint;
+      dfa->nodes[dup_idx].duplicated = 1;
 
-  dup = dfa->nodes[org_idx];
-  dup_idx = re_dfa_add_node (dfa, dup, 1);
-  if (BE (dup_idx == -1, 0))
-    return REG_ESPACE;
-  dfa->nodes[dup_idx].constraint = constraint;
-  if (dfa->nodes[org_idx].type == ANCHOR)
-    dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].opr.ctx_type;
-  dfa->nodes[dup_idx].duplicated = 1;
-  re_node_set_init_empty (dfa->edests + dup_idx);
-  re_node_set_init_empty (dfa->eclosures + dup_idx);
-  re_node_set_init_empty (dfa->inveclosures + dup_idx);
-
-  /* Store the index of the original node.  */
-  dfa->org_indices[dup_idx] = org_idx;
-  *new_idx = dup_idx;
-  return REG_NOERROR;
+      /* Store the index of the original node.  */
+      dfa->org_indices[dup_idx] = org_idx;
+    }
+  return dup_idx;
 }
 
-static void
-calc_inveclosure (dfa)
-     re_dfa_t *dfa;
+static reg_errcode_t
+calc_inveclosure (re_dfa_t *dfa)
 {
-  int src, idx, dest;
+  int src, idx, ret;
+  for (idx = 0; idx < dfa->nodes_len; ++idx)
+    re_node_set_init_empty (dfa->inveclosures + idx);
+
   for (src = 0; src < dfa->nodes_len; ++src)
     {
+      int *elems = dfa->eclosures[src].elems;
       for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx)
        {
-         dest = dfa->eclosures[src].elems[idx];
-         re_node_set_insert (dfa->inveclosures + dest, src);
+         ret = re_node_set_insert_last (dfa->inveclosures + elems[idx], src);
+         if (BE (ret == -1, 0))
+           return REG_ESPACE;
        }
     }
+
+  return REG_NOERROR;
 }
 
 /* Calculate "eclosure" for all the node in DFA.  */
 
 static reg_errcode_t
-calc_eclosure (dfa)
-     re_dfa_t *dfa;
+calc_eclosure (re_dfa_t *dfa)
 {
   int node_idx, incomplete;
 #ifdef DEBUG
@@ -1488,10 +1659,11 @@ calc_eclosure (dfa)
 #ifdef DEBUG
       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'.  */
+      /* Calculate epsilon closure of 'node_idx'.  */
       err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, 1);
       if (BE (err != REG_NOERROR, 0))
        return err;
@@ -1508,16 +1680,13 @@ calc_eclosure (dfa)
 /* Calculate epsilon closure of NODE.  */
 
 static reg_errcode_t
-calc_eclosure_iter (new_set, dfa, node, root)
-     re_node_set *new_set;
-     re_dfa_t *dfa;
-     int node, root;
+calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, int node, int root)
 {
   reg_errcode_t err;
-  unsigned int constraint;
-  int i, incomplete;
+  int i;
   re_node_set eclosure;
-  incomplete = 0;
+  int ret;
+  int incomplete = 0;
   err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1);
   if (BE (err != REG_NOERROR, 0))
     return err;
@@ -1526,15 +1695,14 @@ calc_eclosure_iter (new_set, dfa, node, root)
      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);
-  /* If the current node has constraints, duplicate all nodes.
-     Since they must inherit the constraints.  */
-  if (constraint && !dfa->nodes[dfa->edests[node].elems[0]].duplicated)
+  /* If the current node has constraints, duplicate all nodes
+     since they must inherit the constraints.  */
+  if (dfa->nodes[node].constraint
+      && dfa->edests[node].nelem
+      && !dfa->nodes[dfa->edests[node].elems[0]].duplicated)
     {
-      int org_node, cur_node;
-      org_node = cur_node = node;
-      err = duplicate_node_closure (dfa, node, node, node, constraint);
+      err = duplicate_node_closure (dfa, node, node, node,
+                                   dfa->nodes[node].constraint);
       if (BE (err != REG_NOERROR, 0))
        return err;
     }
@@ -1562,9 +1730,11 @@ calc_eclosure_iter (new_set, dfa, node, root)
          }
        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,
+       /* Merge the epsilon closure of 'edest'.  */
+       err = re_node_set_merge (&eclosure, &eclosure_elem);
+       if (BE (err != REG_NOERROR, 0))
+         return err;
+       /* If the epsilon closure of 'edest' is incomplete,
           the epsilon closure of this node is also incomplete.  */
        if (dfa->eclosures[edest].nelem == 0)
          {
@@ -1573,8 +1743,10 @@ calc_eclosure_iter (new_set, dfa, node, root)
          }
       }
 
-  /* Epsilon closures include itself.  */
-  re_node_set_insert (&eclosure, node);
+  /* An epsilon closure includes itself.  */
+  ret = re_node_set_insert (&eclosure, node);
+  if (BE (ret < 0, 0))
+    return REG_ESPACE;
   if (incomplete && !root)
     dfa->eclosures[node].nelem = 0;
   else
@@ -1588,26 +1760,19 @@ calc_eclosure_iter (new_set, dfa, node, root)
 /* 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;
+static void
+internal_function
+fetch_token (re_token_t *result, 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;
+  re_string_skip_bytes (input, peek_token (result, input, syntax));
 }
 
 /* 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;
+internal_function
+peek_token (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
 {
   unsigned char c;
 
@@ -1620,6 +1785,7 @@ peek_token (token, input, syntax)
   c = re_string_peek_byte (input, 0);
   token->opr.c = c;
 
+  token->word_char = 0;
 #ifdef RE_ENABLE_I18N
   token->mb_partial = 0;
   if (input->mb_cur_max > 1 &&
@@ -1642,6 +1808,17 @@ peek_token (token, input, syntax)
       c2 = re_string_peek_byte_case (input, 1);
       token->opr.c = c2;
       token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+      if (input->mb_cur_max > 1)
+       {
+         wint_t wc = re_string_wchar_at (input,
+                                         re_string_cur_idx (input) + 1);
+         token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+       }
+      else
+#endif
+       token->word_char = IS_WORD_CHAR (c2) != 0;
+
       switch (c2)
        {
        case '|':
@@ -1653,35 +1830,35 @@ peek_token (token, input, syntax)
          if (!(syntax & RE_NO_BK_REFS))
            {
              token->type = OP_BACK_REF;
-             token->opr.idx = c2 - '0';
+             token->opr.idx = c2 - '1';
            }
          break;
        case '<':
          if (!(syntax & RE_NO_GNU_OPS))
            {
              token->type = ANCHOR;
-             token->opr.idx = WORD_FIRST;
+             token->opr.ctx_type = WORD_FIRST;
            }
          break;
        case '>':
          if (!(syntax & RE_NO_GNU_OPS))
            {
              token->type = ANCHOR;
-             token->opr.idx = WORD_LAST;
+             token->opr.ctx_type = WORD_LAST;
            }
          break;
        case 'b':
          if (!(syntax & RE_NO_GNU_OPS))
            {
              token->type = ANCHOR;
-             token->opr.idx = WORD_DELIM;
+             token->opr.ctx_type = WORD_DELIM;
            }
          break;
        case 'B':
          if (!(syntax & RE_NO_GNU_OPS))
            {
              token->type = ANCHOR;
-             token->opr.idx = INSIDE_WORD;
+             token->opr.ctx_type = NOT_WORD_DELIM;
            }
          break;
        case 'w':
@@ -1704,14 +1881,14 @@ peek_token (token, input, syntax)
          if (!(syntax & RE_NO_GNU_OPS))
            {
              token->type = ANCHOR;
-             token->opr.idx = BUF_FIRST;
+             token->opr.ctx_type = BUF_FIRST;
            }
          break;
        case '\'':
          if (!(syntax & RE_NO_GNU_OPS))
            {
              token->type = ANCHOR;
-             token->opr.idx = BUF_LAST;
+             token->opr.ctx_type = BUF_LAST;
            }
          break;
        case '(':
@@ -1745,6 +1922,16 @@ peek_token (token, input, syntax)
     }
 
   token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+  if (input->mb_cur_max > 1)
+    {
+      wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input));
+      token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+    }
+  else
+#endif
+    token->word_char = IS_WORD_CHAR (token->opr.c);
+
   switch (c)
     {
     case '\n':
@@ -1797,7 +1984,7 @@ peek_token (token, input, syntax)
            break;
        }
       token->type = ANCHOR;
-      token->opr.idx = LINE_FIRST;
+      token->opr.ctx_type = LINE_FIRST;
       break;
     case '$':
       if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
@@ -1811,7 +1998,7 @@ peek_token (token, input, syntax)
            break;
        }
       token->type = ANCHOR;
-      token->opr.idx = LINE_LAST;
+      token->opr.ctx_type = LINE_LAST;
       break;
     default:
       break;
@@ -1823,10 +2010,8 @@ peek_token (token, input, syntax)
    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;
+internal_function
+peek_token_bracket (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
 {
   unsigned char c;
   if (re_string_eoi (input))
@@ -1846,7 +2031,8 @@ peek_token_bracket (token, input, syntax)
     }
 #endif /* RE_ENABLE_I18N */
 
-  if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS))
+  if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS)
+      && re_string_cur_idx (input) + 1 < re_string_length (input))
     {
       /* In this case, '\' escape a character.  */
       unsigned char c2;
@@ -1860,7 +2046,10 @@ peek_token_bracket (token, input, syntax)
     {
       unsigned char c2;
       int token_len;
-      c2 = re_string_peek_byte (input, 1);
+      if (re_string_cur_idx (input) + 1 < re_string_length (input))
+       c2 = re_string_peek_byte (input, 1);
+      else
+       c2 = 0;
       token->opr.c = c2;
       token_len = 2;
       switch (c2)
@@ -1907,7 +2096,7 @@ peek_token_bracket (token, input, syntax)
 
 /* 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.
+   If an error occurs, ERR is set by error code, and return NULL.
    This function build the following tree, from regular expression <reg_exp>:
           CAT
           / \
@@ -1918,22 +2107,20 @@ peek_token_bracket (token, input, syntax)
    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;
+parse (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;
-  current_token = fetch_token (regexp, syntax | RE_CARET_ANCHORS_HERE);
+  dfa->syntax = syntax;
+  fetch_token (&current_token, regexp, syntax | RE_CARET_ANCHORS_HERE);
   tree = parse_reg_exp (regexp, preg, &current_token, syntax, 0, err);
   if (BE (*err != REG_NOERROR && tree == NULL, 0))
     return NULL;
-  eor = re_dfa_add_tree_node (dfa, NULL, NULL, current_token);
+  eor = create_tree (dfa, NULL, NULL, END_OF_RE);
   if (tree != NULL)
-    root = create_tree (dfa, tree, eor, CONCAT, 0);
+    root = create_tree (dfa, tree, eor, CONCAT);
   else
     root = eor;
   if (BE (eor == NULL || root == NULL, 0))
@@ -1951,16 +2138,11 @@ parse (regexp, preg, syntax, err)
          /   \
    <branch1> <branch2>
 
-   ALT means alternative, which represents the operator `|'.  */
+   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;
+parse_reg_exp (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;
@@ -1970,24 +2152,26 @@ parse_reg_exp (regexp, preg, token, syntax, nest, err)
 
   while (token->type == OP_ALT)
     {
-      re_token_t alt_token = *token;
-      *token = fetch_token (regexp, syntax | RE_CARET_ANCHORS_HERE);
+      fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
       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 (BE (*err != REG_NOERROR && branch == NULL, 0))
-           return NULL;
+           {
+             if (tree != NULL)
+               postorder (tree, free_tree, NULL);
+             return NULL;
+           }
        }
       else
        branch = NULL;
-      tree = re_dfa_add_tree_node (dfa, tree, branch, alt_token);
+      tree = create_tree (dfa, tree, branch, OP_ALT);
       if (BE (tree == NULL, 0))
        {
          *err = REG_ESPACE;
          return NULL;
        }
-      dfa->has_plural_match = 1;
     }
   return tree;
 }
@@ -2002,13 +2186,8 @@ parse_reg_exp (regexp, preg, token, syntax, nest, err)
    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;
+parse_branch (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;
   re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
@@ -2022,16 +2201,21 @@ parse_branch (regexp, preg, token, syntax, nest, err)
       exp = parse_expression (regexp, preg, token, syntax, nest, err);
       if (BE (*err != REG_NOERROR && exp == NULL, 0))
        {
+         if (tree != NULL)
+           postorder (tree, free_tree, NULL);
          return NULL;
        }
       if (tree != NULL && exp != NULL)
        {
-         tree = create_tree (dfa, tree, exp, CONCAT, 0);
-         if (tree == NULL)
+         bin_tree_t *newtree = create_tree (dfa, tree, exp, CONCAT);
+         if (newtree == NULL)
            {
+             postorder (exp, free_tree, NULL);
+             postorder (tree, free_tree, NULL);
              *err = REG_ESPACE;
              return NULL;
            }
+         tree = newtree;
        }
       else if (tree == NULL)
        tree = exp;
@@ -2047,20 +2231,15 @@ parse_branch (regexp, preg, token, syntax, nest, err)
 */
 
 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;
+parse_expression (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;
   switch (token->type)
     {
     case CHARACTER:
-      tree = re_dfa_add_tree_node (dfa, NULL, NULL, *token);
+      tree = create_token_tree (dfa, NULL, NULL, token);
       if (BE (tree == NULL, 0))
        {
          *err = REG_ESPACE;
@@ -2073,9 +2252,9 @@ parse_expression (regexp, preg, token, syntax, nest, err)
                 && !re_string_first_byte (regexp, re_string_cur_idx (regexp)))
            {
              bin_tree_t *mbc_remain;
-             *token = fetch_token (regexp, syntax);
-             mbc_remain = re_dfa_add_tree_node (dfa, NULL, NULL, *token);
-             tree = create_tree (dfa, tree, mbc_remain, CONCAT, 0);
+             fetch_token (token, regexp, syntax);
+             mbc_remain = create_token_tree (dfa, NULL, NULL, token);
+             tree = create_tree (dfa, tree, mbc_remain, CONCAT);
              if (BE (mbc_remain == NULL || tree == NULL, 0))
                {
                  *err = REG_ESPACE;
@@ -2096,14 +2275,13 @@ parse_expression (regexp, preg, token, syntax, nest, err)
        return NULL;
       break;
     case OP_BACK_REF:
-      if (BE (preg->re_nsub < token->opr.idx
-             || dfa->subexps[token->opr.idx - 1].end == -1, 0))
+      if (!BE (dfa->completed_bkref_map & (1 << token->opr.idx), 1))
        {
          *err = REG_ESUBREG;
          return NULL;
        }
-      dfa->used_bkref_map |= 1 << (token->opr.idx - 1);
-      tree = re_dfa_add_tree_node (dfa, NULL, NULL, *token);
+      dfa->used_bkref_map |= 1 << token->opr.idx;
+      tree = create_token_tree (dfa, NULL, NULL, token);
       if (BE (tree == NULL, 0))
        {
          *err = REG_ESPACE;
@@ -2129,7 +2307,7 @@ parse_expression (regexp, preg, token, syntax, nest, err)
        }
       else if (syntax & RE_CONTEXT_INDEP_OPS)
        {
-         *token = fetch_token (regexp, syntax);
+         fetch_token (token, regexp, syntax);
          return parse_expression (regexp, preg, token, syntax, nest, err);
        }
       /* else fall through  */
@@ -2146,7 +2324,9 @@ parse_expression (regexp, preg, token, syntax, nest, err)
 
       /* Then we can these characters as normal characters.  */
       token->type = CHARACTER;
-      tree = re_dfa_add_tree_node (dfa, NULL, NULL, *token);
+      /* mb_partial and word_char bits should be initialized already
+        by peek_token.  */
+      tree = create_token_tree (dfa, NULL, NULL, token);
       if (BE (tree == NULL, 0))
        {
          *err = REG_ESPACE;
@@ -2154,21 +2334,28 @@ parse_expression (regexp, preg, token, syntax, nest, err)
        }
       break;
     case ANCHOR:
-      if (dfa->word_char == NULL)
-       {
-         *err = init_word_char (dfa);
-         if (BE (*err != REG_NOERROR, 0))
-           return NULL;
-       }
-      if (token->opr.ctx_type == WORD_DELIM)
+      if ((token->opr.ctx_type
+          & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST))
+         && dfa->word_ops_used == 0)
+       init_word_char (dfa);
+      if (token->opr.ctx_type == WORD_DELIM
+         || token->opr.ctx_type == NOT_WORD_DELIM)
        {
          bin_tree_t *tree_first, *tree_last;
-         token->opr.ctx_type = WORD_FIRST;
-         tree_first = re_dfa_add_tree_node (dfa, NULL, NULL, *token);
-         token->opr.ctx_type = WORD_LAST;
-         tree_last = re_dfa_add_tree_node (dfa, NULL, NULL, *token);
-         token->type = OP_ALT;
-         tree = re_dfa_add_tree_node (dfa, tree_first, tree_last, *token);
+         if (token->opr.ctx_type == WORD_DELIM)
+           {
+             token->opr.ctx_type = WORD_FIRST;
+             tree_first = create_token_tree (dfa, NULL, NULL, token);
+             token->opr.ctx_type = WORD_LAST;
+           }
+         else
+           {
+             token->opr.ctx_type = INSIDE_WORD;
+             tree_first = create_token_tree (dfa, NULL, NULL, token);
+             token->opr.ctx_type = INSIDE_NOTWORD;
+           }
+         tree_last = create_token_tree (dfa, NULL, NULL, token);
+         tree = create_tree (dfa, tree_first, tree_last, OP_ALT);
          if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0))
            {
              *err = REG_ESPACE;
@@ -2177,7 +2364,7 @@ parse_expression (regexp, preg, token, syntax, nest, err)
        }
       else
        {
-         tree = re_dfa_add_tree_node (dfa, NULL, NULL, *token);
+         tree = create_token_tree (dfa, NULL, NULL, token);
          if (BE (tree == NULL, 0))
            {
              *err = REG_ESPACE;
@@ -2188,10 +2375,10 @@ parse_expression (regexp, preg, token, syntax, nest, err)
         by repetition operators.
         eg. RE"^*" is invalid or "<ANCHOR(^)><CHAR(*)>",
             it must not be "<ANCHOR(^)><REPEAT(*)>".  */
-      *token = fetch_token (regexp, syntax);
+      fetch_token (token, regexp, syntax);
       return tree;
     case OP_PERIOD:
-      tree = re_dfa_add_tree_node (dfa, NULL, NULL, *token);
+      tree = create_token_tree (dfa, NULL, NULL, token);
       if (BE (tree == NULL, 0))
        {
          *err = REG_ESPACE;
@@ -2201,22 +2388,20 @@ parse_expression (regexp, preg, token, syntax, nest, err)
        dfa->has_mb_node = 1;
       break;
     case OP_WORD:
-      tree = build_charclass_op (dfa, regexp->trans, "alnum", "_", 0, err);
-      if (BE (*err != REG_NOERROR && tree == NULL, 0))
-       return NULL;
-      break;
     case OP_NOTWORD:
-      tree = build_charclass_op (dfa, regexp->trans, "alnum", "_", 1, err);
+      tree = build_charclass_op (dfa, regexp->trans,
+                                (const unsigned char *) "alnum",
+                                (const unsigned char *) "_",
+                                token->type == OP_NOTWORD, err);
       if (BE (*err != REG_NOERROR && tree == NULL, 0))
        return NULL;
       break;
     case OP_SPACE:
-      tree = build_charclass_op (dfa, regexp->trans, "space", "", 0, err);
-      if (BE (*err != REG_NOERROR && tree == NULL, 0))
-       return NULL;
-      break;
     case OP_NOTSPACE:
-      tree = build_charclass_op (dfa, regexp->trans, "space", "", 1, err);
+      tree = build_charclass_op (dfa, regexp->trans,
+                                (const unsigned char *) "space",
+                                (const unsigned char *) "",
+                                token->type == OP_NOTSPACE, err);
       if (BE (*err != REG_NOERROR && tree == NULL, 0))
        return NULL;
       break;
@@ -2233,23 +2418,29 @@ parse_expression (regexp, preg, token, syntax, nest, err)
 #endif
       return NULL;
     }
-  *token = fetch_token (regexp, syntax);
+  fetch_token (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 (BE (*err != REG_NOERROR && tree == NULL, 0))
-       return NULL;
+      bin_tree_t *dup_tree = parse_dup_op (tree, regexp, dfa, token, syntax, err);
+      if (BE (*err != REG_NOERROR && dup_tree == NULL, 0))
+       {
+         if (tree != NULL)
+           postorder (tree, free_tree, NULL);
+         return NULL;
+       }
+      tree = dup_tree;
       /* In BRE consecutive duplications are not allowed.  */
       if ((syntax & RE_CONTEXT_INVALID_DUP)
          && (token->type == OP_DUP_ASTERISK
              || token->type == OP_OPEN_DUP_NUM))
        {
+         if (tree != NULL)
+           postorder (tree, free_tree, NULL);
          *err = REG_BADRPT;
          return NULL;
        }
-      dfa->has_plural_match = 1;
     }
 
   return tree;
@@ -2263,42 +2454,15 @@ parse_expression (regexp, preg, token, syntax, nest, err)
 */
 
 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;
+parse_sub_exp (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, *left_par, *right_par;
+  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 (BE (new_array == NULL, 0))
-       {
-         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;
 
-  left_par = re_dfa_add_tree_node (dfa, NULL, NULL, *token);
-  if (BE (left_par == NULL, 0))
-    {
-      *err = REG_ESPACE;
-      return NULL;
-    }
-  dfa->nodes[left_par->node_idx].opr.idx = cur_nsub;
-  *token = fetch_token (regexp, syntax | RE_CARET_ANCHORS_HERE);
+  fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
 
   /* The subexpression may be a null string.  */
   if (token->type == OP_CLOSE_SUBEXP)
@@ -2306,50 +2470,43 @@ parse_sub_exp (regexp, preg, token, syntax, nest, err)
   else
     {
       tree = parse_reg_exp (regexp, preg, token, syntax, nest, err);
-      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+      if (BE (*err == REG_NOERROR && token->type != OP_CLOSE_SUBEXP, 0))
+       {
+         if (tree != NULL)
+           postorder (tree, free_tree, NULL);
+         *err = REG_EPAREN;
+       }
+      if (BE (*err != REG_NOERROR, 0))
        return NULL;
     }
-  if (BE (token->type != OP_CLOSE_SUBEXP, 0))
-    {
-      *err = REG_EPAREN;
-      return NULL;
-    }
-  right_par = re_dfa_add_tree_node (dfa, NULL, NULL, *token);
-  dfa->subexps[cur_nsub].end = dfa->nodes_len;
-  tree = ((tree == NULL) ? right_par
-         : create_tree (dfa, tree, right_par, CONCAT, 0));
-  tree = create_tree (dfa, left_par, tree, CONCAT, 0);
-  if (BE (right_par == NULL || tree == NULL, 0))
+
+  if (cur_nsub <= '9' - '1')
+    dfa->completed_bkref_map |= 1 << cur_nsub;
+
+  tree = create_tree (dfa, tree, NULL, SUBEXP);
+  if (BE (tree == NULL, 0))
     {
       *err = REG_ESPACE;
       return NULL;
     }
-  dfa->nodes[right_par->node_idx].opr.idx = cur_nsub;
-
+  tree->token.opr.idx = cur_nsub;
   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;
+parse_dup_op (bin_tree_t *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 start_idx = re_string_cur_idx (regexp);
+  bin_tree_t *tree = NULL, *old_tree = NULL;
+  int i, start, end, start_idx = re_string_cur_idx (regexp);
   re_token_t start_token = *token;
+
   if (token->type == OP_OPEN_DUP_NUM)
     {
-      int i;
-      int end = 0;
-      int start = fetch_number (regexp, token, syntax);
-      bin_tree_t *elem;
+      end = 0;
+      start = fetch_number (regexp, token, syntax);
       if (start == -1)
        {
          if (token->type == CHARACTER && token->opr.c == ',')
@@ -2370,118 +2527,102 @@ parse_dup_op (dup_elem, regexp, dfa, token, syntax, err)
       if (BE (start == -2 || end == -2, 0))
        {
          /* Invalid sequence.  */
-         if (token->type == OP_CLOSE_DUP_NUM)
-           goto parse_dup_op_invalid_interval;
-         else
-           goto parse_dup_op_ebrace;
-       }
-      if (BE (start == 0 && end == 0, 0))
-       {
-         /* We treat "<re>{0}" and "<re>{0,0}" as null string.  */
-         *token = fetch_token (regexp, syntax);
-         return NULL;
-       }
-
-      /* 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 (dfa, tree, work_tree, CONCAT, 0);
-           if (BE (work_tree == NULL || tree == NULL, 0))
-             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);
-             work_tree = re_dfa_add_tree_node (dfa, elem, NULL, dup_token);
-             tree = create_tree (dfa, tree, work_tree, CONCAT, 0);
-             if (BE (elem == NULL || work_tree == NULL || tree == NULL, 0))
-               goto parse_dup_op_espace;
-           }
-         else
+         if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
            {
-             tree = re_dfa_add_tree_node (dfa, elem, NULL, dup_token);
-             if (BE (tree == NULL, 0))
-               goto parse_dup_op_espace;
+             if (token->type == END_OF_RE)
+               *err = REG_EBRACE;
+             else
+               *err = REG_BADBR;
+
+             return NULL;
            }
+
+         /* If the syntax bit is set, rollback.  */
+         re_string_set_index (regexp, start_idx);
+         *token = start_token;
+         token->type = CHARACTER;
+         /* mb_partial and word_char bits should be already initialized by
+            peek_token.  */
+         return elem;
        }
-      else if (BE (start > end, 0))
+
+      if (BE ((end != -1 && start > end) || token->type != OP_CLOSE_DUP_NUM, 0))
        {
-         /* First  number greater than first.  */
+         /* First number greater than second.  */
          *err = REG_BADBR;
          return NULL;
        }
-      else if (end - start > 0)
+    }
+  else
+    {
+      start = (token->type == OP_DUP_PLUS) ? 1 : 0;
+      end = (token->type == OP_DUP_QUESTION) ? 1 : -1;
+    }
+
+  fetch_token (token, regexp, syntax);
+
+  if (BE (elem == NULL, 0))
+    return NULL;
+  if (BE (start == 0 && end == 0, 0))
+    {
+      postorder (elem, free_tree, NULL);
+      return NULL;
+    }
+
+  /* Extract "<re>{n,m}" to "<re><re>...<re><re>{0,<m-n>}".  */
+  if (BE (start > 0, 0))
+    {
+      tree = elem;
+      for (i = 2; i <= start; ++i)
        {
-         /* Then extract "<re>{0,m}" to "<re>?<re>?...<re>?".  */
-         dup_token.type = OP_DUP_QUESTION;
-         if (start > 0)
-           {
-             elem = duplicate_tree (elem, dfa);
-             elem = re_dfa_add_tree_node (dfa, elem, NULL, dup_token);
-             tree = create_tree (dfa, tree, elem, CONCAT, 0);
-             if (BE (elem == NULL || tree == NULL, 0))
-               goto parse_dup_op_espace;
-           }
-         else
-           {
-             tree = elem = re_dfa_add_tree_node (dfa, elem, NULL, dup_token);
-             if (BE (tree == NULL, 0))
-               goto parse_dup_op_espace;
-           }
-         for (i = 1; i < end - start; ++i)
-           {
-             work_tree = duplicate_tree (elem, dfa);
-             tree = create_tree (dfa, tree, work_tree, CONCAT, 0);
-             if (BE (work_tree == NULL || tree == NULL, 0))
-               {
-                 *err = REG_ESPACE;
-                 return NULL;
-               }
-           }
+         elem = duplicate_tree (elem, dfa);
+         tree = create_tree (dfa, tree, elem, CONCAT);
+         if (BE (elem == NULL || tree == NULL, 0))
+           goto parse_dup_op_espace;
        }
+
+      if (start == end)
+       return tree;
+
+      /* Duplicate ELEM before it is marked optional.  */
+      elem = duplicate_tree (elem, dfa);
+      if (BE (elem == NULL, 0))
+        goto parse_dup_op_espace;
+      old_tree = tree;
     }
   else
+    old_tree = NULL;
+
+  if (elem->token.type == SUBEXP)
+    postorder (elem, mark_opt_subexp, (void *) (long) elem->token.opr.idx);
+
+  tree = create_tree (dfa, elem, NULL, (end == -1 ? OP_DUP_ASTERISK : OP_ALT));
+  if (BE (tree == NULL, 0))
+    goto parse_dup_op_espace;
+
+  /* This loop is actually executed only when end != -1,
+     to rewrite <re>{0,n} as (<re>(<re>...<re>?)?)?...  We have
+     already created the start+1-th copy.  */
+  for (i = start + 2; i <= end; ++i)
     {
-      tree = re_dfa_add_tree_node (dfa, tree, NULL, *token);
+      elem = duplicate_tree (elem, dfa);
+      tree = create_tree (dfa, tree, elem, CONCAT);
+      if (BE (elem == NULL || tree == NULL, 0))
+       goto parse_dup_op_espace;
+
+      tree = create_tree (dfa, tree, NULL, OP_ALT);
       if (BE (tree == NULL, 0))
-       {
-         *err = REG_ESPACE;
-         return NULL;
-       }
+       goto parse_dup_op_espace;
     }
-  *token = fetch_token (regexp, syntax);
+
+  if (old_tree)
+    tree = create_tree (dfa, old_tree, tree, CONCAT);
+
   return tree;
 
  parse_dup_op_espace:
   *err = REG_ESPACE;
   return NULL;
-
- parse_dup_op_ebrace:
-  if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
-    {
-      *err = REG_EBRACE;
-      return NULL;
-    }
-  goto parse_dup_op_rollback;
- parse_dup_op_invalid_interval:
-  if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
-    {
-      *err = REG_BADBR;
-      return NULL;
-    }
- parse_dup_op_rollback:
-  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.
@@ -2493,19 +2634,18 @@ parse_dup_op (dup_elem, regexp, dfa, token, syntax, err)
      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
+     mbcset->range_ends, is a pointer argument since we may
      update it.  */
 
 static reg_errcode_t
+internal_function
 # ifdef RE_ENABLE_I18N
-build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
-     re_charset_t *mbcset;
-     int *range_alloc;
+build_range_exp (bitset_t sbcset, re_charset_t *mbcset, int *range_alloc,
+                bracket_elem_t *start_elem, bracket_elem_t *end_elem)
 # else /* not RE_ENABLE_I18N */
-build_range_exp (sbcset, start_elem, end_elem)
+build_range_exp (bitset_t sbcset, bracket_elem_t *start_elem,
+                bracket_elem_t *end_elem)
 # endif /* not RE_ENABLE_I18N */
-     re_bitset_ptr_t sbcset;
-     bracket_elem_t *start_elem, *end_elem;
 {
   unsigned int start_ch, end_ch;
   /* Equivalence Classes and Character Classes can't be a range start/end.  */
@@ -2524,7 +2664,9 @@ build_range_exp (sbcset, start_elem, end_elem)
 
 # ifdef RE_ENABLE_I18N
   {
-    wchar_t wc, start_wc, end_wc;
+    wchar_t wc;
+    wint_t start_wc;
+    wint_t end_wc;
     wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
 
     start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch
@@ -2537,40 +2679,50 @@ build_range_exp (sbcset, start_elem, end_elem)
                ? __btowc (start_ch) : start_elem->opr.wch);
     end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM)
              ? __btowc (end_ch) : end_elem->opr.wch);
+    if (start_wc == WEOF || end_wc == WEOF)
+      return REG_ECOLLATE;
     cmp_buf[0] = start_wc;
     cmp_buf[4] = end_wc;
     if (wcscoll (cmp_buf, cmp_buf + 4) > 0)
       return REG_ERANGE;
 
-    /* Check the space of the arrays.  */
-    if (*range_alloc == mbcset->nranges)
+    /* Got valid collation sequence values, add them as a new entry.
+       However, for !_LIBC we have no collation elements: if the
+       character set is single byte, the single byte character set
+       that we build below suffices.  parse_bracket_exp passes
+       no MBCSET if dfa->mb_cur_max == 1.  */
+    if (mbcset)
       {
-       /* There are not enough space, need realloc.  */
-       wchar_t *new_array_start, *new_array_end;
-       int new_nranges;
-
-       /* +1 in case of mbcset->nranges is 0.  */
-       new_nranges = 2 * mbcset->nranges + 1;
-       /* Use realloc since mbcset->range_starts and mbcset->range_ends
-          are NULL if *range_alloc == 0.  */
-       new_array_start = re_realloc (mbcset->range_starts, wchar_t,
-                                     new_nranges);
-       new_array_end = re_realloc (mbcset->range_ends, wchar_t,
-                                   new_nranges);
-
-       if (BE (new_array_start == NULL || new_array_end == NULL, 0))
-         return REG_ESPACE;
-
-       mbcset->range_starts = new_array_start;
-       mbcset->range_ends = new_array_end;
-       *range_alloc = new_nranges;
-      }
+       /* Check the space of the arrays.  */
+       if (BE (*range_alloc == mbcset->nranges, 0))
+         {
+           /* There is not enough space, need realloc.  */
+           wchar_t *new_array_start, *new_array_end;
+           int new_nranges;
+
+           /* +1 in case of mbcset->nranges is 0.  */
+           new_nranges = 2 * mbcset->nranges + 1;
+           /* Use realloc since mbcset->range_starts and mbcset->range_ends
+              are NULL if *range_alloc == 0.  */
+           new_array_start = re_realloc (mbcset->range_starts, wchar_t,
+                                         new_nranges);
+           new_array_end = re_realloc (mbcset->range_ends, wchar_t,
+                                       new_nranges);
+
+           if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+             return REG_ESPACE;
+
+           mbcset->range_starts = new_array_start;
+           mbcset->range_ends = new_array_end;
+           *range_alloc = new_nranges;
+         }
 
-    mbcset->range_starts[mbcset->nranges] = start_wc;
-    mbcset->range_ends[mbcset->nranges++] = end_wc;
+       mbcset->range_starts[mbcset->nranges] = start_wc;
+       mbcset->range_ends[mbcset->nranges++] = end_wc;
+      }
 
     /* Build the table for single byte characters.  */
-    for (wc = 0; wc <= SBC_MAX; ++wc)
+    for (wc = 0; wc < SBC_MAX; ++wc)
       {
        cmp_buf[2] = wc;
        if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
@@ -2590,7 +2742,7 @@ build_range_exp (sbcset, start_elem, end_elem)
     if (start_ch > end_ch)
       return REG_ERANGE;
     /* Build the table for single byte characters.  */
-    for (ch = 0; ch <= SBC_MAX; ++ch)
+    for (ch = 0; ch < SBC_MAX; ++ch)
       if (start_ch <= ch  && ch <= end_ch)
        bitset_set (sbcset, ch);
   }
@@ -2607,15 +2759,13 @@ build_range_exp (sbcset, start_elem, end_elem)
    pointer argument since we may update it.  */
 
 static reg_errcode_t
+internal_function
 # ifdef RE_ENABLE_I18N
-build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name)
-     re_charset_t *mbcset;
-     int *coll_sym_alloc;
+build_collating_symbol (bitset_t sbcset, re_charset_t *mbcset,
+                       int *coll_sym_alloc, const unsigned char *name)
 # else /* not RE_ENABLE_I18N */
-build_collating_symbol (sbcset, name)
+build_collating_symbol (bitset_t sbcset, const unsigned char *name)
 # endif /* not RE_ENABLE_I18N */
-     re_bitset_ptr_t sbcset;
-     const unsigned char *name;
 {
   size_t name_len = strlen ((const char *) name);
   if (BE (name_len != 1, 0))
@@ -2632,12 +2782,8 @@ build_collating_symbol (sbcset, name)
    "[[.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;
+parse_bracket_exp (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;
@@ -2647,47 +2793,40 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
   const int32_t *symb_table;
   const unsigned char *extra;
 
-  /* Local function for parse_bracket_exp used in _LIBC environement.
-     Seek the collating symbol entry correspondings to NAME.
-     Return the index of the symbol in the SYMB_TABLE.  */
-
-  static inline int32_t
-  __attribute ((always_inline))
-  seek_collating_symbol_entry (name, name_len)
-        const unsigned char *name;
-        size_t name_len;
-    {
-      int32_t hash = elem_hash ((const char *) 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;
-           }
+  /* Local function for parse_bracket_exp used in _LIBC environment.
+     Seek the collating symbol entry corresponding to NAME.
+     Return the index of the symbol in the SYMB_TABLE,
+     or -1 if not found.  */
 
-         /* Next entry.  */
-         elem += second;
-       }
-      return elem;
+  auto inline int32_t
+  __attribute__ ((always_inline))
+  seek_collating_symbol_entry (const unsigned char *name, size_t name_len)
+    {
+      int32_t elem;
+
+      for (elem = 0; elem < table_size; elem++)
+       if (symb_table[2 * elem] != 0)
+         {
+           int32_t idx = symb_table[2 * elem + 1];
+           /* Skip the name of collating element name.  */
+           idx += 1 + extra[idx];
+           if (/* Compare the length of the name.  */
+               name_len == extra[idx]
+               /* Compare the name.  */
+               && memcmp (name, &extra[idx + 1], name_len) == 0)
+             /* Yep, this is the entry.  */
+             return elem;
+         }
+      return -1;
     }
 
-  /* Local function for parse_bracket_exp used in _LIBC environement.
+  /* Local function for parse_bracket_exp used in _LIBC environment.
      Look up the collation sequence value of BR_ELEM.
      Return the value if succeeded, UINT_MAX otherwise.  */
 
-  static inline unsigned int
-  __attribute ((always_inline))
-  lookup_collation_sequence_value (br_elem)
-        bracket_elem_t *br_elem;
+  auto inline unsigned int
+  __attribute__ ((always_inline))
+  lookup_collation_sequence_value (bracket_elem_t *br_elem)
     {
       if (br_elem->type == SB_CHAR)
        {
@@ -2704,7 +2843,8 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
        }
       else if (br_elem->type == MB_CHAR)
        {
-         return __collseq_table_lookup (collseqwc, br_elem->opr.wch);
+         if (nrules != 0)
+           return __collseq_table_lookup (collseqwc, br_elem->opr.wch);
        }
       else if (br_elem->type == COLL_SYM)
        {
@@ -2714,7 +2854,7 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
              int32_t elem, idx;
              elem = seek_collating_symbol_entry (br_elem->opr.name,
                                                  sym_name_len);
-             if (symb_table[2 * elem] != 0)
+             if (elem != -1)
                {
                  /* We found the entry.  */
                  idx = symb_table[2 * elem + 1];
@@ -2732,7 +2872,7 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
                  /* Return the collation sequence value.  */
                  return *(unsigned int *) (extra + idx);
                }
-             else if (symb_table[2 * elem] == 0 && sym_name_len == 1)
+             else if (sym_name_len == 1)
                {
                  /* No valid character.  Match it as a single byte
                     character.  */
@@ -2745,56 +2885,22 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
       return UINT_MAX;
     }
 
-  /* Local function for parse_bracket_exp used in _LIBC environement.
+  /* Local function for parse_bracket_exp used in _LIBC environment.
      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
+     mbcset->range_ends, is a pointer argument since we may
      update it.  */
 
-  static inline reg_errcode_t
-  __attribute ((always_inline))
-# ifdef RE_ENABLE_I18N
-  build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
-        re_charset_t *mbcset;
-        int *range_alloc;
-# else /* not RE_ENABLE_I18N */
-  build_range_exp (sbcset, start_elem, end_elem)
-# endif /* not RE_ENABLE_I18N */
-        re_bitset_ptr_t sbcset;
-        bracket_elem_t *start_elem, *end_elem;
+  auto inline reg_errcode_t
+  __attribute__ ((always_inline))
+  build_range_exp (bitset_t sbcset, re_charset_t *mbcset, int *range_alloc,
+                  bracket_elem_t *start_elem, bracket_elem_t *end_elem)
     {
       unsigned int ch;
       uint32_t start_collseq;
       uint32_t end_collseq;
 
-# ifdef RE_ENABLE_I18N
-      /* 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;
-
-         /* +1 in case of mbcset->nranges is 0.  */
-         new_nranges = 2 * mbcset->nranges + 1;
-         /* Use realloc since mbcset->range_starts and mbcset->range_ends
-            are NULL if *range_alloc == 0.  */
-         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 (BE (new_array_start == NULL || new_array_end == NULL, 0))
-           return REG_ESPACE;
-
-         mbcset->range_starts = new_array_start;
-         mbcset->range_ends = new_array_end;
-         *range_alloc = new_nranges;
-       }
-# endif /* RE_ENABLE_I18N */
-
       /* Equivalence Classes and Character Classes can't be a range
         start/end.  */
       if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
@@ -2810,14 +2916,41 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
       if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0))
        return REG_ERANGE;
 
-# ifdef RE_ENABLE_I18N
-      /* 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;
-# endif /* RE_ENABLE_I18N */
+      /* Got valid collation sequence values, add them as a new entry.
+        However, if we have no collation elements, and the character set
+        is single byte, the single byte character set that we
+        build below suffices. */
+      if (nrules > 0 || dfa->mb_cur_max > 1)
+       {
+         /* Check the space of the arrays.  */
+         if (BE (*range_alloc == mbcset->nranges, 0))
+           {
+             /* There is not enough space, need realloc.  */
+             uint32_t *new_array_start;
+             uint32_t *new_array_end;
+             int new_nranges;
+
+             /* +1 in case of mbcset->nranges is 0.  */
+             new_nranges = 2 * mbcset->nranges + 1;
+             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 (BE (new_array_start == NULL || new_array_end == NULL, 0))
+               return REG_ESPACE;
+
+             mbcset->range_starts = new_array_start;
+             mbcset->range_ends = new_array_end;
+             *range_alloc = new_nranges;
+           }
+
+         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++)
+      for (ch = 0; ch < SBC_MAX; ch++)
        {
          uint32_t ch_collseq;
          /*
@@ -2833,37 +2966,30 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
       return REG_NOERROR;
     }
 
-  /* Local function for parse_bracket_exp used in _LIBC environement.
+  /* Local function for parse_bracket_exp used in _LIBC environment.
      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.  */
+     pointer argument since we may update it.  */
 
-  static inline reg_errcode_t
-  __attribute ((always_inline))
-# ifdef RE_ENABLE_I18N
-  build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name)
-        re_charset_t *mbcset;
-        int *coll_sym_alloc;
-# else /* not RE_ENABLE_I18N */
-  build_collating_symbol (sbcset, name)
-# endif /* not RE_ENABLE_I18N */
-        re_bitset_ptr_t sbcset;
-        const unsigned char *name;
+  auto inline reg_errcode_t
+  __attribute__ ((always_inline))
+  build_collating_symbol (bitset_t sbcset, re_charset_t *mbcset,
+                         int *coll_sym_alloc, const unsigned char *name)
     {
       int32_t elem, idx;
       size_t name_len = strlen ((const char *) name);
       if (nrules != 0)
        {
          elem = seek_collating_symbol_entry (name, name_len);
-         if (symb_table[2 * elem] != 0)
+         if (elem != -1)
            {
              /* 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 && name_len == 1)
+         else if (name_len == 1)
            {
              /* No valid character, treat it as a normal
                 character.  */
@@ -2873,23 +2999,23 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
          else
            return REG_ECOLLATE;
 
-# ifdef RE_ENABLE_I18N
          /* Got valid collation sequence, add it as a new entry.  */
          /* Check the space of the arrays.  */
-         if (*coll_sym_alloc == mbcset->ncoll_syms)
+         if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0))
            {
              /* Not enough, realloc it.  */
              /* +1 in case of mbcset->ncoll_syms is 0.  */
-             *coll_sym_alloc = 2 * mbcset->ncoll_syms + 1;
+             int new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1;
              /* Use realloc since mbcset->coll_syms is NULL
                 if *alloc == 0.  */
-             mbcset->coll_syms = re_realloc (mbcset->coll_syms, int32_t,
-                                             *coll_sym_alloc);
-             if (BE (mbcset->coll_syms == NULL, 0))
+             int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t,
+                                                  new_coll_sym_alloc);
+             if (BE (new_coll_syms == NULL, 0))
                return REG_ESPACE;
+             mbcset->coll_syms = new_coll_syms;
+             *coll_sym_alloc = new_coll_sym_alloc;
            }
          mbcset->coll_syms[mbcset->ncoll_syms++] = idx;
-# endif /* RE_ENABLE_I18N */
          return REG_NOERROR;
        }
       else
@@ -2911,11 +3037,11 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
   re_charset_t *mbcset;
   int coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0;
   int equiv_class_alloc = 0, char_class_alloc = 0;
-#else /* not RE_ENABLE_I18N */
-  int non_match = 0;
 #endif /* not RE_ENABLE_I18N */
+  int non_match = 0;
   bin_tree_t *work_tree;
   int token_len;
+  int first_round = 1;
 #ifdef _LIBC
   collseqmb = (const unsigned char *)
     _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
@@ -2925,7 +3051,7 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
       /*
       if (MB_CUR_MAX > 1)
       */
-       collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+      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);
@@ -2933,7 +3059,7 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
                                                   _NL_COLLATE_SYMB_EXTRAMB);
     }
 #endif
-  sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS);
+  sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
 #ifdef RE_ENABLE_I18N
   mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
 #endif /* RE_ENABLE_I18N */
@@ -2943,6 +3069,10 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
   if (BE (sbcset == NULL, 0))
 #endif /* RE_ENABLE_I18N */
     {
+      re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+      re_free (mbcset);
+#endif
       *err = REG_ESPACE;
       return NULL;
     }
@@ -2956,13 +3086,11 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
   if (token->type == OP_NON_MATCH_LIST)
     {
 #ifdef RE_ENABLE_I18N
-      int i;
       mbcset->non_match = 1;
-#else /* not RE_ENABLE_I18N */
-      non_match = 1;
 #endif /* not RE_ENABLE_I18N */
+      non_match = 1;
       if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
-       bitset_set (sbcset, '\0');
+       bitset_set (sbcset, '\n');
       re_string_skip_bytes (regexp, token_len); /* Skip a token.  */
       token_len = peek_token_bracket (token, regexp, syntax);
       if (BE (token->type == END_OF_RE, 0))
@@ -2970,19 +3098,12 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
          *err = REG_BADPAT;
          goto parse_bracket_exp_free_return;
        }
-#ifdef RE_ENABLE_I18N
-      if (dfa->mb_cur_max > 1)
-       for (i = 0; i < SBC_MAX; ++i)
-         if (__btowc (i) == WEOF)
-           bitset_set (sbcset, i);
-#endif /* RE_ENABLE_I18N */
     }
 
   /* We treat the first ']' as a normal character.  */
   if (token->type == OP_CLOSE_BRACKET)
     token->type = CHARACTER;
 
-  int first_round = 1;
   while (1)
     {
       bracket_elem_t start_elem, end_elem;
@@ -2993,6 +3114,7 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
       re_token_t token2;
 
       start_elem.opr.name = start_name_buf;
+      start_elem.type = COLL_SYM;
       ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa,
                                   syntax, first_round);
       if (BE (ret != REG_NOERROR, 0))
@@ -3036,6 +3158,7 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
       if (is_range_exp == 1)
        {
          end_elem.opr.name = end_name_buf;
+         end_elem.type = COLL_SYM;
          ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2,
                                       dfa, syntax, 1);
          if (BE (ret != REG_NOERROR, 0))
@@ -3046,11 +3169,18 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
 
          token_len = peek_token_bracket (token, regexp, syntax);
 
+#ifdef _LIBC
+         *err = build_range_exp (sbcset, mbcset, &range_alloc,
+                                 &start_elem, &end_elem);
+#else
+# ifdef RE_ENABLE_I18N
          *err = build_range_exp (sbcset,
-#ifdef RE_ENABLE_I18N
-                                 mbcset, &range_alloc,
+                                 dfa->mb_cur_max > 1 ? mbcset : NULL,
+                                 &range_alloc, &start_elem, &end_elem);
+# else
+         *err = build_range_exp (sbcset, &start_elem, &end_elem);
+# endif
 #endif /* RE_ENABLE_I18N */
-                                 &start_elem, &end_elem);
          if (BE (*err != REG_NOERROR, 0))
            goto parse_bracket_exp_free_return;
        }
@@ -3064,16 +3194,18 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
 #ifdef RE_ENABLE_I18N
            case MB_CHAR:
              /* Check whether the array has enough space.  */
-             if (mbchar_alloc == mbcset->nmbchars)
+             if (BE (mbchar_alloc == mbcset->nmbchars, 0))
                {
+                 wchar_t *new_mbchars;
                  /* Not enough, realloc it.  */
                  /* +1 in case of mbcset->nmbchars is 0.  */
                  mbchar_alloc = 2 * mbcset->nmbchars + 1;
                  /* Use realloc since array is NULL if *alloc == 0.  */
-                 mbcset->mbchars = re_realloc (mbcset->mbchars, wchar_t,
-                                               mbchar_alloc);
-                 if (BE (mbcset->mbchars == NULL, 0))
+                 new_mbchars = re_realloc (mbcset->mbchars, wchar_t,
+                                           mbchar_alloc);
+                 if (BE (new_mbchars == NULL, 0))
                    goto parse_bracket_exp_espace;
+                 mbcset->mbchars = new_mbchars;
                }
              mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch;
              break;
@@ -3122,62 +3254,66 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
   re_string_skip_bytes (regexp, token_len); /* Skip a token.  */
 
   /* If it is non-matching list.  */
-#ifdef RE_ENABLE_I18N
-  if (mbcset->non_match)
-#else /* not RE_ENABLE_I18N */
   if (non_match)
-#endif /* not RE_ENABLE_I18N */
     bitset_not (sbcset);
 
-  /* Build a tree for simple bracket.  */
-  br_token.type = SIMPLE_BRACKET;
-  br_token.opr.sbcset = sbcset;
-  work_tree = re_dfa_add_tree_node (dfa, NULL, NULL, br_token);
-  if (BE (work_tree == NULL, 0))
-    goto parse_bracket_exp_espace;
-
 #ifdef RE_ENABLE_I18N
+  /* Ensure only single byte characters are set.  */
+  if (dfa->mb_cur_max > 1)
+    bitset_mask (sbcset, dfa->sb_char);
+
   if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes
       || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes
                                                     || mbcset->non_match)))
     {
-      re_token_t alt_token;
       bin_tree_t *mbc_tree;
       int sbc_idx;
       /* Build a tree for complex bracket.  */
       dfa->has_mb_node = 1;
-      dfa->has_plural_match = 1;
-      for (sbc_idx = 0; sbc_idx < BITSET_UINTS; ++sbc_idx)
+      br_token.type = COMPLEX_BRACKET;
+      br_token.opr.mbcset = mbcset;
+      mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+      if (BE (mbc_tree == NULL, 0))
+       goto parse_bracket_exp_espace;
+      for (sbc_idx = 0; sbc_idx < BITSET_WORDS; ++sbc_idx)
        if (sbcset[sbc_idx])
          break;
       /* If there are no bits set in sbcset, there is no point
         of having both SIMPLE_BRACKET and COMPLEX_BRACKET.  */
-      if (sbc_idx == BITSET_UINTS)
+      if (sbc_idx < BITSET_WORDS)
+       {
+         /* Build a tree for simple bracket.  */
+         br_token.type = SIMPLE_BRACKET;
+         br_token.opr.sbcset = sbcset;
+         work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+         if (BE (work_tree == NULL, 0))
+           goto parse_bracket_exp_espace;
+
+         /* Then join them by ALT node.  */
+         work_tree = create_tree (dfa, work_tree, mbc_tree, OP_ALT);
+         if (BE (work_tree == NULL, 0))
+           goto parse_bracket_exp_espace;
+       }
+      else
        {
          re_free (sbcset);
-         dfa->nodes[work_tree->node_idx].type = COMPLEX_BRACKET;
-         dfa->nodes[work_tree->node_idx].opr.mbcset = mbcset;
-         return work_tree;
+         work_tree = mbc_tree;
        }
-      br_token.type = COMPLEX_BRACKET;
-      br_token.opr.mbcset = mbcset;
-      mbc_tree = re_dfa_add_tree_node (dfa, NULL, NULL, br_token);
-      if (BE (mbc_tree == NULL, 0))
-       goto parse_bracket_exp_espace;
-      /* Then join them by ALT node.  */
-      alt_token.type = OP_ALT;
-      work_tree = re_dfa_add_tree_node (dfa, work_tree, mbc_tree, alt_token);
-      if (BE (mbc_tree != NULL, 1))
-       return work_tree;
     }
   else
+#endif /* not RE_ENABLE_I18N */
     {
+#ifdef RE_ENABLE_I18N
       free_charset (mbcset);
-      return work_tree;
+#endif
+      /* Build a tree for simple bracket.  */
+      br_token.type = SIMPLE_BRACKET;
+      br_token.opr.sbcset = sbcset;
+      work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+      if (BE (work_tree == NULL, 0))
+       goto parse_bracket_exp_espace;
     }
-#else /* not RE_ENABLE_I18N */
   return work_tree;
-#endif /* not RE_ENABLE_I18N */
 
  parse_bracket_exp_espace:
   *err = REG_ESPACE;
@@ -3192,15 +3328,9 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
 /* Parse an element in the bracket expression.  */
 
 static reg_errcode_t
-parse_bracket_element (elem, regexp, token, token_len, dfa, syntax,
-                      accept_hyphen)
-     bracket_elem_t *elem;
-     re_string_t *regexp;
-     re_token_t *token;
-     int token_len;
-     re_dfa_t *dfa;
-     reg_syntax_t syntax;
-     int accept_hyphen;
+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, int accept_hyphen)
 {
 #ifdef RE_ENABLE_I18N
   int cur_char_size;
@@ -3238,21 +3368,23 @@ parse_bracket_element (elem, regexp, token, token_len, dfa, syntax,
    [=<equivalent_class>=].  */
 
 static reg_errcode_t
-parse_bracket_symbol (elem, regexp, token)
-     bracket_elem_t *elem;
-     re_string_t *regexp;
-     re_token_t *token;
+parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp,
+                     re_token_t *token)
 {
   unsigned char ch, delim = token->opr.c;
   int i = 0;
+  if (re_string_eoi(regexp))
+    return REG_EBRACK;
   for (;; ++i)
     {
-      if (re_string_eoi(regexp) || i >= BRACKET_NAME_BUF_SIZE)
+      if (i >= BRACKET_NAME_BUF_SIZE)
        return REG_EBRACK;
       if (token->type == OP_OPEN_CHAR_CLASS)
        ch = re_string_fetch_byte_case (regexp);
       else
        ch = re_string_fetch_byte (regexp);
+      if (re_string_eoi(regexp))
+       return REG_EBRACK;
       if (ch == delim && re_string_peek_byte (regexp, 0) == ']')
        break;
       elem->opr.name[i] = ch;
@@ -3280,20 +3412,17 @@ parse_bracket_symbol (elem, regexp, token)
      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.  */
+     is a pointer argument since we may update it.  */
 
 static reg_errcode_t
 #ifdef RE_ENABLE_I18N
-build_equiv_class (sbcset, mbcset, equiv_class_alloc, name)
-     re_charset_t *mbcset;
-     int *equiv_class_alloc;
+build_equiv_class (bitset_t sbcset, re_charset_t *mbcset,
+                  int *equiv_class_alloc, const unsigned char *name)
 #else /* not RE_ENABLE_I18N */
-build_equiv_class (sbcset, name)
+build_equiv_class (bitset_t sbcset, const unsigned char *name)
 #endif /* not RE_ENABLE_I18N */
-     re_bitset_ptr_t sbcset;
-     const unsigned char *name;
 {
-#if defined _LIBC && defined RE_ENABLE_I18N
+#ifdef _LIBC
   uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
   if (nrules != 0)
     {
@@ -3303,8 +3432,6 @@ build_equiv_class (sbcset, name)
       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);
@@ -3314,30 +3441,33 @@ build_equiv_class (sbcset, name)
                                                   _NL_COLLATE_EXTRAMB);
       indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
                                                _NL_COLLATE_INDIRECTMB);
-      idx1 = findidx (&cp);
-      if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0))
+      idx1 = findidx (table, indirect, extra, &cp, -1);
+      if (BE (idx1 == 0 || *cp != '\0', 0))
        /* This isn't a valid character.  */
        return REG_ECOLLATE;
 
       /* Build single byte matcing table for this equivalence class.  */
-      char_buf[1] = (unsigned char) '\0';
-      len = weights[idx1];
+      len = weights[idx1 & 0xffffff];
       for (ch = 0; ch < SBC_MAX; ++ch)
        {
          char_buf[0] = ch;
          cp = char_buf;
-         idx2 = findidx (&cp);
+         idx2 = findidx (table, indirect, extra, &cp, 1);
 /*
          idx2 = table[ch];
 */
          if (idx2 == 0)
            /* This isn't a valid character.  */
            continue;
-         if (len == weights[idx2])
+         /* Compare only if the length matches and the collation rule
+            index is the same.  */
+         if (len == weights[idx2 & 0xffffff] && (idx1 >> 24) == (idx2 >> 24))
            {
              int cnt = 0;
+
              while (cnt <= len &&
-                    weights[idx1 + 1 + cnt] == weights[idx2 + 1 + cnt])
+                    weights[(idx1 & 0xffffff) + 1 + cnt]
+                    == weights[(idx2 & 0xffffff) + 1 + cnt])
                ++cnt;
 
              if (cnt > len)
@@ -3345,21 +3475,24 @@ build_equiv_class (sbcset, name)
            }
        }
       /* Check whether the array has enough space.  */
-      if (*equiv_class_alloc == mbcset->nequiv_classes)
+      if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0))
        {
          /* Not enough, realloc it.  */
          /* +1 in case of mbcset->nequiv_classes is 0.  */
-         *equiv_class_alloc = 2 * mbcset->nequiv_classes + 1;
+         int new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1;
          /* Use realloc since the array is NULL if *alloc == 0.  */
-         mbcset->equiv_classes = re_realloc (mbcset->equiv_classes, int32_t,
-                                             *equiv_class_alloc);
-         if (BE (mbcset->equiv_classes == NULL, 0))
+         int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes,
+                                                  int32_t,
+                                                  new_equiv_class_alloc);
+         if (BE (new_equiv_classes == NULL, 0))
            return REG_ESPACE;
+         mbcset->equiv_classes = new_equiv_classes;
+         *equiv_class_alloc = new_equiv_class_alloc;
        }
       mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1;
     }
   else
-#endif /* _LIBC && RE_ENABLE_I18N */
+#endif /* _LIBC */
     {
       if (BE (strlen ((const char *) name) != 1, 0))
        return REG_ECOLLATE;
@@ -3372,20 +3505,17 @@ build_equiv_class (sbcset, name)
      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.  */
+     is a pointer argument since we may update it.  */
 
 static reg_errcode_t
 #ifdef RE_ENABLE_I18N
-build_charclass (trans, sbcset, mbcset, char_class_alloc, class_name, syntax)
-     re_charset_t *mbcset;
-     int *char_class_alloc;
+build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
+                re_charset_t *mbcset, int *char_class_alloc,
+                const unsigned char *class_name, reg_syntax_t syntax)
 #else /* not RE_ENABLE_I18N */
-build_charclass (trans, sbcset, class_name, syntax)
+build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
+                const unsigned char *class_name, reg_syntax_t syntax)
 #endif /* not RE_ENABLE_I18N */
-     RE_TRANSLATE_TYPE trans;
-     re_bitset_ptr_t sbcset;
-     const unsigned char *class_name;
-     reg_syntax_t syntax;
 {
   int i;
   const char *name = (const char *) class_name;
@@ -3398,54 +3528,62 @@ build_charclass (trans, sbcset, class_name, syntax)
 
 #ifdef RE_ENABLE_I18N
   /* Check the space of the arrays.  */
-  if (*char_class_alloc == mbcset->nchar_classes)
+  if (BE (*char_class_alloc == mbcset->nchar_classes, 0))
     {
       /* Not enough, realloc it.  */
       /* +1 in case of mbcset->nchar_classes is 0.  */
-      *char_class_alloc = 2 * mbcset->nchar_classes + 1;
+      int new_char_class_alloc = 2 * mbcset->nchar_classes + 1;
       /* Use realloc since array is NULL if *alloc == 0.  */
-      mbcset->char_classes = re_realloc (mbcset->char_classes, wctype_t,
-                                        *char_class_alloc);
-      if (BE (mbcset->char_classes == NULL, 0))
+      wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t,
+                                              new_char_class_alloc);
+      if (BE (new_char_classes == NULL, 0))
        return REG_ESPACE;
+      mbcset->char_classes = new_char_classes;
+      *char_class_alloc = new_char_class_alloc;
     }
   mbcset->char_classes[mbcset->nchar_classes++] = __wctype (name);
 #endif /* RE_ENABLE_I18N */
 
 #define BUILD_CHARCLASS_LOOP(ctype_func)       \
-    for (i = 0; i < SBC_MAX; ++i)              \
+  do {                                         \
+    if (BE (trans != NULL, 0))                 \
       {                                                \
-       if (ctype_func (i))                     \
-         {                                     \
-           int ch = trans ? trans[i] : i;      \
-           bitset_set (sbcset, ch);            \
-         }                                     \
-      }
+       for (i = 0; i < SBC_MAX; ++i)           \
+         if (ctype_func (i))                   \
+           bitset_set (sbcset, trans[i]);      \
+      }                                                \
+    else                                       \
+      {                                                \
+       for (i = 0; i < SBC_MAX; ++i)           \
+         if (ctype_func (i))                   \
+           bitset_set (sbcset, i);             \
+      }                                                \
+  } while (0)
 
   if (strcmp (name, "alnum") == 0)
-    BUILD_CHARCLASS_LOOP (isalnum)
+    BUILD_CHARCLASS_LOOP (isalnum);
   else if (strcmp (name, "cntrl") == 0)
-    BUILD_CHARCLASS_LOOP (iscntrl)
+    BUILD_CHARCLASS_LOOP (iscntrl);
   else if (strcmp (name, "lower") == 0)
-    BUILD_CHARCLASS_LOOP (islower)
+    BUILD_CHARCLASS_LOOP (islower);
   else if (strcmp (name, "space") == 0)
-    BUILD_CHARCLASS_LOOP (isspace)
+    BUILD_CHARCLASS_LOOP (isspace);
   else if (strcmp (name, "alpha") == 0)
-    BUILD_CHARCLASS_LOOP (isalpha)
+    BUILD_CHARCLASS_LOOP (isalpha);
   else if (strcmp (name, "digit") == 0)
-    BUILD_CHARCLASS_LOOP (isdigit)
+    BUILD_CHARCLASS_LOOP (isdigit);
   else if (strcmp (name, "print") == 0)
-    BUILD_CHARCLASS_LOOP (isprint)
+    BUILD_CHARCLASS_LOOP (isprint);
   else if (strcmp (name, "upper") == 0)
-    BUILD_CHARCLASS_LOOP (isupper)
+    BUILD_CHARCLASS_LOOP (isupper);
   else if (strcmp (name, "blank") == 0)
-    BUILD_CHARCLASS_LOOP (isblank)
+    BUILD_CHARCLASS_LOOP (isblank);
   else if (strcmp (name, "graph") == 0)
-    BUILD_CHARCLASS_LOOP (isgraph)
+    BUILD_CHARCLASS_LOOP (isgraph);
   else if (strcmp (name, "punct") == 0)
-    BUILD_CHARCLASS_LOOP (ispunct)
+    BUILD_CHARCLASS_LOOP (ispunct);
   else if (strcmp (name, "xdigit") == 0)
-    BUILD_CHARCLASS_LOOP (isxdigit)
+    BUILD_CHARCLASS_LOOP (isxdigit);
   else
     return REG_ECTYPE;
 
@@ -3453,26 +3591,21 @@ build_charclass (trans, sbcset, class_name, syntax)
 }
 
 static bin_tree_t *
-build_charclass_op (dfa, trans, class_name, extra, not, err)
-     re_dfa_t *dfa;
-     RE_TRANSLATE_TYPE trans;
-     const unsigned char *class_name;
-     const unsigned char *extra;
-     int not;
-     reg_errcode_t *err;
+build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans,
+                   const unsigned char *class_name,
+                   const unsigned char *extra, int non_match,
+                   reg_errcode_t *err)
 {
   re_bitset_ptr_t sbcset;
 #ifdef RE_ENABLE_I18N
   re_charset_t *mbcset;
   int alloc = 0;
-#else /* not RE_ENABLE_I18N */
-  int non_match = 0;
 #endif /* not RE_ENABLE_I18N */
   reg_errcode_t ret;
   re_token_t br_token;
   bin_tree_t *tree;
 
-  sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS);
+  sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
 #ifdef RE_ENABLE_I18N
   mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
 #endif /* RE_ENABLE_I18N */
@@ -3487,21 +3620,10 @@ build_charclass_op (dfa, trans, class_name, extra, not, err)
       return NULL;
     }
 
-  if (not)
+  if (non_match)
     {
 #ifdef RE_ENABLE_I18N
-      int i;
-      /*
-      if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
-       bitset_set(cset->sbcset, '\0');
-      */
       mbcset->non_match = 1;
-      if (dfa->mb_cur_max > 1)
-       for (i = 0; i < SBC_MAX; ++i)
-         if (__btowc (i) == WEOF)
-           bitset_set (sbcset, i);
-#else /* not RE_ENABLE_I18N */
-      non_match = 1;
 #endif /* not RE_ENABLE_I18N */
     }
 
@@ -3526,35 +3648,35 @@ build_charclass_op (dfa, trans, class_name, extra, not, err)
     bitset_set (sbcset, *extra);
 
   /* If it is non-matching list.  */
-#ifdef RE_ENABLE_I18N
-  if (mbcset->non_match)
-#else /* not RE_ENABLE_I18N */
   if (non_match)
-#endif /* not RE_ENABLE_I18N */
     bitset_not (sbcset);
 
+#ifdef RE_ENABLE_I18N
+  /* Ensure only single byte characters are set.  */
+  if (dfa->mb_cur_max > 1)
+    bitset_mask (sbcset, dfa->sb_char);
+#endif
+
   /* Build a tree for simple bracket.  */
   br_token.type = SIMPLE_BRACKET;
   br_token.opr.sbcset = sbcset;
-  tree = re_dfa_add_tree_node (dfa, NULL, NULL, br_token);
+  tree = create_token_tree (dfa, NULL, NULL, &br_token);
   if (BE (tree == NULL, 0))
     goto build_word_op_espace;
 
 #ifdef RE_ENABLE_I18N
   if (dfa->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;
-      mbc_tree = re_dfa_add_tree_node (dfa, NULL, NULL, br_token);
+      mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
       if (BE (mbc_tree == NULL, 0))
        goto build_word_op_espace;
       /* Then join them by ALT node.  */
-      alt_token.type = OP_ALT;
-      tree = re_dfa_add_tree_node (dfa, tree, mbc_tree, alt_token);
+      tree = create_tree (dfa, tree, mbc_tree, OP_ALT);
       if (BE (mbc_tree != NULL, 1))
        return tree;
     }
@@ -3582,16 +3704,13 @@ build_charclass_op (dfa, trans, class_name, extra, not, err)
    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;
+fetch_number (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);
+      fetch_token (token, input, syntax);
       c = token->opr.c;
       if (BE (token->type == END_OF_RE, 0))
        return -2;
@@ -3625,12 +3744,17 @@ free_charset (re_charset_t *cset)
 /* Create a tree node.  */
 
 static bin_tree_t *
-create_tree (dfa, left, right, type, index)
-     re_dfa_t *dfa;
-     bin_tree_t *left;
-     bin_tree_t *right;
-     re_token_type_t type;
-     int index;
+create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
+            re_token_type_t type)
+{
+  re_token_t t;
+  t.type = type;
+  return create_token_tree (dfa, left, right, &t);
+}
+
+static bin_tree_t *
+create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
+                  const re_token_t *token)
 {
   bin_tree_t *tree;
   if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0))
@@ -3648,11 +3772,12 @@ create_tree (dfa, left, right, type, index)
   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);
+  tree->token = *token;
+  tree->token.duplicated = 0;
+  tree->token.opt_subexp = 0;
+  tree->first = NULL;
+  tree->next = NULL;
+  tree->node_idx = -1;
 
   if (left != NULL)
     left->parent = tree;
@@ -3661,61 +3786,85 @@ create_tree (dfa, left, right, type, index)
   return tree;
 }
 
-/* Create both a DFA node and a tree for it.  */
+/* Mark the tree SRC as an optional subexpression.
+   To be called from preorder or postorder.  */
 
-static bin_tree_t *
-re_dfa_add_tree_node (dfa, left, right, token)
-     re_dfa_t *dfa;
-     bin_tree_t *left;
-     bin_tree_t *right;
-     re_token_t token;
-{     
-  int new_idx = re_dfa_add_node (dfa, token, 0);
-
-  if (new_idx == -1)
-    return NULL;
+static reg_errcode_t
+mark_opt_subexp (void *extra, bin_tree_t *node)
+{
+  int idx = (int) (long) extra;
+  if (node->token.type == SUBEXP && node->token.opr.idx == idx)
+    node->token.opt_subexp = 1;
+
+  return REG_NOERROR;
+}
+
+/* Free the allocated memory inside NODE. */
+
+static void
+free_token (re_token_t *node)
+{
+#ifdef RE_ENABLE_I18N
+  if (node->type == COMPLEX_BRACKET && node->duplicated == 0)
+    free_charset (node->opr.mbcset);
+  else
+#endif /* RE_ENABLE_I18N */
+    if (node->type == SIMPLE_BRACKET && node->duplicated == 0)
+      re_free (node->opr.sbcset);
+}
 
-  return create_tree (dfa, left, right, 0, new_idx);
+/* Worker function for tree walking.  Free the allocated memory inside NODE
+   and its children. */
+
+static reg_errcode_t
+free_tree (void *extra, bin_tree_t *node)
+{
+  free_token (&node->token);
+  return REG_NOERROR;
 }
 
 
-/* Duplicate the node SRC, and return new node.  */
+/* Duplicate the node SRC, and return new node.  This is a preorder
+   visit similar to the one implemented by the generic visitor, but
+   we need more infrastructure to maintain two parallel trees --- so,
+   it's easier to duplicate.  */
 
 static bin_tree_t *
-duplicate_tree (src, dfa)
-     const bin_tree_t *src;
-     re_dfa_t *dfa;
+duplicate_tree (const bin_tree_t *root, 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;
-    }
+  const bin_tree_t *node;
+  bin_tree_t *dup_root;
+  bin_tree_t **p_new = &dup_root, *dup_node = root->parent;
 
-  /* Secondaly, duplicate the right.  */
-  if (src->right != NULL)
+  for (node = root; ; )
     {
-      right = duplicate_tree (src->right, dfa);
-      if (right == NULL)
+      /* Create a new tree and link it back to the current parent.  */
+      *p_new = create_token_tree (dfa, NULL, NULL, &node->token);
+      if (*p_new == NULL)
        return NULL;
-    }
+      (*p_new)->parent = dup_node;
+      (*p_new)->token.duplicated = 1;
+      dup_node = *p_new;
 
-  /* 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 (BE (new_node_idx == -1, 0))
-       return NULL;
+      /* Go to the left node, or up and to the right.  */
+      if (node->left)
+       {
+         node = node->left;
+         p_new = &dup_node->left;
+       }
+      else
+       {
+         const bin_tree_t *prev = NULL;
+         while (node->right == prev || node->right == NULL)
+           {
+             prev = node;
+             node = node->parent;
+             dup_node = dup_node->parent;
+             if (!node)
+               return dup_root;
+           }
+         node = node->right;
+         p_new = &dup_node->right;
+       }
     }
-  else
-    new_node_idx = src->type;
-
-  new_tree = create_tree (dfa, left, right, src->type, new_node_idx);
-  return new_tree;
 }