about summary refs log tree commit diff
path: root/src/regex/regcomp.c
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2012-03-20 19:44:05 -0400
committerRich Felker <dalias@aerifal.cx>2012-03-20 19:44:05 -0400
commitad47d45e9da8df364cb0a61b6146d51c196c8891 (patch)
tree25b0dc14b0a56306c671dfdfd69b4a8b0f3cbcd8 /src/regex/regcomp.c
parentbaa43bca0a051e8deb0d6a9a8882ceeea5c27249 (diff)
downloadmusl-ad47d45e9da8df364cb0a61b6146d51c196c8891.tar.gz
musl-ad47d45e9da8df364cb0a61b6146d51c196c8891.tar.xz
musl-ad47d45e9da8df364cb0a61b6146d51c196c8891.zip
upgrade to latest upstream TRE regex code (0.8.0)
the main practical results of this change are
1. the regex code is no longer subject to LGPL; it's now 2-clause BSD
2. most (all?) popular nonstandard regex extensions are supported

I hesitate to call this a "sync" since both the old and new code are
heavily modified. in one sense, the old code was "more severely"
modified, in that it was actively hostile to non-strictly-conforming
expressions. on the other hand, the new code has eliminated the
useless translation of the entire regex string to wchar_t prior to
compiling, and now only converts multibyte character literals as
needed.

in the future i may use this modified TRE as a basis for writing the
long-planned new regex engine that will avoid multibyte-to-wide
character conversion entirely by compiling multibyte bracket
expressions specific to UTF-8.
Diffstat (limited to 'src/regex/regcomp.c')
-rw-r--r--src/regex/regcomp.c1597
1 files changed, 822 insertions, 775 deletions
diff --git a/src/regex/regcomp.c b/src/regex/regcomp.c
index 875f56fd..8987f5aa 100644
--- a/src/regex/regcomp.c
+++ b/src/regex/regcomp.c
@@ -1,21 +1,31 @@
 /*
   regcomp.c - TRE POSIX compatible regex compilation functions.
 
-  Copyright (c) 2001-2006 Ville Laurikari <vl@iki.fi>
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2.1 of the License, or (at your option) any later version.
-
-  This library is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with this library; if not, write to the Free Software
-  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+  Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+    1. Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+       notice, this list of conditions and the following disclaimer in the
+       documentation and/or other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 */
 
@@ -31,6 +41,23 @@
 #include <assert.h>
 
 /***********************************************************************
+ from tre-compile.h
+***********************************************************************/
+
+typedef struct {
+  int position;
+  int code_min;
+  int code_max;
+  int *tags;
+  int assertions;
+  tre_ctype_t class;
+  tre_ctype_t *neg_classes;
+  int backref;
+  int *params;
+} tre_pos_and_tags_t;
+
+
+/***********************************************************************
  from tre-ast.c and tre-ast.h
 ***********************************************************************/
 
@@ -54,17 +81,6 @@ typedef enum {
 #define IS_TAG(x)	((x)->code_min == TAG)
 #define IS_BACKREF(x)	((x)->code_min == BACKREF)
 
-/* Taken from tre-compile.h */
-typedef struct {
-  int position;
-  int code_min;
-  int code_max;
-  int *tags;
-  int assertions;
-  tre_ctype_t class;
-  tre_ctype_t *neg_classes;
-  int backref;
-} tre_pos_and_tags_t;
 
 /* A generic AST node.  All AST nodes consist of this node on the top
    level with `obj' pointing to the actual content. */
@@ -87,7 +103,10 @@ typedef struct {
   long code_min;
   long code_max;
   int position;
-  tre_ctype_t class;
+  union {
+    tre_ctype_t class;
+    int *params;
+  } u;
   tre_ctype_t *neg_classes;
 } tre_literal_t;
 
@@ -109,6 +128,10 @@ typedef struct {
   int min;
   /* Maximum number of consecutive matches. */
   int max;
+  /* If 0, match as many characters as possible, if 1 match as few as
+     possible.	Note that this does not always mean the same thing as
+     matching as many/few repetitions as possible. */
+  unsigned int minimal:1;
 } tre_iteration_t;
 
 /* An "union" node.  These are created for the "|" operator. */
@@ -118,6 +141,24 @@ typedef struct {
 } tre_union_t;
 
 static tre_ast_node_t *
+tre_ast_new_node(tre_mem_t mem, tre_ast_type_t type, size_t size);
+
+static tre_ast_node_t *
+tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position);
+
+static tre_ast_node_t *
+tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max,
+		 int minimal);
+
+static tre_ast_node_t *
+tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right);
+
+static tre_ast_node_t *
+tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left,
+		       tre_ast_node_t *right);
+
+
+static tre_ast_node_t *
 tre_ast_new_node(tre_mem_t mem, tre_ast_type_t type, size_t size)
 {
   tre_ast_node_t *node;
@@ -153,7 +194,8 @@ tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position)
 }
 
 static tre_ast_node_t *
-tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max)
+tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max,
+		 int minimal)
 {
   tre_ast_node_t *node;
   tre_iteration_t *iter;
@@ -165,6 +207,7 @@ tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max)
   iter->arg = arg;
   iter->min = min;
   iter->max = max;
+  iter->minimal = minimal;
   node->num_submatches = arg->num_submatches;
 
   return node;
@@ -201,40 +244,82 @@ tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left,
   return node;
 }
 
+
 /***********************************************************************
  from tre-stack.c and tre-stack.h
 ***********************************************************************/
 
+typedef struct tre_stack_rec tre_stack_t;
+
+/* Creates a new stack object.	`size' is initial size in bytes, `max_size'
+   is maximum size, and `increment' specifies how much more space will be
+   allocated with realloc() if all space gets used up.	Returns the stack
+   object or NULL if out of memory. */
+static tre_stack_t *
+tre_stack_new(int size, int max_size, int increment);
+
+/* Frees the stack object. */
+static void
+tre_stack_destroy(tre_stack_t *s);
+
+/* Returns the current number of objects in the stack. */
+static int
+tre_stack_num_objects(tre_stack_t *s);
+
+/* Each tre_stack_push_*(tre_stack_t *s, <type> value) function pushes
+   `value' on top of stack `s'.  Returns REG_ESPACE if out of memory.
+   This tries to realloc() more space before failing if maximum size
+   has not yet been reached.  Returns REG_OK if successful. */
+#define declare_pushf(typetag, type)					      \
+  static reg_errcode_t tre_stack_push_ ## typetag(tre_stack_t *s, type value)
+
+declare_pushf(voidptr, void *);
+declare_pushf(int, int);
+
+/* Each tre_stack_pop_*(tre_stack_t *s) function pops the topmost
+   element off of stack `s' and returns it.  The stack must not be
+   empty. */
+#define declare_popf(typetag, type)		  \
+  static type tre_stack_pop_ ## typetag(tre_stack_t *s)
+
+declare_popf(voidptr, void *);
+declare_popf(int, int);
+
 /* Just to save some typing. */
-#define STACK_PUSH(s, value)						      \
+#define STACK_PUSH(s, typetag, value)					      \
   do									      \
     {									      \
-      status = tre_stack_push(s, (void *)(value));			      \
+      status = tre_stack_push_ ## typetag(s, value);			      \
     }									      \
-  while (0)
+  while (/*CONSTCOND*/0)
 
-#define STACK_PUSHX(s, value)						      \
+#define STACK_PUSHX(s, typetag, value)					      \
   {									      \
-    status = tre_stack_push(s, (void *)(value));			      \
+    status = tre_stack_push_ ## typetag(s, value);			      \
     if (status != REG_OK)						      \
       break;								      \
   }
 
-#define STACK_PUSHR(s, value)						      \
+#define STACK_PUSHR(s, typetag, value)					      \
   {									      \
-    reg_errcode_t status;						      \
-    status = tre_stack_push(s, (void *)(value));			      \
-    if (status != REG_OK)						      \
-      return status;							      \
+    reg_errcode_t _status;						      \
+    _status = tre_stack_push_ ## typetag(s, value);			      \
+    if (_status != REG_OK)						      \
+      return _status;							      \
   }
 
-typedef struct tre_stack_rec {
+union tre_stack_item {
+  void *voidptr_value;
+  int int_value;
+};
+
+struct tre_stack_rec {
   int size;
   int max_size;
   int increment;
   int ptr;
-  void **stack;
-} tre_stack_t;
+  union tre_stack_item *stack;
+};
 
 
 static tre_stack_t *
@@ -273,7 +358,7 @@ tre_stack_num_objects(tre_stack_t *s)
 }
 
 static reg_errcode_t
-tre_stack_push(tre_stack_t *s, void *value)
+tre_stack_push(tre_stack_t *s, union tre_stack_item value)
 {
   if (s->ptr < s->size)
     {
@@ -284,24 +369,20 @@ tre_stack_push(tre_stack_t *s, void *value)
     {
       if (s->size >= s->max_size)
 	{
-	  DPRINT(("tre_stack_push: stack full\n"));
 	  return REG_ESPACE;
 	}
       else
 	{
-	  void **new_buffer;
+	  union tre_stack_item *new_buffer;
 	  int new_size;
-	  DPRINT(("tre_stack_push: trying to realloc more space\n"));
 	  new_size = s->size + s->increment;
 	  if (new_size > s->max_size)
 	    new_size = s->max_size;
 	  new_buffer = xrealloc(s->stack, sizeof(*new_buffer) * new_size);
 	  if (new_buffer == NULL)
 	    {
-	      DPRINT(("tre_stack_push: realloc failed.\n"));
 	      return REG_ESPACE;
 	    }
-	  DPRINT(("tre_stack_push: realloc succeeded.\n"));
 	  assert(new_size > s->size);
 	  s->size = new_size;
 	  s->stack = new_buffer;
@@ -311,12 +392,24 @@ tre_stack_push(tre_stack_t *s, void *value)
   return REG_OK;
 }
 
-static void *
-tre_stack_pop(tre_stack_t *s)
-{
-  return s->stack[--s->ptr];
+#define define_pushf(typetag, type)  \
+  declare_pushf(typetag, type) {     \
+    union tre_stack_item item;	     \
+    item.typetag ## _value = value;  \
+    return tre_stack_push(s, item);  \
 }
 
+define_pushf(int, int)
+define_pushf(voidptr, void *)
+
+#define define_popf(typetag, type)		    \
+  declare_popf(typetag, type) {			    \
+    return s->stack[--s->ptr].typetag ## _value;    \
+  }
+
+define_popf(int, int)
+define_popf(voidptr, void *)
+
 
 /***********************************************************************
  from tre-parse.c and tre-parse.h
@@ -331,24 +424,86 @@ typedef struct {
   /* The parse result. */
   tre_ast_node_t *result;
   /* The regexp to parse and its length. */
-  const tre_char_t *re;
+  const char *re;
   /* The first character of the entire regexp. */
-  const tre_char_t *re_start;
-  /* The first character after the end of the regexp. */
-  const tre_char_t *re_end;
-  int len;
+  const char *re_start;
   /* Current submatch ID. */
   int submatch_id;
   /* Current position (number of literal). */
   int position;
   /* The highest back reference or -1 if none seen so far. */
   int max_backref;
+  /* This flag is set if the regexp uses approximate matching. */
+  int have_approx;
   /* Compilation flags. */
   int cflags;
   /* If this flag is set the top-level submatch is not captured. */
   int nofirstsub;
 } tre_parse_ctx_t;
 
+/* Parses a wide character regexp pattern into a syntax tree.  This parser
+   handles both syntaxes (BRE and ERE), including the TRE extensions. */
+static reg_errcode_t
+tre_parse(tre_parse_ctx_t *ctx);
+
+
+/*
+  This parser is just a simple recursive descent parser for POSIX.2
+  regexps.  The parser supports both the obsolete default syntax and
+  the "extended" syntax, and some nonstandard extensions.
+*/
+
+/* Characters with special meanings in regexp syntax. */
+#define CHAR_PIPE	   '|'
+#define CHAR_LPAREN	   '('
+#define CHAR_RPAREN	   ')'
+#define CHAR_LBRACE	   '{'
+#define CHAR_RBRACE	   '}'
+#define CHAR_LBRACKET	   '['
+#define CHAR_RBRACKET	   ']'
+#define CHAR_MINUS	   '-'
+#define CHAR_STAR	   '*'
+#define CHAR_QUESTIONMARK  '?'
+#define CHAR_PLUS	   '+'
+#define CHAR_PERIOD	   '.'
+#define CHAR_COLON	   ':'
+#define CHAR_EQUAL	   '='
+#define CHAR_COMMA	   ','
+#define CHAR_CARET	   '^'
+#define CHAR_DOLLAR	   '$'
+#define CHAR_BACKSLASH	   '\\'
+#define CHAR_HASH	   '#'
+#define CHAR_TILDE	   '~'
+
+
+/* Some macros for expanding \w, \s, etc. */
+static const struct tre_macro_struct {
+  const char c;
+  const char *expansion;
+} tre_macros[] =
+  { {'t', "\t"},	   {'n', "\n"},		   {'r', "\r"},
+    {'f', "\f"},	   {'a', "\a"},		   {'e', "\033"},
+    {'w', "[[:alnum:]_]"}, {'W', "[^[:alnum:]_]"}, {'s', "[[:space:]]"},
+    {'S', "[^[:space:]]"}, {'d', "[[:digit:]]"},   {'D', "[^[:digit:]]"},
+    { 0, NULL }
+  };
+
+
+/* Expands a macro delimited by `regex' and `regex_end' to `buf', which
+   must have at least `len' items.  Sets buf[0] to zero if the there
+   is no match in `tre_macros'. */
+static const char *
+tre_expand_macro(const char *regex)
+{
+  int i;
+
+  if (!*regex)
+    return 0;
+
+  for (i = 0; tre_macros[i].expansion && tre_macros[i].c != *regex; i++);
+  return tre_macros[i].expansion;
+}
+
 static reg_errcode_t
 tre_new_item(tre_mem_t mem, int min, int max, int *i, int *max_i,
 	 tre_ast_node_t ***items)
@@ -359,7 +514,6 @@ tre_new_item(tre_mem_t mem, int min, int max, int *i, int *max_i,
   if (*i >= *max_i)
     {
       tre_ast_node_t **new_items;
-      DPRINT(("out of array space, i = %d\n", *i));
       /* If the array is already 1024 items large, give up -- there's
 	 probably an error in the regexp (e.g. not a '\0' terminated
 	 string and missing ']') */
@@ -378,47 +532,11 @@ tre_new_item(tre_mem_t mem, int min, int max, int *i, int *max_i,
 }
 
 
-/* Expands a character class to character ranges. */
-static reg_errcode_t
-tre_expand_ctype(tre_mem_t mem, tre_ctype_t class, tre_ast_node_t ***items,
-		 int *i, int *max_i, int cflags)
-{
-  reg_errcode_t status = REG_OK;
-  tre_cint_t c;
-  int j, min = -1, max = 0;
-  assert(TRE_MB_CUR_MAX == 1);
-
-  DPRINT(("  expanding class to character ranges\n"));
-  for (j = 0; (j < 256) && (status == REG_OK); j++)
-    {
-      c = j;
-      if (tre_isctype(c, class)
-	  || ((cflags & REG_ICASE)
-	      && (tre_isctype(tre_tolower(c), class)
-		  || tre_isctype(tre_toupper(c), class))))
-{
-	  if (min < 0)
-	    min = c;
-	  max = c;
-	}
-      else if (min >= 0)
-	{
-	  DPRINT(("  range %c (%d) to %c (%d)\n", min, min, max, max));
-	  status = tre_new_item(mem, min, max, i, max_i, items);
-	  min = -1;
-	}
-    }
-  if (min >= 0 && status == REG_OK)
-    status = tre_new_item(mem, min, max, i, max_i, items);
-  return status;
-}
-
-
 static int
 tre_compare_items(const void *a, const void *b)
 {
-  tre_ast_node_t *node_a = *(tre_ast_node_t **)a;
-  tre_ast_node_t *node_b = *(tre_ast_node_t **)b;
+  const tre_ast_node_t *node_a = *(tre_ast_node_t * const *)a;
+  const tre_ast_node_t *node_b = *(tre_ast_node_t * const *)b;
   tre_literal_t *l_a = node_a->obj, *l_b = node_b->obj;
   int a_min = l_a->code_min, b_min = l_b->code_min;
 
@@ -443,7 +561,7 @@ tre_parse_bracket_items(tre_parse_ctx_t *ctx, int negate,
 			tre_ast_node_t ***items, int *num_items,
 			int *items_size)
 {
-  const tre_char_t *re = ctx->re;
+  const char *re = ctx->re;
   reg_errcode_t status = REG_OK;
   tre_ctype_t class = (tre_ctype_t)0;
   int i = *num_items;
@@ -454,86 +572,56 @@ tre_parse_bracket_items(tre_parse_ctx_t *ctx, int negate,
   while (status == REG_OK)
     {
       skip = 0;
-      if (re == ctx->re_end)
+      if (!*re)
 	{
 	  status = REG_EBRACK;
 	}
-      else if (*re == ']' && re > ctx->re)
+      else if (*re == CHAR_RBRACKET && re > ctx->re)
 	{
-	  DPRINT(("tre_parse_bracket:	done: '%.*" STRF "'\n",
-		  ctx->re_end - re, re));
 	  re++;
 	  break;
 	}
       else
 	{
 	  tre_cint_t min = 0, max = 0;
+	  wchar_t wc;
+	  int clen = mbtowc(&wc, re, -1);
+
+	  if (clen<0) clen=1, wc=WEOF;
 
 	  class = (tre_ctype_t)0;
-	  if (re + 2 < ctx->re_end
-	      && *(re + 1) == '-' && *(re + 2) != ']')
+	  if (*(re + clen) == CHAR_MINUS && *(re + clen + 1) != CHAR_RBRACKET)
 	    {
-	      DPRINT(("tre_parse_bracket:  range: '%.*" STRF "'\n",
-		      ctx->re_end - re, re));
-	      min = *re;
-	      max = *(re + 2);
-	      re += 3;
+	      min = wc;
+	      re += clen+1;
+	      clen = mbtowc(&wc, re, -1);
+	      if (clen<0) clen=1, wc=WEOF;
+	      max = wc;
+	      re += clen;
 	      /* XXX - Should use collation order instead of encoding values
 		 in character ranges. */
 	      if (min > max)
 		status = REG_ERANGE;
 	    }
-	  else if (re + 1 < ctx->re_end
-		   && *re == '[' && *(re + 1) == '.')
+	  else if (*re == CHAR_LBRACKET && *(re + 1) == CHAR_PERIOD)
 	    status = REG_ECOLLATE;
-	  else if (re + 1 < ctx->re_end
-		   && *re == '[' && *(re + 1) == '=')
+	  else if (*re == CHAR_LBRACKET && *(re + 1) == CHAR_EQUAL)
 	    status = REG_ECOLLATE;
-	  else if (re + 1 < ctx->re_end
-		   && *re == '[' && *(re + 1) == ':')
+	  else if (*re == CHAR_LBRACKET && *(re + 1) == CHAR_COLON)
 	    {
 	      char tmp_str[64];
-	      const tre_char_t *endptr = re + 2;
+	      const char *endptr = re + 2;
 	      int len;
-	      DPRINT(("tre_parse_bracket:  class: '%.*" STRF "'\n",
-		      ctx->re_end - re, re));
-	      while (endptr < ctx->re_end && *endptr != ':')
+	      while (*endptr && *endptr != CHAR_COLON)
 		endptr++;
-	      if (endptr != ctx->re_end)
+	      if (*endptr)
 		{
 		  len = MIN(endptr - re - 2, 63);
-#ifdef TRE_WCHAR
-		  {
-		    tre_char_t tmp_wcs[64];
-		    wcsncpy(tmp_wcs, re + 2, len);
-		    tmp_wcs[len] = '\0';
-#if defined HAVE_WCSRTOMBS
-		    {
-		      mbstate_t state;
-		      const tre_char_t *src = tmp_wcs;
-		      memset(&state, '\0', sizeof(state));
-		      len = wcsrtombs(tmp_str, &src, sizeof(tmp_str), &state);
-		    }
-#elif defined HAVE_WCSTOMBS
-		    len = wcstombs(tmp_str, tmp_wcs, 63);
-#endif /* defined HAVE_WCSTOMBS */
-		  }
-#else /* !TRE_WCHAR */
 		  strncpy(tmp_str, re + 2, len);
-#endif /* !TRE_WCHAR */
 		  tmp_str[len] = '\0';
-		  DPRINT(("  class name: %s\n", tmp_str));
 		  class = tre_ctype(tmp_str);
 		  if (!class)
 		    status = REG_ECTYPE;
-		  /* Optimize character classes for 8 bit character sets. */
-		  if (status == REG_OK && TRE_MB_CUR_MAX == 1)
-		    {
-		      status = tre_expand_ctype(ctx->mem, class, items,
-						&i, &max_i, ctx->cflags);
-		      class = (tre_ctype_t)0;
-		      skip = 1;
-		    }
 		  re = endptr + 2;
 		}
 	      else
@@ -543,13 +631,12 @@ tre_parse_bracket_items(tre_parse_ctx_t *ctx, int negate,
 	    }
 	  else
 	    {
-	      DPRINT(("tre_parse_bracket:   char: '%.*" STRF "'\n",
-		      ctx->re_end - re, re));
-	      if (*re == '-' && *(re + 1) != ']'
+	      if (*re == CHAR_MINUS && *(re + 1) != CHAR_RBRACKET
 		  && ctx->re != re)
 		/* Two ranges are not allowed to share and endpoint. */
 		status = REG_ERANGE;
-	      min = max = *re++;
+	      min = max = wc;
+	      re += clen;
 	    }
 
 	  if (status != REG_OK)
@@ -565,16 +652,15 @@ tre_parse_bracket_items(tre_parse_ctx_t *ctx, int negate,
 	      status = tre_new_item(ctx->mem, min, max, &i, &max_i, items);
 	      if (status != REG_OK)
 		break;
-	      ((tre_literal_t*)((*items)[i-1])->obj)->class = class;
+	      ((tre_literal_t*)((*items)[i-1])->obj)->u.class = class;
 	    }
 
 	  /* Add opposite-case counterpoints if REG_ICASE is present.
 	     This is broken if there are more than two "same" characters. */
 	  if (ctx->cflags & REG_ICASE && !class && status == REG_OK && !skip)
 	    {
-	      int cmin, ccurr;
+	      tre_cint_t cmin, ccurr;
 
-	      DPRINT(("adding opposite-case counterpoints\n"));
 	      while (min <= max)
 		{
 		  if (tre_islower(min))
@@ -626,10 +712,8 @@ tre_parse_bracket(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
   if (items == NULL)
     return REG_ESPACE;
 
-  if (*ctx->re == '^')
+  if (*ctx->re == CHAR_CARET)
     {
-      DPRINT(("tre_parse_bracket: negate: '%.*" STRF "'\n",
-	      ctx->re_end - ctx->re, ctx->re));
       negate = 1;
       ctx->re++;
     }
@@ -642,7 +726,7 @@ tre_parse_bracket(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
 
   /* Sort the array if we need to negate it. */
   if (negate)
-    qsort(items, i, sizeof(*items), tre_compare_items);
+    qsort(items, (unsigned)i, sizeof(*items), tre_compare_items);
 
   curr_max = curr_min = 0;
   /* Build a union of the items in the array, negated if necessary. */
@@ -653,16 +737,12 @@ tre_parse_bracket(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
       min = l->code_min;
       max = l->code_max;
 
-      DPRINT(("item: %d - %d, class %ld, curr_max = %d\n",
-	      (int)l->code_min, (int)l->code_max, (long)l->class, curr_max));
-
       if (negate)
 	{
 	  if (min < curr_max)
 	    {
 	      /* Overlap. */
 	      curr_max = MAX(max + 1, curr_max);
-	      DPRINT(("overlap, curr_max = %d\n", curr_max));
 	      l = NULL;
 	    }
 	  else
@@ -671,13 +751,11 @@ tre_parse_bracket(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
 	      curr_max = min - 1;
 	      if (curr_max >= curr_min)
 		{
-		  DPRINT(("no overlap\n"));
 		  l->code_min = curr_min;
 		  l->code_max = curr_max;
 		}
 	      else
 		{
-		  DPRINT(("no overlap, zero room\n"));
 		  l = NULL;
 		}
 	      curr_min = curr_max = max + 1;
@@ -687,7 +765,6 @@ tre_parse_bracket(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
       if (l != NULL)
 	{
 	  int k;
-	  DPRINT(("creating %d - %d\n", (int)l->code_min, (int)l->code_max));
 	  l->position = ctx->position;
 	  if (num_neg_classes > 0)
 	    {
@@ -723,7 +800,6 @@ tre_parse_bracket(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
   if (negate)
     {
       int k;
-      DPRINT(("final: creating %d - %d\n", curr_min, (int)TRE_CHAR_MAX));
       n = tre_ast_new_literal(ctx->mem, curr_min, TRE_CHAR_MAX, ctx->position);
       if (n == NULL)
 	status = REG_ESPACE;
@@ -776,11 +852,11 @@ tre_parse_bracket(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
 /* Parses a positive decimal integer.  Returns -1 if the string does not
    contain a valid number. */
 static int
-tre_parse_int(const tre_char_t **regex, const tre_char_t *regex_end)
+tre_parse_int(const char **regex)
 {
   int num = -1;
-  const tre_char_t *r = *regex;
-  while (r < regex_end && *r >= '0' && *r <= '9')
+  const char *r = *regex;
+  while (*r-'0'<10U)
     {
       if (num < 0)
 	num = 0;
@@ -796,67 +872,29 @@ static reg_errcode_t
 tre_parse_bound(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
 {
   int min, max;
-  const tre_char_t *r = ctx->re;
-  const tre_char_t *start;
-  int counts_set = 0;
+  const char *r = ctx->re;
+  int minimal = 0;
 
   /* Parse number (minimum repetition count). */
   min = -1;
-  if (r < ctx->re_end && *r >= '0' && *r <= '9') {
-    DPRINT(("tre_parse:	  min count: '%.*" STRF "'\n", ctx->re_end - r, r));
-    min = tre_parse_int(&r, ctx->re_end);
+  if (*r >= '0' && *r <= '9') {
+    min = tre_parse_int(&r);
   }
 
   /* Parse comma and second number (maximum repetition count). */
   max = min;
-  if (r < ctx->re_end && *r == ',')
+  if (*r == CHAR_COMMA)
     {
       r++;
-      DPRINT(("tre_parse:   max count: '%.*" STRF "'\n", ctx->re_end - r, r));
-      max = tre_parse_int(&r, ctx->re_end);
+      max = tre_parse_int(&r);
     }
 
   /* Check that the repeat counts are sane. */
   if ((max >= 0 && min > max) || max > RE_DUP_MAX)
     return REG_BADBR;
 
-
-  /*
-   '{'
-     optionally followed immediately by a number == minimum repcount
-     optionally followed by , then a number == maximum repcount
-  */
-
-
-  do {
-    int done;
-    start = r;
-
-    /* Parse count limit settings */
-    done = 0;
-    if (!counts_set)
-      while (r + 1 < ctx->re_end && !done)
-	{
-	  switch (*r)
-	    {
-	    case ',':
-	      r++;
-	      break;
-	    case ' ':
-	      r++;
-	      break;
-	    case '}':
-	      done = 1;
-	      break;
-	    default:
-	      done = 1;
-	      break;
-	    }
-	}
-  } while (start != r);
-
   /* Missing }. */
-  if (r >= ctx->re_end)
+  if (!*r)
     return REG_EBRACE;
 
   /* Empty contents of {}. */
@@ -866,20 +904,17 @@ tre_parse_bound(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
   /* Parse the ending '}' or '\}'.*/
   if (ctx->cflags & REG_EXTENDED)
     {
-      if (r >= ctx->re_end || *r != '}')
+      if (*r != CHAR_RBRACE)
 	return REG_BADBR;
       r++;
     }
   else
     {
-      if (r + 1 >= ctx->re_end
-	  || *r != '\\'
-	  || *(r + 1) != '}')
+      if (*r != CHAR_BACKSLASH || *(r + 1) != CHAR_RBRACE)
 	return REG_BADBR;
       r += 2;
     }
 
-
   /* Create the AST node(s). */
   if (min == 0 && max == 0)
     {
@@ -893,7 +928,7 @@ tre_parse_bound(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
 	/* Only approximate parameters set, no repetitions. */
 	min = max = 1;
 
-      *result = tre_ast_new_iter(ctx->mem, *result, min, max);
+      *result = tre_ast_new_iter(ctx->mem, *result, min, max, minimal);
       if (!*result)
 	return REG_ESPACE;
     }
@@ -927,19 +962,14 @@ tre_parse(tre_parse_ctx_t *ctx)
   int bottom = tre_stack_num_objects(stack);
   int depth = 0;
 
-  DPRINT(("tre_parse: parsing '%.*" STRF "', len = %d\n",
-	  ctx->len, ctx->re, ctx->len));
-
   if (!ctx->nofirstsub)
     {
-      STACK_PUSH(stack, ctx->re);
-      STACK_PUSH(stack, ctx->submatch_id);
-      STACK_PUSH(stack, PARSE_MARK_FOR_SUBMATCH);
+      STACK_PUSH(stack, int, ctx->submatch_id);
+      STACK_PUSH(stack, int, PARSE_MARK_FOR_SUBMATCH);
       ctx->submatch_id++;
     }
-  STACK_PUSH(stack, PARSE_RE);
+  STACK_PUSH(stack, int, PARSE_RE);
   ctx->re_start = ctx->re;
-  ctx->re_end = ctx->re + ctx->len;
 
 
   /* The following is basically just a recursive descent parser.  I use
@@ -950,67 +980,67 @@ tre_parse(tre_parse_ctx_t *ctx)
     {
       if (status != REG_OK)
 	break;
-      symbol = (tre_parse_re_stack_symbol_t)tre_stack_pop(stack);
+      symbol = tre_stack_pop_int(stack);
       switch (symbol)
 	{
 	case PARSE_RE:
 	  /* Parse a full regexp.  A regexp is one or more branches,
 	     separated by the union operator `|'. */
 	  if (ctx->cflags & REG_EXTENDED)
-	    STACK_PUSHX(stack, PARSE_UNION);
-	  STACK_PUSHX(stack, PARSE_BRANCH);
+	    STACK_PUSHX(stack, int, PARSE_UNION);
+	  STACK_PUSHX(stack, int, PARSE_BRANCH);
 	  break;
 
 	case PARSE_BRANCH:
 	  /* Parse a branch.  A branch is one or more pieces, concatenated.
 	     A piece is an atom possibly followed by a postfix operator. */
-	  STACK_PUSHX(stack, PARSE_CATENATION);
-	  STACK_PUSHX(stack, PARSE_PIECE);
+	  STACK_PUSHX(stack, int, PARSE_CATENATION);
+	  STACK_PUSHX(stack, int, PARSE_PIECE);
 	  break;
 
 	case PARSE_PIECE:
 	  /* Parse a piece.  A piece is an atom possibly followed by one
 	     or more postfix operators. */
-	  STACK_PUSHX(stack, PARSE_POSTFIX);
-	  STACK_PUSHX(stack, PARSE_ATOM);
+	    STACK_PUSHX(stack, int, PARSE_POSTFIX);
+	  STACK_PUSHX(stack, int, PARSE_ATOM);
 	  break;
 
 	case PARSE_CATENATION:
 	  /* If the expression has not ended, parse another piece. */
 	  {
 	    tre_char_t c;
-	    if (ctx->re >= ctx->re_end)
+	    if (!*ctx->re)
 	      break;
 	    c = *ctx->re;
-	    if (ctx->cflags & REG_EXTENDED && c == '|')
-	      break;
-	    if ((ctx->cflags & REG_EXTENDED
-	         && c == ')' && depth > 0)
-	        || (!(ctx->cflags & REG_EXTENDED)
-		    && (c == '\\'
-		        && *(ctx->re + 1) == ')')))
+		if (ctx->cflags & REG_EXTENDED && c == CHAR_PIPE)
+		  break;
+		if ((ctx->cflags & REG_EXTENDED
+		     && c == CHAR_RPAREN && depth > 0)
+		    || (!(ctx->cflags & REG_EXTENDED)
+			&& (c == CHAR_BACKSLASH
+			    && *(ctx->re + 1) == CHAR_RPAREN)))
+		  {
+		    if (!(ctx->cflags & REG_EXTENDED) && depth == 0)
+		      status = REG_EPAREN;
+		    depth--;
+		    if (!(ctx->cflags & REG_EXTENDED))
+		      ctx->re += 2;
+		    break;
+		  }
+
 	      {
-	        if (!(ctx->cflags & REG_EXTENDED) && depth == 0)
-	          status = REG_EPAREN;
-	        DPRINT(("tre_parse:	  group end: '%.*" STRF "'\n",
-		        ctx->re_end - ctx->re, ctx->re));
-	        depth--;
-	        if (!(ctx->cflags & REG_EXTENDED))
-	          ctx->re += 2;
-	        break;
+		/* Default case, left associative concatenation. */
+		STACK_PUSHX(stack, int, PARSE_CATENATION);
+		STACK_PUSHX(stack, voidptr, result);
+		STACK_PUSHX(stack, int, PARSE_POST_CATENATION);
+		STACK_PUSHX(stack, int, PARSE_PIECE);
 	      }
-
-	    /* Left associative concatenation. */
-	    STACK_PUSHX(stack, PARSE_CATENATION);
-	    STACK_PUSHX(stack, result);
-	    STACK_PUSHX(stack, PARSE_POST_CATENATION);
-	    STACK_PUSHX(stack, PARSE_PIECE);
 	    break;
 	  }
 
 	case PARSE_POST_CATENATION:
 	  {
-	    tre_ast_node_t *tree = tre_stack_pop(stack);
+	    tre_ast_node_t *tree = tre_stack_pop_voidptr(stack);
 	    tre_ast_node_t *tmp_node;
 	    tmp_node = tre_ast_new_catenation(ctx->mem, tree, result);
 	    if (!tmp_node)
@@ -1020,21 +1050,19 @@ tre_parse(tre_parse_ctx_t *ctx)
 	  }
 
 	case PARSE_UNION:
-	  if (ctx->re >= ctx->re_end)
+	  if (!*ctx->re)
 	    break;
 	  switch (*ctx->re)
 	    {
-	    case '|':
-	      DPRINT(("tre_parse:	union: '%.*" STRF "'\n",
-		      ctx->re_end - ctx->re, ctx->re));
-	      STACK_PUSHX(stack, PARSE_UNION);
-	      STACK_PUSHX(stack, result);
-	      STACK_PUSHX(stack, PARSE_POST_UNION);
-	      STACK_PUSHX(stack, PARSE_BRANCH);
+	    case CHAR_PIPE:
+	      STACK_PUSHX(stack, int, PARSE_UNION);
+	      STACK_PUSHX(stack, voidptr, result);
+	      STACK_PUSHX(stack, int, PARSE_POST_UNION);
+	      STACK_PUSHX(stack, int, PARSE_BRANCH);
 	      ctx->re++;
 	      break;
 
-	    case ')':
+	    case CHAR_RPAREN:
 	      ctx->re++;
 	      break;
 
@@ -1046,7 +1074,7 @@ tre_parse(tre_parse_ctx_t *ctx)
 	case PARSE_POST_UNION:
 	  {
 	    tre_ast_node_t *tmp_node;
-	    tre_ast_node_t *tree = tre_stack_pop(stack);
+	    tre_ast_node_t *tree = tre_stack_pop_voidptr(stack);
 	    tmp_node = tre_ast_new_union(ctx->mem, tree, result);
 	    if (!tmp_node)
 	      return REG_ESPACE;
@@ -1056,38 +1084,55 @@ tre_parse(tre_parse_ctx_t *ctx)
 
 	case PARSE_POSTFIX:
 	  /* Parse postfix operators. */
-	  if (ctx->re >= ctx->re_end)
+	  if (!*ctx->re)
 	    break;
 	  switch (*ctx->re)
 	    {
-	    case '+':
-	    case '?':
+	    case CHAR_PLUS:
+	    case CHAR_QUESTIONMARK:
 	      if (!(ctx->cflags & REG_EXTENDED))
-	        break;
-	    case '*':
+		break;
+		/*FALLTHROUGH*/
+	    case CHAR_STAR:
 	      {
 		tre_ast_node_t *tmp_node;
+		int minimal = 0;
 		int rep_min = 0;
 		int rep_max = -1;
-		if (*ctx->re == '+')
+
+		if (*ctx->re == CHAR_PLUS)
 		  rep_min = 1;
-		if (*ctx->re == '?')
+		if (*ctx->re == CHAR_QUESTIONMARK)
 		  rep_max = 1;
 
+		  {
+		    if (*(ctx->re + 1) == CHAR_QUESTIONMARK)
+		      {
+			minimal = 1;
+			ctx->re++;
+		      }
+		    else if (*(ctx->re + 1) == CHAR_STAR
+			     || *(ctx->re + 1) == CHAR_PLUS)
+		      {
+			/* These are reserved for future extensions. */
+			return REG_BADRPT;
+		      }
+		  }
+
 		ctx->re++;
-		tmp_node = tre_ast_new_iter(ctx->mem, result, rep_min, rep_max);
+		tmp_node = tre_ast_new_iter(ctx->mem, result, rep_min, rep_max,
+					    minimal);
 		if (tmp_node == NULL)
 		  return REG_ESPACE;
 		result = tmp_node;
-		STACK_PUSHX(stack, PARSE_POSTFIX);
-		break;
+		STACK_PUSHX(stack, int, PARSE_POSTFIX);
 	      }
+	      break;
 
-	    case '\\':
+	    case CHAR_BACKSLASH:
 	      /* "\{" is special without REG_EXTENDED */
 	      if (!(ctx->cflags & REG_EXTENDED)
-		  && ctx->re + 1 < ctx->re_end
-		  && *(ctx->re + 1) == '{')
+		  && *(ctx->re + 1) == CHAR_LBRACE)
 		{
 		  ctx->re++;
 		  goto parse_brace;
@@ -1095,20 +1140,18 @@ tre_parse(tre_parse_ctx_t *ctx)
 	      else
 		break;
 
-	    case '{':
+	    case CHAR_LBRACE:
 	      /* "{" is literal without REG_EXTENDED */
 	      if (!(ctx->cflags & REG_EXTENDED))
 		break;
 
 	    parse_brace:
-	      DPRINT(("tre_parse:	bound: '%.*" STRF "'\n",
-		      ctx->re_end - ctx->re, ctx->re));
 	      ctx->re++;
 
 	      status = tre_parse_bound(ctx, &result);
 	      if (status != REG_OK)
 		return status;
-	      STACK_PUSHX(stack, PARSE_POSTFIX);
+	      STACK_PUSHX(stack, int, PARSE_POSTFIX);
 	      break;
 	    }
 	  break;
@@ -1119,29 +1162,25 @@ tre_parse(tre_parse_ctx_t *ctx)
 	     a `\' followed by a character, or a single character. */
 
 	  /* End of regexp? (empty string). */
-	  if (ctx->re >= ctx->re_end)
+	  if (!*ctx->re)
 	    goto parse_literal;
 
 	  switch (*ctx->re)
 	    {
-	    case '(':  /* parenthesized subexpression */
+	    case CHAR_LPAREN:  /* parenthesized subexpression */
 
 	      if (ctx->cflags & REG_EXTENDED
 		  || (ctx->re > ctx->re_start
-		      && *(ctx->re - 1) == '\\'))
+		      && *(ctx->re - 1) == CHAR_BACKSLASH))
 		{
 		  depth++;
 		    {
-		      DPRINT(("tre_parse: group begin: '%.*" STRF
-			      "', submatch %d\n",
-			      ctx->re_end - ctx->re, ctx->re,
-			      ctx->submatch_id));
 		      ctx->re++;
 		      /* First parse a whole RE, then mark the resulting tree
 			 for submatching. */
-		      STACK_PUSHX(stack, ctx->submatch_id);
-		      STACK_PUSHX(stack, PARSE_MARK_FOR_SUBMATCH);
-		      STACK_PUSHX(stack, PARSE_RE);
+		      STACK_PUSHX(stack, int, ctx->submatch_id);
+		      STACK_PUSHX(stack, int, PARSE_MARK_FOR_SUBMATCH);
+		      STACK_PUSHX(stack, int, PARSE_RE);
 		      ctx->submatch_id++;
 		    }
 		}
@@ -1149,13 +1188,11 @@ tre_parse(tre_parse_ctx_t *ctx)
 		goto parse_literal;
 	      break;
 
-	    case ')':  /* end of current subexpression */
+	    case CHAR_RPAREN:  /* end of current subexpression */
 	      if ((ctx->cflags & REG_EXTENDED && depth > 0)
 		  || (ctx->re > ctx->re_start
-		      && *(ctx->re - 1) == '\\'))
+		      && *(ctx->re - 1) == CHAR_BACKSLASH))
 		{
-		  DPRINT(("tre_parse:	    empty: '%.*" STRF "'\n",
-			  ctx->re_end - ctx->re, ctx->re));
 		  /* We were expecting an atom, but instead the current
 		     subexpression was closed.	POSIX leaves the meaning of
 		     this to be implementation-defined.	 We interpret this as
@@ -1170,44 +1207,130 @@ tre_parse(tre_parse_ctx_t *ctx)
 		goto parse_literal;
 	      break;
 
-	    case '[': /* bracket expression */
-	      DPRINT(("tre_parse:     bracket: '%.*" STRF "'\n",
-		      ctx->re_end - ctx->re, ctx->re));
+	    case CHAR_LBRACKET: /* bracket expression */
 	      ctx->re++;
 	      status = tre_parse_bracket(ctx, &result);
 	      if (status != REG_OK)
 		return status;
 	      break;
 
-	    case '\\':
+	    case CHAR_BACKSLASH:
 	      /* If this is "\(" or "\)" chew off the backslash and
 		 try again. */
 	      if (!(ctx->cflags & REG_EXTENDED)
-		  && ctx->re + 1 < ctx->re_end
-		  && (*(ctx->re + 1) == '('
-		      || *(ctx->re + 1) == ')'))
+		  && (*(ctx->re + 1) == CHAR_LPAREN
+		      || *(ctx->re + 1) == CHAR_RPAREN))
 		{
 		  ctx->re++;
-		  STACK_PUSHX(stack, PARSE_ATOM);
+		  STACK_PUSHX(stack, int, PARSE_ATOM);
 		  break;
 		}
 
-	      if (ctx->re + 1 >= ctx->re_end)
+	      /* If a macro is used, parse the expanded macro recursively. */
+	      {
+		const char *buf = tre_expand_macro(ctx->re + 1);
+		if (buf)
+		  {
+		    tre_parse_ctx_t subctx;
+		    memcpy(&subctx, ctx, sizeof(subctx));
+		    subctx.re = buf;
+		    subctx.nofirstsub = 1;
+		    status = tre_parse(&subctx);
+		    if (status != REG_OK)
+		      return status;
+		    ctx->re += 2;
+		    ctx->position = subctx.position;
+		    result = subctx.result;
+		    break;
+		  }
+	      }
+
+	      if (!*ctx->re)
 		/* Trailing backslash. */
 		return REG_EESCAPE;
 
-	      DPRINT(("tre_parse:  bleep: '%.*" STRF "'\n",
-		      ctx->re_end - ctx->re, ctx->re));
 	      ctx->re++;
 	      switch (*ctx->re)
 		{
+		case 'b':
+		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
+					       ASSERT_AT_WB, -1);
+		  ctx->re++;
+		  break;
+		case 'B':
+		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
+					       ASSERT_AT_WB_NEG, -1);
+		  ctx->re++;
+		  break;
+		case '<':
+		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
+					       ASSERT_AT_BOW, -1);
+		  ctx->re++;
+		  break;
+		case '>':
+		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
+					       ASSERT_AT_EOW, -1);
+		  ctx->re++;
+		  break;
+		case 'x':
+		  ctx->re++;
+		  if (ctx->re[0] != CHAR_LBRACE)
+		    {
+		      /* 8 bit hex char. */
+		      char tmp[3] = {0, 0, 0};
+		      long val;
+
+		      if (tre_isxdigit(ctx->re[0]))
+			{
+			  tmp[0] = (char)ctx->re[0];
+			  ctx->re++;
+			}
+		      if (tre_isxdigit(ctx->re[0]))
+			{
+			  tmp[1] = (char)ctx->re[0];
+			  ctx->re++;
+			}
+		      val = strtol(tmp, NULL, 16);
+		      result = tre_ast_new_literal(ctx->mem, (int)val,
+						   (int)val, ctx->position);
+		      ctx->position++;
+		      break;
+		    }
+		  else if (*ctx->re)
+		    {
+		      /* Wide char. */
+		      char tmp[32];
+		      long val;
+		      int i = 0;
+		      ctx->re++;
+		      while (*ctx->re && i < sizeof tmp)
+			{
+			  if (ctx->re[0] == CHAR_RBRACE)
+			    break;
+			  if (tre_isxdigit(ctx->re[0]))
+			    {
+			      tmp[i] = (char)ctx->re[0];
+			      i++;
+			      ctx->re++;
+			      continue;
+			    }
+			  return REG_EBRACE;
+			}
+		      ctx->re++;
+		      tmp[i] = 0;
+		      val = strtol(tmp, NULL, 16);
+		      result = tre_ast_new_literal(ctx->mem, (int)val, (int)val,
+						   ctx->position);
+		      ctx->position++;
+		      break;
+		    }
+		  /*FALLTHROUGH*/
+
 		default:
-		  if (!(ctx->cflags & REG_EXTENDED) && tre_isdigit(*ctx->re))
+		  if (tre_isdigit(*ctx->re))
 		    {
 		      /* Back reference. */
 		      int val = *ctx->re - '0';
-		      DPRINT(("tre_parse:     backref: '%.*" STRF "'\n",
-			      ctx->re_end - ctx->re + 1, ctx->re - 1));
 		      result = tre_ast_new_literal(ctx->mem, BACKREF, val,
 						   ctx->position);
 		      if (result == NULL)
@@ -1219,8 +1342,6 @@ tre_parse(tre_parse_ctx_t *ctx)
 		  else
 		    {
 		      /* Escaped character. */
-		      DPRINT(("tre_parse:     escaped: '%.*" STRF "'\n",
-			      ctx->re_end - ctx->re + 1, ctx->re - 1));
 		      result = tre_ast_new_literal(ctx->mem, *ctx->re, *ctx->re,
 						   ctx->position);
 		      ctx->position++;
@@ -1232,9 +1353,7 @@ tre_parse(tre_parse_ctx_t *ctx)
 		return REG_ESPACE;
 	      break;
 
-	    case '.':	 /* the any-symbol */
-	      DPRINT(("tre_parse:	  any: '%.*" STRF "'\n",
-		      ctx->re_end - ctx->re, ctx->re));
+	    case CHAR_PERIOD:	 /* the any-symbol */
 	      if (ctx->cflags & REG_NEWLINE)
 		{
 		  tre_ast_node_t *tmp1;
@@ -1263,17 +1382,15 @@ tre_parse(tre_parse_ctx_t *ctx)
 	      ctx->re++;
 	      break;
 
-	    case '^':	 /* beginning of line assertion */
+	    case CHAR_CARET:	 /* beginning of line assertion */
 	      /* '^' has a special meaning everywhere in EREs, and in the
 		 beginning of the RE and after \( is BREs. */
 	      if (ctx->cflags & REG_EXTENDED
 		  || (ctx->re - 2 >= ctx->re_start
-		      && *(ctx->re - 2) == '\\'
-		      && *(ctx->re - 1) == '(')
+		      && *(ctx->re - 2) == CHAR_BACKSLASH
+		      && *(ctx->re - 1) == CHAR_LPAREN)
 		  || ctx->re == ctx->re_start)
 		{
-		  DPRINT(("tre_parse:	      BOL: '%.*" STRF "'\n",
-			  ctx->re_end - ctx->re, ctx->re));
 		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
 					       ASSERT_AT_BOL, -1);
 		  if (result == NULL)
@@ -1284,17 +1401,14 @@ tre_parse(tre_parse_ctx_t *ctx)
 		goto parse_literal;
 	      break;
 
-	    case '$':	 /* end of line assertion. */
+	    case CHAR_DOLLAR:	 /* end of line assertion. */
 	      /* '$' is special everywhere in EREs, and in the end of the
 		 string and before \) is BREs. */
 	      if (ctx->cflags & REG_EXTENDED
-		  || (ctx->re + 2 < ctx->re_end
-		      && *(ctx->re + 1) == '\\'
-		      && *(ctx->re + 2) == ')')
-		  || ctx->re + 1 == ctx->re_end)
+		  || (*(ctx->re + 1) == CHAR_BACKSLASH
+		      && *(ctx->re + 2) == CHAR_RPAREN)
+		  || !*(ctx->re + 1))
 		{
-		  DPRINT(("tre_parse:	      EOL: '%.*" STRF "'\n",
-			  ctx->re_end - ctx->re, ctx->re));
 		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
 					       ASSERT_AT_EOL, -1);
 		  if (result == NULL)
@@ -1312,34 +1426,34 @@ tre_parse(tre_parse_ctx_t *ctx)
 		 regexp ends here, we interpret it as an empty expression
 		 (which matches an empty string).  */
 	      if (
-		  (ctx->re >= ctx->re_end
-		   || *ctx->re == '*'
+		  (!*ctx->re
+		   || *ctx->re == CHAR_STAR
 		   || (ctx->cflags & REG_EXTENDED
-		       && (*ctx->re == '|'
-			   || *ctx->re == '{'
-			   || *ctx->re == '+'
-			   || *ctx->re == '?'))
+		       && (*ctx->re == CHAR_PIPE
+			   || *ctx->re == CHAR_LBRACE
+			   || *ctx->re == CHAR_PLUS
+			   || *ctx->re == CHAR_QUESTIONMARK))
 		   /* Test for "\)" in BRE mode. */
 		   || (!(ctx->cflags & REG_EXTENDED)
-		       && ctx->re + 1 < ctx->re_end
-		       && *ctx->re == '\\'
-		       && *(ctx->re + 1) == '{')))
+		       && !*(ctx->re + 1)
+		       && *ctx->re == CHAR_BACKSLASH
+		       && *(ctx->re + 1) == CHAR_LBRACE)))
 		{
-		  DPRINT(("tre_parse:	    empty: '%.*" STRF "'\n",
-			  ctx->re_end - ctx->re, ctx->re));
 		  result = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
 		  if (!result)
 		    return REG_ESPACE;
 		  break;
 		}
 
-	      DPRINT(("tre_parse:     literal: '%.*" STRF "'\n",
-		      ctx->re_end - ctx->re, ctx->re));
+	      wchar_t wc;
+	      int clen = mbtowc(&wc, ctx->re, -1);
+	      if (clen<0) clen=1, wc=WEOF;
+
 	      /* Note that we can't use an tre_isalpha() test here, since there
 		 may be characters which are alphabetic but neither upper or
 		 lower case. */
 	      if (ctx->cflags & REG_ICASE
-		  && (tre_isupper(*ctx->re) || tre_islower(*ctx->re)))
+		  && (tre_isupper(wc) || tre_islower(wc)))
 		{
 		  tre_ast_node_t *tmp1;
 		  tre_ast_node_t *tmp2;
@@ -1352,13 +1466,13 @@ tre_parse(tre_parse_ctx_t *ctx)
 		     that at least for multi-character collating elements there
 		     could be several opposite-case counterpoints, but they
 		     cannot be supported portably anyway. */
-		  tmp1 = tre_ast_new_literal(ctx->mem, tre_toupper(*ctx->re),
-					     tre_toupper(*ctx->re),
+		  tmp1 = tre_ast_new_literal(ctx->mem, tre_toupper(wc),
+					     tre_toupper(wc),
 					     ctx->position);
 		  if (!tmp1)
 		    return REG_ESPACE;
-		  tmp2 = tre_ast_new_literal(ctx->mem, tre_tolower(*ctx->re),
-					     tre_tolower(*ctx->re),
+		  tmp2 = tre_ast_new_literal(ctx->mem, tre_tolower(wc),
+					     tre_tolower(wc),
 					     ctx->position);
 		  if (!tmp2)
 		    return REG_ESPACE;
@@ -1368,20 +1482,20 @@ tre_parse(tre_parse_ctx_t *ctx)
 		}
 	      else
 		{
-		  result = tre_ast_new_literal(ctx->mem, *ctx->re, *ctx->re,
+		  result = tre_ast_new_literal(ctx->mem, wc, wc,
 					       ctx->position);
 		  if (!result)
 		    return REG_ESPACE;
 		}
 	      ctx->position++;
-	      ctx->re++;
+	      ctx->re += clen;
 	      break;
 	    }
 	  break;
 
 	case PARSE_MARK_FOR_SUBMATCH:
 	  {
-	    int submatch_id = (int)tre_stack_pop(stack);
+	    int submatch_id = tre_stack_pop_int(stack);
 
 	    if (result->submatch_id >= 0)
 	      {
@@ -1401,7 +1515,11 @@ tre_parse(tre_parse_ctx_t *ctx)
 	  }
 
 	case PARSE_RESTORE_CFLAGS:
-	  ctx->cflags = (int)tre_stack_pop(stack);
+	  ctx->cflags = tre_stack_pop_int(stack);
+	  break;
+
+	default:
+	  assert(0);
 	  break;
 	}
     }
@@ -1417,10 +1535,18 @@ tre_parse(tre_parse_ctx_t *ctx)
 }
 
 
+
 /***********************************************************************
  from tre-compile.c
 ***********************************************************************/
 
+
+/*
+  TODO:
+   - Fix tre_ast_to_tnfa() to recurse using a stack instead of recursive
+     function calls.
+*/
+
 /*
   Algorithms to setup tags so that submatch addressing can be done.
 */
@@ -1429,42 +1555,60 @@ tre_parse(tre_parse_ctx_t *ctx)
 /* Inserts a catenation node to the root of the tree given in `node'.
    As the left child a new tag with number `tag_id' to `node' is added,
    and the right child is the old root. */
-/*              OR                  */
+static reg_errcode_t
+tre_add_tag_left(tre_mem_t mem, tre_ast_node_t *node, int tag_id)
+{
+  tre_catenation_t *c;
+
+  c = tre_mem_alloc(mem, sizeof(*c));
+  if (c == NULL)
+    return REG_ESPACE;
+  c->left = tre_ast_new_literal(mem, TAG, tag_id, -1);
+  if (c->left == NULL)
+    return REG_ESPACE;
+  c->right = tre_mem_alloc(mem, sizeof(tre_ast_node_t));
+  if (c->right == NULL)
+    return REG_ESPACE;
+
+  c->right->obj = node->obj;
+  c->right->type = node->type;
+  c->right->nullable = -1;
+  c->right->submatch_id = -1;
+  c->right->firstpos = NULL;
+  c->right->lastpos = NULL;
+  c->right->num_tags = 0;
+  node->obj = c;
+  node->type = CATENATION;
+  return REG_OK;
+}
+
 /* Inserts a catenation node to the root of the tree given in `node'.
    As the right child a new tag with number `tag_id' to `node' is added,
    and the left child is the old root. */
 static reg_errcode_t
-tre_add_tag(tre_mem_t mem, tre_ast_node_t *node, int tag_id, int right)
+tre_add_tag_right(tre_mem_t mem, tre_ast_node_t *node, int tag_id)
 {
   tre_catenation_t *c;
-  tre_ast_node_t *child_tag, *child_old;
-
-  DPRINT(("add_tag_%s: tag %d\n", right ? "right" : "left", tag_id));
 
   c = tre_mem_alloc(mem, sizeof(*c));
   if (c == NULL)
     return REG_ESPACE;
-  child_tag = tre_ast_new_literal(mem, TAG, tag_id, -1);
-  if (child_tag == NULL)
+  c->right = tre_ast_new_literal(mem, TAG, tag_id, -1);
+  if (c->right == NULL)
     return REG_ESPACE;
-  child_old = tre_mem_alloc(mem, sizeof(tre_ast_node_t));
-  if (child_old == NULL)
+  c->left = tre_mem_alloc(mem, sizeof(tre_ast_node_t));
+  if (c->left == NULL)
     return REG_ESPACE;
 
-  child_old->obj = node->obj;
-  child_old->type = node->type;
-  child_old->nullable = -1;
-  child_old->submatch_id = -1;
-  child_old->firstpos = NULL;
-  child_old->lastpos = NULL;
-  child_old->num_tags = 0;
+  c->left->obj = node->obj;
+  c->left->type = node->type;
+  c->left->nullable = -1;
+  c->left->submatch_id = -1;
+  c->left->firstpos = NULL;
+  c->left->lastpos = NULL;
+  c->left->num_tags = 0;
   node->obj = c;
   node->type = CATENATION;
-
-  c->right = c->left = child_old;
-  if (right) c->right = child_tag;
-  else c->left = child_tag;
-
   return REG_OK;
 }
 
@@ -1484,6 +1628,27 @@ typedef struct {
   int next_tag;
 } tre_tag_states_t;
 
+
+/* Go through `regset' and set submatch data for submatches that are
+   using this tag. */
+static void
+tre_purge_regset(int *regset, tre_tnfa_t *tnfa, int tag)
+{
+  int i;
+
+  for (i = 0; regset[i] >= 0; i++)
+    {
+      int id = regset[i] / 2;
+      int start = !(regset[i] % 2);
+      if (start)
+	tnfa->submatch_data[id].so_tag = tag;
+      else
+	tnfa->submatch_data[id].eo_tag = tag;
+    }
+  regset[0] = -1;
+}
+
+
 /* Adds tags to appropriate locations in the parse tree in `tree', so that
    subexpressions marked for submatch addressing can be traced. */
 static reg_errcode_t
@@ -1498,15 +1663,20 @@ tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
   int first_pass = (mem == NULL || tnfa == NULL);
   int *regset, *orig_regset;
   int num_tags = 0; /* Total number of tags. */
+  int num_minimals = 0;	 /* Number of special minimal tags. */
   int tag = 0;	    /* The tag that is to be added next. */
   int next_tag = 1; /* Next tag to use after this one. */
   int *parents;	    /* Stack of submatches the current submatch is
 		       contained in. */
+  int minimal_tag = -1; /* Tag that marks the beginning of a minimal match. */
   tre_tag_states_t *saved_states;
 
   tre_tag_direction_t direction = TRE_TAG_MINIMIZE;
   if (!first_pass)
+    {
       tnfa->end_tag = 0;
+      tnfa->minimal_tags[0] = -1;
+    }
 
   regset = xmalloc(sizeof(*regset) * ((tnfa->num_submatches + 1) * 2));
   if (regset == NULL)
@@ -1536,21 +1706,21 @@ tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
 	saved_states[i].tag = -1;
     }
 
-  STACK_PUSH(stack, node);
-  STACK_PUSH(stack, ADDTAGS_RECURSE);
+  STACK_PUSH(stack, voidptr, node);
+  STACK_PUSH(stack, int, ADDTAGS_RECURSE);
 
   while (tre_stack_num_objects(stack) > bottom)
     {
       if (status != REG_OK)
 	break;
 
-      symbol = (tre_addtags_symbol_t)tre_stack_pop(stack);
+      symbol = (tre_addtags_symbol_t)tre_stack_pop_int(stack);
       switch (symbol)
 	{
 
 	case ADDTAGS_SET_SUBMATCH_END:
 	  {
-	    int id = (int)tre_stack_pop(stack);
+	    int id = tre_stack_pop_int(stack);
 	    int i;
 
 	    /* Add end of this submatch to regset. */
@@ -1565,7 +1735,7 @@ tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
 	  }
 
 	case ADDTAGS_RECURSE:
-	  node = tre_stack_pop(stack);
+	  node = tre_stack_pop_voidptr(stack);
 
 	  if (node->submatch_id >= 0)
 	    {
@@ -1600,8 +1770,8 @@ tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
 
 	      /* Add end of this submatch to regset after processing this
 		 node. */
-	      STACK_PUSHX(stack, node->submatch_id);
-	      STACK_PUSHX(stack, ADDTAGS_SET_SUBMATCH_END);
+	      STACK_PUSHX(stack, int, node->submatch_id);
+	      STACK_PUSHX(stack, int, ADDTAGS_SET_SUBMATCH_END);
 	    }
 
 	  switch (node->type)
@@ -1613,38 +1783,30 @@ tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
 		if (!IS_SPECIAL(lit) || IS_BACKREF(lit))
 		  {
 		    int i;
-		    DPRINT(("Literal %d-%d\n",
-			    (int)lit->code_min, (int)lit->code_max));
 		    if (regset[0] >= 0)
 		      {
 			/* Regset is not empty, so add a tag before the
 			   literal or backref. */
 			if (!first_pass)
 			  {
-			    status = tre_add_tag(mem, node, tag, 0 /*left*/);
+			    status = tre_add_tag_left(mem, node, tag);
 			    tnfa->tag_directions[tag] = direction;
-			    /* Go through the regset and set submatch data for
-			       submatches that are using this tag. */
-			    for (i = 0; regset[i] >= 0; i++)
+			    if (minimal_tag >= 0)
 			      {
-				int id = regset[i] >> 1;
-				int start = !(regset[i] & 1);
-				DPRINT(("  Using tag %d for %s offset of "
-					"submatch %d\n", tag,
-					start ? "start" : "end", id));
-				if (start)
-				  tnfa->submatch_data[id].so_tag = tag;
-				else
-				  tnfa->submatch_data[id].eo_tag = tag;
+				for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+				tnfa->minimal_tags[i] = tag;
+				tnfa->minimal_tags[i + 1] = minimal_tag;
+				tnfa->minimal_tags[i + 2] = -1;
+				minimal_tag = -1;
+				num_minimals++;
 			      }
+			    tre_purge_regset(regset, tnfa, tag);
 			  }
 			else
 			  {
-			    DPRINT(("  num_tags = 1\n"));
 			    node->num_tags = 1;
 			  }
 
-			DPRINT(("  num_tags++\n"));
 			regset[0] = -1;
 			tag = next_tag;
 			num_tags++;
@@ -1663,82 +1825,75 @@ tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
 		tre_ast_node_t *left = cat->left;
 		tre_ast_node_t *right = cat->right;
 		int reserved_tag = -1;
-		DPRINT(("Catenation, next_tag = %d\n", next_tag));
 
 
 		/* After processing right child. */
-		STACK_PUSHX(stack, node);
-		STACK_PUSHX(stack, ADDTAGS_AFTER_CAT_RIGHT);
+		STACK_PUSHX(stack, voidptr, node);
+		STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_RIGHT);
 
 		/* Process right child. */
-		STACK_PUSHX(stack, right);
-		STACK_PUSHX(stack, ADDTAGS_RECURSE);
+		STACK_PUSHX(stack, voidptr, right);
+		STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
 
 		/* After processing left child. */
-		STACK_PUSHX(stack, next_tag + left->num_tags);
-		DPRINT(("  Pushing %d for after left\n",
-			next_tag + left->num_tags));
+		STACK_PUSHX(stack, int, next_tag + left->num_tags);
 		if (left->num_tags > 0 && right->num_tags > 0)
 		  {
 		    /* Reserve the next tag to the right child. */
-		    DPRINT(("  Reserving next_tag %d to right child\n",
-			    next_tag));
 		    reserved_tag = next_tag;
 		    next_tag++;
 		  }
-		STACK_PUSHX(stack, reserved_tag);
-		STACK_PUSHX(stack, ADDTAGS_AFTER_CAT_LEFT);
+		STACK_PUSHX(stack, int, reserved_tag);
+		STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_LEFT);
 
 		/* Process left child. */
-		STACK_PUSHX(stack, left);
-		STACK_PUSHX(stack, ADDTAGS_RECURSE);
+		STACK_PUSHX(stack, voidptr, left);
+		STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
 
 		}
 	      break;
 	    case ITERATION:
 	      {
 		tre_iteration_t *iter = node->obj;
-		DPRINT(("Iteration\n"));
 
 		if (first_pass)
 		  {
-		    STACK_PUSHX(stack, regset[0] >= 0);
+		    STACK_PUSHX(stack, int, regset[0] >= 0 || iter->minimal);
 		  }
 		else
 		  {
-		    STACK_PUSHX(stack, tag);
+		    STACK_PUSHX(stack, int, tag);
+		    STACK_PUSHX(stack, int, iter->minimal);
 		  }
-		STACK_PUSHX(stack, node);
-		STACK_PUSHX(stack, ADDTAGS_AFTER_ITERATION);
+		STACK_PUSHX(stack, voidptr, node);
+		STACK_PUSHX(stack, int, ADDTAGS_AFTER_ITERATION);
 
-		STACK_PUSHX(stack, iter->arg);
-		STACK_PUSHX(stack, ADDTAGS_RECURSE);
+		STACK_PUSHX(stack, voidptr, iter->arg);
+		STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
 
 		/* Regset is not empty, so add a tag here. */
-		if (regset[0] >= 0)
+		if (regset[0] >= 0 || iter->minimal)
 		  {
 		    if (!first_pass)
 		      {
 			int i;
-			status = tre_add_tag(mem, node, tag, 0 /*left*/);
-			tnfa->tag_directions[tag] = direction;
-			/* Go through the regset and set submatch data for
-			   submatches that are using this tag. */
-			for (i = 0; regset[i] >= 0; i++)
+			status = tre_add_tag_left(mem, node, tag);
+			if (iter->minimal)
+			  tnfa->tag_directions[tag] = TRE_TAG_MAXIMIZE;
+			else
+			  tnfa->tag_directions[tag] = direction;
+			if (minimal_tag >= 0)
 			  {
-			    int id = regset[i] >> 1;
-			    int start = !(regset[i] & 1);
-			    DPRINT(("  Using tag %d for %s offset of "
-				    "submatch %d\n", tag,
-				    start ? "start" : "end", id));
-			    if (start)
-			      tnfa->submatch_data[id].so_tag = tag;
-			    else
-			      tnfa->submatch_data[id].eo_tag = tag;
+			    for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+			    tnfa->minimal_tags[i] = tag;
+			    tnfa->minimal_tags[i + 1] = minimal_tag;
+			    tnfa->minimal_tags[i + 2] = -1;
+			    minimal_tag = -1;
+			    num_minimals++;
 			  }
+			tre_purge_regset(regset, tnfa, tag);
 		      }
 
-		    DPRINT(("  num_tags++\n"));
 		    regset[0] = -1;
 		    tag = next_tag;
 		    num_tags++;
@@ -1766,28 +1921,26 @@ tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
 		    right_tag = next_tag;
 		  }
 
-		DPRINT(("Union\n"));
-
 		/* After processing right child. */
-		STACK_PUSHX(stack, right_tag);
-		STACK_PUSHX(stack, left_tag);
-		STACK_PUSHX(stack, regset);
-		STACK_PUSHX(stack, regset[0] >= 0);
-		STACK_PUSHX(stack, node);
-		STACK_PUSHX(stack, right);
-		STACK_PUSHX(stack, left);
-		STACK_PUSHX(stack, ADDTAGS_AFTER_UNION_RIGHT);
+		STACK_PUSHX(stack, int, right_tag);
+		STACK_PUSHX(stack, int, left_tag);
+		STACK_PUSHX(stack, voidptr, regset);
+		STACK_PUSHX(stack, int, regset[0] >= 0);
+		STACK_PUSHX(stack, voidptr, node);
+		STACK_PUSHX(stack, voidptr, right);
+		STACK_PUSHX(stack, voidptr, left);
+		STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_RIGHT);
 
 		/* Process right child. */
-		STACK_PUSHX(stack, right);
-		STACK_PUSHX(stack, ADDTAGS_RECURSE);
+		STACK_PUSHX(stack, voidptr, right);
+		STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
 
 		/* After processing left child. */
-		STACK_PUSHX(stack, ADDTAGS_AFTER_UNION_LEFT);
+		STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_LEFT);
 
 		/* Process left child. */
-		STACK_PUSHX(stack, left);
-		STACK_PUSHX(stack, ADDTAGS_RECURSE);
+		STACK_PUSHX(stack, voidptr, left);
+		STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
 
 		/* Regset is not empty, so add a tag here. */
 		if (regset[0] >= 0)
@@ -1795,25 +1948,20 @@ tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
 		    if (!first_pass)
 		      {
 			int i;
-			status = tre_add_tag(mem, node, tag, 0 /*left*/);
+			status = tre_add_tag_left(mem, node, tag);
 			tnfa->tag_directions[tag] = direction;
-			/* Go through the regset and set submatch data for
-			   submatches that are using this tag. */
-			for (i = 0; regset[i] >= 0; i++)
+			if (minimal_tag >= 0)
 			  {
-			    int id = regset[i] >> 1;
-			    int start = !(regset[i] & 1);
-			    DPRINT(("  Using tag %d for %s offset of "
-				    "submatch %d\n", tag,
-				    start ? "start" : "end", id));
-			    if (start)
-			      tnfa->submatch_data[id].so_tag = tag;
-			    else
-			      tnfa->submatch_data[id].eo_tag = tag;
+			    for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+			    tnfa->minimal_tags[i] = tag;
+			    tnfa->minimal_tags[i + 1] = minimal_tag;
+			    tnfa->minimal_tags[i + 2] = -1;
+			    minimal_tag = -1;
+			    num_minimals++;
 			  }
+			tre_purge_regset(regset, tnfa, tag);
 		      }
 
-		    DPRINT(("  num_tags++\n"));
 		    regset[0] = -1;
 		    tag = next_tag;
 		    num_tags++;
@@ -1845,43 +1993,52 @@ tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
 
 	case ADDTAGS_AFTER_ITERATION:
 	  {
+	    int minimal = 0;
 	    int enter_tag;
-	    node = tre_stack_pop(stack);
+	    node = tre_stack_pop_voidptr(stack);
 	    if (first_pass)
+	      {
 		node->num_tags = ((tre_iteration_t *)node->obj)->arg->num_tags
-		  + (int)tre_stack_pop(stack);
+		  + tre_stack_pop_int(stack);
+		minimal_tag = -1;
+	      }
 	    else
-		enter_tag = (int)tre_stack_pop(stack);
+	      {
+		minimal = tre_stack_pop_int(stack);
+		enter_tag = tre_stack_pop_int(stack);
+		if (minimal)
+		  minimal_tag = enter_tag;
+	      }
 
-	    DPRINT(("After iteration\n"));
-	    direction = TRE_TAG_MAXIMIZE;
+	    if (!first_pass)
+	      {
+		if (minimal)
+		  direction = TRE_TAG_MINIMIZE;
+		else
+		  direction = TRE_TAG_MAXIMIZE;
+	      }
 	    break;
 	  }
 
 	case ADDTAGS_AFTER_CAT_LEFT:
 	  {
-	    int new_tag = (int)tre_stack_pop(stack);
-	    next_tag = (int)tre_stack_pop(stack);
-	    DPRINT(("After cat left, tag = %d, next_tag = %d\n",
-		    tag, next_tag));
+	    int new_tag = tre_stack_pop_int(stack);
+	    next_tag = tre_stack_pop_int(stack);
 	    if (new_tag >= 0)
 	      {
-		DPRINT(("  Setting tag to %d\n", new_tag));
 		tag = new_tag;
 	      }
 	    break;
 	  }
 
 	case ADDTAGS_AFTER_CAT_RIGHT:
-	  DPRINT(("After cat right\n"));
-	  node = tre_stack_pop(stack);
+	  node = tre_stack_pop_voidptr(stack);
 	  if (first_pass)
 	    node->num_tags = ((tre_catenation_t *)node->obj)->left->num_tags
 	      + ((tre_catenation_t *)node->obj)->right->num_tags;
 	  break;
 
 	case ADDTAGS_AFTER_UNION_LEFT:
-	  DPRINT(("After union left\n"));
 	  /* Lift the bottom of the `regset' array so that when processing
 	     the right operand the items currently in the array are
 	     invisible.	 The original bottom was saved at ADDTAGS_UNION and
@@ -1893,20 +2050,19 @@ tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
 	case ADDTAGS_AFTER_UNION_RIGHT:
 	  {
 	    int added_tags, tag_left, tag_right;
-	    tre_ast_node_t *left = tre_stack_pop(stack);
-	    tre_ast_node_t *right = tre_stack_pop(stack);
-	    DPRINT(("After union right\n"));
-	    node = tre_stack_pop(stack);
-	    added_tags = (int)tre_stack_pop(stack);
+	    tre_ast_node_t *left = tre_stack_pop_voidptr(stack);
+	    tre_ast_node_t *right = tre_stack_pop_voidptr(stack);
+	    node = tre_stack_pop_voidptr(stack);
+	    added_tags = tre_stack_pop_int(stack);
 	    if (first_pass)
 	      {
 		node->num_tags = ((tre_union_t *)node->obj)->left->num_tags
 		  + ((tre_union_t *)node->obj)->right->num_tags + added_tags
 		  + ((node->num_submatches > 0) ? 2 : 0);
 	      }
-	    regset = tre_stack_pop(stack);
-	    tag_left = (int)tre_stack_pop(stack);
-	    tag_right = (int)tre_stack_pop(stack);
+	    regset = tre_stack_pop_voidptr(stack);
+	    tag_left = tre_stack_pop_int(stack);
+	    tag_right = tre_stack_pop_int(stack);
 
 	    /* Add tags after both children, the left child gets a smaller
 	       tag than the right child.  This guarantees that we prefer
@@ -1921,12 +2077,11 @@ tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
 	      {
 		if (!first_pass)
 		  {
-		    status = tre_add_tag(mem, left, tag_left, 1 /*right*/);
-		    tnfa->tag_directions[tag] = TRE_TAG_MAXIMIZE;
-		    status = tre_add_tag(mem, right, tag_right, 1 /*right*/);
-		    tnfa->tag_directions[tag] = TRE_TAG_MAXIMIZE;
+		    status = tre_add_tag_right(mem, left, tag_left);
+		    tnfa->tag_directions[tag_left] = TRE_TAG_MAXIMIZE;
+		    status = tre_add_tag_right(mem, right, tag_right);
+		    tnfa->tag_directions[tag_right] = TRE_TAG_MAXIMIZE;
 		  }
-		DPRINT(("  num_tags += 2\n"));
 		num_tags += 2;
 	      }
 	    direction = TRE_TAG_MAXIMIZE;
@@ -1941,30 +2096,23 @@ tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
     } /* end while(tre_stack_num_objects(stack) > bottom) */
 
   if (!first_pass)
+    tre_purge_regset(regset, tnfa, tag);
+
+  if (!first_pass && minimal_tag >= 0)
     {
       int i;
-      /* Go through the regset and set submatch data for
-	 submatches that are using this tag. */
-      for (i = 0; regset[i] >= 0; i++)
-	{
-	  int id = regset[i] >> 1;
-	  int start = !(regset[i] & 1);
-	  DPRINT(("  Using tag %d for %s offset of "
-		  "submatch %d\n", num_tags,
-		  start ? "start" : "end", id));
-	  if (start)
-	    tnfa->submatch_data[id].so_tag = num_tags;
-	  else
-	    tnfa->submatch_data[id].eo_tag = num_tags;
-	}
+      for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+      tnfa->minimal_tags[i] = tag;
+      tnfa->minimal_tags[i + 1] = minimal_tag;
+      tnfa->minimal_tags[i + 2] = -1;
+      minimal_tag = -1;
+      num_minimals++;
     }
 
-  DPRINT(("tre_add_tags: %s complete.  Number of tags %d.\n",
-	  first_pass? "First pass" : "Second pass", num_tags));
-
   assert(tree->num_tags == num_tags);
   tnfa->end_tag = num_tags;
   tnfa->num_tags = num_tags;
+  tnfa->num_minimals = num_minimals;
   xfree(orig_regset);
   xfree(parents);
   xfree(saved_states);
@@ -1998,8 +2146,8 @@ tre_copy_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
   tre_ast_node_t **result = copy;
   tre_copyast_symbol_t symbol;
 
-  STACK_PUSH(stack, ast);
-  STACK_PUSH(stack, COPY_RECURSE);
+  STACK_PUSH(stack, voidptr, ast);
+  STACK_PUSH(stack, int, COPY_RECURSE);
 
   while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
     {
@@ -2007,14 +2155,14 @@ tre_copy_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
       if (status != REG_OK)
 	break;
 
-      symbol = (tre_copyast_symbol_t)tre_stack_pop(stack);
+      symbol = (tre_copyast_symbol_t)tre_stack_pop_int(stack);
       switch (symbol)
 	{
 	case COPY_SET_RESULT_PTR:
-	  result = tre_stack_pop(stack);
+	  result = tre_stack_pop_voidptr(stack);
 	  break;
 	case COPY_RECURSE:
-	  node = tre_stack_pop(stack);
+	  node = tre_stack_pop_voidptr(stack);
 	  switch (node->type)
 	    {
 	    case LITERAL:
@@ -2055,52 +2203,53 @@ tre_copy_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
 	    case UNION:
 	      {
 		tre_union_t *uni = node->obj;
-		tre_union_t *copy;
+		tre_union_t *tmp;
 		*result = tre_ast_new_union(mem, uni->left, uni->right);
 		if (*result == NULL)
 		  {
 		    status = REG_ESPACE;
 		    break;
 		  }
-		copy = (*result)->obj;
-		result = &copy->left;
-		STACK_PUSHX(stack, uni->right);
-		STACK_PUSHX(stack, COPY_RECURSE);
-		STACK_PUSHX(stack, &copy->right);
-		STACK_PUSHX(stack, COPY_SET_RESULT_PTR);
-		STACK_PUSHX(stack, uni->left);
-		STACK_PUSHX(stack, COPY_RECURSE);
+		tmp = (*result)->obj;
+		result = &tmp->left;
+		STACK_PUSHX(stack, voidptr, uni->right);
+		STACK_PUSHX(stack, int, COPY_RECURSE);
+		STACK_PUSHX(stack, voidptr, &tmp->right);
+		STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR);
+		STACK_PUSHX(stack, voidptr, uni->left);
+		STACK_PUSHX(stack, int, COPY_RECURSE);
 		break;
 	      }
 	    case CATENATION:
 	      {
 		tre_catenation_t *cat = node->obj;
-		tre_catenation_t *copy;
+		tre_catenation_t *tmp;
 		*result = tre_ast_new_catenation(mem, cat->left, cat->right);
 		if (*result == NULL)
 		  {
 		    status = REG_ESPACE;
 		    break;
 		  }
-		copy = (*result)->obj;
-		copy->left = NULL;
-		copy->right = NULL;
-		result = &copy->left;
-
-		STACK_PUSHX(stack, cat->right);
-		STACK_PUSHX(stack, COPY_RECURSE);
-		STACK_PUSHX(stack, &copy->right);
-		STACK_PUSHX(stack, COPY_SET_RESULT_PTR);
-		STACK_PUSHX(stack, cat->left);
-		STACK_PUSHX(stack, COPY_RECURSE);
+		tmp = (*result)->obj;
+		tmp->left = NULL;
+		tmp->right = NULL;
+		result = &tmp->left;
+
+		STACK_PUSHX(stack, voidptr, cat->right);
+		STACK_PUSHX(stack, int, COPY_RECURSE);
+		STACK_PUSHX(stack, voidptr, &tmp->right);
+		STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR);
+		STACK_PUSHX(stack, voidptr, cat->left);
+		STACK_PUSHX(stack, int, COPY_RECURSE);
 		break;
 	      }
 	    case ITERATION:
 	      {
 		tre_iteration_t *iter = node->obj;
-		STACK_PUSHX(stack, iter->arg);
-		STACK_PUSHX(stack, COPY_RECURSE);
-		*result = tre_ast_new_iter(mem, iter->arg, iter->min, iter->max);
+		STACK_PUSHX(stack, voidptr, iter->arg);
+		STACK_PUSHX(stack, int, COPY_RECURSE);
+		*result = tre_ast_new_iter(mem, iter->arg, iter->min,
+					   iter->max, iter->minimal);
 		if (*result == NULL)
 		  {
 		    status = REG_ESPACE;
@@ -2138,11 +2287,10 @@ tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
   int pos_add = 0;
   int pos_add_total = 0;
   int max_pos = 0;
-  /* Approximate parameter nesting level. */
   int iter_depth = 0;
 
-  STACK_PUSHR(stack, ast);
-  STACK_PUSHR(stack, EXPAND_RECURSE);
+  STACK_PUSHR(stack, voidptr, ast);
+  STACK_PUSHR(stack, int, EXPAND_RECURSE);
   while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
     {
       tre_ast_node_t *node;
@@ -2151,10 +2299,8 @@ tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
       if (status != REG_OK)
 	break;
 
-      DPRINT(("pos_add %d\n", pos_add));
-
-      symbol = (tre_expand_ast_symbol_t)tre_stack_pop(stack);
-      node = tre_stack_pop(stack);
+      symbol = (tre_expand_ast_symbol_t)tre_stack_pop_int(stack);
+      node = tre_stack_pop_voidptr(stack);
       switch (symbol)
 	{
 	case EXPAND_RECURSE:
@@ -2174,36 +2320,35 @@ tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
 	    case UNION:
 	      {
 		tre_union_t *uni = node->obj;
-		STACK_PUSHX(stack, uni->right);
-		STACK_PUSHX(stack, EXPAND_RECURSE);
-		STACK_PUSHX(stack, uni->left);
-		STACK_PUSHX(stack, EXPAND_RECURSE);
+		STACK_PUSHX(stack, voidptr, uni->right);
+		STACK_PUSHX(stack, int, EXPAND_RECURSE);
+		STACK_PUSHX(stack, voidptr, uni->left);
+		STACK_PUSHX(stack, int, EXPAND_RECURSE);
 		break;
 	      }
 	    case CATENATION:
 	      {
 		tre_catenation_t *cat = node->obj;
-		STACK_PUSHX(stack, cat->right);
-		STACK_PUSHX(stack, EXPAND_RECURSE);
-		STACK_PUSHX(stack, cat->left);
-		STACK_PUSHX(stack, EXPAND_RECURSE);
+		STACK_PUSHX(stack, voidptr, cat->right);
+		STACK_PUSHX(stack, int, EXPAND_RECURSE);
+		STACK_PUSHX(stack, voidptr, cat->left);
+		STACK_PUSHX(stack, int, EXPAND_RECURSE);
 		break;
 	      }
 	    case ITERATION:
 	      {
 		tre_iteration_t *iter = node->obj;
-		STACK_PUSHX(stack, pos_add);
-		STACK_PUSHX(stack, node);
-		STACK_PUSHX(stack, EXPAND_AFTER_ITER);
-		STACK_PUSHX(stack, iter->arg);
-		STACK_PUSHX(stack, EXPAND_RECURSE);
+		STACK_PUSHX(stack, int, pos_add);
+		STACK_PUSHX(stack, voidptr, node);
+		STACK_PUSHX(stack, int, EXPAND_AFTER_ITER);
+		STACK_PUSHX(stack, voidptr, iter->arg);
+		STACK_PUSHX(stack, int, EXPAND_RECURSE);
 		/* If we are going to expand this node at EXPAND_AFTER_ITER
 		   then don't increase the `pos' fields of the nodes now, it
 		   will get done when expanding. */
 		if (iter->min > 1 || iter->max > 1)
 		  pos_add = 0;
 		iter_depth++;
-		DPRINT(("iter\n"));
 		break;
 	      }
 	    default:
@@ -2215,23 +2360,22 @@ tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
 	  {
 	    tre_iteration_t *iter = node->obj;
 	    int pos_add_last;
-	    pos_add = (int)tre_stack_pop(stack);
+	    pos_add = tre_stack_pop_int(stack);
 	    pos_add_last = pos_add;
 	    if (iter->min > 1 || iter->max > 1)
 	      {
 		tre_ast_node_t *seq1 = NULL, *seq2 = NULL;
-		int i;
+		int j;
 		int pos_add_save = pos_add;
 
 		/* Create a catenated sequence of copies of the node. */
-		for (i = 0; i < iter->min; i++)
+		for (j = 0; j < iter->min; j++)
 		  {
 		    tre_ast_node_t *copy;
 		    /* Remove tags from all but the last copy. */
-		    int flags = ((i + 1 < iter->min)
+		    int flags = ((j + 1 < iter->min)
 				 ? COPY_REMOVE_TAGS
 				 : COPY_MAXIMIZE_FIRST_TAG);
-		    DPRINT(("  pos_add %d\n", pos_add));
 		    pos_add_save = pos_add;
 		    status = tre_copy_ast(mem, stack, iter->arg, flags,
 					  &pos_add, tag_directions, &copy,
@@ -2254,13 +2398,13 @@ tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
 					  &pos_add, NULL, &seq2, &max_pos);
 		    if (status != REG_OK)
 		      return status;
-		    seq2 = tre_ast_new_iter(mem, seq2, 0, -1);
+		    seq2 = tre_ast_new_iter(mem, seq2, 0, -1, 0);
 		    if (seq2 == NULL)
 		      return REG_ESPACE;
 		  }
 		else
 		  {
-		    for (i = iter->min; i < iter->max; i++)
+		    for (j = iter->min; j < iter->max; j++)
 		      {
 			tre_ast_node_t *tmp, *copy;
 			pos_add_save = pos_add;
@@ -2316,12 +2460,6 @@ tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
   if (max_pos > *position)
     *position = max_pos;
 
-#ifdef TRE_DEBUG
-  DPRINT(("Expanded AST:\n"));
-  tre_ast_print(ast);
-  DPRINT(("*position %d, max_pos %d\n", *position, max_pos));
-#endif
-
   return status;
 }
 
@@ -2441,7 +2579,8 @@ tre_set_union(tre_mem_t mem, tre_pos_and_tags_t *set1, tre_pos_and_tags_t *set2,
    set to the number of tags seen on the path. */
 static reg_errcode_t
 tre_match_empty(tre_stack_t *stack, tre_ast_node_t *node, int *tags,
-		int *assertions, int *num_tags_seen)
+		int *assertions, int *params, int *num_tags_seen,
+		int *params_seen)
 {
   tre_literal_t *lit;
   tre_union_t *uni;
@@ -2453,12 +2592,12 @@ tre_match_empty(tre_stack_t *stack, tre_ast_node_t *node, int *tags,
   if (num_tags_seen)
     *num_tags_seen = 0;
 
-  status = tre_stack_push(stack, node);
+  status = tre_stack_push_voidptr(stack, node);
 
   /* Walk through the tree recursively. */
   while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
     {
-      node = tre_stack_pop(stack);
+      node = tre_stack_pop_voidptr(stack);
 
       switch (node->type)
 	{
@@ -2505,9 +2644,9 @@ tre_match_empty(tre_stack_t *stack, tre_ast_node_t *node, int *tags,
 	     right subexpression. */
 	  uni = (tre_union_t *)node->obj;
 	  if (uni->left->nullable)
-	    STACK_PUSHX(stack, uni->left)
+	    STACK_PUSHX(stack, voidptr, uni->left)
 	  else if (uni->right->nullable)
-	    STACK_PUSHX(stack, uni->right)
+	    STACK_PUSHX(stack, voidptr, uni->right)
 	  else
 	    assert(0);
 	  break;
@@ -2517,8 +2656,8 @@ tre_match_empty(tre_stack_t *stack, tre_ast_node_t *node, int *tags,
 	  cat = (tre_catenation_t *)node->obj;
 	  assert(cat->left->nullable);
 	  assert(cat->right->nullable);
-	  STACK_PUSHX(stack, cat->left);
-	  STACK_PUSHX(stack, cat->right);
+	  STACK_PUSHX(stack, voidptr, cat->left);
+	  STACK_PUSHX(stack, voidptr, cat->right);
 	  break;
 
 	case ITERATION:
@@ -2526,7 +2665,7 @@ tre_match_empty(tre_stack_t *stack, tre_ast_node_t *node, int *tags,
 	     all, so we go through the argument if possible. */
 	  iter = (tre_iteration_t *)node->obj;
 	  if (iter->arg->nullable)
-	    STACK_PUSHX(stack, iter->arg);
+	    STACK_PUSHX(stack, voidptr, iter->arg);
 	  break;
 
 	default:
@@ -2554,16 +2693,16 @@ tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree)
 {
   int bottom = tre_stack_num_objects(stack);
 
-  STACK_PUSHR(stack, tree);
-  STACK_PUSHR(stack, NFL_RECURSE);
+  STACK_PUSHR(stack, voidptr, tree);
+  STACK_PUSHR(stack, int, NFL_RECURSE);
 
   while (tre_stack_num_objects(stack) > bottom)
     {
       tre_nfl_stack_symbol_t symbol;
       tre_ast_node_t *node;
 
-      symbol = (tre_nfl_stack_symbol_t) tre_stack_pop(stack);
-      node = tre_stack_pop(stack);
+      symbol = (tre_nfl_stack_symbol_t)tre_stack_pop_int(stack);
+      node = tre_stack_pop_voidptr(stack);
       switch (symbol)
 	{
 	case NFL_RECURSE:
@@ -2583,13 +2722,13 @@ tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree)
 		      return REG_ESPACE;
 		    node->lastpos = tre_set_one(mem, lit->position, 0,
 						TRE_CHAR_MAX, 0, NULL,
-						lit->code_max);
+						(int)lit->code_max);
 		    if (!node->lastpos)
 		      return REG_ESPACE;
 		  }
 		else if (lit->code_min < 0)
 		  {
-		    /* Tags, empty strings and zero width assertions:
+		    /* Tags, empty strings, params, and zero width assertions:
 		       nullable = true, firstpos = {}, and lastpos = {}. */
 		    node->nullable = 1;
 		    node->firstpos = tre_set_empty(mem);
@@ -2605,13 +2744,14 @@ tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree)
 		       lastpos = {i}. */
 		    node->nullable = 0;
 		    node->firstpos =
-		      tre_set_one(mem, lit->position, lit->code_min,
-				  lit->code_max, 0, NULL, -1);
+		      tre_set_one(mem, lit->position, (int)lit->code_min,
+				  (int)lit->code_max, 0, NULL, -1);
 		    if (!node->firstpos)
 		      return REG_ESPACE;
 		    node->lastpos = tre_set_one(mem, lit->position,
-						lit->code_min, lit->code_max,
-						lit->class, lit->neg_classes,
+						(int)lit->code_min,
+						(int)lit->code_max,
+						lit->u.class, lit->neg_classes,
 						-1);
 		    if (!node->lastpos)
 		      return REG_ESPACE;
@@ -2622,32 +2762,32 @@ tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree)
 	    case UNION:
 	      /* Compute the attributes for the two subtrees, and after that
 		 for this node. */
-	      STACK_PUSHR(stack, node);
-	      STACK_PUSHR(stack, NFL_POST_UNION);
-	      STACK_PUSHR(stack, ((tre_union_t *)node->obj)->right);
-	      STACK_PUSHR(stack, NFL_RECURSE);
-	      STACK_PUSHR(stack, ((tre_union_t *)node->obj)->left);
-	      STACK_PUSHR(stack, NFL_RECURSE);
+	      STACK_PUSHR(stack, voidptr, node);
+	      STACK_PUSHR(stack, int, NFL_POST_UNION);
+	      STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->right);
+	      STACK_PUSHR(stack, int, NFL_RECURSE);
+	      STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->left);
+	      STACK_PUSHR(stack, int, NFL_RECURSE);
 	      break;
 
 	    case CATENATION:
 	      /* Compute the attributes for the two subtrees, and after that
 		 for this node. */
-	      STACK_PUSHR(stack, node);
-	      STACK_PUSHR(stack, NFL_POST_CATENATION);
-	      STACK_PUSHR(stack, ((tre_catenation_t *)node->obj)->right);
-	      STACK_PUSHR(stack, NFL_RECURSE);
-	      STACK_PUSHR(stack, ((tre_catenation_t *)node->obj)->left);
-	      STACK_PUSHR(stack, NFL_RECURSE);
+	      STACK_PUSHR(stack, voidptr, node);
+	      STACK_PUSHR(stack, int, NFL_POST_CATENATION);
+	      STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->right);
+	      STACK_PUSHR(stack, int, NFL_RECURSE);
+	      STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->left);
+	      STACK_PUSHR(stack, int, NFL_RECURSE);
 	      break;
 
 	    case ITERATION:
 	      /* Compute the attributes for the subtree, and after that for
 		 this node. */
-	      STACK_PUSHR(stack, node);
-	      STACK_PUSHR(stack, NFL_POST_ITERATION);
-	      STACK_PUSHR(stack, ((tre_iteration_t *)node->obj)->arg);
-	      STACK_PUSHR(stack, NFL_RECURSE);
+	      STACK_PUSHR(stack, voidptr, node);
+	      STACK_PUSHR(stack, int, NFL_POST_ITERATION);
+	      STACK_PUSHR(stack, voidptr, ((tre_iteration_t *)node->obj)->arg);
+	      STACK_PUSHR(stack, int, NFL_RECURSE);
 	      break;
 	    }
 	  break; /* end case: NFL_RECURSE */
@@ -2682,7 +2822,8 @@ tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree)
 
 	case NFL_POST_CATENATION:
 	  {
-	    int num_tags, *tags, assertions;
+	    int num_tags, *tags, assertions, params_seen;
+	    int *params;
 	    reg_errcode_t status;
 	    tre_catenation_t *cat = node->obj;
 	    node->nullable = cat->left->nullable && cat->right->nullable;
@@ -2691,9 +2832,11 @@ tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree)
 	    if (cat->left->nullable)
 	      {
 		/* The left side matches the empty string.  Make a first pass
-		   with tre_match_empty() to get the number of tags. */
+		   with tre_match_empty() to get the number of tags and
+		   parameters. */
 		status = tre_match_empty(stack, cat->left,
-					 NULL, NULL, &num_tags);
+					 NULL, NULL, NULL, &num_tags,
+					 &params_seen);
 		if (status != REG_OK)
 		  return status;
 		/* Allocate arrays for the tags and parameters. */
@@ -2703,9 +2846,9 @@ tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree)
 		tags[0] = -1;
 		assertions = 0;
 		/* Second pass with tre_mach_empty() to get the list of
-		   tags. */
+		   tags and parameters. */
 		status = tre_match_empty(stack, cat->left, tags,
-					 &assertions, NULL);
+					 &assertions, params, NULL, NULL);
 		if (status != REG_OK)
 		  {
 		    xfree(tags);
@@ -2727,9 +2870,11 @@ tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree)
 	    if (cat->right->nullable)
 	      {
 		/* The right side matches the empty string.  Make a first pass
-		   with tre_match_empty() to get the number of tags. */
+		   with tre_match_empty() to get the number of tags and
+		   parameters. */
 		status = tre_match_empty(stack, cat->right,
-					 NULL, NULL, &num_tags);
+					 NULL, NULL, NULL, &num_tags,
+					 &params_seen);
 		if (status != REG_OK)
 		  return status;
 		/* Allocate arrays for the tags and parameters. */
@@ -2739,9 +2884,9 @@ tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree)
 		tags[0] = -1;
 		assertions = 0;
 		/* Second pass with tre_mach_empty() to get the list of
-		   tags. */
+		   tags and parameters. */
 		status = tre_match_empty(stack, cat->right, tags,
-					 &assertions, NULL);
+					 &assertions, params, NULL, NULL);
 		if (status != REG_OK)
 		  {
 		    xfree(tags);
@@ -2814,7 +2959,6 @@ tre_make_trans(tre_pos_and_tags_t *p1, tre_pos_and_tags_t *p2,
 		   detect it here. */
 		if (trans->state_id == p2->position)
 		  {
-		    DPRINT(("*"));
 		    break;
 		  }
 #endif
@@ -2903,39 +3047,6 @@ tre_make_trans(tre_pos_and_tags_t *p1, tre_pos_and_tags_t *p2,
 		trans->tags[l] = -1;
 	      }
 
-
-#ifdef TRE_DEBUG
-	    {
-	      int *tags;
-
-	      DPRINT(("	 %2d -> %2d on %3d", p1->position, p2->position,
-		      p1->code_min));
-	      if (p1->code_max != p1->code_min)
-		DPRINT(("-%3d", p1->code_max));
-	      tags = trans->tags;
-	      if (tags)
-		{
-		  DPRINT((", tags ["));
-		  while (*tags >= 0)
-		    {
-		      DPRINT(("%d", *tags));
-		      tags++;
-		      if (*tags >= 0)
-			DPRINT((","));
-		    }
-		  DPRINT(("]"));
-		}
-	      if (trans->assertions)
-		DPRINT((", assert %d", trans->assertions));
-	      if (trans->assertions & ASSERT_BACKREF)
-		DPRINT((", backref %d", trans->u.backref));
-	      else if (trans->class)
-		DPRINT((", class %ld", (long)trans->class));
-	      if (trans->neg_classes)
-		DPRINT((", neg_classes %p", trans->neg_classes));
-	      DPRINT(("\n"));
-	    }
-#endif /* TRE_DEBUG */
 	    p2++;
 	  }
 	p1++;
@@ -3017,63 +3128,18 @@ tre_ast_to_tnfa(tre_ast_node_t *node, tre_tnfa_transition_t *transitions,
 }
 
 
-static void
-tre_free(regex_t *preg)
-{
-  tre_tnfa_t *tnfa;
-  unsigned int i;
-  tre_tnfa_transition_t *trans;
-
-  tnfa = (void *)preg->TRE_REGEX_T_FIELD;
-  if (!tnfa)
-    return;
-
-  for (i = 0; i < tnfa->num_transitions; i++)
-    if (tnfa->transitions[i].state)
-      {
-	if (tnfa->transitions[i].tags)
-	  xfree(tnfa->transitions[i].tags);
-	if (tnfa->transitions[i].neg_classes)
-	  xfree(tnfa->transitions[i].neg_classes);
-      }
-  if (tnfa->transitions)
-    xfree(tnfa->transitions);
-
-  if (tnfa->initial)
-    {
-      for (trans = tnfa->initial; trans->state; trans++)
-	{
-	  if (trans->tags)
-	    xfree(trans->tags);
-	}
-      xfree(tnfa->initial);
-    }
-
-  if (tnfa->submatch_data)
-    {
-      for (i = 0; i < tnfa->num_submatches; i++)
-	if (tnfa->submatch_data[i].parents)
-	  xfree(tnfa->submatch_data[i].parents);
-      xfree(tnfa->submatch_data);
-    }
-
-  if (tnfa->tag_directions)
-    xfree(tnfa->tag_directions);
-  xfree(tnfa);
-}
-
-
 #define ERROR_EXIT(err)		  \
   do				  \
     {				  \
       errcode = err;		  \
-      if (1) goto error_exit;	  \
+      if (/*CONSTCOND*/1)	  \
+      	goto error_exit;	  \
     }				  \
- while (0)
+ while (/*CONSTCOND*/0)
 
 
-static int
-tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags)
+int
+regcomp(regex_t *preg, const char *regex, int cflags)
 {
   tre_stack_t *stack;
   tre_ast_node_t *tree, *tmp_ast_l, *tmp_ast_r;
@@ -3108,16 +3174,19 @@ tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags)
   parse_ctx.mem = mem;
   parse_ctx.stack = stack;
   parse_ctx.re = regex;
-  parse_ctx.len = n;
   parse_ctx.cflags = cflags;
   parse_ctx.max_backref = -1;
-  DPRINT(("tre_compile: parsing '%.*" STRF "'\n", n, regex));
   errcode = tre_parse(&parse_ctx);
   if (errcode != REG_OK)
     ERROR_EXIT(errcode);
-  preg->re_nsub = preg->__nsub2 = parse_ctx.submatch_id - 1;
+  preg->re_nsub = parse_ctx.submatch_id - 1;
   tree = parse_ctx.result;
 
+  /* Back references and approximate matching cannot currently be used
+     in the same regexp. */
+  if (parse_ctx.max_backref >= 0 && parse_ctx.have_approx)
+    ERROR_EXIT(REG_BADPAT);
+
 #ifdef TRE_DEBUG
   tre_ast_print(tree);
 #endif /* TRE_DEBUG */
@@ -3131,21 +3200,18 @@ tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags)
   if (tnfa == NULL)
     ERROR_EXIT(REG_ESPACE);
   tnfa->have_backrefs = parse_ctx.max_backref >= 0;
+  tnfa->have_approx = parse_ctx.have_approx;
   tnfa->num_submatches = parse_ctx.submatch_id;
 
   /* Set up tags for submatch addressing.  If REG_NOSUB is set and the
      regexp does not have back references, this can be skipped. */
   if (tnfa->have_backrefs || !(cflags & REG_NOSUB))
     {
-      DPRINT(("tre_compile: setting up tags\n"));
 
       /* Figure out how many tags we will need. */
       errcode = tre_add_tags(NULL, stack, tree, tnfa);
       if (errcode != REG_OK)
 	ERROR_EXIT(errcode);
-#ifdef TRE_DEBUG
-      tre_ast_print(tree);
-#endif /* TRE_DEBUG */
 
       if (tnfa->num_tags > 0)
 	{
@@ -3157,8 +3223,13 @@ tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags)
 	  memset(tag_directions, -1,
 		 sizeof(*tag_directions) * (tnfa->num_tags + 1));
 	}
+      tnfa->minimal_tags = xcalloc((unsigned)tnfa->num_tags * 2 + 1,
+				   sizeof(tnfa->minimal_tags));
+      if (tnfa->minimal_tags == NULL)
+	ERROR_EXIT(REG_ESPACE);
 
-      submatch_data = xcalloc(parse_ctx.submatch_id, sizeof(*submatch_data));
+      submatch_data = xcalloc((unsigned)parse_ctx.submatch_id,
+			      sizeof(*submatch_data));
       if (submatch_data == NULL)
 	ERROR_EXIT(REG_ESPACE);
       tnfa->submatch_data = submatch_data;
@@ -3167,20 +3238,11 @@ tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags)
       if (errcode != REG_OK)
 	ERROR_EXIT(errcode);
 
-#ifdef TRE_DEBUG
-      for (i = 0; i < parse_ctx.submatch_id; i++)
-	DPRINT(("pmatch[%d] = {t%d, t%d}\n",
-		i, submatch_data[i].so_tag, submatch_data[i].eo_tag));
-      for (i = 0; i < tnfa->num_tags; i++)
-	DPRINT(("t%d is %s\n", i,
-		tag_directions[i] == TRE_TAG_MINIMIZE ?
-		"minimized" : "maximized"));
-#endif /* TRE_DEBUG */
     }
 
   /* Expand iteration nodes. */
   errcode = tre_expand_ast(mem, stack, tree, &parse_ctx.position,
-			   tag_directions, NULL);
+			   tag_directions, &tnfa->params_depth);
   if (errcode != REG_OK)
     ERROR_EXIT(errcode);
 
@@ -3197,11 +3259,6 @@ tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags)
   if (tree == NULL)
     ERROR_EXIT(REG_ESPACE);
 
-#ifdef TRE_DEBUG
-  tre_ast_print(tree);
-  DPRINT(("Number of states: %d\n", parse_ctx.position));
-#endif /* TRE_DEBUG */
-
   errcode = tre_compute_nfl(mem, stack, tree);
   if (errcode != REG_OK)
     ERROR_EXIT(errcode);
@@ -3225,49 +3282,27 @@ tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags)
       add += counts[i] + 1;
       counts[i] = 0;
     }
-  transitions = xcalloc(add + 1, sizeof(*transitions));
+  transitions = xcalloc((unsigned)add + 1, sizeof(*transitions));
   if (transitions == NULL)
     ERROR_EXIT(REG_ESPACE);
   tnfa->transitions = transitions;
   tnfa->num_transitions = add;
 
-  DPRINT(("Converting to TNFA:\n"));
   errcode = tre_ast_to_tnfa(tree, transitions, counts, offs);
   if (errcode != REG_OK)
     ERROR_EXIT(errcode);
 
+  tnfa->firstpos_chars = NULL;
+
   p = tree->firstpos;
   i = 0;
   while (p->position >= 0)
     {
       i++;
-
-#ifdef TRE_DEBUG
-      {
-	int *tags;
-	DPRINT(("initial: %d", p->position));
-	tags = p->tags;
-	if (tags != NULL)
-	  {
-	    if (*tags >= 0)
-	      DPRINT(("/"));
-	    while (*tags >= 0)
-	      {
-		DPRINT(("%d", *tags));
-		tags++;
-		if (*tags >= 0)
-		  DPRINT((","));
-	      }
-	  }
-	DPRINT((", assert %d", p->assertions));
-	DPRINT(("\n"));
-      }
-#endif /* TRE_DEBUG */
-
       p++;
     }
 
-  initial = xcalloc(i + 1, sizeof(tre_tnfa_transition_t));
+  initial = xcalloc((unsigned)i + 1, sizeof(tre_tnfa_transition_t));
   if (initial == NULL)
     ERROR_EXIT(REG_ESPACE);
   tnfa->initial = initial;
@@ -3278,7 +3313,7 @@ tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags)
       initial[i].state = transitions + offs[p->position];
       initial[i].state_id = p->position;
       initial[i].tags = NULL;
-      /* Copy the arrays p->tags, they are allocated
+      /* Copy the arrays p->tags, and p->params, they are allocated
 	 from a tre_mem object. */
       if (p->tags)
 	{
@@ -3299,8 +3334,6 @@ tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags)
   tnfa->num_states = parse_ctx.position;
   tnfa->cflags = cflags;
 
-  DPRINT(("final state %p\n", (void *)tnfa->final));
-
   tre_mem_destroy(mem);
   tre_stack_destroy(stack);
   xfree(counts);
@@ -3319,44 +3352,58 @@ tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags)
   if (offs != NULL)
     xfree(offs);
   preg->TRE_REGEX_T_FIELD = (void *)tnfa;
-  tre_free(preg);
+  regfree(preg);
   return errcode;
 }
 
 
-/***********************************************************************
- from regcomp.c
-***********************************************************************/
 
-int
-regcomp(regex_t *preg, const char *regex, int cflags)
+
+void
+regfree(regex_t *preg)
 {
-  int ret;
-  tre_char_t *wregex;
-  size_t n = strlen(regex);
+  tre_tnfa_t *tnfa;
+  unsigned int i;
+  tre_tnfa_transition_t *trans;
 
-  if (n+1 > SIZE_MAX/sizeof(tre_char_t))
-    return REG_ESPACE;
-  wregex = xmalloc(sizeof(tre_char_t) * (n + 1));
-  if (wregex == NULL)
-    return REG_ESPACE;
+  tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+  if (!tnfa)
+    return;
 
-  n = mbstowcs(wregex, regex, n+1);
-  if (n == (size_t)-1) {
-    xfree(wregex);
-    return REG_BADPAT;
-  }
+  for (i = 0; i < tnfa->num_transitions; i++)
+    if (tnfa->transitions[i].state)
+      {
+	if (tnfa->transitions[i].tags)
+	  xfree(tnfa->transitions[i].tags);
+	if (tnfa->transitions[i].neg_classes)
+	  xfree(tnfa->transitions[i].neg_classes);
+      }
+  if (tnfa->transitions)
+    xfree(tnfa->transitions);
 
-  ret = tre_compile(preg, wregex, n, cflags);
-  xfree(wregex);
+  if (tnfa->initial)
+    {
+      for (trans = tnfa->initial; trans->state; trans++)
+	{
+	  if (trans->tags)
+	    xfree(trans->tags);
+	}
+      xfree(tnfa->initial);
+    }
 
-  return ret;
-}
+  if (tnfa->submatch_data)
+    {
+      for (i = 0; i < tnfa->num_submatches; i++)
+	if (tnfa->submatch_data[i].parents)
+	  xfree(tnfa->submatch_data[i].parents);
+      xfree(tnfa->submatch_data);
+    }
 
-void
-regfree(regex_t *preg)
-{
-  tre_free(preg);
+  if (tnfa->tag_directions)
+    xfree(tnfa->tag_directions);
+  if (tnfa->firstpos_chars)
+    xfree(tnfa->firstpos_chars);
+  if (tnfa->minimal_tags)
+    xfree(tnfa->minimal_tags);
+  xfree(tnfa);
 }
-
-/* EOF */