tre-parse.c   [plain text]


/*
  tre-parse.c - Regexp parser

  This software is released under a BSD-style license.
  See the file LICENSE for details and copyright.

*/

/*
  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.
*/


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include <assert.h>
#include <limits.h>
#include <stddef.h>

#include "xmalloc.h"
#include "tre-mem.h"
#include "tre-ast.h"
#include "tre-stack.h"
#include "tre-parse.h"

/* BSD compatibility:
     Before looking up a collating symbol, check if the name matches in
     the character names (cnames) array; if so, use the corresponding
     character.

     Also set ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND, which will preserve
     the implementation choice that for ERE, a non-numeric character following
     a left brace that would normally be a bound, causes the left brace to be
     literal. */
#define BSD_COMPATIBILITY
#ifdef BSD_COMPATIBILITY
#include "cname.h"
#define ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND
#endif /* BSD_COMPATIBILITY */

/* Characters with special meanings in regexp syntax. */
#define CHAR_PIPE	   L'|'
#define CHAR_LPAREN	   L'('
#define CHAR_RPAREN	   L')'
#define CHAR_LBRACE	   L'{'
#define CHAR_RBRACE	   L'}'
#define CHAR_LBRACKET	   L'['
#define CHAR_RBRACKET	   L']'
#define CHAR_MINUS	   L'-'
#define CHAR_STAR	   L'*'
#define CHAR_QUESTIONMARK  L'?'
#define CHAR_PLUS	   L'+'
#define CHAR_PERIOD	   L'.'
#define CHAR_COLON	   L':'
#define CHAR_EQUAL	   L'='
#define CHAR_COMMA	   L','
#define CHAR_CARET	   L'^'
#define CHAR_DOLLAR	   L'$'
#define CHAR_BACKSLASH	   L'\\'
#define CHAR_HASH	   L'#'
#define CHAR_TILDE	   L'~'


/* 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 void
tre_expand_macro(const tre_char_t *regex, const tre_char_t *regex_end,
		 tre_char_t *buf, size_t buf_len)
{
  int i;

  buf[0] = 0;
  if (regex >= regex_end)
    return;

  for (i = 0; tre_macros[i].expansion; i++)
    {
      if (tre_macros[i].c == *regex)
	{
	  unsigned int j;
	  DPRINT(("Expanding macro '%c' => '%s'\n",
		  tre_macros[i].c, tre_macros[i].expansion));
	  for (j = 0; tre_macros[i].expansion[j] && j < buf_len; j++)
	    buf[j] = tre_macros[i].expansion[j];
	  buf[j] = 0;
	  break;
	}
    }
}

static reg_errcode_t
tre_new_item(tre_mem_t mem, int type, int val, int *max_i,
	 tre_bracket_match_list_t **items)
{
  reg_errcode_t status = REG_OK;
  tre_bracket_match_list_t *array = *items;
  int i = array->num_bracket_matches;
  /* Allocate more space if necessary. */
  if (i >= *max_i)
    {
      tre_bracket_match_list_t *new_items;
      DPRINT(("out of tre_bracket_match_list_t array space (%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 ']') */
      if (*max_i >= 1024)
	return REG_ESPACE;
      *max_i *= 2;
      new_items = xrealloc(array, SIZEOF_BRACKET_MATCH_LIST_N(*max_i));
      if (new_items == NULL)
	return REG_ESPACE;
      *items = array = new_items;
    }
  array->bracket_matches[i].type = type;
  array->bracket_matches[i].value = val;
  array->num_bracket_matches++;
  return status;
}

#ifndef TRE_USE_SYSTEM_WCTYPE

/* isalnum() and the rest may be macros, so wrap them to functions. */
int tre_isalnum_func(tre_cint_t c) { return tre_isalnum(c); }
int tre_isalpha_func(tre_cint_t c) { return tre_isalpha(c); }

#ifdef tre_isascii
int tre_isascii_func(tre_cint_t c) { return tre_isascii(c); }
#else /* !tre_isascii */
int tre_isascii_func(tre_cint_t c) { return !(c >> 7); }
#endif /* !tre_isascii */

#ifdef tre_isblank
int tre_isblank_func(tre_cint_t c) { return tre_isblank(c); }
#else /* !tre_isblank */
int tre_isblank_func(tre_cint_t c) { return ((c == ' ') || (c == '\t')); }
#endif /* !tre_isblank */

int tre_iscntrl_func(tre_cint_t c) { return tre_iscntrl(c); }
int tre_isdigit_func(tre_cint_t c) { return tre_isdigit(c); }
int tre_isgraph_func(tre_cint_t c) { return tre_isgraph(c); }
int tre_islower_func(tre_cint_t c) { return tre_islower(c); }
int tre_isprint_func(tre_cint_t c) { return tre_isprint(c); }
int tre_ispunct_func(tre_cint_t c) { return tre_ispunct(c); }
int tre_isspace_func(tre_cint_t c) { return tre_isspace(c); }
int tre_isupper_func(tre_cint_t c) { return tre_isupper(c); }
int tre_isxdigit_func(tre_cint_t c) { return tre_isxdigit(c); }

struct {
  char *name;
  int (*func)(tre_cint_t);
} tre_ctype_map[] = {
  { "alnum", &tre_isalnum_func },
  { "alpha", &tre_isalpha_func },
#ifdef tre_isascii
  { "ascii", &tre_isascii_func },
#endif /* tre_isascii */
#ifdef tre_isblank
  { "blank", &tre_isblank_func },
#endif /* tre_isblank */
  { "cntrl", &tre_iscntrl_func },
  { "digit", &tre_isdigit_func },
  { "graph", &tre_isgraph_func },
  { "lower", &tre_islower_func },
  { "print", &tre_isprint_func },
  { "punct", &tre_ispunct_func },
  { "space", &tre_isspace_func },
  { "upper", &tre_isupper_func },
  { "xdigit", &tre_isxdigit_func },
  { NULL, NULL}
};

tre_ctype_t tre_ctype(const char *name)
{
  int i;
  for (i = 0; tre_ctype_map[i].name != NULL; i++)
    {
      if (strcmp(name, tre_ctype_map[i].name) == 0)
	return tre_ctype_map[i].func;
    }
  return (tre_ctype_t)0;
}
#endif /* !TRE_USE_SYSTEM_WCTYPE */

#define REST(re) (int)(ctx->re_end - (re)), (re)

#define START_COLLATING_SYMBOLS		16
#define MAX_COLLATING_SYMBOL_LEN	4

typedef struct {
  const tre_char_t *start;
  int len;
} tre_collating_symbol;

#include <xlocale.h>

int __collate_equiv_value(locale_t loc, const wchar_t *str, size_t len);

#ifdef BSD_COMPATIBILITY
static wchar_t
tre_search_cnames(const wchar_t *name, size_t len)
{
  size_t low = 0;
  size_t high = NCNAMES - 1;
  size_t cur;
  int cmp;

  while(low <= high)
    {
      cur = (low + high) / 2;
      cmp = wcsncmp(name, cnames[cur].name, len);
      if (cmp == 0 && cnames[cur].name[len] == 0) return cnames[cur].code;
      if (cmp > 0) low = cur + 1;
      else high = cur - 1;
    }
  return (wchar_t)-1;
}
#endif /* BSD_COMPATIBILITY */

/* Scan the contents of a bracket expression, and create a
 * tre_bracket_match_list_t encoding the bracket expression.  If during
 * the scan, multi-character collating symbols are detected, switch
 * into a mode to collect those MCCSs into a tre_collating_symbol
 * list and pass them back.  tre_parse_bracket will use that to
 * create a new string composed of a union of the bracket expression
 * without the MCCSs and the MCCSs (e.g., [x[.ch.]] => [x]|ch), and
 * call tre_parse (recursive) to parse that new string (which will
 * call tre_parse_bracket and tre_parse_bracket_items again. */
static reg_errcode_t
tre_parse_bracket_items(tre_parse_ctx_t *ctx, tre_bracket_match_list_t **items,
			int *items_size, tre_collating_symbol **result)
{
  const tre_char_t *re = ctx->re;
  const tre_char_t *re_end = ctx->re_end;
  tre_collating_symbol *col_syms = NULL;
  tre_collating_symbol *cp = NULL;
  int n_col_syms = 0;
  reg_errcode_t status;
  int max_i = *items_size;
  int other = 0;  /* contains content other than multi-character collating
		   * symbols */
  int range = -1; /* -1 unset, 0 begin range set, +1 end range expected */
  tre_cint_t min, c;
  int invert = ((*items)->flags & TRE_BRACKET_MATCH_FLAG_NEGATE);
  int collect_MCCS = 0;
  const tre_char_t *start;

  for ( ;re < re_end; re++)
    {
      switch (*re)
	{
	case CHAR_MINUS:
	  /* A first hyphen */
	  if (re == ctx->re)
	    {
	      DPRINT(("tre_parse_bracket:   char: '%.*" STRF "'\n", REST(re)));
	      min = CHAR_MINUS;
	      other++;
	      range = 0;
	      break;
	    }
	  /* The hyphen is the end range */
	  if (range > 0)
	    {
	      DPRINT(("tre_parse_bracket:   char: '%.*" STRF "'\n", REST(re)));
	      c = CHAR_MINUS;
	      goto process_end_range;
	    }
	  if (re + 1 >= re_end)
	    {
	      status = REG_EBRACK;
	      goto error;
	    }
	  /* The hyphen is at the end */
	  if (re[1] == CHAR_RBRACKET)
	    {
	      DPRINT(("tre_parse_bracket:   char: '%.*" STRF "'\n", REST(re)));
	      c = CHAR_MINUS;
	      goto process_begin_range;
	    }
	  /* Two ranges are not allowed to share an endpoint, or begin
	   * range is illegal. */
	  if (range < 0)
	    {
	      status = REG_ERANGE;
	      goto error;
	    }
	  range = 1; /* Expect end range */
	  DPRINT(("tre_parse_bracket:   range: '%.*" STRF "'\n", REST(re)));
	  break;

	case CHAR_LBRACKET:
	  if (re + 1 >= re_end)
	    {
	      status = REG_EBRACK;
	      goto error;
	    }
	  switch (re[1])
	    {
	    case CHAR_PERIOD:
	      {
		re += 2;
		start = re;
		for (;; re++)
		  {
		    if (re >= re_end)
		      {
			status = REG_ECOLLATE;
			goto error;
		      }
		    if (*re == CHAR_PERIOD)
		      {
			if (re + 1 >= re_end)
			  {
			    status = REG_ECOLLATE;
			    goto error;
			  }
			/* Found end */
			if (re[1] == CHAR_RBRACKET)
			  {
			    DPRINT(("tre_parse_bracket:   collating "
				    "symbol: '%.*" STRF "'\n",
				    REST(start - 2)));
			    /* Empty name */
			    if (re == start)
			      {
				status = REG_ECOLLATE;
				goto error;
			      }
#ifdef BSD_COMPATIBILITY
			    /* Check if the name is in cnames; if so, use
			       the corresponding code */
			    c = tre_search_cnames(start, re - start);
			    if (c != (wchar_t)-1)
			      {
				re++;
				goto process_single_character;
			      }
#endif /* BSD_COMPATIBILITY */
			    /* Verify this is a known sequence */
			    if (__collate_equiv_value(ctx->loc, start,
							  re - start) <= 0)
			      {
				status = REG_ECOLLATE;
				goto error;
			      }
			    /* Process single character collating symbols */
			    if (re - start == 1)
			      {
				c = *start;
				re++;
				goto process_single_character;
			      }
			    /* Inverted MCCSs are undefined */
			    if (invert)
			      {
				status = REG_ECOLLATE;
				goto error;
			      }
			    /* Can't have MCCSs as an endpoint to a range */
			    if (range > 0)
			      {
				status = REG_ERANGE;
				goto error;
			      }
			    range = -1;
			    /* Switch into MCCS collection mode (if not
			     * already there */
#if TRE_DEBUG
			    if (!collect_MCCS)
			      {
				collect_MCCS = 1;
				DPRINT(("tre_parse_bracket: Detected MCCS\n"));
			      }
#else /* !TRE_DEBUG */
			    collect_MCCS = 1;
#endif /* !TRE_DEBUG */
			    /* Allocate a memory block the first time */
			    if (!cp)
			      {
				if ((col_syms = xmalloc(sizeof(*col_syms) *
					    (START_COLLATING_SYMBOLS + 2)))
					    == NULL)
				  return REG_ESPACE;
				cp = col_syms + 1;
				n_col_syms = START_COLLATING_SYMBOLS;
			      }
			    /* Enlarge the memory block is more is needed */
			    if ((cp - col_syms) - 1 >= n_col_syms)
			      {
				int i = n_col_syms;
				tre_collating_symbol *tmp =
				    xrealloc(col_syms, sizeof(*col_syms) *
					     ((n_col_syms *= 2) + 2));
				if (tmp == NULL)
				  {
				    xfree(col_syms);
				    return REG_ESPACE;
				  }
				DPRINT(("tre_list_collating_symbols: "
					"Enlarging col_syms to %d\n",
					n_col_syms));
				col_syms = tmp;
				cp = col_syms + i + 1;
			      }
			    cp->start = start;
			    cp->len = re - start;
			    cp++;
			    re++;
			    break;
			  }
		      }
		  }
		break;
	      }

	    case CHAR_EQUAL:
	    case CHAR_COLON:
	      {
		/* Process equivalence and character classes */
		tre_char_t kind = re[1];

		/* Can't have a class as an endpoint to a range */
		if (range > 0)
		  {
		    status = REG_ERANGE;
		    goto error;
		  }
		if (!collect_MCCS && range == 0)
		  {
		    status = tre_new_item(ctx->mem, TRE_BRACKET_MATCH_TYPE_CHAR,
					  min, &max_i, items);
		    if (status != REG_OK)
		      goto error;
		  }
		range = -1;
		re += 2;
		start = re;
		for (;; re++)
		  {
		    if (re >= re_end)
		      {
			status = kind == CHAR_EQUAL ? REG_ECOLLATE : REG_ECTYPE;
			goto error;
		      }
		    if (*re == kind)
		      {
			if (re + 1 >= re_end)
			  {
			    status = kind == CHAR_EQUAL ? REG_ECOLLATE :
							  REG_ECTYPE;
			    goto error;
			  }
			/* Found end */
			if (re[1] == CHAR_RBRACKET)
			  {
			    if (re == start)
			      {
				/* Empty class name */
				status = kind == CHAR_EQUAL ? REG_ECOLLATE :
							      REG_ECTYPE;
				goto error;
			      }
			    /* Process equivalence class */
			    if (kind == CHAR_EQUAL)
			      {
				int equiv;

				DPRINT(("tre_parse_bracket:   equivalence: '%.*"
					STRF "'\n", REST(start - 2)));

				/* While we find the collation value even for
				   multi-character collating elements , we
				   don't (yet) match any collation values
				   against multi-character sequences.  We'd have
				   to enumerate those multi-character sequences
				   and like multi-character collating symbols,
				   create a union of those sequences with the
				   rest of the bracket expression.  While
				   doable, a bracket expression matching
				   multiple characters, that doesn't explicitly
				   contain multi-character sequences, might
				   be unexpected, so we punt for now. */
				if ((equiv = __collate_equiv_value(ctx->loc,
					     start, re - start)) <= 0)
				  {
				    /* The standard says that if no collating
				       element if found, we use the collating
				       symbol itself.  But __collate_equiv_value
				       doesn't make a distinction between
				       an element that is in a equvalence
				       class with others, or is the only member,
				       so we already know there is no collating
				       symbol.  (Note that in the case of a
				       collating element whose collation value
				       is unique, matching against the
				       collating element itself, or against
				       its collation value, is equivalent.) */
#ifdef BSD_COMPATIBILITY
				    /* Check if the name is in cnames; if so,
				       use the corresponding code */
				    c = tre_search_cnames(start, re - start);
				    if (c != (wchar_t)-1)
				      {
					re++;
					goto process_single_character;
				      }
#endif /* BSD_COMPATIBILITY */
				    status = REG_ECOLLATE;
				    goto error;
				  }
				if (!collect_MCCS)
				  {
				    status = tre_new_item(ctx->mem,
					     TRE_BRACKET_MATCH_TYPE_EQUIVALENCE,
					     equiv, &max_i, items);
				    if (status != REG_OK)
				      goto error;
				  }
			      }
			    else
			      {
				/* Process character class */
				DPRINT(("tre_parse_bracket:  class: '%.*" STRF
					"'\n", REST(start - 2)));
				if (!collect_MCCS)
				  {
				    char tmp_str[64];
				    tre_ctype_t class;
				    int len = MIN(re - start, 63);
#ifdef TRE_WCHAR
				    {
				      tre_char_t tmp_wcs[64];
				      wcsncpy(tmp_wcs, start, (size_t)len);
				      tmp_wcs[len] = L'\0';
#if defined HAVE_WCSRTOMBS
				      {
					mbstate_t state;
					const tre_char_t *src = tmp_wcs;
					memset(&state, '\0', sizeof(state));
					len = wcsrtombs_l(tmp_str, &src,
						      sizeof(tmp_str), &state,
						      ctx->loc);
				      }
#elif defined HAVE_WCSTOMBS
				      len = wcstombs(tmp_str, tmp_wcs, 63);
#endif /* defined HAVE_WCSTOMBS */
				    }
#else /* !TRE_WCHAR */
				    strncpy(tmp_str, (const char*)start, len);
#endif /* !TRE_WCHAR */
				    tmp_str[len] = '\0';
				    DPRINT(("  class name: %s\n", tmp_str));
				    class = tre_ctype_l(tmp_str, ctx->loc);
				    if (!class)
				      {
					status = REG_ECTYPE;
					goto error;
				      }
				    status = tre_new_item(ctx->mem,
					     TRE_BRACKET_MATCH_TYPE_CLASS,
					     class, &max_i, items);
				    if (status != REG_OK)
				      goto error;
				  }
			      }
			    re++;
			    break;
			  }
		      }
		  }
		other++;
		break;
	      }

	    default:
	      DPRINT(("tre_parse_bracket:   char: '%.*" STRF "'\n", REST(re)));
	      c = CHAR_LBRACKET;
	      goto process_single_character;
	      break;
	    }
	  break;

	case CHAR_RBRACKET:
	  /* A first right bracket */
	  if (re == ctx->re)
	    {
	      DPRINT(("tre_parse_bracket:   char: '%.*" STRF "'\n", REST(re)));
	      min = CHAR_RBRACKET;
	      range = 0;
	      other++;
	      break;
	    }
	  /* Done */
	  if (collect_MCCS)
	    {
	      DPRINT(("tre_parse_bracket:	done: '%.*" STRF "'\n",
		      REST(re)));
	      if (col_syms)
		{
		  /* Mark the character following the right bracket.  Set len
		   * to whether there are other things besides the
		   * multi-character collating symbols */
		  col_syms->start = re + 1;
		  col_syms->len = other;
		  /* Mark the end of the list */
		  cp->start = NULL;
		}
	      *result = col_syms;
	      return REG_OK;
	    }
	  /* range > 0 is not possible, since we did a lookahead after the
	   * hyphen */
	  if (range == 0)
	    {
	      status = tre_new_item(ctx->mem, TRE_BRACKET_MATCH_TYPE_CHAR,
				    min, &max_i, items);
	      if (status != REG_OK)
		goto error;
	    }
	  DPRINT(("tre_parse_bracket:	done: '%.*" STRF "'\n", REST(re)));
	  *items_size = max_i;
	  ctx->re = re + 1;
	  return REG_OK;

	default:
	  DPRINT(("tre_parse_bracket:   char: '%.*" STRF "'\n", REST(re)));
	  c = *re;
process_single_character:
	  /* Process single character */
	  if (range > 0)
	    {
	      int mine, maxe;

process_end_range:
	      /* Get collation equivalence values */
	      mine = __collate_equiv_value(ctx->loc, &min, 1);
	      maxe = __collate_equiv_value(ctx->loc, &c, 1);
	      if (maxe < mine)
		{
		  status = REG_ERANGE;
		  goto error;
		}
	      if (!collect_MCCS)
		{
		  status = tre_new_item(ctx->mem,
					TRE_BRACKET_MATCH_TYPE_RANGE_BEGIN,
					mine, &max_i, items);
		  if (status != REG_OK)
		    goto error;
		  status = tre_new_item(ctx->mem,
					TRE_BRACKET_MATCH_TYPE_RANGE_END,
					maxe, &max_i, items);
		  if (status != REG_OK)
		    goto error;
		}
	      range = -1;
	    }
	  else
	    {
process_begin_range:
	      if (!collect_MCCS)
		{
		  if (range == 0)
		    {
		      status = tre_new_item(ctx->mem,
					    TRE_BRACKET_MATCH_TYPE_CHAR,
					    min, &max_i, items);
		      if (status != REG_OK)
			goto error;
		    }
		  min = c;
		}
	      range = 0;
	    }
	  other++;
	  break;
	}
    }
  status = REG_EBRACK;
error:
  DPRINT(("tre_parse_bracket:	error: '%.*" STRF "', status=%d\n",
	  REST(re), status));
  if (col_syms)
    xfree(col_syms);
  return status;
}

#ifdef TRE_DEBUG
static const char *bracket_match_type_str[] = {
  "unused",
  "char",
  "range begin",
  "range end",
  "class",
  "equivalence value",
};
#endif /* TRE_DEBUG */

static reg_errcode_t
tre_parse_bracket(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
{
  tre_ast_node_t *node;
  reg_errcode_t status = REG_OK;
  tre_bracket_match_list_t *items;
  int max_i = 32;
  tre_collating_symbol *col_syms = NULL;

  /* Handle special cases [[:<:]] and [[:>:]] */
  if (ctx->re_end - ctx->re >= 6 && ctx->re[0] == CHAR_LBRACKET
      && ctx->re[1] == CHAR_COLON && (ctx->re[2] == L'<' || ctx->re[2] == L'>')
      && ctx->re[3] == CHAR_COLON && ctx->re[4] == CHAR_RBRACKET
      && ctx->re[5] == CHAR_RBRACKET)
    {
      *result = tre_ast_new_literal(ctx->mem, ASSERTION,
		      (ctx->re[2] == L'<') ? ASSERT_AT_BOW : ASSERT_AT_EOW,
		      -1);
      DPRINT(("tre_parse_bracket: special case %s\n", (ctx->re[2] == L'<') ?
	      "[[:<:]]" : "[[:>:]]"));
      ctx->re += 6;
      return *result ? REG_OK : REG_ESPACE;
    }

  /* Start off with an array of `max_i' elements. */
  items = xcalloc(1, SIZEOF_BRACKET_MATCH_LIST_N(max_i));
  if (items == NULL)
    return REG_ESPACE;

  if (*ctx->re == CHAR_CARET)
    {
      DPRINT(("tre_parse_bracket: negate: '%.*" STRF "'\n", REST(ctx->re)));
      items->flags |= TRE_BRACKET_MATCH_FLAG_NEGATE;
      ctx->re++;
    }

  status = tre_parse_bracket_items(ctx, &items, &max_i, &col_syms);

  if (status != REG_OK)
    goto parse_bracket_done;

  /* If there are collating symbols, split off the multi-character ones
   * into a union of the bracket expression (without the collating symbols)
   * and the multiple-character sequences.  We create an equivalent input
   * string and run tre_parse() recursively */
  if (col_syms)
    {
      tre_char_t *str, *sp;
      tre_collating_symbol *cp;
      tre_parse_ctx_t subctx;

      /* Allocate a new string.  We start with the size of the original
       * bracket expression (minus 1) and add 2 (for a leading "[" and
       * a trailing nil; don't need a "^", since it is illegal to have
       * inverted MCCSs).  Since a multi-character collating symbols
       * will be converted from "[.xx.]" to "|xx" (n+4 to n+1), we don't
       * need to worry about the new string getting too long. */
      xfree(items);
      str = xmalloc(sizeof(*str) * ((col_syms->start - ctx->re) + 2));
      if (str == NULL)
	{
	  xfree(col_syms);
	  return REG_ESPACE;
	}
      sp = str;
      if (col_syms->len > 0)
	{
	  /* There are other items in the bracket expression besides the
	   * multi-character collating symbols, so create a new bracket
	   * expression with only those other itmes. */
	  const tre_char_t *re;
	  ptrdiff_t i;

	  *sp++ = '[';
	  re = ctx->re;
	  for (cp = col_syms + 1; cp->start; cp++)
	    {
	      /* The "- 2" is to account for the "[." */
	      if ((i = ((cp->start - re) - 2)) > 0)
		{
		  memcpy(sp, re, sizeof(*sp) * i);
		  sp += i;
		}
	      /* The "+ 2" is to account for the ".]" */
	      re = cp->start + cp->len + 2;
	    }
	    i = col_syms->start - re; /* Includes the trailing right bracket */
	    memcpy(sp, re, sizeof(*sp) * i);
	    sp += i;
	    *sp++ = '|';
	}
      for (cp = col_syms + 1; cp->start; cp++)
	{
	  memcpy(sp, cp->start, sizeof(*sp) * cp->len);
	  sp += cp->len;
	  if (cp[1].start)
	    *sp++ = '|';
	}
      *sp = 0;
      DPRINT(("tre_parse_bracket: Reparsing bracket expression with '%ls'\n",
	      str));

      memcpy(&subctx, ctx, sizeof(subctx));
      subctx.re = str;
      subctx.len = sp - str;
      subctx.nofirstsub = 1;
      subctx.cflags |= REG_EXTENDED; /* Force extended mode for parsing */
      status = tre_parse(&subctx);
      xfree(str);
      if (status != REG_OK)
	{
	  xfree(col_syms);
	  return status;
	}
      ctx->re = col_syms->start;
      ctx->position = subctx.position;
      xfree(col_syms);
      *result = subctx.result;
      DPRINT(("tre_parse_bracket: Returning to original string\n"));
      return REG_OK;
    }

  DPRINT(("tre_parse_bracket: creating bracket expression literal\n"));
  node = tre_ast_new_literal(ctx->mem, 0, TRE_CHAR_MAX, ctx->position);
  if (node == NULL)
    {
      status = REG_ESPACE;
      goto parse_bracket_done;
    }
  else
    {
      tre_literal_t *l = node->obj;
      l->u.bracket_match_list = tre_mem_alloc(ctx->mem,
					 SIZEOF_BRACKET_MATCH_LIST(items));
      if (l->u.bracket_match_list == NULL)
	{
	  status = REG_ESPACE;
	  goto parse_bracket_done;
	}
      memcpy(l->u.bracket_match_list, items, SIZEOF_BRACKET_MATCH_LIST(items));
    }

#ifdef TRE_DEBUG
  {
    int i;
    tre_bracket_match_t *b;
    DPRINT(("tre_parse_bracket: %d bracket match items, flags 0x%x\n",
	    items->num_bracket_matches, items->flags));
    for (i = 0, b = items->bracket_matches;
	 i < items->num_bracket_matches; i++, b++)
      {
	DPRINT(("   %d: %s %d\n", i, bracket_match_type_str[b->type],
		b->value));
      }
  }
#endif /* TRE_DEBUG */

 parse_bracket_done:
  xfree(items);
  ctx->position++;
  *result = node;
  return status;
}


/* 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)
{
  int num = -1;
  const tre_char_t *r = *regex;
  while (r < regex_end && *r >= L'0' && *r <= L'9')
    {
      if (num < 0)
	num = 0;
      num = num * 10 + *r - L'0';
      r++;
    }
  *regex = r;
  return num;
}


static reg_errcode_t
tre_parse_bound(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
{
  int min, max;
#ifdef TRE_APPROX
  int i;
  int cost_ins, cost_del, cost_subst, cost_max;
  int limit_ins, limit_del, limit_subst, limit_err;
  const tre_char_t *start;
#endif /* TRE_APPROX */
  const tre_char_t *r = ctx->re;
  int minimal = (ctx->cflags & REG_UNGREEDY) ? 1 : 0;
#ifdef TRE_APPROX
  int approx = 0;
  int costs_set = 0;
  int counts_set = 0;

  cost_ins = cost_del = cost_subst = cost_max = TRE_PARAM_UNSET;
  limit_ins = limit_del = limit_subst = limit_err = TRE_PARAM_UNSET;
#endif /* TRE_APPROX */

  /* Parse number (minimum repetition count). */
  min = -1;
  if (r >= ctx->re_end)
#ifdef ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND
    return (ctx->cflags & REG_EXTENDED) ? REG_NOMATCH : REG_EBRACE;
#else /* !ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND */
    return REG_EBRACE;
#endif /* !ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND */
  if (*r >= L'0' && *r <= L'9') {
    DPRINT(("tre_parse:	  min count: '%.*" STRF "'\n", REST(r)));
    min = tre_parse_int(&r, ctx->re_end);
  }
#ifndef TRE_APPROX
  else
#ifdef ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND
      /* For ERE, return REG_NOMATCH to signal that the lbrace should
         be treated as a literal */
      return (ctx->cflags & REG_EXTENDED) ? REG_NOMATCH : REG_BADBR;
#else /* !ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND */
      return REG_BADBR;
#endif /* !ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND */
#endif /* !TRE_APPROX */

  /* Parse comma and second number (maximum repetition count). */
  max = min;
  if (r < ctx->re_end && *r == CHAR_COMMA)
    {
      r++;
      DPRINT(("tre_parse:   max count: '%.*" STRF "'\n", REST(r)));
      max = tre_parse_int(&r, ctx->re_end);
    }

  /* Check that the repeat counts are sane. */
  if ((max >= 0 && min > max) || min > RE_DUP_MAX || max > RE_DUP_MAX)
    return REG_BADBR;


#ifdef TRE_APPROX
  /*
   '{'
     optionally followed immediately by a number == minimum repcount
     optionally followed by , then a number == maximum repcount
      + then a number == maximum insertion count
      - then a number == maximum deletion count
      # then a number == maximum substitution count
      ~ then a number == maximum number of errors
      Any of +, -, # or ~ without followed by a number means that
      the maximum count/number of errors is infinite.

      An equation of the form
	Xi + Yd + Zs < C
      can be specified to set costs and the cost limit to a value
      different from the default value:
	- X is the cost of an insertion
	- Y is the cost of a deletion
	- Z is the cost of a substitution
	- C is the maximum cost

      If no count limit or cost is set for an operation, the operation
      is not allowed at all.
  */


  do {
    int done;
    start = r;

    /* Parse count limit settings */
    done = 0;
    if (!counts_set)
      while (r + 1 < ctx->re_end && !done)
	{
	  switch (*r)
	    {
	    case CHAR_PLUS:  /* Insert limit */
	      DPRINT(("tre_parse:   ins limit: '%.*" STRF "'\n", REST(r)));
	      r++;
	      limit_ins = tre_parse_int(&r, ctx->re_end);
	      if (limit_ins < 0)
		limit_ins = INT_MAX;
	      counts_set = 1;
	      break;
	    case CHAR_MINUS: /* Delete limit */
	      DPRINT(("tre_parse:   del limit: '%.*" STRF "'\n", REST(r)));
	      r++;
	      limit_del = tre_parse_int(&r, ctx->re_end);
	      if (limit_del < 0)
		limit_del = INT_MAX;
	      counts_set = 1;
	      break;
	    case CHAR_HASH:  /* Substitute limit */
	      DPRINT(("tre_parse: subst limit: '%.*" STRF "'\n", REST(r)));
	      r++;
	      limit_subst = tre_parse_int(&r, ctx->re_end);
	      if (limit_subst < 0)
		limit_subst = INT_MAX;
	      counts_set = 1;
	      break;
	    case CHAR_TILDE: /* Maximum number of changes */
	      DPRINT(("tre_parse: count limit: '%.*" STRF "'\n", REST(r)));
	      r++;
	      limit_err = tre_parse_int(&r, ctx->re_end);
	      if (limit_err < 0)
		limit_err = INT_MAX;
	      approx = 1;
	      break;
	    case CHAR_COMMA:
	      r++;
	      break;
	    case L' ':
	      r++;
	      break;
	    case L'}':
	      done = 1;
	      break;
	    default:
	      done = 1;
	      break;
	    }
	}

    /* Parse cost restriction equation. */
    done = 0;
    if (!costs_set)
      while (r + 1 < ctx->re_end && !done)
	{
	  switch (*r)
	    {
	    case CHAR_PLUS:
	    case L' ':
	      r++;
	      break;
	    case L'<':
	      DPRINT(("tre_parse:    max cost: '%.*" STRF "'\n", REST(r)));
	      r++;
	      while (*r == L' ')
		r++;
	      cost_max = tre_parse_int(&r, ctx->re_end);
	      if (cost_max < 0)
		cost_max = INT_MAX;
	      else
		cost_max--;
	      approx = 1;
	      break;
	    case CHAR_COMMA:
	      r++;
	      done = 1;
	      break;
	    default:
	      if (*r >= L'0' && *r <= L'9')
		{
#ifdef TRE_DEBUG
		  const tre_char_t *sr = r;
#endif /* TRE_DEBUG */
		  int cost = tre_parse_int(&r, ctx->re_end);
		  /* XXX - make sure r is not past end. */
		  switch (*r)
		    {
		    case L'i':	/* Insert cost */
		      DPRINT(("tre_parse:    ins cost: '%.*" STRF "'\n",
			      REST(sr)));
		      r++;
		      cost_ins = cost;
		      costs_set = 1;
		      break;
		    case L'd':	/* Delete cost */
		      DPRINT(("tre_parse:    del cost: '%.*" STRF "'\n",
			      REST(sr)));
		      r++;
		      cost_del = cost;
		      costs_set = 1;
		      break;
		    case L's':	/* Substitute cost */
		      DPRINT(("tre_parse:  subst cost: '%.*" STRF "'\n",
			      REST(sr)));
		      r++;
		      cost_subst = cost;
		      costs_set = 1;
		      break;
		    default:
		      return REG_BADBR;
		    }
		}
	      else
		{
		  done = 1;
		  break;
		}
	    }
	}
  } while (start != r);
#endif /* TRE_APPROX */

  /*{*//* Missing }. */
  if (r >= ctx->re_end)
    return REG_EBRACE;

  /* Empty contents of {}. */
  if (r == ctx->re)
    return REG_BADBR;

  /* Parse the ending '}' or '\}'.*/
  if (ctx->cflags & REG_EXTENDED)
    {
      if (r >= ctx->re_end || *r != CHAR_RBRACE)
	return REG_BADBR;
      r++;
      /* Parse trailing '?' marking minimal repetition. */
      if (r < ctx->re_end)
	{
	  if (*r == CHAR_QUESTIONMARK)
	    {
	      /* Process the question mark only in enhanced mode.
		 Otherwise, the question mark is an error in ERE
		 or a literal in BRE */
	      if (ctx->cflags & REG_ENHANCED)
		{
		  minimal = !(ctx->cflags & REG_UNGREEDY);
		  r++;
		}
	      else return REG_BADRPT;
	    }
	  else if (*r == CHAR_STAR || *r == CHAR_PLUS)
	    {
	      /* These are reserved for future extensions. */
	      return REG_BADRPT;
	    }
	}
    }
  else
    {
      if (r + 1 >= ctx->re_end
	  || *r != CHAR_BACKSLASH
	  || *(r + 1) != CHAR_RBRACE)
	return REG_BADBR;
      r += 2;
      if (r < ctx->re_end && *r == CHAR_STAR)
	{
	  /* This is reserved for future extensions. */
	  return REG_BADRPT;
	}
    }

  if (minimal)
    ctx->num_reorder_tags++;

  if (!result) goto parse_bound_exit;
  /* Create the AST node(s). */
  /* Originally, if min == 0 && max == 0, we immediately replace the whole
     iteration with EMPTY.  This unfortunately drops any submatches, and
     messes up setting the pmatch values (we can get tags of -1, and
     tag values in the billions).  So we leave it and process this case as
     usual, and wait until tre_expand_ast() to replace with EMPTY */
#ifdef TRE_APPROX
  if (min < 0 && max < 0)
    /* Only approximate parameters set, no repetitions. */
    min = max = 1;
#endif /* TRE_APPROX */

  *result = tre_ast_new_iter(ctx->mem, *result, min, max, minimal);
  if (!*result)
    return REG_ESPACE;

#ifdef TRE_APPROX
  /* If approximate matching parameters are set, add them to the
     iteration node. */
  if (approx || costs_set || counts_set)
    {
      int *params;
      tre_iteration_t *iter = (*result)->obj;

      if (costs_set || counts_set)
	{
	  if (limit_ins == TRE_PARAM_UNSET)
	    {
	      if (cost_ins == TRE_PARAM_UNSET)
		limit_ins = 0;
	      else
		limit_ins = INT_MAX;
	    }

	  if (limit_del == TRE_PARAM_UNSET)
	    {
	      if (cost_del == TRE_PARAM_UNSET)
		limit_del = 0;
	      else
		limit_del = INT_MAX;
	    }

	  if (limit_subst == TRE_PARAM_UNSET)
	    {
	      if (cost_subst == TRE_PARAM_UNSET)
		limit_subst = 0;
	      else
		limit_subst = INT_MAX;
	    }
	}

      if (cost_max == TRE_PARAM_UNSET)
	cost_max = INT_MAX;
      if (limit_err == TRE_PARAM_UNSET)
	limit_err = INT_MAX;

      ctx->have_approx = 1;
      params = tre_mem_alloc(ctx->mem, sizeof(*params) * TRE_PARAM_LAST);
      if (!params)
	return REG_ESPACE;
      for (i = 0; i < TRE_PARAM_LAST; i++)
	params[i] = TRE_PARAM_UNSET;
      params[TRE_PARAM_COST_INS] = cost_ins;
      params[TRE_PARAM_COST_DEL] = cost_del;
      params[TRE_PARAM_COST_SUBST] = cost_subst;
      params[TRE_PARAM_COST_MAX] = cost_max;
      params[TRE_PARAM_MAX_INS] = limit_ins;
      params[TRE_PARAM_MAX_DEL] = limit_del;
      params[TRE_PARAM_MAX_SUBST] = limit_subst;
      params[TRE_PARAM_MAX_ERR] = limit_err;
      iter->params = params;
    }
#endif /* TRE_APPROX */

parse_bound_exit:
#ifdef TRE_APPROX
  DPRINT(("tre_parse_bound: min %d, max %d, costs [%d,%d,%d, total %d], "
	  "limits [%d,%d,%d, total %d]\n",
	  min, max, cost_ins, cost_del, cost_subst, cost_max,
	  limit_ins, limit_del, limit_subst, limit_err));
#else /* !TRE_APPROX */
  DPRINT(("tre_parse_bound: min %d, max %d\n", min, max));
#endif /* !TRE_APPROX */


  ctx->re = r;
  return REG_OK;
}

/* Previously, we had PARSE_RESTORE_CFLAGS restore the cflags, but for
   non-self-contained options, like (?i), this causes ((?i)fu)bar to be
   treated more like ((?i)fu(?-i)bar), so the pmatch value is incorrect.
   Because we now set up tags for even non-capturing parenthesized
   subexpressions, we always call PARSE_MARK_FOR_SUBMATCH.  So if we
   pass the unmodified version of cflags to PARSE_MARK_FOR_SUBMATCH and
   have it restore cflags after the subexpression, we don't need to have
   a separate PARSE_RESTORE_CFLAGS, and then after processing the
   non-self-contained option, we can call PARSE_ATOM instead of PARSE_RE.
   This has the side-benefit of now matching the perl behavior: the RE
   foo(?i)bar|zap is foo(?i)bar OR (?i)zap instead of TRE previous behavior
   of foo AND (?i) (bar OR zap). */
typedef enum {
  PARSE_RE = 0,
  PARSE_ATOM,
  PARSE_MARK_FOR_SUBMATCH,
  PARSE_BRANCH,
  PARSE_PIECE,
  PARSE_CATENATION,
  PARSE_POST_CATENATION,
  PARSE_UNION,
  PARSE_POST_UNION,
  PARSE_POSTFIX,
} tre_parse_re_stack_symbol_t;


reg_errcode_t
tre_parse(tre_parse_ctx_t *ctx)
{
  tre_ast_node_t *result = NULL;
  tre_parse_re_stack_symbol_t symbol;
  reg_errcode_t status = REG_OK;
  tre_stack_t *stack = ctx->stack;
  int bottom = tre_stack_num_objects(stack);
  int depth = 0;
  int temporary_cflags = 0;
  int bre_branch_begin;
#ifdef TRE_DEBUG
  const tre_char_t *tmp_re;
#endif

  DPRINT(("tre_parse: parsing '%.*" STRF "', len = %d cflags = 0%o\n",
	  ctx->len, ctx->re, ctx->len, ctx->cflags));

  if (ctx->len <= 0) return REG_EMPTY;
  if (!ctx->nofirstsub)
    {
      STACK_PUSH(stack, int, ctx->cflags);
      STACK_PUSH(stack, int, ctx->submatch_id);
      STACK_PUSH(stack, int, PARSE_MARK_FOR_SUBMATCH);
      ctx->submatch_id++;
    }
  STACK_PUSH(stack, int, 0); // bre_branch_begin
  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
     an explicit stack instead of recursive functions mostly because of
     two reasons: compatibility with systems which have an overflowable
     call stack, and efficiency (both in lines of code and speed).  */
  while (tre_stack_num_objects(stack) > bottom)
    {
      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 `|'. */
	  bre_branch_begin = tre_stack_pop_int(stack);
	  if (
#ifdef REG_LITERAL
	      !(ctx->cflags & REG_LITERAL) &&
#endif /* REG_LITERAL */
	      ctx->cflags & (REG_EXTENDED | REG_ENHANCED))
	    STACK_PUSHX(stack, int, PARSE_UNION);
	  STACK_PUSHX(stack, int, bre_branch_begin);
	  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. */
	  bre_branch_begin = tre_stack_pop_int(stack);
	  STACK_PUSHX(stack, int, PARSE_CATENATION);
	  STACK_PUSHX(stack, int, bre_branch_begin);
	  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. */
	  bre_branch_begin = tre_stack_pop_int(stack);
	  STACK_PUSHX(stack, int, PARSE_POSTFIX);
	  STACK_PUSHX(stack, int, bre_branch_begin);
	  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)
	      break;
	    c = *ctx->re;
#ifdef REG_LITERAL
	    if (!(ctx->cflags & REG_LITERAL))
	      {
#endif /* REG_LITERAL */
		if ((ctx->cflags & REG_EXTENDED && c == CHAR_PIPE) ||
		    ((ctx->cflags & (REG_EXTENDED | REG_ENHANCED)) == REG_ENHANCED
		    && ctx->re + 1 < ctx->re_end && c == CHAR_BACKSLASH &&
		    *(ctx->re + 1) == CHAR_PIPE))
		  break;
		if ((ctx->cflags & REG_EXTENDED
		     && c == CHAR_RPAREN && depth > 0)
		    || (!(ctx->cflags & REG_EXTENDED)
			&& ctx->re + 1 < ctx->re_end && c == CHAR_BACKSLASH
			    && *(ctx->re + 1) == CHAR_RPAREN))
		  {
		    if (!(ctx->cflags & REG_EXTENDED) && depth == 0)
		      return REG_EPAREN;
		    DPRINT(("tre_parse:	  group end: '%.*" STRF "'\n",
			    REST(ctx->re)));
		    depth--;
		    if (!(ctx->cflags & (REG_EXTENDED | REG_ENHANCED)))
		      ctx->re += 2;
		    break;
		  }
#ifdef REG_LITERAL
	      }
#endif /* REG_LITERAL */

#ifdef REG_LEFT_ASSOC
	    if (ctx->cflags & REG_LEFT_ASSOC)
	      {
		/* 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, 0); // bre_branch_begin
		STACK_PUSHX(stack, int, PARSE_PIECE);
	      }
	    else
#endif /* REG_LEFT_ASSOC */
	      {
		/* Default case, right associative concatenation. */
		STACK_PUSHX(stack, voidptr, result);
		STACK_PUSHX(stack, int, PARSE_POST_CATENATION);
		STACK_PUSHX(stack, int, PARSE_CATENATION);
		STACK_PUSHX(stack, int, 0); // bre_branch_begin
		STACK_PUSHX(stack, int, PARSE_PIECE);
	      }
	    break;
	  }

	case PARSE_POST_CATENATION:
	  {
	    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)
	      return REG_ESPACE;
	    result = tmp_node;
	    break;
	  }

	case PARSE_UNION:
	  if (ctx->re >= ctx->re_end)
	    break;
#ifdef REG_LITERAL
	  if (ctx->cflags & REG_LITERAL)
	    break;
#endif /* REG_LITERAL */
	  if (!(ctx->cflags & REG_EXTENDED))
	    {
	      if (*ctx->re != CHAR_BACKSLASH || ctx->re + 1 >= ctx->re_end)
		break;
	      ctx->re++;
	    }
	  switch (*ctx->re)
	    {
	    case CHAR_PIPE:
	      DPRINT(("tre_parse:	union: '%.*" STRF "'\n",
		      REST(ctx->re)));
	      STACK_PUSHX(stack, int, PARSE_UNION);
	      STACK_PUSHX(stack, voidptr, (void *)ctx->re);
	      STACK_PUSHX(stack, voidptr, result);
	      STACK_PUSHX(stack, int, PARSE_POST_UNION);
	      /* We need to pass a boolean (eventually) to PARSE_ATOM to
		 indicate if this is the beginning of a BRE extended branch. */
	      STACK_PUSHX(stack, int, (ctx->cflags & (REG_EXTENDED | REG_ENHANCED)) == REG_ENHANCED); // bre_branch_begin
	      STACK_PUSHX(stack, int, PARSE_BRANCH);
	      ctx->re++;
	      break;

	    case CHAR_RPAREN:
	      ctx->re++;
	      break;

	    default:
	      if (!(ctx->cflags & REG_EXTENDED))
		ctx->re--;
	      break;
	    }
	  break;

	case PARSE_POST_UNION:
	  {
	    tre_ast_node_t *tmp_node;
	    tre_ast_node_t *tree = tre_stack_pop_voidptr(stack);
	    const tre_char_t *pipechar = tre_stack_pop_voidptr(stack);
	    /* error on empty expression at end of union */
	    if (pipechar == ctx->re - 1)
	      {
		return REG_EMPTY;
	      }
	    tmp_node = tre_ast_new_union(ctx->mem, tree, result);
	    if (!tmp_node)
	      return REG_ESPACE;
	    result = tmp_node;
	    break;
	  }

	case PARSE_POSTFIX:
	  /* Parse postfix operators. */
	  if (ctx->re >= ctx->re_end)
	    break;
#ifdef REG_LITERAL
	  if (ctx->cflags & REG_LITERAL)
	    break;
#endif /* REG_LITERAL */
	  int minimal = (ctx->cflags & REG_UNGREEDY) ? 1 : 0;
	  int rep_min = 0;
	  int rep_max = -1;
#ifdef TRE_DEBUG
	  int lbrace_off;
#endif
	  switch (*ctx->re)
	    {
	    case CHAR_PLUS:
	    case CHAR_QUESTIONMARK:
	      if (!(ctx->cflags & REG_EXTENDED))
		break;
		/*FALLTHROUGH*/
	    case CHAR_STAR:
	      {
		tre_ast_node_t *tmp_node;
#ifdef TRE_DEBUG
		const char *tstr = "star";
		tmp_re = ctx->re;
#endif

	handle_plus_or_question:
		/* error on iteration of raw assertion (not in subexpression) */
		if (result->type == LITERAL && result->submatch_id < 0 &&
		    IS_ASSERTION((tre_literal_t *)result->obj))
		  {
		    if (!(ctx->cflags & REG_EXTENDED)) break;
		    return REG_BADRPT;
		  }
		if (*ctx->re == CHAR_PLUS)
		  {
		    rep_min = 1;
#ifdef TRE_DEBUG
		    tstr = "plus";
#endif
		  }
		if (*ctx->re == CHAR_QUESTIONMARK)
		  {
		    rep_max = 1;
#ifdef TRE_DEBUG
		    tstr = "questionmark";
#endif
		  }

		if (ctx->cflags & REG_EXTENDED)
		  {
		    if (ctx->re + 1 < ctx->re_end)
		      {
			if (*(ctx->re + 1) == CHAR_QUESTIONMARK)
			  {
			    /* Process the question mark only in enhanced mode.
			       Otherwise, the question mark is an error in ERE */
			    if (ctx->cflags & REG_ENHANCED)
			      {
				minimal = !(ctx->cflags & REG_UNGREEDY);
				ctx->re++;
			      }
			    else return REG_BADRPT;
			  }
			else if (*(ctx->re + 1) == CHAR_STAR
				 || *(ctx->re + 1) == CHAR_PLUS)
			  {
			    /* These are reserved for future extensions. */
			    return REG_BADRPT;
			  }
		      }
		  }
		else
		  {
		    if (ctx->re + 1 < ctx->re_end && *(ctx->re + 1) == CHAR_STAR)
		      {
			/* This is reserved for future extensions. */
			return REG_BADRPT;
		      }
		    if (ctx->re + 2 < ctx->re_end)
		      {
			if (*(ctx->re + 1) == CHAR_BACKSLASH && *(ctx->re + 1) == CHAR_QUESTIONMARK)
			  {
			    /* Process the question mark only in enhanced mode.
			       Otherwise, the question mark is a literal in BRE */
			    if (ctx->cflags & REG_ENHANCED)
			      {
				minimal = !(ctx->cflags & REG_UNGREEDY);
				ctx->re += 2;
			      }
			  }
			else if (*(ctx->re + 1) == CHAR_BACKSLASH && *(ctx->re + 2) == CHAR_PLUS)
			  {
			    /* This is reserved for future extensions. */
			    return REG_BADRPT;
			  }
		      }
		  }

		if (minimal)
		  ctx->num_reorder_tags++;

		DPRINT(("tre_parse: %s %s: '%.*" STRF "'\n",
			minimal ? "  minimal" : "greedy", tstr, REST(tmp_re)));
		if (result == NULL)
		  {
		    if (ctx->cflags & REG_EXTENDED) return REG_BADRPT;
		    else goto parse_literal;
		  }
		ctx->re++;
		tmp_node = tre_ast_new_iter(ctx->mem, result, rep_min, rep_max,
					    minimal);
		if (tmp_node == NULL)
		  return REG_ESPACE;
		result = tmp_node;

		/* Set the iterator with a submatch id in the invisible range
		 * (which will be overridden if a real submatch is needed) */
		result->submatch_id = ctx->submatch_id_invisible++;

#if 0
		/* We don't allow multiple postfixes, but this might be needed
		   to support approximate matching */
		STACK_PUSHX(stack, int, PARSE_POSTFIX);
#endif
	      }
	      break;

	    case CHAR_BACKSLASH:
	      /* "\{" is special without REG_EXTENDED */
	      /* "\+" and "\?" are special with REG_ENHANCED for BRE */
	      if (!(ctx->cflags & REG_EXTENDED)
		  && ctx->re + 1 < ctx->re_end)
		{
		  switch (*(ctx->re + 1))
		    {
		    case CHAR_LBRACE:
		      ctx->re++;
#ifdef TRE_DEBUG
		      lbrace_off = 2;
#endif
		      goto parse_brace;
		    case CHAR_PLUS:
		    case CHAR_QUESTIONMARK:
		      if (ctx->cflags & REG_ENHANCED)
			{
#ifdef TRE_DEBUG
			  tmp_re = ctx->re;
#endif
			  ctx->re++;
			  goto handle_plus_or_question;
			}
		      break;
		    }
		  break;
		}
	      else
		break;

	    case CHAR_LBRACE:
	      {
		int raw_assertion;

		/* "{" is literal without REG_EXTENDED */
		if (!(ctx->cflags & REG_EXTENDED))
		  break;
#ifdef TRE_DEBUG
		lbrace_off = 1;
#endif

	    parse_brace:
		/* error on iteration of raw assertion (not in subexpression),
		   but wait until after parsing bounds */
		raw_assertion = (result->type == LITERAL
				 && result->submatch_id < 0
				 && IS_ASSERTION((tre_literal_t *)result->obj));
		ctx->re++;

		status = tre_parse_bound(ctx, &result);
#ifdef ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND
		/* For ERE, if status is REG_NOMATCH, this mean the lbrace
		   is to be treated as a literal. */
		if (status == REG_NOMATCH)
		  {
		    ctx->re--;
		    break;
		  }
#endif /* ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND */
		DPRINT(("tre_parse:	bound: '%.*" STRF "'\n",
			REST(ctx->re - lbrace_off)));
		if (status != REG_OK)
		  return status;
		if (raw_assertion) return REG_BADRPT;

		/* Set the iterator with a submatch id in the invisible range
		 * (which will be overridden if a real submatch is needed) */
		if (result->type == ITERATION)
		  result->submatch_id = ctx->submatch_id_invisible++;

#if 0
		/* We don't allow multiple postfixes, but this might be needed
		   to support approximate matching */
		STACK_PUSHX(stack, int, PARSE_POSTFIX);
#endif
		break;
	      }
	    }
	  break;

	case PARSE_ATOM:
	  {
	    /* Parse an atom.  An atom is a regular expression enclosed in `()',
	       an empty set of `()', a bracket expression, `.', `^', `$',
	       a `\' followed by a character, or a single character. */

	    /* The stack contains a boolean value, whether PARSE_ATOM is
	       being called just after the start of a group (left paren)
	       in a BRE */
	    bre_branch_begin = tre_stack_pop_int(stack);

	    /* End of regexp? (empty string). */
	    if (ctx->re >= ctx->re_end)
	      goto parse_literal;

#ifdef REG_LITERAL
	    if (ctx->cflags & REG_LITERAL)
	      goto parse_literal;
#endif /* REG_LITERAL */

	    switch (*ctx->re)
	      {
	      case CHAR_LPAREN:  /* parenthesized subexpression */

		/* Handle "(?...)" extensions.  They work in a way similar
		   to Perls corresponding extensions. */
		if ((ctx->cflags & (REG_EXTENDED|REG_ENHANCED)) ==
		    (REG_EXTENDED|REG_ENHANCED)
		    && *(ctx->re + 1) == CHAR_QUESTIONMARK)
		  {
		    int new_cflags = ctx->cflags;
		    int bit = 1;
		    int invisible_submatch = 0;
		    DPRINT(("tre_parse:	extension: '%.*" STRF "'\n",
			    REST(ctx->re)));
		    ctx->re += 2;
		    while (/*CONSTCOND*/1)
		      {
			if (*ctx->re == L'i')
			  {
			    DPRINT(("tre_parse:	    icase: '%.*" STRF "'\n",
				    REST(ctx->re)));
			    if (bit)
			      new_cflags |= REG_ICASE;
			    else
			      new_cflags &= ~REG_ICASE;
			    ctx->re++;
			  }
			else if (*ctx->re == L'n')
			  {
			    DPRINT(("tre_parse:	  newline: '%.*" STRF "'\n",
				    REST(ctx->re)));
			    if (bit)
			      new_cflags |= REG_NEWLINE;
			    else
			      new_cflags &= ~REG_NEWLINE;
			    ctx->re++;
			  }
#ifdef REG_LEFT_ASSOC
			else if (*ctx->re == L'l')
			  {
			    DPRINT(("tre_parse: left assoc: '%.*" STRF "'\n",
				    REST(ctx->re)));
			    if (bit)
			      new_cflags |= REG_LEFT_ASSOC;
			    else
			      new_cflags &= ~REG_LEFT_ASSOC;
			    ctx->re++;
			  }
#endif /* REG_LEFT_ASSOC */
#ifdef REG_UNGREEDY
			else if (*ctx->re == L'U')
			  {
			    DPRINT(("tre_parse:    ungreedy: '%.*" STRF "'\n",
				    REST(ctx->re)));
			    if (bit)
			      new_cflags |= REG_UNGREEDY;
			    else
			      new_cflags &= ~REG_UNGREEDY;
			    ctx->re++;
			  }
#endif /* REG_UNGREEDY */
			else if (*ctx->re == CHAR_MINUS)
			  {
			    DPRINT(("tre_parse:	 turn off: '%.*" STRF "'\n",
				    REST(ctx->re)));
			    ctx->re++;
			    bit = 0;
			  }
			else if (*ctx->re == CHAR_COLON)
			  {
			    DPRINT(("tre_parse:	 no group: '%.*" STRF
				    "', (invisible submatch %d)\n",
				    REST(ctx->re), ctx->submatch_id_invisible));
			    ctx->re++;
			    depth++;
			    invisible_submatch = 1;
			    break;
			  }
			else if (*ctx->re == CHAR_HASH)
			  {
			    DPRINT(("tre_parse:    comment: '%.*" STRF "'\n",
				    REST(ctx->re)));
			    /* A comment can contain any character except a
			       right parenthesis */
			    while (*ctx->re != CHAR_RPAREN
				   && ctx->re < ctx->re_end)
			      ctx->re++;
			    if (*ctx->re == CHAR_RPAREN && ctx->re < ctx->re_end)
			      {
				ctx->re++;
				break;
			      }
			    else
			      return REG_BADPAT;
			  }
			else if (*ctx->re == CHAR_RPAREN)
			  {
			    ctx->re++;
			    break;
			  }
			else
			  return REG_BADRPT;
		      }

		    /* Turn on the cflags changes for the rest of the
		       enclosing group. */
		    if (invisible_submatch)
		      {
			STACK_PUSHX(stack, int, ctx->cflags);
			STACK_PUSHX(stack, int, ctx->submatch_id_invisible);
			STACK_PUSHX(stack, int, PARSE_MARK_FOR_SUBMATCH);
			ctx->submatch_id_invisible++;
			STACK_PUSHX(stack, int, 0); // bre_branch_begin
			STACK_PUSHX(stack, int, PARSE_RE);
		      }
		    else {
			STACK_PUSHX(stack, int, 0); // bre_branch_begin
			STACK_PUSHX(stack, int, PARSE_ATOM);
		    }
		    ctx->cflags = new_cflags;
		    break;
		  }

		if (ctx->cflags & REG_EXTENDED)
		  {
		parse_bre_lparen:
		    DPRINT(("tre_parse: group begin: '%.*" STRF
			    "', submatch %d\n", REST(ctx->re),
			    ctx->submatch_id));
		    ctx->re++;
		    /* First parse a whole RE, then mark the resulting tree
		       for submatching. */
		    STACK_PUSHX(stack, int, ctx->cflags);
		    STACK_PUSHX(stack, int, ctx->submatch_id);
		    STACK_PUSHX(stack, int, PARSE_MARK_FOR_SUBMATCH);
		    /* We need to pass a boolean (eventually) to PARSE_ATOM to
		       indicate if this is the beginning of a BRE group. */
		    STACK_PUSHX(stack, int, !(ctx->cflags & REG_EXTENDED));
		    STACK_PUSHX(stack, int, PARSE_RE);
		    ctx->submatch_id++;
		    depth++;
		  }
		else
		  goto parse_literal;
		break;

	      case CHAR_RPAREN:  /* end of current subexpression */
		if (ctx->cflags & REG_EXTENDED && depth > 0)
		  {
	      parse_bre_rparen_empty:
		    if (!(ctx->cflags & REG_EXTENDED) && depth == 0)
		      return REG_EPAREN;
		    DPRINT(("tre_parse:	    empty: '%.*" STRF "'\n",
			    REST(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
		       an empty expression (which matches an empty string).  */
		    result = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
		    if (result == NULL)
		      return REG_ESPACE;
		    if (!(ctx->cflags & REG_EXTENDED))
		      ctx->re--;
		  }
		else
		  goto parse_literal;
		break;

	      case CHAR_LBRACKET: /* bracket expression */
		DPRINT(("tre_parse:     bracket: '%.*" STRF "'\n",
			REST(ctx->re)));
		ctx->re++;
		status = tre_parse_bracket(ctx, &result);
		if (status != REG_OK)
		  return status;
		break;

	      case CHAR_BACKSLASH:
		/* Deal with "\(", "\)" or "\{" for BREs */
		if (!(ctx->cflags & REG_EXTENDED)
		    && ctx->re + 1 < ctx->re_end)
		  {
		    if (*(ctx->re + 1) == CHAR_LPAREN)
		      {
			ctx->re++;
			goto parse_bre_lparen;
		      }
		    else if (*(ctx->re + 1) == CHAR_RPAREN)
		      {
			ctx->re++;
			goto parse_bre_rparen_empty;
		      }
		    if (*(ctx->re + 1) == CHAR_LBRACE) goto parse_literal;
		  }

		if (ctx->re + 1 >= ctx->re_end)
		  /* Trailing backslash. */
		  return REG_EESCAPE;

		if (!(ctx->cflags & REG_ENHANCED))
		  {
		    DPRINT(("tre_parse:  unenhanced bleep: '%.*" STRF "'\n", REST(ctx->re)));
		    ctx->re++;
		    goto unenhanced_backslash;
		  }

		/* If a macro is used, parse the expanded macro recursively. */
		{
		  tre_char_t buf[64];
		  tre_expand_macro(ctx->re + 1, ctx->re_end,
				   buf, elementsof(buf));
		  if (buf[0] != 0)
		    {
		      tre_parse_ctx_t subctx;
		      memcpy(&subctx, ctx, sizeof(subctx));
		      subctx.re = buf;
		      subctx.len = tre_strlen(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;
		    }
		}

#ifdef REG_LITERAL
		if (*(ctx->re + 1) == L'Q')
		  {
		    DPRINT(("tre_parse: tmp literal: '%.*" STRF "'\n",
			    REST(ctx->re)));
		    ctx->cflags |= REG_LITERAL;
		    temporary_cflags |= REG_LITERAL;
		    ctx->re += 2;
		    STACK_PUSHX(stack, int, 0);
		    STACK_PUSHX(stack, int, PARSE_ATOM);
		    break;
		  }
#endif /* REG_LITERAL */

		DPRINT(("tre_parse:  bleep: '%.*" STRF "'\n", REST(ctx->re)));
		ctx->re++;
		switch (*ctx->re)
		  {
		  case L'b':
		    result = tre_ast_new_literal(ctx->mem, ASSERTION,
						 ASSERT_AT_WB, -1);
		    ctx->re++;
		    break;
		  case L'B':
		    result = tre_ast_new_literal(ctx->mem, ASSERTION,
						 ASSERT_AT_WB_NEG, -1);
		    ctx->re++;
		    break;
		  case L'<':
		    result = tre_ast_new_literal(ctx->mem, ASSERTION,
						 ASSERT_AT_BOW, -1);
		    ctx->re++;
		    break;
		  case L'>':
		    result = tre_ast_new_literal(ctx->mem, ASSERTION,
						 ASSERT_AT_EOW, -1);
		    ctx->re++;
		    break;
		  case L'x':
		    ctx->re++;
		    if (ctx->re[0] != CHAR_LBRACE && ctx->re < ctx->re_end)
		      {
			/* 8 bit hex char. */
			char tmp[3] = {0, 0, 0};
			long val;
			DPRINT(("tre_parse:  8 bit hex: '%.*" STRF "'\n",
				REST(ctx->re - 2)));

			if (tre_isxdigit_l(ctx->re[0], ctx->loc) &&
			    ctx->re < ctx->re_end)
			  {
			    tmp[0] = (char)ctx->re[0];
			    ctx->re++;
			  }
			if (tre_isxdigit_l(ctx->re[0], ctx->loc) &&
			    ctx->re < ctx->re_end)
			  {
			    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 < ctx->re_end)
		      {
			/* Wide char. */
			char tmp[32];
			long val;
			int i = 0;
			ctx->re++;
			while (ctx->re_end - ctx->re >= 0)
			  {
			    if (ctx->re[0] == CHAR_RBRACE)
			      break;
			    if (tre_isxdigit_l(ctx->re[0], ctx->loc))
			      {
				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:
		  unenhanced_backslash:
		    if ((ctx->cflags & (REG_EXTENDED | REG_ENHANCED)) !=
			REG_EXTENDED &&
			tre_isdigit_l(*ctx->re, ctx->loc) && *ctx->re != L'0')
		      {
			/* Back reference (only in BRE or enhanced). */
			int val = *ctx->re - L'0';
			DPRINT(("tre_parse:     backref: '%.*" STRF "'\n",
				REST(ctx->re - 1)));
			result = tre_ast_new_literal(ctx->mem, BACKREF, val,
						     ctx->position);
			if (result == NULL)
			  return REG_ESPACE;

			/* Set the backref with a submatch id in the invisible
			 * range (which will be overridden if a real submatch
			 * is needed) */
			result->submatch_id = ctx->submatch_id_invisible++;

			ctx->position++;
			ctx->num_reorder_tags++;
			ctx->max_backref = MAX(val, ctx->max_backref);
			ctx->re++;
		      }
		    else
		      {
			/* Escaped character. */
			DPRINT(("tre_parse:     escaped: '%.*" STRF "'\n",
				REST(ctx->re - 1)));
			result = tre_ast_new_literal(ctx->mem, *ctx->re, *ctx->re,
						     ctx->position);
			ctx->position++;
			ctx->re++;
		      }
		    break;
		  }
		if (result == NULL)
		  return REG_ESPACE;
		break;

	      case CHAR_PERIOD:	 /* the any-symbol */
		DPRINT(("tre_parse:	  any: '%.*" STRF "'\n",
			REST(ctx->re)));
		if (ctx->cflags & REG_NEWLINE)
		  {
		    tre_ast_node_t *tmp1;
		    tre_ast_node_t *tmp2;
		    tmp1 = tre_ast_new_literal(ctx->mem, 0, L'\n' - 1,
					       ctx->position);
		    if (!tmp1)
		      return REG_ESPACE;
		    tmp2 = tre_ast_new_literal(ctx->mem, L'\n' + 1, TRE_CHAR_MAX,
					       ctx->position + 1);
		    if (!tmp2)
		      return REG_ESPACE;
		    result = tre_ast_new_union(ctx->mem, tmp1, tmp2);
		    if (!result)
		      return REG_ESPACE;
		    ctx->position += 2;
		  }
		else
		  {
		    result = tre_ast_new_literal(ctx->mem, 0, TRE_CHAR_MAX,
						 ctx->position);
		    if (!result)
		      return REG_ESPACE;
		    ctx->position++;
		  }
		ctx->re++;
		break;

	      case CHAR_CARET:	 /* beginning of line assertion */
		/* '^' has a special meaning everywhere in EREs, at the
		   beginning of the RE and after \( is BREs.  It is also
		   special in enhanced BREs at the beginning of each branches
		   of a union */
		if (ctx->cflags & REG_EXTENDED
		    || bre_branch_begin
		    || ctx->re == ctx->re_start)
		  {
		    DPRINT(("tre_parse:	      BOL: '%.*" STRF "'\n",
			    REST(ctx->re)));
		    result = tre_ast_new_literal(ctx->mem, ASSERTION,
						 ASSERT_AT_BOL, -1);
		    if (result == NULL)
		      return REG_ESPACE;
		    ctx->re++;
		  }
		else
		  goto parse_literal;
		break;

	      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) == CHAR_BACKSLASH
			&& *(ctx->re + 2) == CHAR_RPAREN)
		    || ctx->re + 1 == ctx->re_end)
		  {
		    DPRINT(("tre_parse:	      EOL: '%.*" STRF "'\n",
			    REST(ctx->re)));
		    result = tre_ast_new_literal(ctx->mem, ASSERTION,
						 ASSERT_AT_EOL, -1);
		    if (result == NULL)
		      return REG_ESPACE;
		    ctx->re++;
		  }
		else
		  goto parse_literal;
		break;

	      default:
	      parse_literal:

		if (temporary_cflags && ctx->re + 1 < ctx->re_end
		    && *ctx->re == CHAR_BACKSLASH && *(ctx->re + 1) == L'E')
		  {
		    DPRINT(("tre_parse:	 end tmps: '%.*" STRF "'\n",
			    REST(ctx->re)));
		    ctx->cflags &= ~temporary_cflags;
		    temporary_cflags = 0;
		    ctx->re += 2;
		    if (ctx->re < ctx->re_end)
		      {
			STACK_PUSHX(stack, int, 0);
			STACK_PUSHX(stack, int, PARSE_ATOM);
		      }
		    else
		      {
			result = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
			if (!result) return REG_ESPACE;
		      }
		    break;
		  }


		/* We are expecting an atom.  If the subexpression (or the whole
		   regexp ends here, we interpret it as an empty expression
		   (which matches an empty string), which is an error.
		   Iterations of an empty expression is also an error. */
#ifdef REG_LITERAL
		if (!(ctx->cflags & REG_LITERAL))
		  {
#endif /* REG_LITERAL */
		    /* error on end of string */
		    if (ctx->re >= ctx->re_end) return depth > 0 ? REG_EPAREN
						       : REG_EMPTY;
		    /* error on unions and iterations of empty expressions */
		    if (ctx->cflags & REG_EXTENDED)
		      {
			if (ctx->re < ctx->re_end)
			  {
			    if (*ctx->re == CHAR_PIPE) return REG_EMPTY;
			    if (*ctx->re == CHAR_LBRACE)
			      {
				ctx->re++;
		  empty_parse_bound:
				/* We need to parse the bound first and return
				   any error, before returning REG_BADRPT */
				status = tre_parse_bound(ctx, NULL);
#ifdef ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND
				/* For ERE, if REG_NOMATCH is returned, we
				   treat the lbrace as a literal. */
				if (status == REG_NOMATCH)
				  {
				    ctx->re--;
				    /* Drop down to literal-handling code */
				  }
				else
				  {
#endif /* ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND */
				    if (status != REG_OK)
				      return status;
				    return REG_BADRPT;
#ifdef ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND
				  }
#endif /* ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND */
			      }
#ifdef ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND
			    else
#endif /* ERE_LITERAL_LBRACE_ON_NON_NUMERIC_BOUND */
			    if (*ctx->re == CHAR_STAR
				|| *ctx->re == CHAR_PLUS
				|| *ctx->re == CHAR_QUESTIONMARK)
			      {
				return REG_BADRPT;
			      }
			  }
		      }
		    else if (ctx->re + 1 < ctx->re_end
			     && *ctx->re == CHAR_BACKSLASH
			     && *(ctx->re + 1) == CHAR_LBRACE)
		      {
			ctx->re += 2;
			goto empty_parse_bound;
		      }
#ifdef REG_LITERAL
		  }
#endif /* REG_LITERAL */

		DPRINT(("tre_parse:     literal: '%.*" STRF "'\n",
			REST(ctx->re)));
		/* 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_l(*ctx->re, ctx->loc) ||
		    tre_islower_l(*ctx->re, ctx->loc)))
		  {
		    tre_ast_node_t *tmp1;
		    tre_ast_node_t *tmp2;

		    /* XXX - Can there be more than one opposite-case
		       counterpoints for some character in some locale?  Or
		       more than two characters which all should be regarded
		       the same character if case is ignored?  If yes, there
		       does not seem to be a portable way to detect it.  I guess
		       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_l(*ctx->re, ctx->loc),
					       tre_toupper_l(*ctx->re, ctx->loc),
					       ctx->position);
		    if (!tmp1)
		      return REG_ESPACE;
		    tmp2 = tre_ast_new_literal(ctx->mem,
					       tre_tolower_l(*ctx->re, ctx->loc),
					       tre_tolower_l(*ctx->re, ctx->loc),
					       ctx->position);
		    if (!tmp2)
		      return REG_ESPACE;
		    result = tre_ast_new_union(ctx->mem, tmp1, tmp2);
		    if (!result)
		      return REG_ESPACE;
		  }
		else
		  {
		    result = tre_ast_new_literal(ctx->mem, *ctx->re, *ctx->re,
						 ctx->position);
		    if (!result)
		      return REG_ESPACE;
		  }
		ctx->position++;
		ctx->re++;
		break;
	      }
	    break;
	  }

	case PARSE_MARK_FOR_SUBMATCH:
	  {
	    int submatch_id = tre_stack_pop_int(stack);

	    ctx->cflags = tre_stack_pop_int(stack); /* restore cflags */
	    if (result->submatch_id >= 0 &&
		result->submatch_id < SUBMATCH_ID_INVISIBLE_START)
	      {
		tre_ast_node_t *n, *tmp_node;
		if (submatch_id >= SUBMATCH_ID_INVISIBLE_START)
		  break;
		n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
		if (n == NULL)
		  return REG_ESPACE;
		tmp_node = tre_ast_new_catenation(ctx->mem, n, result);
		if (tmp_node == NULL)
		  return REG_ESPACE;
		tmp_node->num_submatches = result->num_submatches;
		result = tmp_node;
	      }
	    result->submatch_id = submatch_id;
	    if (submatch_id < SUBMATCH_ID_INVISIBLE_START)
	      result->num_submatches++;
	    break;
	  }

	default:
	  assert(0);
	  break;
	}
    }

  /* Check for missing closing parentheses. */
  if (depth > 0)
    return REG_EPAREN;

  ctx->result = result;

  return REG_OK;
}

/* EOF */