regex.c:1207
char *
re_compile_pattern(pattern, size, bufp)
     const char *pattern;
     int size;
     struct re_pattern_buffer *bufp;
{
  register char *b = bufp->buffer;
  register const char *p = pattern;
  const char *nextp;
  const char *pend = pattern + size;
  register unsigned int c, c1 = 0;
  const char *p0;
  int numlen;
#define ERROR_MSG_MAX_SIZE 200
  static char error_msg[ERROR_MSG_MAX_SIZE+1];

  /* Address of the count-byte of the most recently inserted `exactn'
     command.  This makes it possible to tell whether a new exact-match
     character can be added to that command or requires a new `exactn'
     command.  */

  char *pending_exact = 0;

  /* Address of the place where a forward-jump should go to the end of
     the containing expression.  Each alternative of an `or', except the
     last, ends with a forward-jump of this sort.  */

  char *fixup_alt_jump = 0;

  /* Address of start of the most recently finished expression.
     This tells postfix * where to find the start of its operand.  */

  char *laststart = 0;

  /* In processing a repeat, 1 means zero matches is allowed.  */

  char zero_times_ok;

  /* In processing a repeat, 1 means many matches is allowed.  */

  char many_times_ok;

  /* In processing a repeat, 1 means non-greedy matches.  */

  char greedy;

  /* Address of beginning of regexp, or inside of last (.  */

  char *begalt = b;

  /* Place in the uncompiled pattern (i.e., the {) to
     which to go back if the interval is invalid.  */
  const char *beg_interval;

  /* In processing an interval, at least this many matches must be made.  */
  int lower_bound;

  /* In processing an interval, at most this many matches can be made.  */
  int upper_bound;

  /* Stack of information saved by ( and restored by ).
     Five stack elements are pushed by each (:
     First, the value of b.
     Second, the value of fixup_alt_jump.
     Third, the value of begalt.
     Fourth, the value of regnum.
     Fifth, the type of the paren. */

  int stacka[40];
  int *stackb = stacka;
  int *stackp = stackb;
  int *stacke = stackb + 40;

  /* Counts ('s as they are encountered.  Remembered for the matching ),
     where it becomes the register number to put in the stop_memory
     command.  */

  int regnum = 1;

  int range = 0;
  int had_mbchar = 0;
  int had_num_literal = 0;
  int had_char_class = 0;

  int options = bufp->options;

  bufp->fastmap_accurate = 0;
  bufp->must = 0;
  bufp->must_skip = 0;

  /* Initialize the syntax table.  */
  init_syntax_once();

  if (bufp->allocated == 0) {
    bufp->allocated = INIT_BUF_SIZE;
    /* EXTEND_BUFFER loses when bufp->allocated is 0.  */
    bufp->buffer = (char*)xrealloc(bufp->buffer, INIT_BUF_SIZE);
    if (!bufp->buffer) goto memory_exhausted; /* this not happen */
    begalt = b = bufp->buffer;
  }

  while (p != pend) {
    PATFETCH(c);

    switch (c) {
    case '$':
      if (bufp->options & RE_OPTION_SINGLELINE) {
	BUFPUSH(endbuf);
      }
      else {
	p0 = p;
	/* When testing what follows the $,
	   look past the \-constructs that don't consume anything.  */

	while (p0 != pend) {
	  if (*p0 == '\\' && p0 + 1 != pend
	      && (p0[1] == 'b' || p0[1] == 'B'))
	    p0 += 2;
	  else
	    break;
	}
	BUFPUSH(endline);
      }
      break;

    case '^':
      if (bufp->options & RE_OPTION_SINGLELINE)
	BUFPUSH(begbuf);
      else
	BUFPUSH(begline);
      break;

    case '+':
    case '?':
    case '*':
      /* If there is no previous pattern, char not special. */
      if (!laststart) {
	snprintf(error_msg, ERROR_MSG_MAX_SIZE, 
		 "invalid regular expression; there's no previous pattern, to which '%c' would define cardinality at %d", 
		 c, p-pattern);
	FREE_AND_RETURN(stackb, error_msg);
      }
      /* If there is a sequence of repetition chars,
	 collapse it down to just one.  */
      zero_times_ok = c != '+';
      many_times_ok = c != '?';
      greedy = 1;
      if (p != pend) {
	PATFETCH(c);
	switch (c) {
	case '?':
	  greedy = 0;
	  break;
	case '*':
	case '+':
	  goto nested_meta;
	default:
	  PATUNFETCH;
	  break;
	}
      }

    repeat:
      /* Star, etc. applied to an empty pattern is equivalent
	 to an empty pattern.  */
      if (!laststart)  
	break;

      if (greedy && many_times_ok && *laststart == anychar && b - laststart <= 2) {
	if (b[-1] == stop_paren)
	  b--;
	if (zero_times_ok)
	  *laststart = anychar_repeat;
	else {
	  BUFPUSH(anychar_repeat);
	}
	break;
      }
      /* Now we know whether or not zero matches is allowed
	 and also whether or not two or more matches is allowed.  */
      if (many_times_ok) {
	/* If more than one repetition is allowed, put in at the
	   end a backward relative jump from b to before the next
	   jump we're going to put in below (which jumps from
	   laststart to after this jump).  */
	GET_BUFFER_SPACE(3);
	store_jump(b,greedy?maybe_finalize_jump:finalize_push,laststart-3);
	b += 3;  	/* Because store_jump put stuff here.  */
      }

      /* On failure, jump from laststart to next pattern, which will be the
	 end of the buffer after this jump is inserted.  */
      GET_BUFFER_SPACE(3);
      insert_jump(on_failure_jump, laststart, b + 3, b);
      b += 3;

      if (zero_times_ok) {
	if (greedy == 0) {
	  GET_BUFFER_SPACE(3);
	  insert_jump(try_next, laststart, b + 3, b);
	  b += 3;
	}
      }
      else {
	/* At least one repetition is required, so insert a
	   `dummy_failure_jump' before the initial
	   `on_failure_jump' instruction of the loop. This
	   effects a skip over that instruction the first time
	   we hit that loop.  */
	GET_BUFFER_SPACE(3);
	insert_jump(dummy_failure_jump, laststart, laststart + 6, b);
	b += 3;
      }
      break;

    case '.':
      laststart = b;
      BUFPUSH(anychar);
      break;

    case '[':
      if (p == pend)
	FREE_AND_RETURN(stackb, "invalid regular expression; '[' can't be the last character ie. can't start range at the end of pattern");
      while ((b - bufp->buffer + 9 + (1 << BYTEWIDTH) / BYTEWIDTH)
	     > bufp->allocated)
	EXTEND_BUFFER;

      laststart = b;
      if (*p == '^') {
	BUFPUSH(charset_not); 
	p++;
      }
      else
	BUFPUSH(charset);
      p0 = p;

      BUFPUSH((1 << BYTEWIDTH) / BYTEWIDTH);
      /* Clear the whole map */
      memset(b, 0, (1 << BYTEWIDTH) / BYTEWIDTH + 2);

      had_mbchar = 0;
      had_num_literal = 0;
      had_char_class = 0;

      /* Read in characters and ranges, setting map bits.  */
      for (;;) {
	int size;
	unsigned last = (unsigned)-1;

	if ((size = EXTRACT_UNSIGNED(&b[(1 << BYTEWIDTH) / BYTEWIDTH])) || current_mbctype) {
	  /* Ensure the space is enough to hold another interval
	     of multi-byte chars in charset(_not)?.  */
	  size = (1 << BYTEWIDTH) / BYTEWIDTH + 2 + size*8 + 8;
	  while (b + size + 1 > bufp->buffer + bufp->allocated)
	    EXTEND_BUFFER;
	}
      range_retry:
	if (range && had_char_class) {
	  FREE_AND_RETURN(stackb, "invalid regular expression; can't use character class as an end value of range");
	}
	PATFETCH_RAW(c);

	if (c == ']') {
	  if (p == p0 + 1) {
	    if (p == pend)
	      FREE_AND_RETURN(stackb, "invalid regular expression; empty character class");
            re_warning("character class has `]' without escape");
	  }
	  else 
	    /* Stop if this isn't merely a ] inside a bracket
	       expression, but rather the end of a bracket
	       expression.  */
	    break;
	}
	/* Look ahead to see if it's a range when the last thing
	   was a character class.  */
	if (had_char_class && c == '-' && *p != ']')
	  FREE_AND_RETURN(stackb, "invalid regular expression; can't use character class as a start value of range");
	if (ismbchar(c)) {
	  PATFETCH_MBC(c);
	  had_mbchar++;
	}
	had_char_class = 0;

	if (c == '-' && ((p != p0 + 1 && *p != ']') ||
                         (p[0] == '-' && p[1] != ']') ||
                         range))
          re_warning("character class has `-' without escape");
        if (c == '[' && *p != ':')
          re_warning("character class has `[' without escape");

	/* \ escapes characters when inside [...].  */
	if (c == '\\') {
	  PATFETCH_RAW(c);
	  switch (c) {
	  case 'w':
	    for (c = 0; c < (1 << BYTEWIDTH); c++) {
	      if (SYNTAX(c) == Sword ||
		  (!current_mbctype && SYNTAX(c) == Sword2))
		SET_LIST_BIT(c);
	    }
	    if (current_mbctype) {
	      set_list_bits(0x80, 0xffffffff, b);
	    }
	    had_char_class = 1;
	    last = -1;
	    continue;

	  case 'W':
	    for (c = 0; c < (1 << BYTEWIDTH); c++) {
	      if (SYNTAX(c) != Sword &&
		  ((current_mbctype && !re_mbctab[c]) ||
		  (!current_mbctype && SYNTAX(c) != Sword2)))
		SET_LIST_BIT(c);
	    }
	    had_char_class = 1;
	    last = -1;
	    continue;

	  case 's':
	    for (c = 0; c < 256; c++)
	      if (ISSPACE(c))
		SET_LIST_BIT(c);
	    had_char_class = 1;
	    last = -1;
	    continue;

	  case 'S':
	    for (c = 0; c < 256; c++)
	      if (!ISSPACE(c))
		SET_LIST_BIT(c);
	    if (current_mbctype)
	      set_list_bits(0x80, 0xffffffff, b);
	    had_char_class = 1;
	    last = -1;
	    continue;

	  case 'd':
	    for (c = '0'; c <= '9'; c++)
	      SET_LIST_BIT(c);
	    had_char_class = 1;
	    last = -1;
	    continue;

	  case 'D':
	    for (c = 0; c < 256; c++)
	      if (!ISDIGIT(c))
		SET_LIST_BIT(c);
	    if (current_mbctype)
	      set_list_bits(0x80, 0xffffffff, b);
	    had_char_class = 1;
	    last = -1;
	    continue;

	  case 'x':
	    c = scan_hex(p, 2, &numlen);
	    if (numlen == 0) goto invalid_escape;
	    p += numlen;
	    had_num_literal = 1;
	    break;

	  case '0': case '1': case '2': case '3': case '4':
	  case '5': case '6': case '7': case '8': case '9':
	    PATUNFETCH;
	    c = scan_oct(p, 3, &numlen);
	    p += numlen;
	    had_num_literal = 1;
	    break;

	  case 'M':
	  case 'C':
	  case 'c':
	    {
	      char *pp;

	      --p;
	      c = read_special(p, pend, &pp);
	      if (c > 255) goto invalid_escape;
	      p = pp;
	      had_num_literal = 1;
	    }
	    break;

	  default:
	    c = read_backslash(c);
	    if (ismbchar(c)) {
	      PATFETCH_MBC(c);
	      had_mbchar++;
	    }
	    break;
	  }
	}
        else if (c == '[' && *p == ':') { /* [:...:] */
	  /* Leave room for the null.  */
	  char str[CHAR_CLASS_MAX_LENGTH + 1];

	  PATFETCH_RAW(c);
	  c1 = 0;

	  /* If pattern is `[[:'.  */
	  if (p == pend) 
	    FREE_AND_RETURN(stackb, "invalid regular expression; re can't end '[[:'");

	  for (;;) {
	    PATFETCH_RAW(c);
	    if (c == ':' || c == ']' || p == pend
		|| c1 == CHAR_CLASS_MAX_LENGTH)
	      break;
	    str[c1++] = c;
	  }
	  str[c1] = '\0';

	  /* If isn't a word bracketed by `[:' and `:]':
	     undo the ending character, the letters, and
	     the leading `:' and `['.  */
	  if (c == ':' && *p == ']') {
	    int ch;
	    char is_alnum = STREQ(str, "alnum");
	    char is_alpha = STREQ(str, "alpha");
	    char is_blank = STREQ(str, "blank");
	    char is_cntrl = STREQ(str, "cntrl");
	    char is_digit = STREQ(str, "digit");
	    char is_graph = STREQ(str, "graph");
	    char is_lower = STREQ(str, "lower");
	    char is_print = STREQ(str, "print");
	    char is_punct = STREQ(str, "punct");
	    char is_space = STREQ(str, "space");
	    char is_upper = STREQ(str, "upper");
	    char is_xdigit = STREQ(str, "xdigit");

	    if (!IS_CHAR_CLASS(str)){
	      snprintf(error_msg, ERROR_MSG_MAX_SIZE, 
		       "invalid regular expression; [:%s:] is not a character class", str);
	      FREE_AND_RETURN(stackb, error_msg);
	    }

	    /* Throw away the ] at the end of the character class.  */
	    PATFETCH(c);

	    if (p == pend) 
	      FREE_AND_RETURN(stackb, "invalid regular expression; range doesn't have ending ']' after a character class");

	    for (ch = 0; ch < 1 << BYTEWIDTH; ch++) {
	      if (   (is_alnum  && ISALNUM(ch))
		  || (is_alpha  && ISALPHA(ch))
		  || (is_blank  && ISBLANK(ch))
		  || (is_cntrl  && ISCNTRL(ch))
		  || (is_digit  && ISDIGIT(ch))
		  || (is_graph  && ISGRAPH(ch))
		  || (is_lower  && ISLOWER(ch))
		  || (is_print  && ISPRINT(ch))
		  || (is_punct  && ISPUNCT(ch))
		  || (is_space  && ISSPACE(ch))
		  || (is_upper  && ISUPPER(ch))
		  || (is_xdigit && ISXDIGIT(ch)))
		SET_LIST_BIT(ch);
	    }
	    had_char_class = 1;
            continue;
	  }
	  else {
	    c1 += 2;
	    while (c1--)    
	      PATUNFETCH;
            re_warning("character class has `[' without escape");
            c = '[';
	  }
	}

	/* Get a range.  */
	if (range) {
	  if (last > c)
	    goto invalid_pattern;

	  range = 0;
	  if (had_mbchar == 0) {
	    if (TRANSLATE_P()) {
	      for (;last<=c;last++) 
		SET_LIST_BIT(translate[last]);
	    }
	    else {
	      for (;last<=c;last++) 
		SET_LIST_BIT(last);
	    }
	  }
	  else if (had_mbchar == 2) {
	    set_list_bits(last, c, b);
	  }
	  else {
	    /* restriction: range between sbc and mbc */
	    goto invalid_pattern;
	  }
	}
	else if (p[0] == '-' && p[1] != ']') {
	  last = c;
	  PATFETCH_RAW(c1);
	  range = 1;
	  goto range_retry;
	}
	else {
	  if (TRANSLATE_P()) c = (unsigned char)translate[c];
	  if (had_mbchar == 0 && (!current_mbctype || !had_num_literal)) {
	    SET_LIST_BIT(c);
	    had_num_literal = 0;
	  }
	  else {
	    set_list_bits(c, c, b);
	  }
	}
	had_mbchar = 0;
      }

      /* Discard any character set/class bitmap bytes that are all
	 0 at the end of the map. Decrement the map-length byte too.  */
      while ((int)b[-1] > 0 && b[b[-1] - 1] == 0) 
	b[-1]--; 
      if (b[-1] != (1 << BYTEWIDTH) / BYTEWIDTH)
	memmove(&b[(unsigned char)b[-1]], &b[(1 << BYTEWIDTH) / BYTEWIDTH],
		2 + EXTRACT_UNSIGNED(&b[(1 << BYTEWIDTH) / BYTEWIDTH])*8);
      b += b[-1] + 2 + EXTRACT_UNSIGNED(&b[(unsigned char)b[-1]])*8;
      break;

    case '(':
      {
	int old_options = options;
	int push_option = 0;
	int casefold = 0;

	PATFETCH(c);
	if (c == '?') {
	  int negative = 0;

	  PATFETCH_RAW(c);
	  switch (c) {
	  case 'x': case 'm': case 'i': case '-':
	    for (;;) {
	      switch (c) {
	      case '-':
		negative = 1;
		break;

	      case ':':
	      case ')':
		break;

	      case 'x':
		if (negative)
		  options &= ~RE_OPTION_EXTENDED;
		else
		  options |= RE_OPTION_EXTENDED;
		break;

	      case 'm':
		if (negative) {
		  if (options&RE_OPTION_MULTILINE) {
		    options &= ~RE_OPTION_MULTILINE;
		  }
		}
		else if (!(options&RE_OPTION_MULTILINE)) {
		  options |= RE_OPTION_MULTILINE;
		}
		push_option = 1;
		break;

	      case 'i':
		if (negative) {
		  if (options&RE_OPTION_IGNORECASE) {
		    options &= ~RE_OPTION_IGNORECASE;
		  }
		}
		else if (!(options&RE_OPTION_IGNORECASE)) {
		  options |= RE_OPTION_IGNORECASE;
		}
		casefold = 1;
		break;

	      default:
		FREE_AND_RETURN(stackb, "undefined (?...) inline option");
	      }
	      if (c == ')') {
		c = '#';	/* read whole in-line options */
		break;
	      }
	      if (c == ':') break;
	      PATFETCH_RAW(c);
	    }
	    break;

	  case '#':
	    for (;;) {
	      PATFETCH(c);
	      if (c == ')') break;
	    }
	    c = '#';
	    break;

	  case ':':
	  case '=':
	  case '!':
	  case '>':
	    break;

	  default:
	    FREE_AND_RETURN(stackb, "undefined (?...) sequence");
	  }
	}
	else {
	  PATUNFETCH;
	  c = '(';
	}
	if (c == '#') {
	  if (push_option) {
	    BUFPUSH(option_set);
	    BUFPUSH(options);
	  }
	  if (casefold) {
	    if (options & RE_OPTION_IGNORECASE)
	      BUFPUSH(casefold_on);
	    else
	      BUFPUSH(casefold_off);
	  }
	  break;
	}
	if (stackp+8 >= stacke) {
	  DOUBLE_STACK(int);
	}

	/* Laststart should point to the start_memory that we are about
	   to push (unless the pattern has RE_NREGS or more ('s).  */
	/* obsolete: now RE_NREGS is just a default register size. */
	*stackp++ = b - bufp->buffer;    
	*stackp++ = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
	*stackp++ = begalt - bufp->buffer;
	switch (c) {
	case '(':
	  BUFPUSH(start_memory);
	  BUFPUSH(regnum);
	  *stackp++ = regnum++;
	  *stackp++ = b - bufp->buffer;
	  BUFPUSH(0);
	  /* too many ()'s to fit in a byte. (max 254) */
	  if (regnum >= RE_REG_MAX) goto too_big;
	  break;

	case '=':
	case '!':
	case '>':
	  BUFPUSH(start_nowidth);
	  *stackp++ = b - bufp->buffer;
	  BUFPUSH(0);	/* temporary value */
	  BUFPUSH(0);
	  if (c != '!') break;

	  BUFPUSH(on_failure_jump);
	  *stackp++ = b - bufp->buffer;
	  BUFPUSH(0);	/* temporary value */
	  BUFPUSH(0);
	  break;

	case ':':
	  BUFPUSH(start_paren);
	  pending_exact = 0;
	default:
	  break;
	}
	if (push_option) {
	  BUFPUSH(option_set);
	  BUFPUSH(options);
	}
	if (casefold) {
	  if (options & RE_OPTION_IGNORECASE)
	    BUFPUSH(casefold_on);
	  else
	    BUFPUSH(casefold_off);
	}
	*stackp++ = c;
	*stackp++ = old_options;
	fixup_alt_jump = 0;
	laststart = 0;
	begalt = b;
      }
      break;

    case ')':
      if (stackp == stackb) 
	FREE_AND_RETURN(stackb, "unmatched )");

      pending_exact = 0;
      if (fixup_alt_jump) {
	/* Push a dummy failure point at the end of the
	   alternative for a possible future
	   `finalize_jump' to pop.  See comments at
	   `push_dummy_failure' in `re_match'.  */
	BUFPUSH(push_dummy_failure);

	/* We allocated space for this jump when we assigned
	   to `fixup_alt_jump', in the `handle_alt' case below.  */
	store_jump(fixup_alt_jump, jump, b);
      }
      if (options != stackp[-1]) {
	if ((options ^ stackp[-1]) & RE_OPTION_IGNORECASE) {
	  BUFPUSH((options&RE_OPTION_IGNORECASE)?casefold_off:casefold_on);
	}
	if ((options ^ stackp[-1]) != RE_OPTION_IGNORECASE) {
	  BUFPUSH(option_set);
	  BUFPUSH(stackp[-1]);
	}
      }
      p0 = b;
      options = *--stackp;
      switch (c = *--stackp) {
      case '(':
	{
	  char *loc = bufp->buffer + *--stackp;
	  *loc = regnum - stackp[-1];
	  BUFPUSH(stop_memory);
	  BUFPUSH(stackp[-1]);
	  BUFPUSH(regnum - stackp[-1]);
	  stackp--;
	}
	break;

      case '!':
	BUFPUSH(pop_and_fail);
	/* back patch */
	STORE_NUMBER(bufp->buffer+stackp[-1], b - bufp->buffer - stackp[-1] - 2);
	stackp--;
	/* fall through */
      case '=':
	BUFPUSH(stop_nowidth);
	/* tell stack-pos place to start_nowidth */
	STORE_NUMBER(bufp->buffer+stackp[-1], b - bufp->buffer - stackp[-1] - 2);
	BUFPUSH(0);	/* space to hold stack pos */
	BUFPUSH(0);
	stackp--;
	break;

      case '>':
	BUFPUSH(stop_backtrack);
	/* tell stack-pos place to start_nowidth */
	STORE_NUMBER(bufp->buffer+stackp[-1], b - bufp->buffer - stackp[-1] - 2);
	BUFPUSH(0);	/* space to hold stack pos */
	BUFPUSH(0);
	stackp--;
	break;

      case ':':
	BUFPUSH(stop_paren);
	break;

      default:
	break;
      }
      begalt = *--stackp + bufp->buffer;
      stackp--;
      fixup_alt_jump = *stackp ? *stackp + bufp->buffer - 1 : 0;
      laststart = *--stackp + bufp->buffer;
      if (c == '!' || c == '=') laststart = b;
      break;

    case '|':
      /* Insert before the previous alternative a jump which
	 jumps to this alternative if the former fails.  */
      GET_BUFFER_SPACE(3);
      insert_jump(on_failure_jump, begalt, b + 6, b);
      pending_exact = 0;
      b += 3;
      /* The alternative before this one has a jump after it
	 which gets executed if it gets matched.  Adjust that
	 jump so it will jump to this alternative's analogous
	 jump (put in below, which in turn will jump to the next
	 (if any) alternative's such jump, etc.).  The last such
	 jump jumps to the correct final destination.  A picture:
	 _____ _____ 
	 |   | |   |   
	 |   v |   v 
	 a | b   | c   

	 If we are at `b', then fixup_alt_jump right now points to a
	 three-byte space after `a'.  We'll put in the jump, set
	 fixup_alt_jump to right after `b', and leave behind three
	 bytes which we'll fill in when we get to after `c'.  */

      if (fixup_alt_jump)
	store_jump(fixup_alt_jump, jump_past_alt, b);

      /* Mark and leave space for a jump after this alternative,
	 to be filled in later either by next alternative or
	 when know we're at the end of a series of alternatives.  */
      fixup_alt_jump = b;
      GET_BUFFER_SPACE(3);
      b += 3;

      laststart = 0;
      begalt = b;
      break;

    case '{':
      /* If there is no previous pattern, this is an invalid pattern.  */
      if (!laststart) {
	snprintf(error_msg, ERROR_MSG_MAX_SIZE, 
		 "invalid regular expression; there's no previous pattern, to which '{' would define cardinality at %d", 
		 p-pattern);
	FREE_AND_RETURN(stackb, error_msg);
      }
      if( p == pend)
	FREE_AND_RETURN(stackb, "invalid regular expression; '{' can't be last character" );

      beg_interval = p - 1;

      lower_bound = -1;			/* So can see if are set.  */
      upper_bound = -1;
      GET_UNSIGNED_NUMBER(lower_bound);
      if (c == ',') {
	GET_UNSIGNED_NUMBER(upper_bound);
      }
      else
	/* Interval such as `{1}' => match exactly once. */
	upper_bound = lower_bound;

      if (lower_bound < 0 || c != '}')
	goto unfetch_interval;

      if (lower_bound >= RE_DUP_MAX || upper_bound >= RE_DUP_MAX)
	FREE_AND_RETURN(stackb, "too big quantifier in {,}");
      if (upper_bound < 0) upper_bound = RE_DUP_MAX;
      if (lower_bound > upper_bound)
	FREE_AND_RETURN(stackb, "can't do {n,m} with n > m");

      beg_interval = 0;
      pending_exact = 0;

      greedy = 1;
      if (p != pend) {
	PATFETCH(c);
	if (c == '?') greedy = 0;
	else PATUNFETCH;
      }

      if (lower_bound == 0) {
	zero_times_ok = 1;
	if (upper_bound == RE_DUP_MAX) {
	  many_times_ok = 1;
	  goto repeat;
	}
	if (upper_bound == 1) {
	  many_times_ok = 0;
	  goto repeat;
	}
      }
      if (lower_bound == 1) {
	if (upper_bound == 1) {
	  /* No need to repeat */
	  break;
	}
	if (upper_bound == RE_DUP_MAX) {
	  many_times_ok = 1;
	  zero_times_ok = 0;
	  goto repeat;
	}
      }

      /* If upper_bound is zero, don't want to succeed at all; 
	 jump from laststart to b + 3, which will be the end of
	 the buffer after this jump is inserted.  */

      if (upper_bound == 0) {
	GET_BUFFER_SPACE(3);
	insert_jump(jump, laststart, b + 3, b);
	b += 3;
	break;
      }

      /* If lower_bound == upper_bound, repeat count can be removed */
      if (lower_bound == upper_bound) {
	int mcnt;
	int skip_stop_paren = 0;

	if (b[-1] == stop_paren) {
	  skip_stop_paren = 1;
	  b--;
	}

	if (*laststart == exactn && laststart[1]+2 == b - laststart
	    && laststart[1]*lower_bound < 256) {
	  mcnt = laststart[1];
	  GET_BUFFER_SPACE((lower_bound-1)*mcnt);
	  laststart[1] = lower_bound*mcnt;
	  while (--lower_bound) {
	    memcpy(b, laststart+2, mcnt);
	    b += mcnt;
	  }
	  if (skip_stop_paren) BUFPUSH(stop_paren);
	  break;
	}

	if (lower_bound < 5 && b - laststart < 10) {
	  /* 5 and 10 are the magic numbers */

	  mcnt = b - laststart;
	  GET_BUFFER_SPACE((lower_bound-1)*mcnt);
	  while (--lower_bound) {
	    memcpy(b, laststart, mcnt);
	    b += mcnt;
	  }
	  if (skip_stop_paren) BUFPUSH(stop_paren);
	  break;
	}
	if (skip_stop_paren) b++; /* push back stop_paren */
      }

      /* Otherwise, we have a nontrivial interval.  When
	 we're all done, the pattern will look like:
	 set_number_at <jump count> <upper bound>
	 set_number_at <succeed_n count> <lower bound>
	 succeed_n <after jump addr> <succed_n count>
	 <body of loop>
	 jump_n <succeed_n addr> <jump count>
	 (The upper bound and `jump_n' are omitted if
	 `upper_bound' is 1, though.)  */
      { /* If the upper bound is > 1, we need to insert
	   more at the end of the loop.  */
	unsigned nbytes = upper_bound == 1 ? 10 : 20;

	GET_BUFFER_SPACE(nbytes);
	/* Initialize lower bound of the `succeed_n', even
	   though it will be set during matching by its
	   attendant `set_number_at' (inserted next),
	   because `re_compile_fastmap' needs to know.
	   Jump to the `jump_n' we might insert below.  */
	insert_jump_n(succeed_n, laststart, b + (nbytes/2), 
		      b, lower_bound);
	b += 5; 	/* Just increment for the succeed_n here.  */

	/* Code to initialize the lower bound.  Insert 
	   before the `succeed_n'.  The `5' is the last two
	   bytes of this `set_number_at', plus 3 bytes of
	   the following `succeed_n'.  */
	insert_op_2(set_number_at, laststart, b, 5, lower_bound);
	b += 5;

	if (upper_bound > 1) {
	  /* More than one repetition is allowed, so
	     append a backward jump to the `succeed_n'
	     that starts this interval.

	     When we've reached this during matching,
	     we'll have matched the interval once, so
	     jump back only `upper_bound - 1' times.  */
	  GET_BUFFER_SPACE(5);
	  store_jump_n(b, greedy?jump_n:finalize_push_n, laststart + 5,
		       upper_bound - 1);
	  b += 5;

	  /* The location we want to set is the second
	     parameter of the `jump_n'; that is `b-2' as
	     an absolute address.  `laststart' will be
	     the `set_number_at' we're about to insert;
	     `laststart+3' the number to set, the source
	     for the relative address.  But we are
	     inserting into the middle of the pattern --
	     so everything is getting moved up by 5.
	     Conclusion: (b - 2) - (laststart + 3) + 5,
	     i.e., b - laststart.

	     We insert this at the beginning of the loop
	     so that if we fail during matching, we'll
	     reinitialize the bounds.  */
	  insert_op_2(set_number_at, laststart, b, b - laststart,
		      upper_bound - 1);
	  b += 5;
	}
      }
      break;

    unfetch_interval:
      /* If an invalid interval, match the characters as literals.  */
      p = beg_interval;
      beg_interval = 0;

      /* normal_char and normal_backslash need `c'.  */
      PATFETCH(c);	
      goto normal_char;

    case '\\':
      if (p == pend)
	FREE_AND_RETURN(stackb, "invalid regular expression; '\\' can't be last character");
      /* Do not translate the character after the \, so that we can
	 distinguish, e.g., \B from \b, even if we normally would
	 translate, e.g., B to b.  */
      PATFETCH_RAW(c);
      switch (c) {
      case 's':
      case 'S':
      case 'd':
      case 'D':
	while (b - bufp->buffer + 9 + (1 << BYTEWIDTH) / BYTEWIDTH
	       > bufp->allocated)
	  EXTEND_BUFFER;

	laststart = b;
	if (c == 's' || c == 'd') {
	  BUFPUSH(charset);
	}
	else {
	  BUFPUSH(charset_not);
	}

	BUFPUSH((1 << BYTEWIDTH) / BYTEWIDTH);
	memset(b, 0, (1 << BYTEWIDTH) / BYTEWIDTH + 2);
	if (c == 's' || c == 'S') {
	  SET_LIST_BIT(' ');
	  SET_LIST_BIT('\t');
	  SET_LIST_BIT('\n');
	  SET_LIST_BIT('\r');
	  SET_LIST_BIT('\f');
	}
	else {
	  char cc;

	  for (cc = '0'; cc <= '9'; cc++) {
	    SET_LIST_BIT(cc);
	  }
	}

	while ((int)b[-1] > 0 && b[b[-1] - 1] == 0) 
	  b[-1]--; 
	if (b[-1] != (1 << BYTEWIDTH) / BYTEWIDTH)
	  memmove(&b[(unsigned char)b[-1]], &b[(1 << BYTEWIDTH) / BYTEWIDTH],
		  2 + EXTRACT_UNSIGNED(&b[(1 << BYTEWIDTH) / BYTEWIDTH])*8);
	b += b[-1] + 2 + EXTRACT_UNSIGNED(&b[(unsigned char)b[-1]])*8;
	break;

      case 'w':
	laststart = b;
	BUFPUSH(wordchar);
	break;

      case 'W':
	laststart = b;
	BUFPUSH(notwordchar);
	break;

#ifndef RUBY
      case '<':
	BUFPUSH(wordbeg);
	break;

      case '>':
	BUFPUSH(wordend);
	break;
#endif

      case 'b':
	BUFPUSH(wordbound);
	break;

      case 'B':
	BUFPUSH(notwordbound);
	break;

      case 'A':
	BUFPUSH(begbuf);
	break;

      case 'Z':
	if ((bufp->options & RE_OPTION_SINGLELINE) == 0) {
	  BUFPUSH(endbuf2);
	  break;
	}
	/* fall through */
      case 'z':
	BUFPUSH(endbuf);
	break;

      case 'G':
	BUFPUSH(begpos);
	break;

	/* hex */
      case 'x':
	had_mbchar = 0;
	c = scan_hex(p, 2, &numlen);
	if (numlen == 0) goto invalid_escape;
	p += numlen;
	had_num_literal = 1;
	goto numeric_char;

	/* octal */
      case '0':
	had_mbchar = 0;
	c = scan_oct(p, 2, &numlen);
	p += numlen;
	had_num_literal = 1;
	goto numeric_char;

	/* back-ref or octal */
      case '1': case '2': case '3':
      case '4': case '5': case '6':
      case '7': case '8': case '9':
	PATUNFETCH;
	p0 = p;

	had_mbchar = 0;
	c1 = 0;
	GET_UNSIGNED_NUMBER(c1);
	if (!ISDIGIT(c)) PATUNFETCH;

	if (9 < c1 && c1 >= regnum) {
	  /* need to get octal */
	  c = scan_oct(p0, 3, &numlen) & 0xff;
	  p = p0 + numlen;
	  c1 = 0;
	  had_num_literal = 1;
	  goto numeric_char;
	}

	laststart = b;
	BUFPUSH(duplicate);
	BUFPUSH(c1);
	break;

      case 'M':
      case 'C':
      case 'c':
	p0 = --p;
	c = read_special(p, pend, &p0);
	if (c > 255) goto invalid_escape;
	p = p0;
	had_num_literal = 1;
	goto numeric_char;

      default:
	c = read_backslash(c);
	goto normal_char;
      }
      break;

    case '#':
      if (options & RE_OPTION_EXTENDED) {
	while (p != pend) {
	  PATFETCH(c);
	  if (c == '\n') break;
	}
	break;
      }
      goto normal_char;

    case ' ':
    case '\t':
    case '\f':
    case '\r':
    case '\n':
      if (options & RE_OPTION_EXTENDED)
	break;

    default:
      if (c == ']')
        re_warning("regexp has `]' without escape");
    normal_char:		/* Expects the character in `c'.  */
      had_mbchar = 0;
      if (ismbchar(c)) {
	had_mbchar = 1;
	c1 = p - pattern;
      }
    numeric_char:
      nextp = p + mbclen(c) - 1;
      if (!pending_exact || pending_exact + *pending_exact + 1 != b
	  || *pending_exact >= (c1 ? 0176 : 0177)
	  || *nextp == '+' || *nextp == '?'
	  || *nextp == '*' || *nextp == '^'
	  || *nextp == '{') {
	laststart = b;
	BUFPUSH(exactn);
	pending_exact = b;
	BUFPUSH(0);
      }
      if (had_num_literal || c == 0xff) {
	BUFPUSH(0xff);
	(*pending_exact)++;
	had_num_literal = 0;
      }
      BUFPUSH(c);
      (*pending_exact)++;
      if (had_mbchar) {
	int len = mbclen(c) - 1;
	while (len--) {
	  PATFETCH_RAW(c);
	  BUFPUSH(c);
	  (*pending_exact)++;
	}
      }
    }
  }

  if (fixup_alt_jump)
    store_jump(fixup_alt_jump, jump, b);

  if (stackp != stackb)
    FREE_AND_RETURN(stackb, "unmatched (");

  /* set optimize flags */
  laststart = bufp->buffer;
  if (laststart != b) {
    if (*laststart == dummy_failure_jump) laststart += 3;
    else if (*laststart == try_next) laststart += 3;
    if (*laststart == anychar_repeat) {
      bufp->options |= RE_OPTIMIZE_ANCHOR;
    }
  }

  bufp->used = b - bufp->buffer;
  bufp->re_nsub = regnum;
  laststart = bufp->buffer;
  if (laststart != b) {
    if (*laststart == start_memory) laststart += 3;
    if (*laststart == exactn) {
      bufp->options |= RE_OPTIMIZE_EXACTN;
      bufp->must = laststart+1;
    }
  }
  if (!bufp->must) {
    bufp->must = calculate_must_string(bufp->buffer, b);
  }
  if (current_mbctype == MBCTYPE_SJIS) bufp->options |= RE_OPTIMIZE_NO_BM;
  else if (bufp->must) {
    int i;
    int len = (unsigned char)bufp->must[0];

    for (i=1; i<len; i++) {
      if ((unsigned char)bufp->must[i] == 0xff ||
	  (current_mbctype && ismbchar(bufp->must[i]))) {
	bufp->options |= RE_OPTIMIZE_NO_BM;
	break;
      }
    }
    if (!(bufp->options & RE_OPTIMIZE_NO_BM)) {
      bufp->must_skip = (int *) xmalloc((1 << BYTEWIDTH)*sizeof(int));
      bm_init_skip(bufp->must_skip, (unsigned char*)bufp->must+1,
		   (unsigned char)bufp->must[0],
		   (unsigned char*)(MAY_TRANSLATE()?translate:0));
    }
  }

  bufp->regstart = TMALLOC(regnum, unsigned char*);
  bufp->regend = TMALLOC(regnum, unsigned char*);
  bufp->old_regstart = TMALLOC(regnum, unsigned char*);
  bufp->old_regend = TMALLOC(regnum, unsigned char*);
  bufp->reg_info = TMALLOC(regnum, register_info_type);
  bufp->best_regstart = TMALLOC(regnum, unsigned char*);
  bufp->best_regend = TMALLOC(regnum, unsigned char*);
  FREE_AND_RETURN(stackb, 0);

 invalid_pattern:
  FREE_AND_RETURN(stackb, "invalid regular expression");

 end_of_pattern:
  FREE_AND_RETURN(stackb, "premature end of regular expression");

 too_big:
  FREE_AND_RETURN(stackb, "regular expression too big");

 memory_exhausted:
  FREE_AND_RETURN(stackb, "memory exhausted");

 nested_meta:
  FREE_AND_RETURN(stackb, "nested *?+ in regexp");

 invalid_escape:
  FREE_AND_RETURN(stackb, "Invalid escape character syntax");
}
