Tcl Source Code

Artifact [b663ec9a69]
Login

Artifact b663ec9a6963e72b5dfa45671f943b8bd2621fb6:

Attachment "sync-with-postgres.patch" to ticket [187d7f499b] added by tgl 2015-09-19 16:59:08. (unpublished)
diff -pcdr tcl8.6.4.withqbr/generic/regc_color.c tcl8.6.4/generic/regc_color.c
*** tcl8.6.4.withqbr/generic/regc_color.c	Thu Feb 26 11:57:28 2015
--- tcl8.6.4/generic/regc_color.c	Fri Sep 18 15:10:57 2015
*************** dumpcolors(
*** 777,794 ****
  	    }
  
  	    /*
! 	     * It's hard to do this more efficiently.
  	     */
  
! 	    for (c=CHR_MIN ; c<CHR_MAX ; c++) {
  		if (GETCOLOR(cm, c) == co) {
  		    dumpchr(c, f);
  		}
  	    }
- 	    assert(c == CHR_MAX);
- 	    if (GETCOLOR(cm, c) == co) {
- 		dumpchr(c, f);
- 	    }
  	    fprintf(f, "\n");
  	}
      }
--- 777,795 ----
  	    }
  
  	    /*
! 	     * Unfortunately, it's hard to do this next bit more efficiently.
! 	     *
! 	     * Spencer's original coding has the loop iterating from CHR_MIN
! 	     * to CHR_MAX, but that's utterly unusable for 32-bit chr, or
! 	     * even 16-bit.  For debugging purposes it seems fine to print
! 	     * only chr codes up to 1000 or so.
  	     */
  
! 	    for (c=CHR_MIN ; c<1000 ; c++) {
  		if (GETCOLOR(cm, c) == co) {
  		    dumpchr(c, f);
  		}
  	    }
  	    fprintf(f, "\n");
  	}
      }
diff -pcdr tcl8.6.4.withqbr/generic/regc_cvec.c tcl8.6.4/generic/regc_cvec.c
*** tcl8.6.4.withqbr/generic/regc_cvec.c	Thu Feb 26 11:57:28 2015
--- tcl8.6.4/generic/regc_cvec.c	Fri Sep 18 15:46:49 2015
*************** addchr(
*** 81,86 ****
--- 81,87 ----
      struct cvec *cv,		/* character vector */
      pchr c)			/* character to add */
  {
+     assert(cv->nchrs < cv->chrspace);
      cv->chrs[cv->nchrs++] = (chr)c;
  }
  
diff -pcdr tcl8.6.4.withqbr/generic/regc_lex.c tcl8.6.4/generic/regc_lex.c
*** tcl8.6.4.withqbr/generic/regc_lex.c	Thu Feb 26 11:57:28 2015
--- tcl8.6.4/generic/regc_lex.c	Fri Sep 18 14:24:03 2015
*************** newline(void)
*** 1139,1162 ****
  }
  
  /*
-  - ch - return the chr sequence for regc_locale.c's fake collating element ch
-  * This helps confine use of CHR to this source file.  Beware that the caller
-  * knows how long the sequence is.
-  ^ #ifdef REG_DEBUG
-  ^ static const chr *ch(NOPARMS);
-  ^ #endif
-  */
- #ifdef REG_DEBUG
- static const chr *
- ch(void)
- {
-     static const chr chstr[] = { CHR('c'), CHR('h'), CHR('\0') };
- 
-     return chstr;
- }
- #endif
- 
- /*
   - chrnamed - return the chr known by a given (chr string) name
   * The code is a bit clumsy, but this routine gets only such specialized
   * use that it hardly matters.
--- 1139,1144 ----
diff -pcdr tcl8.6.4.withqbr/generic/regc_nfa.c tcl8.6.4/generic/regc_nfa.c
*** tcl8.6.4.withqbr/generic/regc_nfa.c	Thu Feb 26 11:57:28 2015
--- tcl8.6.4/generic/regc_nfa.c	Fri Sep 18 16:18:49 2015
*************** newnfa(
*** 49,54 ****
--- 49,55 ----
  
      nfa = (struct nfa *) MALLOC(sizeof(struct nfa));
      if (nfa == NULL) {
+ 	ERR(REG_ESPACE);
  	return NULL;
      }
  
*************** compact(
*** 1651,1663 ****
      narcs = 0;
      for (s = nfa->states; s != NULL; s = s->next) {
  	nstates++;
! 	narcs += 1 + s->nouts + 1;
! 	/* 1 as a fake for flags, nouts for arcs, 1 as endmarker */
      }
  
      cnfa->states = (struct carc **) MALLOC(nstates * sizeof(struct carc *));
      cnfa->arcs = (struct carc *) MALLOC(narcs * sizeof(struct carc));
!     if (cnfa->states == NULL || cnfa->arcs == NULL) {
  	if (cnfa->states != NULL) {
  	    FREE(cnfa->states);
  	}
--- 1652,1667 ----
      narcs = 0;
      for (s = nfa->states; s != NULL; s = s->next) {
  	nstates++;
! 	narcs += s->nouts + 1;	/* need one extra for endmarker */
      }
  
+     cnfa->stflags = (char *) MALLOC(nstates * sizeof(char));
      cnfa->states = (struct carc **) MALLOC(nstates * sizeof(struct carc *));
      cnfa->arcs = (struct carc *) MALLOC(narcs * sizeof(struct carc));
!     if (cnfa->stflags == NULL || cnfa->states == NULL || cnfa->arcs == NULL) {
! 	if (cnfa->stflags != NULL) {
! 	    FREE(cnfa->stflags);
! 	}
  	if (cnfa->states != NULL) {
  	    FREE(cnfa->states);
  	}
*************** compact(
*** 1680,1688 ****
      ca = cnfa->arcs;
      for (s = nfa->states; s != NULL; s = s->next) {
  	assert((size_t) s->no < nstates);
  	cnfa->states[s->no] = ca;
- 	ca->co = 0;		/* clear and skip flags "arc" */
- 	ca++;
  	first = ca;
  	for (a = s->outs; a != NULL; a = a->outchain) {
  	    switch (a->type) {
--- 1684,1691 ----
      ca = cnfa->arcs;
      for (s = nfa->states; s != NULL; s = s->next) {
  	assert((size_t) s->no < nstates);
+ 	cnfa->stflags[s->no] = 0;
  	cnfa->states[s->no] = ca;
  	first = ca;
  	for (a = s->outs; a != NULL; a = a->outchain) {
  	    switch (a->type) {
*************** compact(
*** 1716,1724 ****
       */
  
      for (a = nfa->pre->outs; a != NULL; a = a->outchain) {
! 	cnfa->states[a->to->no]->co = 1;
      }
!     cnfa->states[nfa->pre->no]->co = 1;
  }
  
  /*
--- 1719,1727 ----
       */
  
      for (a = nfa->pre->outs; a != NULL; a = a->outchain) {
! 	cnfa->stflags[a->to->no] = CNFA_NOPROGRESS;
      }
!     cnfa->stflags[nfa->pre->no] = CNFA_NOPROGRESS;
  }
  
  /*
*************** freecnfa(
*** 1762,1767 ****
--- 1765,1771 ----
  {
      assert(cnfa->nstates != 0);	/* not empty already */
      cnfa->nstates = 0;
+     FREE(cnfa->stflags);
      FREE(cnfa->states);
      FREE(cnfa->arcs);
  }
*************** dumpcnfa(
*** 1984,1990 ****
      }
      fprintf(f, "\n");
      for (st = 0; st < cnfa->nstates; st++) {
! 	dumpcstate(st, cnfa->states[st], cnfa, f);
      }
      fflush(f);
  #endif
--- 1988,1994 ----
      }
      fprintf(f, "\n");
      for (st = 0; st < cnfa->nstates; st++) {
! 	dumpcstate(st, cnfa, f);
      }
      fflush(f);
  #endif
*************** dumpcnfa(
*** 1997,2021 ****
  
  /*
   - dumpcstate - dump a compacted-NFA state in human-readable form
!  ^ static void dumpcstate(int, struct carc *, struct cnfa *, FILE *);
   */
  static void
  dumpcstate(
      int st,
-     struct carc *ca,
      struct cnfa *cnfa,
      FILE *f)
  {
!     int i;
      int pos;
  
!     fprintf(f, "%d%s", st, (ca[0].co) ? ":" : ".");
      pos = 1;
!     for (i = 1; ca[i].co != COLORLESS; i++) {
! 	if (ca[i].co < cnfa->ncolors) {
! 	    fprintf(f, "\t[%ld]->%d", (long) ca[i].co, ca[i].to);
  	} else {
! 	    fprintf(f, "\t:%ld:->%d", (long) ca[i].co-cnfa->ncolors,ca[i].to);
  	}
  	if (pos == 5) {
  	    fprintf(f, "\n");
--- 2001,2024 ----
  
  /*
   - dumpcstate - dump a compacted-NFA state in human-readable form
!  ^ static void dumpcstate(int, struct cnfa *, FILE *);
   */
  static void
  dumpcstate(
      int st,
      struct cnfa *cnfa,
      FILE *f)
  {
!     struct carc *ca;
      int pos;
  
!     fprintf(f, "%d%s", st, (cnfa->stflags[st] & CNFA_NOPROGRESS) ? ":" : ".");
      pos = 1;
!     for (ca = cnfa->states[st]; ca->co != COLORLESS; ca++) {
! 	if (ca->co < cnfa->ncolors) {
! 	    fprintf(f, "\t[%ld]->%d", (long) ca->co, ca->to);
  	} else {
! 	    fprintf(f, "\t:%ld:->%d", (long) (ca->co - cnfa->ncolors), ca->to);
  	}
  	if (pos == 5) {
  	    fprintf(f, "\n");
*************** dumpcstate(
*** 2024,2030 ****
  	    pos++;
  	}
      }
!     if (i == 1 || pos != 1) {
  	fprintf(f, "\n");
      }
      fflush(f);
--- 2027,2033 ----
  	    pos++;
  	}
      }
!     if (ca == cnfa->states[st] || pos != 1) {
  	fprintf(f, "\n");
      }
      fflush(f);
diff -pcdr tcl8.6.4.withqbr/generic/regcomp.c tcl8.6.4/generic/regcomp.c
*** tcl8.6.4.withqbr/generic/regcomp.c	Fri Sep 18 12:16:30 2015
--- tcl8.6.4/generic/regcomp.c	Fri Sep 18 16:20:14 2015
*************** static int lexdigits(struct vars *, int,
*** 83,91 ****
  static int brenext(struct vars *, pchr);
  static void skip(struct vars *);
  static chr newline(NOPARMS);
- #ifdef REG_DEBUG
- static const chr *ch(NOPARMS);
- #endif
  static chr chrnamed(struct vars *, const chr *, const chr *, pchr);
  /* === regc_color.c === */
  static void initcm(struct vars *, struct colormap *);
--- 83,88 ----
*************** static void dumparc(struct arc *, struct
*** 165,171 ****
  #endif
  static void dumpcnfa(struct cnfa *, FILE *);
  #ifdef REG_DEBUG
! static void dumpcstate(int, struct carc *, struct cnfa *, FILE *);
  #endif
  /* === regc_cvec.c === */
  static struct cvec *clearcvec(struct cvec *);
--- 162,168 ----
  #endif
  static void dumpcnfa(struct cnfa *, FILE *);
  #ifdef REG_DEBUG
! static void dumpcstate(int, struct cnfa *, FILE *);
  #endif
  /* === regc_cvec.c === */
  static struct cvec *clearcvec(struct cvec *);
*************** struct vars {
*** 210,216 ****
      struct subre *tree;		/* subexpression tree */
      struct subre *treechain;	/* all tree nodes allocated */
      struct subre *treefree;	/* any free tree nodes */
!     int ntree;			/* number of tree nodes */
      struct cvec *cv;		/* interface cvec */
      struct cvec *cv2;		/* utility cvec */
      struct subre *lacons;	/* lookahead-constraint vector */
--- 207,213 ----
      struct subre *tree;		/* subexpression tree */
      struct subre *treechain;	/* all tree nodes allocated */
      struct subre *treefree;	/* any free tree nodes */
!     int ntree;			/* number of tree nodes, plus one */
      struct cvec *cv;		/* interface cvec */
      struct cvec *cv2;		/* utility cvec */
      struct subre *lacons;	/* lookahead-constraint vector */
*************** struct vars {
*** 223,235 ****
  #define	EAT(t)	(SEE(t) && next(v))	/* if next is this, swallow it */
  #define	VISERR(vv)	((vv)->err != 0)/* have we seen an error yet? */
  #define	ISERR()	VISERR(v)
! #define	VERR(vv,e) \
! 	((vv)->nexttype = EOS, ((vv)->err) ? (vv)->err : ((vv)->err = (e)))
  #define	ERR(e)	VERR(v, e)		/* record an error */
  #define	NOERR()	{if (ISERR()) return;}	/* if error seen, return */
  #define	NOERRN()	{if (ISERR()) return NULL;}	/* NOERR with retval */
  #define	NOERRZ()	{if (ISERR()) return 0;}	/* NOERR with retval */
! #define	INSIST(c, e)	((c) ? 0 : ERR(e))	/* if condition false, error */
  #define	NOTE(b)	(v->re->re_info |= (b))		/* note visible condition */
  #define	EMPTYARC(x, y)	newarc(v->nfa, EMPTY, 0, x, y)
  
--- 220,232 ----
  #define	EAT(t)	(SEE(t) && next(v))	/* if next is this, swallow it */
  #define	VISERR(vv)	((vv)->err != 0)/* have we seen an error yet? */
  #define	ISERR()	VISERR(v)
! #define VERR(vv,e)	((vv)->nexttype = EOS, \
! 			 (vv)->err = ((vv)->err ? (vv)->err : (e)))
  #define	ERR(e)	VERR(v, e)		/* record an error */
  #define	NOERR()	{if (ISERR()) return;}	/* if error seen, return */
  #define	NOERRN()	{if (ISERR()) return NULL;}	/* NOERR with retval */
  #define	NOERRZ()	{if (ISERR()) return 0;}	/* NOERR with retval */
! #define INSIST(c, e) do { if (!(c)) ERR(e); } while (0)	/* error if c false */
  #define	NOTE(b)	(v->re->re_info |= (b))		/* note visible condition */
  #define	EMPTYARC(x, y)	newarc(v->nfa, EMPTY, 0, x, y)
  
*************** struct vars {
*** 258,269 ****
  	((a)->type == PLAIN || (a)->type == AHEAD || (a)->type == BEHIND)
  
  /* static function list */
! static struct fns functions = {
      rfree,			/* regfree insides */
  };
  
  /*
   - compile - compile regular expression
   ^ int compile(regex_t *, const chr *, size_t, int);
   */
  int
--- 255,268 ----
  	((a)->type == PLAIN || (a)->type == AHEAD || (a)->type == BEHIND)
  
  /* static function list */
! static const struct fns functions = {
      rfree,			/* regfree insides */
  };
  
  /*
   - compile - compile regular expression
+  * Note: on failure, no resources remain allocated, so regfree()
+  * need not be applied to re.
   ^ int compile(regex_t *, const chr *, size_t, int);
   */
  int
*************** parseqatom(
*** 982,987 ****
--- 981,987 ----
  	NOERR();
  	assert(v->nextvalue > 0);
  	atom = subre(v, 'b', BACKR, lp, rp);
+ 	NOERR();
  	subno = v->nextvalue;
  	atom->subno = subno;
  	EMPTYARC(lp, rp);	/* temporarily, so there's something */
*************** parseqatom(
*** 1133,1138 ****
--- 1133,1139 ----
       */
  
      t = subre(v, '.', COMBINE(qprefer, atom->flags), lp, rp);
+     NOERR();
      t->left = atom;
      atomp = &t->left;
  
*************** parseqatom(
*** 1146,1151 ****
--- 1147,1153 ----
  
      assert(top->op == '=' && top->left == NULL && top->right == NULL);
      top->left = subre(v, '=', top->flags, top->begin, lp);
+     NOERR();
      top->op = '.';
      top->right = t;
  
*************** freesrnode(
*** 1783,1789 ****
      }
      sr->flags = 0;
  
!     if (v != NULL) {
  	sr->left = v->treefree;
  	v->treefree = sr;
      } else {
--- 1785,1792 ----
      }
      sr->flags = 0;
  
!     if (v != NULL && v->treechain != NULL) {
! 	/* we're still parsing, maybe we can reuse the subre */
  	sr->left = v->treefree;
  	v->treefree = sr;
      } else {
*************** numst(
*** 1836,1841 ****
--- 1839,1857 ----
  
  /*
   - markst - mark tree nodes as INUSE
+  * Note: this is a great deal more subtle than it looks.  During initial
+  * parsing of a regex, all subres are linked into the treechain list;
+  * discarded ones are also linked into the treefree list for possible reuse.
+  * After we are done creating all subres required for a regex, we run markst()
+  * then cleanst(), which results in discarding all subres not reachable from
+  * v->tree.  We then clear v->treechain, indicating that subres must be found
+  * by descending from v->tree.  This changes the behavior of freesubre(): it
+  * will henceforth FREE() unwanted subres rather than sticking them into the
+  * treefree list.  (Doing that any earlier would result in dangling links in
+  * the treechain list.)  This all means that freev() will clean up correctly
+  * if invoked before or after markst()+cleanst(); but it would not work if
+  * called partway through this state conversion, so we mustn't error out
+  * in or between these two functions.
   ^ static void markst(struct subre *);
   */
  static void
*************** newlacon(
*** 1942,1965 ****
      struct state *end,
      int pos)
  {
-     struct subre *sub;
      int n;
  
      if (v->nlacons == 0) {
- 	v->lacons = (struct subre *) MALLOC(2 * sizeof(struct subre));
  	n = 1;		/* skip 0th */
! 	v->nlacons = 2;
      } else {
! 	v->lacons = (struct subre *) REALLOC(v->lacons,
! 		(v->nlacons+1)*sizeof(struct subre));
! 	n = v->nlacons++;
      }
  
!     if (v->lacons == NULL) {
  	ERR(REG_ESPACE);
  	return 0;
      }
  
      sub = &v->lacons[n];
      sub->begin = begin;
      sub->end = end;
--- 1958,1983 ----
      struct state *end,
      int pos)
  {
      int n;
+     struct subre *newlacons;
+     struct subre *sub;
  
      if (v->nlacons == 0) {
  	n = 1;		/* skip 0th */
! 	newlacons = (struct subre *) MALLOC(2 * sizeof(struct subre));
      } else {
! 	n = v->nlacons;
! 	newlacons = (struct subre *) REALLOC(v->lacons,
! 					     (n + 1) * sizeof(struct subre));
      }
  
!     if (newlacons == NULL) {
  	ERR(REG_ESPACE);
  	return 0;
      }
  
+     v->lacons = newlacons;
+     v->nlacons = n + 1;
      sub = &v->lacons[n];
      sub->begin = begin;
      sub->end = end;
*************** rfree(
*** 2007,2024 ****
      g = (struct guts *) re->re_guts;
      re->re_guts = NULL;
      re->re_fns = NULL;
!     g->magic = 0;
!     freecm(&g->cmap);
!     if (g->tree != NULL) {
! 	freesubre(NULL, g->tree);
!     }
!     if (g->lacons != NULL) {
! 	freelacons(g->lacons, g->nlacons);
!     }
!     if (!NULLCNFA(g->search)) {
! 	freecnfa(&g->search);
      }
-     FREE(g);
  }
  
  /*
--- 2025,2044 ----
      g = (struct guts *) re->re_guts;
      re->re_guts = NULL;
      re->re_fns = NULL;
!     if (g != NULL) {
! 	g->magic = 0;
! 	freecm(&g->cmap);
! 	if (g->tree != NULL) {
! 	    freesubre(NULL, g->tree);
! 	}
! 	if (g->lacons != NULL) {
! 	    freelacons(g->lacons, g->nlacons);
! 	}
! 	if (!NULLCNFA(g->search)) {
! 	    freecnfa(&g->search);
! 	}
! 	FREE(g);
      }
  }
  
  /*
*************** dump(
*** 2050,2056 ****
  
      fprintf(f, "\n\n\n========= DUMP ==========\n");
      fprintf(f, "nsub %d, info 0%lo, csize %d, ntree %d\n",
! 	    re->re_nsub, re->re_info, re->re_csize, g->ntree);
  
      dumpcolors(&g->cmap, f);
      if (!NULLCNFA(g->search)) {
--- 2070,2076 ----
  
      fprintf(f, "\n\n\n========= DUMP ==========\n");
      fprintf(f, "nsub %d, info 0%lo, csize %d, ntree %d\n",
! 	    (int) re->re_nsub, re->re_info, re->re_csize, g->ntree);
  
      dumpcolors(&g->cmap, f);
      if (!NULLCNFA(g->search)) {
diff -pcdr tcl8.6.4.withqbr/generic/rege_dfa.c tcl8.6.4/generic/rege_dfa.c
*** tcl8.6.4.withqbr/generic/rege_dfa.c	Thu Feb 26 11:57:28 2015
--- tcl8.6.4/generic/rege_dfa.c	Fri Sep 18 14:41:04 2015
*************** longest(
*** 84,90 ****
  
      if (v->eflags&REG_FTRACE) {
  	while (cp < realstop) {
! 	    FDEBUG(("+++ at c%d +++\n", css - d->ssets));
  	    co = GETCOLOR(cm, *cp);
  	    FDEBUG(("char %c, color %ld\n", (char)*cp, (long)co));
  	    ss = css->outs[co];
--- 84,90 ----
  
      if (v->eflags&REG_FTRACE) {
  	while (cp < realstop) {
! 	    FDEBUG(("+++ at c%d +++\n", (int) (css - d->ssets)));
  	    co = GETCOLOR(cm, *cp);
  	    FDEBUG(("char %c, color %ld\n", (char)*cp, (long)co));
  	    ss = css->outs[co];
*************** longest(
*** 118,124 ****
       * Shutdown.
       */
  
!     FDEBUG(("+++ shutdown at c%d +++\n", css - d->ssets));
      if (cp == v->stop && stop == v->stop) {
  	if (hitstopp != NULL) {
  	    *hitstopp = 1;
--- 118,124 ----
       * Shutdown.
       */
  
!     FDEBUG(("+++ shutdown at c%d +++\n", (int) (css - d->ssets)));
      if (cp == v->stop && stop == v->stop) {
  	if (hitstopp != NULL) {
  	    *hitstopp = 1;
*************** shortest(
*** 213,219 ****
  
      if (v->eflags&REG_FTRACE) {
  	while (cp < realmax) {
! 	    FDEBUG(("--- at c%d ---\n", css - d->ssets));
  	    co = GETCOLOR(cm, *cp);
  	    FDEBUG(("char %c, color %ld\n", (char)*cp, (long)co));
  	    ss = css->outs[co];
--- 213,219 ----
  
      if (v->eflags&REG_FTRACE) {
  	while (cp < realmax) {
! 	    FDEBUG(("--- at c%d ---\n", (int) (css - d->ssets)));
  	    co = GETCOLOR(cm, *cp);
  	    FDEBUG(("char %c, color %ld\n", (char)*cp, (long)co));
  	    ss = css->outs[co];
*************** miss(
*** 516,529 ****
      gotState = 0;
      for (i = 0; i < d->nstates; i++) {
  	if (ISBSET(css->states, i)) {
! 	    for (ca = cnfa->states[i]+1; ca->co != COLORLESS; ca++) {
  		if (ca->co == co) {
  		    BSET(d->work, ca->to);
  		    gotState = 1;
  		    if (ca->to == cnfa->post) {
  			isPost = 1;
  		    }
! 		    if (!cnfa->states[ca->to]->co) {
  			noProgress = 0;
  		    }
  		    FDEBUG(("%d -> %d\n", i, ca->to));
--- 516,529 ----
      gotState = 0;
      for (i = 0; i < d->nstates; i++) {
  	if (ISBSET(css->states, i)) {
! 	    for (ca = cnfa->states[i]; ca->co != COLORLESS; ca++) {
  		if (ca->co == co) {
  		    BSET(d->work, ca->to);
  		    gotState = 1;
  		    if (ca->to == cnfa->post) {
  			isPost = 1;
  		    }
! 		    if (!(cnfa->stflags[ca->to] & CNFA_NOPROGRESS)) {
  			noProgress = 0;
  		    }
  		    FDEBUG(("%d -> %d\n", i, ca->to));
*************** miss(
*** 537,544 ****
  	doLAConstraints = 0;
  	for (i = 0; i < d->nstates; i++) {
  	    if (ISBSET(d->work, i)) {
! 		for (ca = cnfa->states[i]+1; ca->co != COLORLESS; ca++) {
! 		    if (ca->co <= cnfa->ncolors) {
  			continue;	/* NOTE CONTINUE */
  		    }
  		    sawLAConstraints = 1;
--- 537,544 ----
  	doLAConstraints = 0;
  	for (i = 0; i < d->nstates; i++) {
  	    if (ISBSET(d->work, i)) {
! 		for (ca = cnfa->states[i]; ca->co != COLORLESS; ca++) {
! 		    if (ca->co < cnfa->ncolors) {
  			continue;	/* NOTE CONTINUE */
  		    }
  		    sawLAConstraints = 1;
*************** miss(
*** 553,559 ****
  		    if (ca->to == cnfa->post) {
  			isPost = 1;
  		    }
! 		    if (!cnfa->states[ca->to]->co) {
  			noProgress = 0;
  		    }
  		    FDEBUG(("%d :> %d\n", i, ca->to));
--- 553,559 ----
  		    if (ca->to == cnfa->post) {
  			isPost = 1;
  		    }
! 		    if (!(cnfa->stflags[ca->to] & CNFA_NOPROGRESS)) {
  			noProgress = 0;
  		    }
  		    FDEBUG(("%d :> %d\n", i, ca->to));
*************** miss(
*** 572,578 ****
  
      for (p = d->ssets, i = d->nssused; i > 0; p++, i--) {
  	 if (HIT(h, d->work, p, d->wordsper)) {
! 	     FDEBUG(("cached c%d\n", p - d->ssets));
  	     break;			/* NOTE BREAK OUT */
  	 }
      }
--- 572,578 ----
  
      for (p = d->ssets, i = d->nssused; i > 0; p++, i--) {
  	 if (HIT(h, d->work, p, d->wordsper)) {
! 	     FDEBUG(("cached c%d\n", (int) (p - d->ssets)));
  	     break;			/* NOTE BREAK OUT */
  	 }
      }
*************** miss(
*** 594,600 ****
      }
  
      if (!sawLAConstraints) {	/* lookahead conds. always cache miss */
! 	FDEBUG(("c%d[%d]->c%d\n", css - d->ssets, co, p - d->ssets));
  	css->outs[co] = p;
  	css->inchain[co] = p->ins;
  	p->ins.ss = css;
--- 594,601 ----
      }
  
      if (!sawLAConstraints) {	/* lookahead conds. always cache miss */
! 	FDEBUG(("c%d[%d]->c%d\n",
! 		(int) (css - d->ssets), co, (int) (p - d->ssets)));
  	css->outs[co] = p;
  	css->inchain[co] = p->ins;
  	p->ins.ss = css;
*************** getVacantSS(
*** 663,669 ****
      ap = ss->ins;
      while ((p = ap.ss) != NULL) {
  	co = ap.co;
! 	FDEBUG(("zapping c%d's %ld outarc\n", p - d->ssets, (long)co));
  	p->outs[co] = NULL;
  	ap = p->inchain[co];
  	p->inchain[co].ss = NULL; /* paranoia */
--- 664,670 ----
      ap = ss->ins;
      while ((p = ap.ss) != NULL) {
  	co = ap.co;
! 	FDEBUG(("zapping c%d's %ld outarc\n", (int) (p - d->ssets), (long)co));
  	p->outs[co] = NULL;
  	ap = p->inchain[co];
  	p->inchain[co].ss = NULL; /* paranoia */
*************** getVacantSS(
*** 680,686 ****
  	if (p == NULL) {
  	    continue;		/* NOTE CONTINUE */
  	}
! 	FDEBUG(("del outarc %d from c%d's in chn\n", i, p - d->ssets));
  	if (p->ins.ss == ss && p->ins.co == i) {
  	    p->ins = ss->inchain[i];
  	} else {
--- 681,687 ----
  	if (p == NULL) {
  	    continue;		/* NOTE CONTINUE */
  	}
! 	FDEBUG(("del outarc %d from c%d's in chn\n", i, (int) (p - d->ssets)));
  	if (p->ins.ss == ss && p->ins.co == i) {
  	    p->ins = ss->inchain[i];
  	} else {
*************** pickNextSS(
*** 772,778 ****
  	if ((ss->lastseen == NULL || ss->lastseen < ancient)
  		&& !(ss->flags&LOCKED)) {
  	    d->search = ss + 1;
! 	    FDEBUG(("replacing c%d\n", ss - d->ssets));
  	    return ss;
  	}
      }
--- 773,779 ----
  	if ((ss->lastseen == NULL || ss->lastseen < ancient)
  		&& !(ss->flags&LOCKED)) {
  	    d->search = ss + 1;
! 	    FDEBUG(("replacing c%d\n", (int) (ss - d->ssets)));
  	    return ss;
  	}
      }
*************** pickNextSS(
*** 780,786 ****
  	if ((ss->lastseen == NULL || ss->lastseen < ancient)
  		&& !(ss->flags&LOCKED)) {
  	    d->search = ss + 1;
! 	    FDEBUG(("replacing c%d\n", ss - d->ssets));
  	    return ss;
  	}
      }
--- 781,787 ----
  	if ((ss->lastseen == NULL || ss->lastseen < ancient)
  		&& !(ss->flags&LOCKED)) {
  	    d->search = ss + 1;
! 	    FDEBUG(("replacing c%d\n", (int) (ss - d->ssets)));
  	    return ss;
  	}
      }
diff -pcdr tcl8.6.4.withqbr/generic/regerror.c tcl8.6.4/generic/regerror.c
*** tcl8.6.4.withqbr/generic/regerror.c	Thu Feb 26 11:57:28 2015
--- tcl8.6.4/generic/regerror.c	Fri Sep 18 14:18:44 2015
*************** static const char unk[] = "*** unknown r
*** 41,47 ****
   * Struct to map among codes, code names, and explanations.
   */
  
! static struct rerr {
      int code;
      const char *name;
      const char *explain;
--- 41,47 ----
   * Struct to map among codes, code names, and explanations.
   */
  
! static const struct rerr {
      int code;
      const char *name;
      const char *explain;
*************** regerror(
*** 62,68 ****
      char *errbuf,		/* Result buffer (unless errbuf_size==0) */
      size_t errbuf_size)		/* Available space in errbuf, can be 0 */
  {
!     struct rerr *r;
      const char *msg;
      char convbuf[sizeof(unk)+50]; /* 50 = plenty for int */
      size_t len;
--- 62,68 ----
      char *errbuf,		/* Result buffer (unless errbuf_size==0) */
      size_t errbuf_size)		/* Available space in errbuf, can be 0 */
  {
!     const struct rerr *r;
      const char *msg;
      char convbuf[sizeof(unk)+50]; /* 50 = plenty for int */
      size_t len;
diff -pcdr tcl8.6.4.withqbr/generic/regexec.c tcl8.6.4/generic/regexec.c
*** tcl8.6.4.withqbr/generic/regexec.c	Fri Sep 18 12:24:14 2015
--- tcl8.6.4/generic/regexec.c	Fri Sep 18 17:46:15 2015
*************** struct vars {
*** 107,118 ****
      chr *start;			/* start of string */
      chr *stop;			/* just past end of string */
      int err;			/* error code if any (0 none) */
      struct smalldfa dfa1;
      struct smalldfa dfa2;
  };
  #define	VISERR(vv) ((vv)->err != 0)	/* have we seen an error yet? */
  #define	ISERR()	VISERR(v)
! #define	VERR(vv,e) (((vv)->err) ? (vv)->err : ((vv)->err = (e)))
  #define	ERR(e)	VERR(v, e)	/* record an error */
  #define	NOERR()	{if (ISERR()) return v->err;}	/* if error seen, return it */
  #define	OFF(p)	((p) - v->start)
--- 107,119 ----
      chr *start;			/* start of string */
      chr *stop;			/* just past end of string */
      int err;			/* error code if any (0 none) */
+     struct dfa **subdfas;	/* per-subre DFAs */
      struct smalldfa dfa1;
      struct smalldfa dfa2;
  };
  #define	VISERR(vv) ((vv)->err != 0)	/* have we seen an error yet? */
  #define	ISERR()	VISERR(v)
! #define VERR(vv,e) ((vv)->err = ((vv)->err ? (vv)->err : (e)))
  #define	ERR(e)	VERR(v, e)	/* record an error */
  #define	NOERR()	{if (ISERR()) return v->err;}	/* if error seen, return it */
  #define	OFF(p)	((p) - v->start)
*************** struct vars {
*** 125,130 ****
--- 126,132 ----
  /* automatically gathered by fwd; do not hand-edit */
  /* === regexec.c === */
  int exec(regex_t *, const chr *, size_t, rm_detail_t *, size_t, regmatch_t [], int);
+ static struct dfa *getsubdfa(struct vars *, struct subre *);
  static int simpleFind(struct vars *const, struct cnfa *const, struct colormap *const);
  static int complicatedFind(struct vars *const, struct cnfa *const, struct colormap *const);
  static int complicatedFindLoop(struct vars *const, struct cnfa *const, struct colormap *const, struct dfa *const, struct dfa *const, chr **const);
*************** exec(
*** 171,178 ****
--- 173,183 ----
      AllocVars(v);
      int st, backref;
      size_t n;
+     size_t i;
  #define	LOCALMAT	20
      regmatch_t mat[LOCALMAT];
+ #define LOCALDFAS	40
+     struct dfa *subdfas[LOCALDFAS];
  
      /*
       * Sanity checks.
*************** exec(
*** 230,235 ****
--- 235,254 ----
      v->start = (chr *)string;
      v->stop = (chr *)string + len;
      v->err = 0;
+     assert(v->g->ntree >= 0);
+     n = (size_t) v->g->ntree;
+     if (n <= LOCALDFAS)
+ 	v->subdfas = subdfas;
+     else
+ 	v->subdfas = (struct dfa **) MALLOC(n * sizeof(struct dfa *));
+     if (v->subdfas == NULL) {
+ 	if (v->pmatch != pmatch && v->pmatch != mat)
+ 	    FREE(v->pmatch);
+ 	FreeVars(v);
+ 	return REG_ESPACE;
+     }
+     for (i = 0; i < n; i++)
+ 	v->subdfas[i] = NULL;
  
      /*
       * Do it.
*************** exec(
*** 259,269 ****
--- 278,312 ----
      if (v->pmatch != pmatch && v->pmatch != mat) {
  	FREE(v->pmatch);
      }
+     n = (size_t) v->g->ntree;
+     for (i = 0; i < n; i++) {
+ 	if (v->subdfas[i] != NULL)
+ 	    freeDFA(v->subdfas[i]);
+     }
+     if (v->subdfas != subdfas)
+ 	FREE(v->subdfas);
      FreeVars(v);
      return st;
  }
  
  /*
+  - getsubdfa - create or re-fetch the DFA for a subre node
+  * We only need to create the DFA once per overall regex execution.
+  * The DFA will be freed by the cleanup step in exec().
+  */
+ static struct dfa *
+ getsubdfa(struct vars * v,
+ 	  struct subre * t)
+ {
+     if (v->subdfas[t->id] == NULL) {
+ 	v->subdfas[t->id] = newDFA(v, &t->cnfa, &v->g->cmap, DOMALLOC);
+ 	if (ISERR())
+ 	    return NULL;
+     }
+     return v->subdfas[t->id];
+ }
+ 
+ /*
   - simpleFind - find a match for the main NFA (no-complications case)
   ^ static int simpleFind(struct vars *, struct cnfa *, struct colormap *);
   */
*************** simpleFind(
*** 327,333 ****
  	} else {
  	    end = longest(v, d, begin, v->stop, &hitend);
  	}
! 	NOERR();
  	if (hitend && cold == NULL) {
  	    cold = begin;
  	}
--- 370,379 ----
  	} else {
  	    end = longest(v, d, begin, v->stop, &hitend);
  	}
! 	if (ISERR()) {
! 	    freeDFA(d);
! 	    return v->err;
! 	}
  	if (hitend && cold == NULL) {
  	    cold = begin;
  	}
*************** complicatedFindLoop(
*** 470,475 ****
--- 516,522 ----
  		}
  		if (er != REG_NOMATCH) {
  		    ERR(er);
+ 		    *coldp = cold;
  		    return er;
  		}
  		if ((shorter) ? end == estop : end == begin) {
*************** cdissect(
*** 636,642 ****
  }
  
  /*
!  - ccondissect - concatenation subexpression matches (with complications)
   ^ static int ccondissect(struct vars *, struct subre *, chr *, chr *);
   */
  static int			/* regexec return code */
--- 683,689 ----
  }
  
  /*
!  - ccondissect - dissect match for concatenation node
   ^ static int ccondissect(struct vars *, struct subre *, chr *, chr *);
   */
  static int			/* regexec return code */
*************** ccondissect(
*** 654,668 ****
      assert(t->right != NULL && t->right->cnfa.nstates > 0);
      assert(!(t->left->flags & SHORTER));
  
!     d = newDFA(v, &t->left->cnfa, &v->g->cmap, DOMALLOC);
!     if (ISERR()) {
! 	return v->err;
!     }
!     d2 = newDFA(v, &t->right->cnfa, &v->g->cmap, DOMALLOC);
!     if (ISERR()) {
! 	freeDFA(d);
! 	return v->err;
!     }
      MDEBUG(("cConcat %d\n", t->id));
  
      /*
--- 701,711 ----
      assert(t->right != NULL && t->right->cnfa.nstates > 0);
      assert(!(t->left->flags & SHORTER));
  
!     d = getsubdfa(v, t->left);
!     NOERR();
!     d2 = getsubdfa(v, t->right);
!     NOERR();
! 
      MDEBUG(("cConcat %d\n", t->id));
  
      /*
*************** ccondissect(
*** 670,677 ****
       */
      mid = longest(v, d, begin, end, (int *) NULL);
      if (mid == NULL) {
- 	freeDFA(d);
- 	freeDFA(d2);
  	return REG_NOMATCH;
      }
      MDEBUG(("tentative midpoint %ld\n", LOFF(mid)));
--- 713,718 ----
*************** ccondissect(
*** 696,709 ****
  		     */
  
  		    MDEBUG(("successful\n"));
- 		    freeDFA(d);
- 		    freeDFA(d2);
  		    return REG_OKAY;
  		}
  	    }
  	    if (er != REG_NOMATCH) {
- 		freeDFA(d);
- 		freeDFA(d2);
  		return er;
  	    }
  	}
--- 737,746 ----
*************** ccondissect(
*** 718,725 ****
  	     */
  
  	    MDEBUG(("%d no midpoint\n", t->id));
- 	    freeDFA(d);
- 	    freeDFA(d2);
  	    return REG_NOMATCH;
  	}
  	mid = longest(v, d, begin, mid-1, NULL);
--- 755,760 ----
*************** ccondissect(
*** 729,736 ****
  	     */
  
  	    MDEBUG(("%d failed midpoint\n", t->id));
- 	    freeDFA(d);
- 	    freeDFA(d2);
  	    return REG_NOMATCH;
  	}
  	MDEBUG(("%d: new midpoint %ld\n", t->id, LOFF(mid)));
--- 764,769 ----
*************** crevcondissect(
*** 758,772 ****
      assert(t->right != NULL && t->right->cnfa.nstates > 0);
      assert(t->left->flags&SHORTER);
  
!     d = newDFA(v, &t->left->cnfa, &v->g->cmap, DOMALLOC);
!     if (ISERR()) {
! 	return v->err;
!     }
!     d2 = newDFA(v, &t->right->cnfa, &v->g->cmap, DOMALLOC);
!     if (ISERR()) {
! 	freeDFA(d);
! 	return v->err;
!     }
      MDEBUG(("crevcon %d\n", t->id));
  
      /*
--- 791,801 ----
      assert(t->right != NULL && t->right->cnfa.nstates > 0);
      assert(t->left->flags&SHORTER);
  
!     d = getsubdfa(v, t->left);
!     NOERR();
!     d2 = getsubdfa(v, t->right);
!     NOERR();
! 
      MDEBUG(("crevcon %d\n", t->id));
  
      /*
*************** crevcondissect(
*** 775,782 ****
  
      mid = shortest(v, d, begin, begin, end, (chr **) NULL, (int *) NULL);
      if (mid == NULL) {
- 	freeDFA(d);
- 	freeDFA(d2);
  	return REG_NOMATCH;
      }
      MDEBUG(("tentative midpoint %ld\n", LOFF(mid)));
--- 804,809 ----
*************** crevcondissect(
*** 801,814 ****
  		     */
  
  		    MDEBUG(("successful\n"));
- 		    freeDFA(d);
- 		    freeDFA(d2);
  		    return REG_OKAY;
  		}
  	    }
  	    if (er != REG_NOMATCH) {
- 		freeDFA(d);
- 		freeDFA(d2);
  		return er;
  	    }
  	}
--- 828,837 ----
*************** crevcondissect(
*** 823,830 ****
  	     */
  
  	    MDEBUG(("%d no midpoint\n", t->id));
- 	    freeDFA(d);
- 	    freeDFA(d2);
  	    return REG_NOMATCH;
  	}
  	mid = shortest(v, d, begin, mid+1, end, NULL, NULL);
--- 846,851 ----
*************** crevcondissect(
*** 834,841 ****
  	     */
  
  	    MDEBUG(("%d failed midpoint\n", t->id));
- 	    freeDFA(d);
- 	    freeDFA(d2);
  	    return REG_NOMATCH;
  	}
  	MDEBUG(("%d: new midpoint %ld\n", t->id, LOFF(mid)));
--- 855,860 ----
*************** caltdissect(
*** 943,959 ****
  
  	MDEBUG(("calt n%d\n", t->id));
  
! 	d = newDFA(v, &t->left->cnfa, &v->g->cmap, DOMALLOC);
  	NOERR();
  	if (longest(v, d, begin, end, (int *) NULL) == end) {
- 	    freeDFA(d);
  	    MDEBUG(("calt matched\n"));
  	    er = cdissect(v, t->left, begin, end);
  	    if (er != REG_NOMATCH) {
  		return er;
  	    }
  	}
- 	freeDFA(d);
  
  	t = t->right;
      }
--- 962,976 ----
  
  	MDEBUG(("calt n%d\n", t->id));
  
! 	d = getsubdfa(v, t->left);
  	NOERR();
  	if (longest(v, d, begin, end, (int *) NULL) == end) {
  	    MDEBUG(("calt matched\n"));
  	    er = cdissect(v, t->left, begin, end);
  	    if (er != REG_NOMATCH) {
  		return er;
  	    }
  	}
  
  	t = t->right;
      }
*************** citerdissect(struct vars * v,
*** 1017,1023 ****
  	return REG_ESPACE;
      endpts[0] = begin;
  
!     d = newDFA(v, &t->left->cnfa, &v->g->cmap, DOMALLOC);
      if (ISERR()) {
  	FREE(endpts);
  	return v->err;
--- 1034,1040 ----
  	return REG_ESPACE;
      endpts[0] = begin;
  
!     d = getsubdfa(v, t->left);
      if (ISERR()) {
  	FREE(endpts);
  	return v->err;
*************** citerdissect(struct vars * v,
*** 1094,1100 ****
  	    if (er == REG_NOMATCH)
  		break;
  	    /* oops, something failed */
- 	    freeDFA(d);
  	    FREE(endpts);
  	    return er;
  	}
--- 1111,1116 ----
*************** citerdissect(struct vars * v,
*** 1102,1108 ****
  	if (i > k) {
  	    /* satisfaction */
  	    MDEBUG(("%d successful\n", t->id));
- 	    freeDFA(d);
  	    FREE(endpts);
  	    return REG_OKAY;
  	}
--- 1118,1123 ----
*************** citerdissect(struct vars * v,
*** 1132,1138 ****
  
      /* all possibilities exhausted */
      MDEBUG(("%d failed\n", t->id));
-     freeDFA(d);
      FREE(endpts);
      return REG_NOMATCH;
  }
--- 1147,1152 ----
*************** creviterdissect(struct vars * v,
*** 1193,1199 ****
  	return REG_ESPACE;
      endpts[0] = begin;
  
!     d = newDFA(v, &t->left->cnfa, &v->g->cmap, DOMALLOC);
      if (ISERR()) {
  	FREE(endpts);
  	return v->err;
--- 1207,1213 ----
  	return REG_ESPACE;
      endpts[0] = begin;
  
!     d = getsubdfa(v, t->left);
      if (ISERR()) {
  	FREE(endpts);
  	return v->err;
*************** creviterdissect(struct vars * v,
*** 1276,1282 ****
  	    if (er == REG_NOMATCH)
  		break;
  	    /* oops, something failed */
- 	    freeDFA(d);
  	    FREE(endpts);
  	    return er;
  	}
--- 1290,1295 ----
*************** creviterdissect(struct vars * v,
*** 1284,1290 ****
  	if (i > k) {
  	    /* satisfaction */
  	    MDEBUG(("%d successful\n", t->id));
- 	    freeDFA(d);
  	    FREE(endpts);
  	    return REG_OKAY;
  	}
--- 1297,1302 ----
*************** creviterdissect(struct vars * v,
*** 1308,1314 ****
  
      /* all possibilities exhausted */
      MDEBUG(("%d failed\n", t->id));
-     freeDFA(d);
      FREE(endpts);
      return REG_NOMATCH;
  }
--- 1320,1325 ----
diff -pcdr tcl8.6.4.withqbr/generic/regguts.h tcl8.6.4/generic/regguts.h
*** tcl8.6.4.withqbr/generic/regguts.h	Thu Sep 17 18:52:18 2015
--- tcl8.6.4/generic/regguts.h	Fri Sep 18 14:49:53 2015
*************** struct colordesc {
*** 186,192 ****
      union tree *block;		/* block of solid color, if any */
  };
  
! /* the color map itself */
  struct colormap {
      int magic;
  #define	CMMAGIC	0x876
--- 186,199 ----
      union tree *block;		/* block of solid color, if any */
  };
  
! /*
!  * The color map itself
!  *
!  * Much of the data in the colormap struct is only used at compile time.
!  * However, the bulk of the space usage is in the "tree" structure, so it's
!  * not clear that there's much point in converting the rest to a more compact
!  * form when compilation is finished.
!  */
  struct colormap {
      int magic;
  #define	CMMAGIC	0x876
*************** struct cvec {
*** 242,256 ****
  struct state;
  
  struct arc {
!     int type;
! #define	ARCFREE	'\0'
      color co;
      struct state *from;		/* where it's from (and contained within) */
      struct state *to;		/* where it's to */
!     struct arc *outchain;	/* *from's outs chain or free chain */
  #define	freechain	outchain
!     struct arc *inchain;	/* *to's ins chain */
!     struct arc *colorchain;	/* color's arc chain */
      struct arc *colorchainRev;	/* back-link in color's arc chain */
  };
  
--- 249,262 ----
  struct state;
  
  struct arc {
!     int type;			/* 0 if free, else an NFA arc type code */
      color co;
      struct state *from;		/* where it's from (and contained within) */
      struct state *to;		/* where it's to */
!     struct arc *outchain;	/* link in *from's outs chain or free chain */
  #define	freechain	outchain
!     struct arc *inchain;	/* link in *to's ins chain */
!     struct arc *colorchain;	/* link in color's arc chain */
      struct arc *colorchainRev;	/* back-link in color's arc chain */
  };
  
*************** struct nfa {
*** 297,307 ****
  
  /*
   * definitions for compacted NFA
   */
  
  struct carc {
      color co;			/* COLORLESS is list terminator */
!     int to;			/* state number */
  };
  
  struct cnfa {
--- 303,324 ----
  
  /*
   * definitions for compacted NFA
+  *
+  * The main space savings in a compacted NFA is from making the arcs as small
+  * as possible.  We store only the transition color and next-state number for
+  * each arc.  The list of out arcs for each state is an array beginning at
+  * cnfa.states[statenumber], and terminated by a dummy carc struct with
+  * co == COLORLESS.
+  *
+  * The non-dummy carc structs are of two types: plain arcs and LACON arcs.
+  * Plain arcs just store the transition color number as "co".  LACON arcs
+  * store the lookahead constraint number plus cnfa.ncolors as "co".  LACON
+  * arcs can be distinguished from plain by testing for co >= cnfa.ncolors.
   */
  
  struct carc {
      color co;			/* COLORLESS is list terminator */
!     int to;			/* next-state number */
  };
  
  struct cnfa {
*************** struct cnfa {
*** 313,319 ****
--- 330,339 ----
      int post;			/* teardown state number */
      color bos[2];		/* colors, if any, assigned to BOS and BOL */
      color eos[2];		/* colors, if any, assigned to EOS and EOL */
+     char *stflags;		/* vector of per-state flags bytes */
+ #define CNFA_NOPROGRESS	01	/* flag bit for a no-progress state */
      struct carc **states;	/* vector of pointers to outarc lists */
+     /* states[n] are pointers into a single malloc'd array of arcs */
      struct carc *arcs;		/* the area for the lists */
  };
  #define	ZAPCNFA(cnfa)	((cnfa).nstates = 0)
*************** struct subre {
*** 366,372 ****
  #define	PREF(f)	((f)&NOPROP)
  #define	PREF2(f1, f2)	((PREF(f1) != 0) ? PREF(f1) : PREF(f2))
  #define	COMBINE(f1, f2)	(UP((f1)|(f2)) | PREF2(f1, f2))
!     short id;			/* ID of subre (1..ntree) */
      int subno;			/* subexpression number (for 'b' and '(') */
      short min;			/* min repetitions for iteration or backref */
      short max;			/* max repetitions for iteration or backref */
--- 386,392 ----
  #define	PREF(f)	((f)&NOPROP)
  #define	PREF2(f1, f2)	((PREF(f1) != 0) ? PREF(f1) : PREF(f2))
  #define	COMBINE(f1, f2)	(UP((f1)|(f2)) | PREF2(f1, f2))
!     short id;			/* ID of subre (1..ntree-1) */
      int subno;			/* subexpression number (for 'b' and '(') */
      short min;			/* min repetitions for iteration or backref */
      short max;			/* max repetitions for iteration or backref */
*************** struct guts {
*** 399,405 ****
      size_t nsub;		/* copy of re_nsub */
      struct subre *tree;
      struct cnfa search;		/* for fast preliminary search */
!     int ntree;
      struct colormap cmap;
      int FUNCPTR(compare, (const chr *, const chr *, size_t));
      struct subre *lacons;	/* lookahead-constraint vector */
--- 419,425 ----
      size_t nsub;		/* copy of re_nsub */
      struct subre *tree;
      struct cnfa search;		/* for fast preliminary search */
!     int ntree;			/* number of subre's, plus one */
      struct colormap cmap;
      int FUNCPTR(compare, (const chr *, const chr *, size_t));
      struct subre *lacons;	/* lookahead-constraint vector */