--- old/exception_lists/cstyle	Tue Feb 13 07:11:43 2018
+++ new/exception_lists/cstyle	Tue Feb 13 07:11:43 2018
@@ -124,6 +124,24 @@
 usr/src/grub/grub-0.97/stage2/shared.h
 usr/src/grub/grub-0.97/stage2/stage1_5.c
 usr/src/lib/libc/port/gen/arc4random_uniform.c
+usr/src/lib/libc/port/regex/regcomp.c
+usr/src/lib/libc/port/regex/regerror.c
+usr/src/lib/libc/port/regex/regexec.c
+usr/src/lib/libc/port/regex/tre-ast.c
+usr/src/lib/libc/port/regex/tre-ast.h
+usr/src/lib/libc/port/regex/tre-compile.c
+usr/src/lib/libc/port/regex/tre-compile.h
+usr/src/lib/libc/port/regex/tre-internal.h
+usr/src/lib/libc/port/regex/tre-match-backtrack.c
+usr/src/lib/libc/port/regex/tre-match-parallel.c
+usr/src/lib/libc/port/regex/tre-match-utils.h
+usr/src/lib/libc/port/regex/tre-mem.c
+usr/src/lib/libc/port/regex/tre-mem.h
+usr/src/lib/libc/port/regex/tre-parse.c
+usr/src/lib/libc/port/regex/tre-parse.h
+usr/src/lib/libc/port/regex/tre-stack.c
+usr/src/lib/libc/port/regex/tre-stack.h
+usr/src/lib/libc/port/regex/tre.h
 usr/src/lib/gss_mechs/mech_krb5/crypto/aes/aes_s2k.c
 usr/src/lib/gss_mechs/mech_krb5/crypto/cksumtype_to_string.c
 usr/src/lib/gss_mechs/mech_krb5/crypto/coll_proof_cksum.c
--- old/usr/src/cmd/awk_xpg4/awk1.c	Tue Feb 13 07:11:44 2018
+++ new/usr/src/cmd/awk_xpg4/awk1.c	Tue Feb 13 07:11:44 2018
@@ -1644,7 +1644,7 @@
 		return (REG_ESPACE);
 
 	if (rp == NULL || rpl == NULL || src == NULL || dst ==  NULL)
-		return (REG_EFATAL);
+		return (REG_BADPAT);
 
 	glob = 0;	/* match count */
 	ip = src;	/* source position */
--- old/usr/src/cmd/expr/compile.c	Tue Feb 13 07:11:44 2018
+++ new/usr/src/cmd/expr/compile.c	Tue Feb 13 07:11:44 2018
@@ -41,8 +41,6 @@
  *
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <stdio.h>
 #include <widec.h>
 #include <sys/types.h>
@@ -373,8 +371,6 @@
  * 1 REG_NOMATCH  No match		0  - Pattern didn't match
  * 2 REG_ECOLLATE Bad collation elmnt.	67 - Returned by compile on mbtowc err
  * 3 REG_EESCAPE  trailing \ in patrn	45 - } expected after \.
- * 4 REG_ENEWLINE \n before end pattrn	36 - Illegal or missing delimiter.
- * 5 REG_ENSUB	  Over 9 \( \) pairs 	43 - Too many \(
  * 6 REG_ESUBREG  Bad number in \[0-9]  25 - ``\digit'' out of range.
  * 7 REG_EBRACK   [ ] inbalance		49 - [ ] imbalance.
  * 8 REG_EPAREN   ( ) inbalance         42 - \(~\) imbalance.
@@ -385,10 +381,7 @@
  * 13 REG_ECTYPE  invalid char-class    67 - illegal byte sequence
  * 14 REG_BADPAT  syntax error		50 - Regular expression overflow.
  * 15 REG_BADBR   \{ \} contents bad	46 - First number exceeds 2nd in \{~\}
- * 16 REG_EFATAL  internal error	50 - Regular expression overflow.
  * 17 REG_ECHAR   bad mulitbyte char	67 - illegal byte sequence
- * 18 REG_STACK   stack overflow	50 - Regular expression overflow.
- * 19 REG_ENOSYS  function not supported 50- Regular expression overflow.
  *
  *	For reference here's the compile/step errno's. We don't generate
  *	41 here - it's done earlier, nor 44 since we can't tell if from 46.
@@ -417,12 +410,6 @@
 	case REG_EESCAPE:
 		regerrno = 45;
 		break;
-	case REG_ENEWLINE:
-		regerrno = 36;
-		break;
-	case REG_ENSUB:
-		regerrno = 43;
-		break;
 	case REG_ESUBREG:
 		regerrno = 25;
 		break;
@@ -453,18 +440,9 @@
 	case REG_BADBR:
 		regerrno = 46;
 		break;
-	case REG_EFATAL:
-		regerrno = 50;
-		break;
 	case REG_ECHAR:
 		regerrno = 67;
 		break;
-	case REG_STACK:
-		regerrno = 50;
-		break;
-	case REG_ENOSYS:
-		regerrno = 50;
-		break;
 	default:
 		regerrno = 50;
 		break;
--- old/usr/src/head/regex.h	Tue Feb 13 07:11:45 2018
+++ new/usr/src/head/regex.h	Tue Feb 13 07:11:45 2018
@@ -21,8 +21,6 @@
  */
 
 /*
- * Copyright 2014 Garrett D'Amore <garrett@damore.org>
- *
  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
@@ -33,7 +31,8 @@
  */
 
 /*
- * Copyright 2017 Nexenta Systems, Inc.
+ * Copyright 2014 Garrett D'Amore <garrett@damore.org>
+ * Copyright 2018 Nexenta Systems, Inc.
  */
 
 #ifndef	_REGEX_H
@@ -46,7 +45,8 @@
 extern "C" {
 #endif
 
-
+/* XXX is this still needed?!?! */
+#if 0
 /*
  * wchar_t is a built-in type in standard C++ and as such is not
  * defined here when using standard C++. However, the GNU compiler
@@ -70,79 +70,83 @@
 #endif
 #endif	/* !_WCHAR_T */
 #endif	/* !defined(__cplusplus) ... */
+#endif
 
 typedef ssize_t regoff_t;
 
 /* regcomp flags */
 #define	REG_BASIC	0x00000
-#define	REG_EXTENDED	0x00001		/* Use Extended Regular Expressions */
-#define	REG_NOSUB	0x00002		/* Don't set subexpression */
-#define	REG_ICASE	0x00004		/* Ignore case in match */
-#define	REG_NEWLINE	0x00008		/* Treat \n as regular character */
-#define	REG_DELIM	0x00010		/* legacy, no effect */
-#define	REG_DEBUG	0x00020		/* legacy, no effect */
-#define	REG_ANCHOR	0x00040		/* legacy, no effect */
-#define	REG_WORDS	0x00080		/* legacy, no effect */
-#define	REG_EGREP	0x01000		/* legacy, no effect */
-#define	REG_DUMP	0x02000		/* internal */
+#define	REG_EXTENDED	0x00001		/* use EREs (POSIX) */
+#define	REG_NOSUB	0x00002		/* don't set subexpression (POSIX) */
+#define	REG_ICASE	0x00004		/* ignore case in match (POSIX) */
+#define	REG_NEWLINE	0x00008		/* treat \n as regular char (POSIX) */
+/* was	REG_DELIM	0x00010 */
+/* was	REG_DEBUG	0x00020 */
+/* was	REG_ANCHOR	0x00040 */
+/* was	REG_WORDS	0x00080 */
+/* was	REG_EGREP	0x01000 */
+/* was	REG_DUMP	0x02000 */
 #define	REG_PEND	0x04000		/* NULs are ordinary characters */
 #define	REG_NOSPEC	0x08000		/* no special characters */
+#define	REG_LITERAL	0x08000		/* no special characters */
 
-/* internal flags */
-#define	REG_MUST	0x00100		/* legacy, no effect */
-
 /* regexec flags */
-#define	REG_NOTBOL	0x00200		/* string is not BOL */
-#define	REG_NOTEOL	0x00400		/* string has no EOL */
-#define	REG_NOOPT	0x00800		/* legacy, no effect */
-#define	REG_STARTEND	0x10000		/* match whole pattern */
-#define	REG_TRACE	0x20000		/* tracing of execution */
-#define	REG_LARGE	0x40000		/* force large representation */
-#define	REG_BACKR	0x80000		/* force use of backref code */
+/* was	REG_MUST	0x00100 */
+#define	REG_NOTBOL	0x00200		/* string is not BOL (POSIX) */
+#define	REG_NOTEOL	0x00400		/* string has no EOL (POSIX) */
+/* was	REG_NOOPT	0x00800 */
+#define	REG_STARTEND	0x10000		/* match whole pattern (BSD) */
 
 /* regcomp and regexec return codes */
 #define	REG_OK		0		/* success (non-standard) */
-#define	REG_NOMATCH	1		/* regexec failed to match */
-#define	REG_ECOLLATE	2		/* invalid collation element ref. */
-#define	REG_EESCAPE	3		/* trailing \ in pattern */
-#define	REG_ENEWLINE	4		/* \n found before end of pattern */
-#define	REG_ENSUB	5		/* more than 9 \( \) pairs (OBS) */
-#define	REG_ESUBREG	6		/* number in \[0-9] invalid */
-#define	REG_EBRACK	7		/* [ ] imbalance */
-#define	REG_EPAREN	8		/* ( ) imbalance */
-#define	REG_EBRACE	9		/* \{ \} imbalance */
-#define	REG_ERANGE	10		/* invalid endpoint in range */
-#define	REG_ESPACE	11		/* no memory for compiled pattern */
-#define	REG_BADRPT	12		/* invalid repetition */
-#define	REG_ECTYPE	13		/* invalid char-class type */
-#define	REG_BADPAT	14		/* syntax error */
-#define	REG_BADBR	15		/* \{ \} contents bad */
-#define	REG_EFATAL	16		/* internal error, not POSIX.2 */
+#define	REG_NOMATCH	1		/* regexec failed to match (POSIX) */
+#define	REG_ECOLLATE	2		/* invalid coll. element ref. (POSIX) */
+#define	REG_EESCAPE	3		/* trailing \ in pattern (POSIX) */
+/* was	REG_ENEWLINE	4 */
+/* was	REG_ENSUB	5 */
+#define	REG_ESUBREG	6		/* number in \[0-9] invalid (POSIX) */
+#define	REG_EBRACK	7		/* [ ] imbalance (POSIX) */
+#define	REG_EPAREN	8		/* ( ) imbalance (POSIX) */
+#define	REG_EBRACE	9		/* \{ \} imbalance (POSIX) */
+#define	REG_ERANGE	10		/* invalid endpoint in range (POSIX) */
+#define	REG_ESPACE	11		/* out of memory (POSIX) */
+#define	REG_BADRPT	12		/* invalid repetition (POSIX) */
+#define	REG_ECTYPE	13		/* invalid char-class type (POSIX) */
+#define	REG_BADPAT	14		/* syntax error (POSIX) */
+#define	REG_BADBR	15		/* \{ \} contents bad (POSIX) */
+/* was	REG_EFATAL	16 */
 #define	REG_ECHAR	17		/* invalid multibyte character */
-#define	REG_STACK	18		/* backtrack stack overflow */
+#define	REG_ILLSEQ	17		/* invalid multibyte character (BSD) */
+/* was	REG_STACK	18 */
+/* REG_ENOSYS was removed in XPG7 */
+#if defined(_STRICT_SYMBOLS) && !defined(_XPG7)
 #define	REG_ENOSYS	19		/* function not supported (XPG4) */
-#define	REG__LAST	20		/* first unused code */
-#define	REG_EBOL	21		/* ^ anchor and not BOL */
-#define	REG_EEOL	22		/* $ anchor and not EOL */
-#define	REG_ATOI	255		/* convert name to number (!) */
-#define	REG_ITOA	256		/* convert number to name (!) */
+#endif
+/* was	REG__LAST	20 */
+/* was	REG_EBOL	21 */
+/* was	REG_EEOL	22 */
+#define	REG_EMPTY	23		/* empty (sub)expression */
+#define	REG_INVARG	24		/* invalid argument */
 
 #define	_REG_BACKREF_MAX 9		/* Max # of subexp. backreference */
 
+/*
+ * Note that any changes to this structure have to preserve sizing,
+ * as it is baked into applications.  Size needs to stay 24/48 bytes
+ * for 32/64 bit libc, respectively.
+ */
 typedef struct {		/* regcomp() data saved for regexec() */
 	size_t  re_nsub;	/* # of subexpressions in RE pattern */
+	void *value;		/* internal use only */
+	int re_magic;
+	const void *re_endp;
 
 	/*
-	 * Internal use only.  Note that any changes to this structure
-	 * have to preserve sizing, as it is baked into applications.
+	 * These are here for binary compatibility (see the note about
+	 * sizing above).
 	 */
-	struct re_guts *re_g;
-	int re_magic;
-	const char *re_endp;
-
-	/* here for compat */
-	size_t	re_len;		/* # wchar_t chars in compiled pattern */
-	struct _regex_ext_t *re_sc;	/* for binary compatibility */
+	size_t	__pad1;
+	void	*__pad2;
 } regex_t;
 
 /* subexpression positions */
@@ -152,7 +156,6 @@
 	int		rm_ss, rm_es;	/* Used internally */
 } regmatch_t;
 
-
 /*
  * IEEE Std 1003.2 ("POSIX.2") regular expressions API.
  */
--- old/usr/src/lib/libc/amd64/Makefile	Tue Feb 13 07:11:45 2018
+++ new/usr/src/lib/libc/amd64/Makefile	Tue Feb 13 07:11:45 2018
@@ -437,6 +437,7 @@
 	getwd.o			\
 	getwidth.o		\
 	getxby_door.o		\
+	glob.o			\
 	gtxt.o			\
 	hsearch.o		\
 	iconv.o			\
@@ -514,9 +515,9 @@
 	readdir.o		\
 	readdir_r.o		\
 	reallocarray.o		\
-	recallocarray.o		\
 	realpath.o		\
 	reboot.o		\
+	recallocarray.o		\
 	regexpr.o		\
 	remove.o		\
 	rewinddir.o		\
@@ -593,6 +594,7 @@
 	waitpid.o		\
 	walkstack.o		\
 	wdata.o			\
+	wordexp.o		\
 	xgetwidth.o		\
 	xpg4.o			\
 	xpg6.o
@@ -933,14 +935,18 @@
 	zone.o
 
 PORTREGEX=			\
-	glob.o			\
 	regcmp.o		\
 	regcomp.o		\
 	regerror.o		\
 	regex.o			\
 	regexec.o		\
-	regfree.o		\
-	wordexp.o
+	tre-ast.o		\
+	tre-compile.o		\
+	tre-match-backtrack.o	\
+	tre-match-parallel.o	\
+	tre-mem.o		\
+	tre-parse.o		\
+	tre-stack.o
 
 VALUES=				\
 	values-Xa.o
--- old/usr/src/lib/libc/i386/Makefile.com	Tue Feb 13 07:11:46 2018
+++ new/usr/src/lib/libc/i386/Makefile.com	Tue Feb 13 07:11:46 2018
@@ -340,6 +340,7 @@
 	_xftw64.o		\
 	attropen64.o		\
 	ftw64.o			\
+	glob64.o		\
 	mkstemp64.o		\
 	nftw64.o		\
 	tell64.o		\
@@ -473,6 +474,7 @@
 	getwd.o			\
 	getwidth.o		\
 	getxby_door.o		\
+	glob.o			\
 	gtxt.o			\
 	hsearch.o		\
 	iconv.o			\
@@ -550,9 +552,9 @@
 	readdir.o		\
 	readdir_r.o		\
 	reallocarray.o		\
-	recallocarray.o		\
 	realpath.o		\
 	reboot.o		\
+	recallocarray.o		\
 	regexpr.o		\
 	remove.o		\
 	rewinddir.o		\
@@ -625,6 +627,7 @@
 	waitpid.o		\
 	walkstack.o		\
 	wdata.o			\
+	wordexp.o		\
 	xgetwidth.o		\
 	xpg4.o			\
 	xpg6.o
@@ -972,18 +975,19 @@
 	zone.o
 
 PORTREGEX=			\
-	glob.o			\
 	regcmp.o		\
 	regcomp.o		\
 	regerror.o		\
 	regex.o			\
 	regexec.o		\
-	regfree.o		\
-	wordexp.o
+	tre-ast.o		\
+	tre-compile.o		\
+	tre-match-backtrack.o	\
+	tre-match-parallel.o	\
+	tre-mem.o		\
+	tre-parse.o		\
+	tre-stack.o
 
-PORTREGEX64=			\
-	glob64.o
-
 MOSTOBJS=			\
 	$(STRETS)		\
 	$(CRTOBJS)		\
@@ -1006,7 +1010,6 @@
 	$(PORTPRINT_C89)	\
 	$(PORTPRINT_W)		\
 	$(PORTREGEX)		\
-	$(PORTREGEX64)		\
 	$(PORTSTDIO)		\
 	$(PORTSTDIO64)		\
 	$(PORTSTDIO_C89)	\
@@ -1241,9 +1244,6 @@
 $(PORTGEN64:%=pics/%) := \
 	CPPFLAGS += -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
 
-$(PORTREGEX64:%=pics/%) := \
-	CPPFLAGS += -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
-
 $(PORTSTDIO64:%=pics/%) := \
 	CPPFLAGS += -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
 
--- old/usr/src/lib/libc/port/regex/glob.c	Tue Feb 13 07:11:46 2018
+++ /dev/null	Tue Feb 13 07:04:46 2018
@@ -1,1360 +0,0 @@
-/*
- * Copyright (c) 1989, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Guido van Rossum.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * Copyright (c) 2013 Gary Mills
- */
-
-/*
- * glob(3) -- a superset of the one defined in POSIX 1003.2.
- *
- * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
- *
- * Optional extra services, controlled by flags not defined by POSIX:
- *
- * GLOB_QUOTE:
- *	Escaping convention: \ inhibits any special meaning the following
- *	character might have (except \ at end of string is retained).
- * GLOB_MAGCHAR:
- *	Set in gl_flags if pattern contained a globbing character.
- * GLOB_NOMAGIC:
- *	Same as GLOB_NOCHECK, but it will only append pattern if it did
- *	not contain any magic characters.  [Used in csh style globbing]
- * GLOB_ALTDIRFUNC:
- *	Use alternately specified directory access functions.
- * GLOB_TILDE:
- *	expand ~user/foo to the /home/dir/of/user/foo
- * GLOB_BRACE:
- *	expand {1,2}{a,b} to 1a 1b 2a 2b
- * gl_matchc:
- *	Number of matches in the current invocation of glob.
- */
-
-#include "lint.h"
-
-#include <sys/param.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <dirent.h>
-#include <errno.h>
-#include <glob.h>
-#include <limits.h>
-#include <pwd.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <wchar.h>
-#include <wctype.h>
-
-/*
- * This is the legacy glob_t prior to illumos enhancement 1097,
- * used when old programs call the old libc glob functions.
- * (New programs call the _glob_ext, _globfree_ext functions.)
- * This struct should be considered "carved in stone".
- */
-typedef	struct	old_glob	{
-	size_t	gl_pathc;		/* Count of paths matched by pattern */
-	char	**gl_pathv;		/* List of matched pathnames */
-	size_t	gl_offs;		/* # of slots reserved in gl_pathv */
-	/* following are internal to the implementation */
-	char	**gl_pathp;		/* gl_pathv + gl_offs */
-	int	gl_pathn;		/* # of elements allocated */
-}	old_glob_t;
-
-/*
- * For old programs, the external names need to be the old names:
- * glob() and globfree() .  We've redefined those already to
- *  _glob_ext() and _globfree_ext() .  Now redefine old_glob()
- * and old_globfree() to glob() and globfree() .
- */
-#ifdef __PRAGMA_REDEFINE_EXTNAME
-#pragma	redefine_extname	old_glob	glob
-#pragma	redefine_extname	old_globfree	globfree
-#endif /* __PRAGMA_REDEFINE_EXTNAME */
-extern int old_glob(const char *, int, int (*)(const char *, int),
-    old_glob_t *);
-extern void old_globfree(old_glob_t *);
-
-/*
- * The various extensions to glob(3C) allow for stat and dirent structures to
- * show up whose size may change in a largefile environment. If libc defines
- * _FILE_OFFSET_BITS to be 64 that is the key to indicate that we're building
- * the LFS version of this file. As such, we rename the public functions here,
- * _glob_ext() and _globfree_ext() to have a 64 suffix. When building the LFS
- * version, we do not include the old versions.
- */
-#if !defined(_LP64) && _FILE_OFFSET_BITS == 64
-#define	_glob_ext	_glob_ext64
-#define	_globfree_ext	_globfree_ext64
-#endif	/* !_LP64 && _FILE_OFFSET_BITS == 64 */
-
-#define	DOLLAR		'$'
-#define	DOT		'.'
-#define	EOS		'\0'
-#define	LBRACKET	'['
-#define	NOT		'!'
-#define	QUESTION	'?'
-#define	QUOTE		'\\'
-#define	RANGE		'-'
-#define	RBRACKET	']'
-#define	SEP		'/'
-#define	STAR		'*'
-#define	TILDE		'~'
-#define	UNDERSCORE	'_'
-#define	LBRACE		'{'
-#define	RBRACE		'}'
-#define	SLASH		'/'
-#define	COMMA		','
-#define	COLON		':'
-
-#define	M_QUOTE		0x800000
-#define	M_PROTECT	0x400000
-
-typedef struct wcat {
-	wchar_t w_wc;
-	uint_t w_at;
-} wcat_t;
-
-#define	M_ALL		'*'	/* Plus M_QUOTE */
-#define	M_END		']'	/* Plus M_QUOTE */
-#define	M_NOT		'!'	/* Plus M_QUOTE */
-#define	M_ONE		'?'	/* Plus M_QUOTE */
-#define	M_RNG		'-'	/* Plus M_QUOTE */
-#define	M_SET		'['	/* Plus M_QUOTE */
-#define	M_CLASS		':'	/* Plus M_QUOTE */
-#define	ismeta(c)	(((c).w_at&M_QUOTE) != 0)
-
-#define	INITIAL			8	/* initial pathv allocation */
-
-#define	GLOB_LIMIT_MALLOC	65536
-#define	GLOB_LIMIT_STAT		2048
-#define	GLOB_LIMIT_READDIR	16384
-
-struct glob_lim {
-	size_t	glim_malloc;
-	size_t	glim_stat;
-	size_t	glim_readdir;
-};
-
-struct glob_path_stat {
-	char		*gps_path;
-	struct stat	*gps_stat;
-};
-
-static int	 compare(const void *, const void *);
-static int	 compare_gps(const void *, const void *);
-static int	 g_Ctoc(const wcat_t *, char *, uint_t);
-static int	 g_lstat(wcat_t *, struct stat *, glob_t *);
-static DIR	*g_opendir(wcat_t *, glob_t *);
-static wcat_t	*g_strchr(const wcat_t *, wchar_t);
-static int	 g_stat(wcat_t *, struct stat *, glob_t *);
-static int	 glob0(const wcat_t *, glob_t *, struct glob_lim *,
-			int (*)(const char *, int));
-static int	 glob1(wcat_t *, wcat_t *, glob_t *, struct glob_lim *,
-			int (*)(const char *, int));
-static int	 glob2(wcat_t *, wcat_t *, wcat_t *, wcat_t *, wcat_t *,
-			wcat_t *, glob_t *, struct glob_lim *,
-			int (*)(const char *, int));
-static int	 glob3(wcat_t *, wcat_t *, wcat_t *, wcat_t *, wcat_t *,
-			wcat_t *, wcat_t *, glob_t *, struct glob_lim *,
-			int (*)(const char *, int));
-static int	 globextend(const wcat_t *, glob_t *, struct glob_lim *,
-		    struct stat *);
-static
-const wcat_t	*globtilde(const wcat_t *, wcat_t *, size_t, glob_t *);
-static int	 globexp1(const wcat_t *, glob_t *, struct glob_lim *,
-		    int (*)(const char *, int));
-static int	 globexp2(const wcat_t *, const wcat_t *, glob_t *,
-		    struct glob_lim *, int (*)(const char *, int));
-static int	 match(wcat_t *, wcat_t *, wcat_t *);
-
-/*
- * Extended glob() function, selected by #pragma redefine_extname
- * in glob.h with the external name _glob_ext() .
- */
-int
-_glob_ext(const char *pattern, int flags, int (*errfunc)(const char *, int),
-    glob_t *pglob)
-{
-	const char *patnext;
-	int n;
-	size_t patlen;
-	wchar_t c;
-	wcat_t *bufnext, *bufend, patbuf[PATH_MAX];
-	struct glob_lim limit = { 0, 0, 0 };
-
-	patnext = pattern;
-	if (!(flags & GLOB_APPEND)) {
-		pglob->gl_pathc = 0;
-		pglob->gl_pathn = 0;
-		pglob->gl_pathv = NULL;
-		if ((flags & GLOB_KEEPSTAT) != 0)
-			pglob->gl_statv = NULL;
-		if (!(flags & GLOB_DOOFFS))
-			pglob->gl_offs = 0;
-	}
-	pglob->gl_flags = flags & ~GLOB_MAGCHAR;
-	pglob->gl_matchc = 0;
-
-	if ((patlen = strnlen(pattern, PATH_MAX)) == PATH_MAX)
-		return (GLOB_NOMATCH);
-
-	if (pglob->gl_offs >= INT_MAX || pglob->gl_pathc >= INT_MAX ||
-	    pglob->gl_pathc >= INT_MAX - pglob->gl_offs - 1)
-		return (GLOB_NOSPACE);
-
-	bufnext = patbuf;
-	bufend = bufnext + PATH_MAX - 1;
-	patlen += 1;
-	if (flags & GLOB_NOESCAPE) {
-		while (bufnext < bufend) {
-			if ((n = mbtowc(&c, patnext, patlen)) > 0) {
-				patnext += n;
-				patlen -= n;
-				bufnext->w_at = 0;
-				(bufnext++)->w_wc = c;
-			} else if (n == 0) {
-				break;
-			} else {
-				return (GLOB_NOMATCH);
-			}
-		}
-	} else {
-		/* Protect the quoted characters. */
-		while (bufnext < bufend) {
-			if ((n = mbtowc(&c, patnext, patlen)) > 0) {
-				patnext += n;
-				patlen -= n;
-				if (c == QUOTE) {
-					n = mbtowc(&c, patnext, patlen);
-					if (n < 0)
-						return (GLOB_NOMATCH);
-					if (n > 0) {
-						patnext += n;
-						patlen -= n;
-					}
-					if (n == 0)
-						c = QUOTE;
-					bufnext->w_at = M_PROTECT;
-					(bufnext++)->w_wc = c;
-				} else {
-					bufnext->w_at = 0;
-					(bufnext++)->w_wc = c;
-				}
-			} else if (n == 0) {
-				break;
-			} else {
-				return (GLOB_NOMATCH);
-			}
-		}
-	}
-	bufnext->w_at = 0;
-	bufnext->w_wc = EOS;
-
-	if (flags & GLOB_BRACE)
-		return (globexp1(patbuf, pglob, &limit, errfunc));
-	else
-		return (glob0(patbuf, pglob, &limit, errfunc));
-}
-
-/*
- * Expand recursively a glob {} pattern. When there is no more expansion
- * invoke the standard globbing routine to glob the rest of the magic
- * characters
- */
-static int
-globexp1(const wcat_t *pattern, glob_t *pglob, struct glob_lim *limitp,
-    int (*errfunc)(const char *, int))
-{
-	const wcat_t *ptr = pattern;
-
-	/* Protect a single {}, for find(1), like csh */
-	if (pattern[0].w_wc == LBRACE && pattern[1].w_wc == RBRACE &&
-	    pattern[2].w_wc == EOS)
-		return (glob0(pattern, pglob, limitp, errfunc));
-
-	if ((ptr = (const wcat_t *) g_strchr(ptr, LBRACE)) != NULL)
-		return (globexp2(ptr, pattern, pglob, limitp, errfunc));
-
-	return (glob0(pattern, pglob, limitp, errfunc));
-}
-
-
-/*
- * Recursive brace globbing helper. Tries to expand a single brace.
- * If it succeeds then it invokes globexp1 with the new pattern.
- * If it fails then it tries to glob the rest of the pattern and returns.
- */
-static int
-globexp2(const wcat_t *ptr, const wcat_t *pattern, glob_t *pglob,
-    struct glob_lim *limitp, int (*errfunc)(const char *, int))
-{
-	int	i, rv;
-	wcat_t   *lm, *ls;
-	const wcat_t *pe, *pm, *pl;
-	wcat_t    patbuf[PATH_MAX];
-
-	/* copy part up to the brace */
-	for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
-		;
-	lm->w_at = 0;
-	lm->w_wc = EOS;
-	ls = lm;
-
-	/* Find the balanced brace */
-	for (i = 0, pe = ++ptr; pe->w_wc != EOS; pe++)
-		if (pe->w_wc == LBRACKET) {
-			/* Ignore everything between [] */
-			for (pm = pe++; pe->w_wc != RBRACKET &&
-			    pe->w_wc != EOS; pe++)
-				;
-			if (pe->w_wc == EOS) {
-				/*
-				 * We could not find a matching RBRACKET.
-				 * Ignore and just look for RBRACE
-				 */
-				pe = pm;
-			}
-		} else if (pe->w_wc == LBRACE) {
-			i++;
-		} else if (pe->w_wc == RBRACE) {
-			if (i == 0)
-				break;
-			i--;
-		}
-
-	/* Non matching braces; just glob the pattern */
-	if (i != 0 || pe->w_wc == EOS)
-		return (glob0(patbuf, pglob, limitp, errfunc));
-
-	for (i = 0, pl = pm = ptr; pm <= pe; pm++) {
-		switch (pm->w_wc) {
-		case LBRACKET:
-			/* Ignore everything between [] */
-			for (pl = pm++; pm->w_wc != RBRACKET && pm->w_wc != EOS;
-			    pm++)
-				;
-			if (pm->w_wc == EOS) {
-				/*
-				 * We could not find a matching RBRACKET.
-				 * Ignore and just look for RBRACE
-				 */
-				pm = pl;
-			}
-			break;
-
-		case LBRACE:
-			i++;
-			break;
-
-		case RBRACE:
-			if (i) {
-				i--;
-				break;
-			}
-			/* FALLTHROUGH */
-		case COMMA:
-			if (i && pm->w_wc == COMMA)
-				break;
-			else {
-				/* Append the current string */
-				for (lm = ls; (pl < pm); *lm++ = *pl++)
-					;
-
-				/*
-				 * Append the rest of the pattern after the
-				 * closing brace
-				 */
-				for (pl = pe + 1;
-				    (*lm++ = *pl++).w_wc != EOS; /* */)
-					;
-
-				/* Expand the current pattern */
-				rv = globexp1(patbuf, pglob, limitp, errfunc);
-				if (rv && rv != GLOB_NOMATCH)
-					return (rv);
-
-				/* move after the comma, to the next string */
-				pl = pm + 1;
-			}
-			break;
-
-		default:
-			break;
-		}
-	}
-	return (0);
-}
-
-
-
-/*
- * expand tilde from the passwd file.
- */
-static const wcat_t *
-globtilde(const wcat_t *pattern, wcat_t *patbuf, size_t patbuf_len,
-    glob_t *pglob)
-{
-	struct passwd *pwd;
-	char *h;
-	const wcat_t *p;
-	wcat_t *b, *eb, *q;
-	int n;
-	size_t lenh;
-	wchar_t c;
-
-	if (pattern->w_wc != TILDE || !(pglob->gl_flags & GLOB_TILDE))
-		return (pattern);
-
-	/* Copy up to the end of the string or / */
-	eb = &patbuf[patbuf_len - 1];
-	for (p = pattern + 1, q = patbuf;
-	    q < eb && p->w_wc != EOS && p->w_wc != SLASH; *q++ = *p++)
-		;
-
-	q->w_at = 0;
-	q->w_wc = EOS;
-
-	/* What to do if patbuf is full? */
-
-	if (patbuf[0].w_wc == EOS) {
-		/*
-		 * handle a plain ~ or ~/ by expanding $HOME
-		 * first and then trying the password file
-		 */
-		if (issetugid() != 0)
-			return (pattern);
-		if ((h = getenv("HOME")) == NULL) {
-			if ((pwd = getpwuid(getuid())) == NULL)
-				return (pattern);
-			else
-				h = pwd->pw_dir;
-		}
-	} else {
-		/*
-		 * Expand a ~user
-		 */
-		if ((pwd = getpwnam((char *)patbuf)) == NULL)
-			return (pattern);
-		else
-			h = pwd->pw_dir;
-	}
-
-	/* Copy the home directory */
-	lenh = strlen(h) + 1;
-	for (b = patbuf; b < eb && *h != EOS; b++) {
-		if ((n = mbtowc(&c, h, lenh)) > 0) {
-			h += n;
-			lenh -= n;
-			b->w_at = 0;
-			b->w_wc = c;
-		} else if (n < 0) {
-			return (pattern);
-		} else {
-			break;
-		}
-	}
-
-	/* Append the rest of the pattern */
-	while (b < eb && (*b++ = *p++).w_wc != EOS)
-		;
-	b->w_at = 0;
-	b->w_wc = EOS;
-
-	return (patbuf);
-}
-
-static int
-g_charclass(const wcat_t **patternp, wcat_t **bufnextp)
-{
-	const wcat_t *pattern = *patternp + 1;
-	wcat_t *bufnext = *bufnextp;
-	const wcat_t *colon;
-	char cbuf[MB_LEN_MAX + 32];
-	wctype_t cc;
-	size_t len;
-
-	if ((colon = g_strchr(pattern, COLON)) == NULL ||
-	    colon[1].w_wc != RBRACKET)
-		return (1);	/* not a character class */
-
-	len = (size_t)(colon - pattern);
-	if (len + MB_LEN_MAX + 1 > sizeof (cbuf))
-		return (-1);	/* invalid character class */
-	{
-		wchar_t w;
-		const wcat_t *s1 = pattern;
-		char *s2 = cbuf;
-		size_t n = len;
-
-		/* Copy the string. */
-		while (n > 0) {
-			w = (s1++)->w_wc;
-			/* Character class names must be ASCII. */
-			if (iswascii(w)) {
-				n--;
-				*s2++ = w;
-			} else {
-				return (-1);	/* invalid character class */
-			}
-		}
-		*s2 = EOS;
-	}
-	if ((cc = wctype(cbuf)) == 0)
-		return (-1);	/* invalid character class */
-	bufnext->w_at = M_QUOTE;
-	(bufnext++)->w_wc = M_CLASS;
-	bufnext->w_at = 0;
-	(bufnext++)->w_wc = cc;
-	*bufnextp = bufnext;
-	*patternp += len + 3;
-
-	return (0);
-}
-
-/*
- * The main glob() routine: compiles the pattern (optionally processing
- * quotes), calls glob1() to do the real pattern matching, and finally
- * sorts the list (unless unsorted operation is requested).  Returns 0
- * if things went well, nonzero if errors occurred.  It is not an error
- * to find no matches.
- */
-static int
-glob0(const wcat_t *pattern, glob_t *pglob, struct glob_lim *limitp,
-    int (*errfunc)(const char *, int))
-{
-	const wcat_t *qpatnext;
-	int err, oldpathc;
-	wchar_t c;
-	int a;
-	wcat_t *bufnext, patbuf[PATH_MAX];
-
-	qpatnext = globtilde(pattern, patbuf, PATH_MAX, pglob);
-	oldpathc = pglob->gl_pathc;
-	bufnext = patbuf;
-
-	/*
-	 * We don't need to check for buffer overflow any more.
-	 * The pattern has already been copied to an internal buffer.
-	 */
-	while ((a = qpatnext->w_at), (c = (qpatnext++)->w_wc) != EOS) {
-		switch (c) {
-		case LBRACKET:
-			if (a != 0) {
-				bufnext->w_at = a;
-				(bufnext++)->w_wc = c;
-				break;
-			}
-			a = qpatnext->w_at;
-			c = qpatnext->w_wc;
-			if (a == 0 && c == NOT)
-				++qpatnext;
-			if (qpatnext->w_wc == EOS ||
-			    g_strchr(qpatnext+1, RBRACKET) == NULL) {
-				bufnext->w_at = 0;
-				(bufnext++)->w_wc = LBRACKET;
-				if (a == 0 && c == NOT)
-					--qpatnext;
-				break;
-			}
-			bufnext->w_at = M_QUOTE;
-			(bufnext++)->w_wc = M_SET;
-			if (a == 0 && c == NOT) {
-				bufnext->w_at = M_QUOTE;
-				(bufnext++)->w_wc = M_NOT;
-			}
-			a = qpatnext->w_at;
-			c = (qpatnext++)->w_wc;
-			do {
-				if (a == 0 && c == LBRACKET &&
-				    qpatnext->w_wc == COLON) {
-					do {
-						err = g_charclass(&qpatnext,
-						    &bufnext);
-						if (err)
-							break;
-						a = qpatnext->w_at;
-						c = (qpatnext++)->w_wc;
-					} while (a == 0 && c == LBRACKET &&
-					    qpatnext->w_wc == COLON);
-					if (err == -1 &&
-					    !(pglob->gl_flags & GLOB_NOCHECK))
-						return (GLOB_NOMATCH);
-					if (a == 0 && c == RBRACKET)
-						break;
-				}
-				bufnext->w_at = a;
-				(bufnext++)->w_wc = c;
-				if (qpatnext->w_at == 0 &&
-				    qpatnext->w_wc == RANGE) {
-					a = qpatnext[1].w_at;
-					c = qpatnext[1].w_wc;
-					if (qpatnext[1].w_at != 0 ||
-					    qpatnext[1].w_wc != RBRACKET) {
-						bufnext->w_at = M_QUOTE;
-						(bufnext++)->w_wc = M_RNG;
-						bufnext->w_at = a;
-						(bufnext++)->w_wc = c;
-						qpatnext += 2;
-					}
-				}
-				a = qpatnext->w_at;
-				c = (qpatnext++)->w_wc;
-			} while (a != 0 || c != RBRACKET);
-			pglob->gl_flags |= GLOB_MAGCHAR;
-			bufnext->w_at = M_QUOTE;
-			(bufnext++)->w_wc = M_END;
-			break;
-		case QUESTION:
-			if (a != 0) {
-				bufnext->w_at = a;
-				(bufnext++)->w_wc = c;
-				break;
-			}
-			pglob->gl_flags |= GLOB_MAGCHAR;
-			bufnext->w_at = M_QUOTE;
-			(bufnext++)->w_wc = M_ONE;
-			break;
-		case STAR:
-			if (a != 0) {
-				bufnext->w_at = a;
-				(bufnext++)->w_wc = c;
-				break;
-			}
-			pglob->gl_flags |= GLOB_MAGCHAR;
-			/*
-			 * collapse adjacent stars to one,
-			 * to avoid exponential behavior
-			 */
-			if (bufnext == patbuf ||
-			    bufnext[-1].w_at != M_QUOTE ||
-			    bufnext[-1].w_wc != M_ALL) {
-				bufnext->w_at = M_QUOTE;
-				(bufnext++)->w_wc = M_ALL;
-			}
-			break;
-		default:
-			bufnext->w_at = a;
-			(bufnext++)->w_wc = c;
-			break;
-		}
-	}
-	bufnext->w_at = 0;
-	bufnext->w_wc = EOS;
-
-	if ((err = glob1(patbuf, patbuf+PATH_MAX-1, pglob, limitp, errfunc))
-	    != 0)
-		return (err);
-
-	/*
-	 * If there was no match we are going to append the pattern
-	 * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
-	 * and the pattern did not contain any magic characters
-	 * GLOB_NOMAGIC is there just for compatibility with csh.
-	 */
-	if (pglob->gl_pathc == oldpathc) {
-		if ((pglob->gl_flags & GLOB_NOCHECK) ||
-		    ((pglob->gl_flags & GLOB_NOMAGIC) &&
-		    !(pglob->gl_flags & GLOB_MAGCHAR)))
-			return (globextend(pattern, pglob, limitp, NULL));
-		else
-			return (GLOB_NOMATCH);
-	}
-	if (!(pglob->gl_flags & GLOB_NOSORT)) {
-		if ((pglob->gl_flags & GLOB_KEEPSTAT)) {
-			/* Keep the paths and stat info synced during sort */
-			struct glob_path_stat *path_stat;
-			int i;
-			int n = pglob->gl_pathc - oldpathc;
-			int o = pglob->gl_offs + oldpathc;
-
-			if ((path_stat = calloc(n, sizeof (*path_stat))) ==
-			    NULL)
-				return (GLOB_NOSPACE);
-			for (i = 0; i < n; i++) {
-				path_stat[i].gps_path = pglob->gl_pathv[o + i];
-				path_stat[i].gps_stat = pglob->gl_statv[o + i];
-			}
-			qsort(path_stat, n, sizeof (*path_stat), compare_gps);
-			for (i = 0; i < n; i++) {
-				pglob->gl_pathv[o + i] = path_stat[i].gps_path;
-				pglob->gl_statv[o + i] = path_stat[i].gps_stat;
-			}
-			free(path_stat);
-		} else {
-			qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
-			    pglob->gl_pathc - oldpathc, sizeof (char *),
-			    compare);
-		}
-	}
-	return (0);
-}
-
-static int
-compare(const void *p, const void *q)
-{
-	return (strcmp(*(char **)p, *(char **)q));
-}
-
-static int
-compare_gps(const void *_p, const void *_q)
-{
-	const struct glob_path_stat *p = (const struct glob_path_stat *)_p;
-	const struct glob_path_stat *q = (const struct glob_path_stat *)_q;
-
-	return (strcmp(p->gps_path, q->gps_path));
-}
-
-static int
-glob1(wcat_t *pattern, wcat_t *pattern_last, glob_t *pglob,
-    struct glob_lim *limitp, int (*errfunc)(const char *, int))
-{
-	wcat_t pathbuf[PATH_MAX];
-
-	/* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
-	if (pattern->w_wc == EOS)
-		return (0);
-	return (glob2(pathbuf, pathbuf+PATH_MAX-1,
-	    pathbuf, pathbuf+PATH_MAX-1,
-	    pattern, pattern_last, pglob, limitp, errfunc));
-}
-
-/*
- * The functions glob2 and glob3 are mutually recursive; there is one level
- * of recursion for each segment in the pattern that contains one or more
- * meta characters.
- */
-static int
-glob2(wcat_t *pathbuf, wcat_t *pathbuf_last, wcat_t *pathend,
-    wcat_t *pathend_last, wcat_t *pattern, wcat_t *pattern_last,
-    glob_t *pglob, struct glob_lim *limitp, int (*errfunc)(const char *, int))
-{
-	struct stat sb;
-	wcat_t *p, *q;
-	int anymeta;
-
-	/*
-	 * Loop over pattern segments until end of pattern or until
-	 * segment with meta character found.
-	 */
-	for (anymeta = 0; ; ) {
-		if (pattern->w_wc == EOS) {		/* End of pattern? */
-			pathend->w_at = 0;
-			pathend->w_wc = EOS;
-
-			if ((pglob->gl_flags & GLOB_LIMIT) &&
-			    limitp->glim_stat++ >= GLOB_LIMIT_STAT) {
-				errno = 0;
-				pathend->w_at = 0;
-				(pathend++)->w_wc = SEP;
-				pathend->w_at = 0;
-				pathend->w_wc = EOS;
-				return (GLOB_NOSPACE);
-			}
-			if (g_lstat(pathbuf, &sb, pglob))
-				return (0);
-
-			if (((pglob->gl_flags & GLOB_MARK) &&
-			    (pathend[-1].w_at != 0 ||
-			    pathend[-1].w_wc != SEP)) &&
-			    (S_ISDIR(sb.st_mode) ||
-			    (S_ISLNK(sb.st_mode) &&
-			    (g_stat(pathbuf, &sb, pglob) == 0) &&
-			    S_ISDIR(sb.st_mode)))) {
-				if (pathend+1 > pathend_last)
-					return (GLOB_NOSPACE);
-				pathend->w_at = 0;
-				(pathend++)->w_wc = SEP;
-				pathend->w_at = 0;
-				pathend->w_wc = EOS;
-			}
-			++pglob->gl_matchc;
-			return (globextend(pathbuf, pglob, limitp, &sb));
-		}
-
-		/* Find end of next segment, copy tentatively to pathend. */
-		q = pathend;
-		p = pattern;
-		while (p->w_wc != EOS && p->w_wc != SEP) {
-			if (ismeta(*p))
-				anymeta = 1;
-			if (q+1 > pathend_last)
-				return (GLOB_NOSPACE);
-			*q++ = *p++;
-		}
-
-		if (!anymeta) {		/* No expansion, do next segment. */
-			pathend = q;
-			pattern = p;
-			while (pattern->w_wc == SEP) {
-				if (pathend+1 > pathend_last)
-					return (GLOB_NOSPACE);
-				*pathend++ = *pattern++;
-			}
-		} else  {
-			/* Need expansion, recurse. */
-			return (glob3(pathbuf, pathbuf_last, pathend,
-			    pathend_last, pattern, p, pattern_last,
-			    pglob, limitp, errfunc));
-		}
-	}
-	/* NOTREACHED */
-}
-
-static int
-glob3(wcat_t *pathbuf, wcat_t *pathbuf_last, wcat_t *pathend,
-    wcat_t *pathend_last, wcat_t *pattern, wcat_t *restpattern,
-    wcat_t *restpattern_last, glob_t *pglob, struct glob_lim *limitp,
-    int (*errfunc)(const char *, int))
-{
-	struct dirent *dp;
-	DIR *dirp;
-	int err;
-	char buf[PATH_MAX];
-
-	/*
-	 * The readdirfunc declaration can't be prototyped, because it is
-	 * assigned, below, to two functions which are prototyped in glob.h
-	 * and dirent.h as taking pointers to differently typed opaque
-	 * structures.
-	 */
-	struct dirent *(*readdirfunc)(void *);
-
-	if (pathend > pathend_last)
-		return (GLOB_NOSPACE);
-	pathend->w_at = 0;
-	pathend->w_wc = EOS;
-	errno = 0;
-
-	if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
-		/* TODO: don't call for ENOENT or ENOTDIR? */
-		if (errfunc) {
-			if (g_Ctoc(pathbuf, buf, sizeof (buf)))
-				return (GLOB_ABORTED);
-			if (errfunc(buf, errno) ||
-			    pglob->gl_flags & GLOB_ERR)
-				return (GLOB_ABORTED);
-		}
-		return (0);
-	}
-
-	err = 0;
-
-	/* Search directory for matching names. */
-	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
-		readdirfunc = pglob->gl_readdir;
-	else
-		readdirfunc = (struct dirent *(*)(void *))readdir;
-	while ((dp = (*readdirfunc)(dirp))) {
-		char *sc;
-		wcat_t *dc;
-		int n;
-		int lensc;
-		wchar_t w;
-
-		if ((pglob->gl_flags & GLOB_LIMIT) &&
-		    limitp->glim_readdir++ >= GLOB_LIMIT_READDIR) {
-			errno = 0;
-			pathend->w_at = 0;
-			(pathend++)->w_wc = SEP;
-			pathend->w_at = 0;
-			pathend->w_wc = EOS;
-			err = GLOB_NOSPACE;
-			break;
-		}
-
-		/* Initial DOT must be matched literally. */
-		if (dp->d_name[0] == DOT && pattern->w_wc != DOT)
-			continue;
-		dc = pathend;
-		sc = dp->d_name;
-		lensc = strlen(sc) + 1;
-		while (dc < pathend_last) {
-			if ((n = mbtowc(&w, sc, lensc)) <= 0) {
-				sc += 1;
-				lensc -= 1;
-				dc->w_at = 0;
-				dc->w_wc = EOS;
-			} else {
-				sc += n;
-				lensc -= n;
-				dc->w_at = 0;
-				dc->w_wc = w;
-			}
-			dc++;
-			if (n <= 0)
-				break;
-		}
-		if (dc >= pathend_last) {
-			dc->w_at = 0;
-			dc->w_wc = EOS;
-			err = GLOB_NOSPACE;
-			break;
-		}
-		if (n < 0) {
-			err = GLOB_NOMATCH;
-			break;
-		}
-
-		if (!match(pathend, pattern, restpattern)) {
-			pathend->w_at = 0;
-			pathend->w_wc = EOS;
-			continue;
-		}
-		err = glob2(pathbuf, pathbuf_last, --dc, pathend_last,
-		    restpattern, restpattern_last, pglob, limitp,
-		    errfunc);
-		if (err)
-			break;
-	}
-
-	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
-		(*pglob->gl_closedir)(dirp);
-	else
-		(void) closedir(dirp);
-	return (err);
-}
-
-
-/*
- * Extend the gl_pathv member of a glob_t structure to accommodate a new item,
- * add the new item, and update gl_pathc.  Avoids excessive reallocation
- * by doubling the number of elements each time.  Uses gl_pathn to contain
- * the number.
- *
- * Return 0 if new item added, error code if memory couldn't be allocated.
- *
- * Invariant of the glob_t structure:
- *	Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
- *	gl_pathv points to (gl_offs + gl_pathc + 1) items.
- */
-static int
-globextend(const wcat_t *path, glob_t *pglob, struct glob_lim *limitp,
-    struct stat *sb)
-{
-	char **pathv;
-	ssize_t i;
-	size_t allocn, newn, len;
-	char *copy = NULL;
-	const wcat_t *p;
-	struct stat **statv;
-	char junk[MB_LEN_MAX];
-	int n;
-
-	allocn = pglob->gl_pathn;
-	newn = 2 + pglob->gl_pathc + pglob->gl_offs;
-
-	if (newn <= allocn) {
-		pathv = pglob->gl_pathv;
-		if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0)
-			statv = pglob->gl_statv;
-	} else {
-		if (allocn == 0)
-			allocn = pglob->gl_offs + INITIAL;
-		allocn *= 2;
-		if (pglob->gl_offs >= INT_MAX ||
-		    pglob->gl_pathc >= INT_MAX ||
-		    allocn >= INT_MAX ||
-		    SIZE_MAX / sizeof (*pathv) <= allocn ||
-		    SIZE_MAX / sizeof (*statv) <= allocn) {
-		nospace:
-			for (i = pglob->gl_offs; i < (ssize_t)(newn - 2);
-			    i++) {
-				if (pglob->gl_pathv && pglob->gl_pathv[i])
-					free(pglob->gl_pathv[i]);
-				if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0 &&
-				    pglob->gl_statv && pglob->gl_statv[i])
-					free(pglob->gl_statv[i]);
-			}
-			free(pglob->gl_pathv);
-			pglob->gl_pathv = NULL;
-			if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0) {
-				free(pglob->gl_statv);
-				pglob->gl_statv = NULL;
-			}
-			return (GLOB_NOSPACE);
-		}
-		limitp->glim_malloc += allocn * sizeof (*pathv);
-		pathv = reallocarray(pglob->gl_pathv, allocn, sizeof (*pathv));
-		if (pathv == NULL)
-			goto nospace;
-		if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0) {
-			limitp->glim_malloc += allocn * sizeof (*statv);
-			statv = reallocarray(pglob->gl_statv, allocn,
-			    sizeof (*statv));
-			if (statv == NULL)
-				goto nospace;
-		}
-	}
-	pglob->gl_pathn = allocn;
-
-	if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
-		/* first time around -- clear initial gl_offs items */
-		pathv += pglob->gl_offs;
-		for (i = pglob->gl_offs; --i >= 0; )
-			*--pathv = NULL;
-	}
-	pglob->gl_pathv = pathv;
-
-	if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0) {
-		if (pglob->gl_statv == NULL && pglob->gl_offs > 0) {
-			/* first time around -- clear initial gl_offs items */
-			statv += pglob->gl_offs;
-			for (i = pglob->gl_offs; --i >= 0; )
-				*--statv = NULL;
-		}
-		pglob->gl_statv = statv;
-		if (sb == NULL)
-			statv[pglob->gl_offs + pglob->gl_pathc] = NULL;
-		else {
-			limitp->glim_malloc += sizeof (**statv);
-			if ((statv[pglob->gl_offs + pglob->gl_pathc] =
-			    malloc(sizeof (**statv))) == NULL)
-				goto copy_error;
-			(void) memcpy(statv[pglob->gl_offs + pglob->gl_pathc],
-			    sb, sizeof (*sb));
-		}
-		statv[pglob->gl_offs + pglob->gl_pathc + 1] = NULL;
-	}
-
-	len = MB_LEN_MAX;
-	p = path;
-	while ((n = wctomb(junk, p->w_wc)) > 0) {
-		len += n;
-		if ((p++)->w_wc == EOS)
-			break;
-	}
-	if (n < 0)
-		return (GLOB_NOMATCH);
-
-	limitp->glim_malloc += len;
-	if ((copy = malloc(len)) != NULL) {
-		if (g_Ctoc(path, copy, len)) {
-			free(copy);
-			return (GLOB_NOSPACE);
-		}
-		pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
-	}
-	pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
-
-	if ((pglob->gl_flags & GLOB_LIMIT) &&
-	    limitp->glim_malloc >= GLOB_LIMIT_MALLOC) {
-		errno = 0;
-		return (GLOB_NOSPACE);
-	}
-	copy_error:
-	return (copy == NULL ? GLOB_NOSPACE : 0);
-}
-
-
-/*
- * pattern matching function for filenames.  Each occurrence of the *
- * pattern causes an iteration.
- *
- * Note, this function differs from the original as per the discussion
- * here: https://research.swtch.com/glob
- *
- * Basically we removed the recursion and made it use the algorithm
- * from Russ Cox to not go quadratic on cases like a file called
- * ("a" x 100) . "x" matched against a pattern like "a*a*a*a*a*a*a*y".
- */
-static int
-match(wcat_t *name, wcat_t *pat, wcat_t *patend)
-{
-	int ok, negate_range;
-	wcat_t c, k;
-	wcat_t *nextp = NULL;
-	wcat_t *nextn = NULL;
-
-loop:
-	while (pat < patend) {
-		c = *pat++;
-		switch (c.w_wc) {
-		case M_ALL:
-			if (c.w_at != M_QUOTE) {
-				k = *name++;
-				if (k.w_at != c.w_at || k.w_wc != c.w_wc)
-					return (0);
-				break;
-			}
-			while (pat < patend && pat->w_at == M_QUOTE &&
-			    pat->w_wc == M_ALL)
-				pat++;	/* eat consecutive '*' */
-			if (pat == patend)
-				return (1);
-			if (name->w_wc == EOS)
-				return (0);
-			nextn = name + 1;
-			nextp = pat - 1;
-			break;
-		case M_ONE:
-			if (c.w_at != M_QUOTE) {
-				k = *name++;
-				if (k.w_at != c.w_at || k.w_wc != c.w_wc)
-					goto fail;
-				break;
-			}
-			if ((name++)->w_wc == EOS)
-				goto fail;
-			break;
-		case M_SET:
-			if (c.w_at != M_QUOTE) {
-				k = *name++;
-				if (k.w_at != c.w_at || k.w_wc != c.w_wc)
-					goto fail;
-				break;
-			}
-			ok = 0;
-			if ((k = *name++).w_wc == EOS)
-				goto fail;
-			if ((negate_range = (pat->w_at == M_QUOTE &&
-			    pat->w_wc == M_NOT)) != 0)
-				++pat;
-			while (((c = *pat++).w_at != M_QUOTE) ||
-			    c.w_wc != M_END) {
-				if (c.w_at == M_QUOTE && c.w_wc == M_CLASS) {
-					wcat_t cc;
-
-					cc.w_at = pat->w_at;
-					cc.w_wc = pat->w_wc;
-					if (iswctype(k.w_wc, cc.w_wc))
-						ok = 1;
-					++pat;
-				}
-				if (pat->w_at == M_QUOTE &&
-				    pat->w_wc == M_RNG) {
-					if (c.w_wc <= k.w_wc &&
-					    k.w_wc <= pat[1].w_wc)
-						ok = 1;
-					pat += 2;
-				} else if (c.w_wc == k.w_wc)
-					ok = 1;
-			}
-			if (ok == negate_range)
-				goto fail;
-			break;
-		default:
-			k = *name++;
-			if (k.w_at != c.w_at || k.w_wc != c.w_wc)
-				goto fail;
-			break;
-		}
-	}
-	if (name->w_wc == EOS)
-		return (1);
-
-fail:
-	if (nextn) {
-		pat = nextp;
-		name = nextn;
-		goto loop;
-	}
-	return (0);
-}
-
-/*
- * Extended globfree() function, selected by #pragma redefine_extname
- * in glob.h with the external name _globfree_ext() .
- */
-void
-_globfree_ext(glob_t *pglob)
-{
-	int i;
-	char **pp;
-
-	if (pglob->gl_pathv != NULL) {
-		pp = pglob->gl_pathv + pglob->gl_offs;
-		for (i = pglob->gl_pathc; i--; ++pp)
-			free(*pp);
-		free(pglob->gl_pathv);
-		pglob->gl_pathv = NULL;
-	}
-	if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0 &&
-	    pglob->gl_statv != NULL) {
-		for (i = 0; i < pglob->gl_pathc; i++) {
-			free(pglob->gl_statv[i]);
-		}
-		free(pglob->gl_statv);
-		pglob->gl_statv = NULL;
-	}
-}
-
-static DIR *
-g_opendir(wcat_t *str, glob_t *pglob)
-{
-	char buf[PATH_MAX];
-
-	if (str->w_wc == EOS)
-		(void) strlcpy(buf, ".", sizeof (buf));
-	else {
-		if (g_Ctoc(str, buf, sizeof (buf)))
-			return (NULL);
-	}
-
-	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
-		return ((*pglob->gl_opendir)(buf));
-
-	return (opendir(buf));
-}
-
-static int
-g_lstat(wcat_t *fn, struct stat *sb, glob_t *pglob)
-{
-	char buf[PATH_MAX];
-
-	if (g_Ctoc(fn, buf, sizeof (buf)))
-		return (-1);
-	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
-		return ((*pglob->gl_lstat)(buf, sb));
-	return (lstat(buf, sb));
-}
-
-static int
-g_stat(wcat_t *fn, struct stat *sb, glob_t *pglob)
-{
-	char buf[PATH_MAX];
-
-	if (g_Ctoc(fn, buf, sizeof (buf)))
-		return (-1);
-	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
-		return ((*pglob->gl_stat)(buf, sb));
-	return (stat(buf, sb));
-}
-
-static wcat_t *
-g_strchr(const wcat_t *str, wchar_t ch)
-{
-	do {
-		if (str->w_at == 0 && str->w_wc == ch)
-			return ((wcat_t *)str);
-	} while ((str++)->w_wc != EOS);
-	return (NULL);
-}
-
-static int
-g_Ctoc(const wcat_t *str, char *buf, uint_t len)
-{
-	int n;
-	wchar_t w;
-
-	while (len >= MB_LEN_MAX) {
-		w = (str++)->w_wc;
-		if ((n = wctomb(buf, w)) > 0) {
-			len -= n;
-			buf += n;
-		}
-		if (n < 0)
-			break;
-		if (w == EOS)
-			return (0);
-	}
-	return (1);
-}
-
-#if defined(_LP64) || _FILE_OFFSET_BITS != 64
-
-/* glob() function with legacy glob structure */
-int
-old_glob(const char *pattern, int flags, int (*errfunc)(const char *, int),
-    old_glob_t *pglob)
-{
-
-	glob_t gl;
-	int rv;
-
-	flags &= GLOB_POSIX;
-
-	(void) memset(&gl, 0, sizeof (gl));
-
-	/*
-	 * Copy all the members, old to new.  There's
-	 * really no point in micro-optimizing the copying.
-	 * Other members are set to zero.
-	 */
-	gl.gl_pathc = pglob->gl_pathc;
-	gl.gl_pathv = pglob->gl_pathv;
-	gl.gl_offs = pglob->gl_offs;
-	gl.gl_pathp = pglob->gl_pathp;
-	gl.gl_pathn = pglob->gl_pathn;
-
-	rv = _glob_ext(pattern, flags, errfunc, &gl);
-
-	/*
-	 * Copy all the members, new to old.  There's
-	 * really no point in micro-optimizing the copying.
-	 */
-	pglob->gl_pathc = gl.gl_pathc;
-	pglob->gl_pathv = gl.gl_pathv;
-	pglob->gl_offs = gl.gl_offs;
-	pglob->gl_pathp = gl.gl_pathp;
-	pglob->gl_pathn = gl.gl_pathn;
-
-	return (rv);
-}
-
-/* globfree() function with legacy glob structure */
-void
-old_globfree(old_glob_t *pglob)
-{
-	glob_t gl;
-
-	(void) memset(&gl, 0, sizeof (gl));
-
-	/*
-	 * Copy all the members, old to new.  There's
-	 * really no point in micro-optimizing the copying.
-	 * Other members are set to zero.
-	 */
-	gl.gl_pathc = pglob->gl_pathc;
-	gl.gl_pathv = pglob->gl_pathv;
-	gl.gl_offs = pglob->gl_offs;
-	gl.gl_pathp = pglob->gl_pathp;
-	gl.gl_pathn = pglob->gl_pathn;
-
-	_globfree_ext(&gl);
-
-	/*
-	 * Copy all the members, new to old.  There's
-	 * really no point in micro-optimizing the copying.
-	 */
-	pglob->gl_pathc = gl.gl_pathc;
-	pglob->gl_pathv = gl.gl_pathv;
-	pglob->gl_offs = gl.gl_offs;
-	pglob->gl_pathp = gl.gl_pathp;
-	pglob->gl_pathn = gl.gl_pathn;
-
-}
-
-#endif	/* _LP64 || _FILE_OFFSET_BITS != 64 */
--- /dev/null	Tue Feb 13 07:04:46 2018
+++ new/usr/src/lib/libc/port/gen/glob.c	Tue Feb 13 07:11:46 2018
@@ -0,0 +1,1360 @@
+/*
+ * Copyright (c) 1989, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 2013 Gary Mills
+ */
+
+/*
+ * glob(3) -- a superset of the one defined in POSIX 1003.2.
+ *
+ * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
+ *
+ * Optional extra services, controlled by flags not defined by POSIX:
+ *
+ * GLOB_QUOTE:
+ *	Escaping convention: \ inhibits any special meaning the following
+ *	character might have (except \ at end of string is retained).
+ * GLOB_MAGCHAR:
+ *	Set in gl_flags if pattern contained a globbing character.
+ * GLOB_NOMAGIC:
+ *	Same as GLOB_NOCHECK, but it will only append pattern if it did
+ *	not contain any magic characters.  [Used in csh style globbing]
+ * GLOB_ALTDIRFUNC:
+ *	Use alternately specified directory access functions.
+ * GLOB_TILDE:
+ *	expand ~user/foo to the /home/dir/of/user/foo
+ * GLOB_BRACE:
+ *	expand {1,2}{a,b} to 1a 1b 2a 2b
+ * gl_matchc:
+ *	Number of matches in the current invocation of glob.
+ */
+
+#include "lint.h"
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <glob.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+/*
+ * This is the legacy glob_t prior to illumos enhancement 1097,
+ * used when old programs call the old libc glob functions.
+ * (New programs call the _glob_ext, _globfree_ext functions.)
+ * This struct should be considered "carved in stone".
+ */
+typedef	struct	old_glob	{
+	size_t	gl_pathc;		/* Count of paths matched by pattern */
+	char	**gl_pathv;		/* List of matched pathnames */
+	size_t	gl_offs;		/* # of slots reserved in gl_pathv */
+	/* following are internal to the implementation */
+	char	**gl_pathp;		/* gl_pathv + gl_offs */
+	int	gl_pathn;		/* # of elements allocated */
+}	old_glob_t;
+
+/*
+ * For old programs, the external names need to be the old names:
+ * glob() and globfree() .  We've redefined those already to
+ *  _glob_ext() and _globfree_ext() .  Now redefine old_glob()
+ * and old_globfree() to glob() and globfree() .
+ */
+#ifdef __PRAGMA_REDEFINE_EXTNAME
+#pragma	redefine_extname	old_glob	glob
+#pragma	redefine_extname	old_globfree	globfree
+#endif /* __PRAGMA_REDEFINE_EXTNAME */
+extern int old_glob(const char *, int, int (*)(const char *, int),
+    old_glob_t *);
+extern void old_globfree(old_glob_t *);
+
+/*
+ * The various extensions to glob(3C) allow for stat and dirent structures to
+ * show up whose size may change in a largefile environment. If libc defines
+ * _FILE_OFFSET_BITS to be 64 that is the key to indicate that we're building
+ * the LFS version of this file. As such, we rename the public functions here,
+ * _glob_ext() and _globfree_ext() to have a 64 suffix. When building the LFS
+ * version, we do not include the old versions.
+ */
+#if !defined(_LP64) && _FILE_OFFSET_BITS == 64
+#define	_glob_ext	_glob_ext64
+#define	_globfree_ext	_globfree_ext64
+#endif	/* !_LP64 && _FILE_OFFSET_BITS == 64 */
+
+#define	DOLLAR		'$'
+#define	DOT		'.'
+#define	EOS		'\0'
+#define	LBRACKET	'['
+#define	NOT		'!'
+#define	QUESTION	'?'
+#define	QUOTE		'\\'
+#define	RANGE		'-'
+#define	RBRACKET	']'
+#define	SEP		'/'
+#define	STAR		'*'
+#define	TILDE		'~'
+#define	UNDERSCORE	'_'
+#define	LBRACE		'{'
+#define	RBRACE		'}'
+#define	SLASH		'/'
+#define	COMMA		','
+#define	COLON		':'
+
+#define	M_QUOTE		0x800000
+#define	M_PROTECT	0x400000
+
+typedef struct wcat {
+	wchar_t w_wc;
+	uint_t w_at;
+} wcat_t;
+
+#define	M_ALL		'*'	/* Plus M_QUOTE */
+#define	M_END		']'	/* Plus M_QUOTE */
+#define	M_NOT		'!'	/* Plus M_QUOTE */
+#define	M_ONE		'?'	/* Plus M_QUOTE */
+#define	M_RNG		'-'	/* Plus M_QUOTE */
+#define	M_SET		'['	/* Plus M_QUOTE */
+#define	M_CLASS		':'	/* Plus M_QUOTE */
+#define	ismeta(c)	(((c).w_at&M_QUOTE) != 0)
+
+#define	INITIAL			8	/* initial pathv allocation */
+
+#define	GLOB_LIMIT_MALLOC	65536
+#define	GLOB_LIMIT_STAT		2048
+#define	GLOB_LIMIT_READDIR	16384
+
+struct glob_lim {
+	size_t	glim_malloc;
+	size_t	glim_stat;
+	size_t	glim_readdir;
+};
+
+struct glob_path_stat {
+	char		*gps_path;
+	struct stat	*gps_stat;
+};
+
+static int	 compare(const void *, const void *);
+static int	 compare_gps(const void *, const void *);
+static int	 g_Ctoc(const wcat_t *, char *, uint_t);
+static int	 g_lstat(wcat_t *, struct stat *, glob_t *);
+static DIR	*g_opendir(wcat_t *, glob_t *);
+static wcat_t	*g_strchr(const wcat_t *, wchar_t);
+static int	 g_stat(wcat_t *, struct stat *, glob_t *);
+static int	 glob0(const wcat_t *, glob_t *, struct glob_lim *,
+			int (*)(const char *, int));
+static int	 glob1(wcat_t *, wcat_t *, glob_t *, struct glob_lim *,
+			int (*)(const char *, int));
+static int	 glob2(wcat_t *, wcat_t *, wcat_t *, wcat_t *, wcat_t *,
+			wcat_t *, glob_t *, struct glob_lim *,
+			int (*)(const char *, int));
+static int	 glob3(wcat_t *, wcat_t *, wcat_t *, wcat_t *, wcat_t *,
+			wcat_t *, wcat_t *, glob_t *, struct glob_lim *,
+			int (*)(const char *, int));
+static int	 globextend(const wcat_t *, glob_t *, struct glob_lim *,
+		    struct stat *);
+static
+const wcat_t	*globtilde(const wcat_t *, wcat_t *, size_t, glob_t *);
+static int	 globexp1(const wcat_t *, glob_t *, struct glob_lim *,
+		    int (*)(const char *, int));
+static int	 globexp2(const wcat_t *, const wcat_t *, glob_t *,
+		    struct glob_lim *, int (*)(const char *, int));
+static int	 match(wcat_t *, wcat_t *, wcat_t *);
+
+/*
+ * Extended glob() function, selected by #pragma redefine_extname
+ * in glob.h with the external name _glob_ext() .
+ */
+int
+_glob_ext(const char *pattern, int flags, int (*errfunc)(const char *, int),
+    glob_t *pglob)
+{
+	const char *patnext;
+	int n;
+	size_t patlen;
+	wchar_t c;
+	wcat_t *bufnext, *bufend, patbuf[PATH_MAX];
+	struct glob_lim limit = { 0, 0, 0 };
+
+	patnext = pattern;
+	if (!(flags & GLOB_APPEND)) {
+		pglob->gl_pathc = 0;
+		pglob->gl_pathn = 0;
+		pglob->gl_pathv = NULL;
+		if ((flags & GLOB_KEEPSTAT) != 0)
+			pglob->gl_statv = NULL;
+		if (!(flags & GLOB_DOOFFS))
+			pglob->gl_offs = 0;
+	}
+	pglob->gl_flags = flags & ~GLOB_MAGCHAR;
+	pglob->gl_matchc = 0;
+
+	if ((patlen = strnlen(pattern, PATH_MAX)) == PATH_MAX)
+		return (GLOB_NOMATCH);
+
+	if (pglob->gl_offs >= INT_MAX || pglob->gl_pathc >= INT_MAX ||
+	    pglob->gl_pathc >= INT_MAX - pglob->gl_offs - 1)
+		return (GLOB_NOSPACE);
+
+	bufnext = patbuf;
+	bufend = bufnext + PATH_MAX - 1;
+	patlen += 1;
+	if (flags & GLOB_NOESCAPE) {
+		while (bufnext < bufend) {
+			if ((n = mbtowc(&c, patnext, patlen)) > 0) {
+				patnext += n;
+				patlen -= n;
+				bufnext->w_at = 0;
+				(bufnext++)->w_wc = c;
+			} else if (n == 0) {
+				break;
+			} else {
+				return (GLOB_NOMATCH);
+			}
+		}
+	} else {
+		/* Protect the quoted characters. */
+		while (bufnext < bufend) {
+			if ((n = mbtowc(&c, patnext, patlen)) > 0) {
+				patnext += n;
+				patlen -= n;
+				if (c == QUOTE) {
+					n = mbtowc(&c, patnext, patlen);
+					if (n < 0)
+						return (GLOB_NOMATCH);
+					if (n > 0) {
+						patnext += n;
+						patlen -= n;
+					}
+					if (n == 0)
+						c = QUOTE;
+					bufnext->w_at = M_PROTECT;
+					(bufnext++)->w_wc = c;
+				} else {
+					bufnext->w_at = 0;
+					(bufnext++)->w_wc = c;
+				}
+			} else if (n == 0) {
+				break;
+			} else {
+				return (GLOB_NOMATCH);
+			}
+		}
+	}
+	bufnext->w_at = 0;
+	bufnext->w_wc = EOS;
+
+	if (flags & GLOB_BRACE)
+		return (globexp1(patbuf, pglob, &limit, errfunc));
+	else
+		return (glob0(patbuf, pglob, &limit, errfunc));
+}
+
+/*
+ * Expand recursively a glob {} pattern. When there is no more expansion
+ * invoke the standard globbing routine to glob the rest of the magic
+ * characters
+ */
+static int
+globexp1(const wcat_t *pattern, glob_t *pglob, struct glob_lim *limitp,
+    int (*errfunc)(const char *, int))
+{
+	const wcat_t *ptr = pattern;
+
+	/* Protect a single {}, for find(1), like csh */
+	if (pattern[0].w_wc == LBRACE && pattern[1].w_wc == RBRACE &&
+	    pattern[2].w_wc == EOS)
+		return (glob0(pattern, pglob, limitp, errfunc));
+
+	if ((ptr = (const wcat_t *) g_strchr(ptr, LBRACE)) != NULL)
+		return (globexp2(ptr, pattern, pglob, limitp, errfunc));
+
+	return (glob0(pattern, pglob, limitp, errfunc));
+}
+
+
+/*
+ * Recursive brace globbing helper. Tries to expand a single brace.
+ * If it succeeds then it invokes globexp1 with the new pattern.
+ * If it fails then it tries to glob the rest of the pattern and returns.
+ */
+static int
+globexp2(const wcat_t *ptr, const wcat_t *pattern, glob_t *pglob,
+    struct glob_lim *limitp, int (*errfunc)(const char *, int))
+{
+	int	i, rv;
+	wcat_t   *lm, *ls;
+	const wcat_t *pe, *pm, *pl;
+	wcat_t    patbuf[PATH_MAX];
+
+	/* copy part up to the brace */
+	for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
+		;
+	lm->w_at = 0;
+	lm->w_wc = EOS;
+	ls = lm;
+
+	/* Find the balanced brace */
+	for (i = 0, pe = ++ptr; pe->w_wc != EOS; pe++)
+		if (pe->w_wc == LBRACKET) {
+			/* Ignore everything between [] */
+			for (pm = pe++; pe->w_wc != RBRACKET &&
+			    pe->w_wc != EOS; pe++)
+				;
+			if (pe->w_wc == EOS) {
+				/*
+				 * We could not find a matching RBRACKET.
+				 * Ignore and just look for RBRACE
+				 */
+				pe = pm;
+			}
+		} else if (pe->w_wc == LBRACE) {
+			i++;
+		} else if (pe->w_wc == RBRACE) {
+			if (i == 0)
+				break;
+			i--;
+		}
+
+	/* Non matching braces; just glob the pattern */
+	if (i != 0 || pe->w_wc == EOS)
+		return (glob0(patbuf, pglob, limitp, errfunc));
+
+	for (i = 0, pl = pm = ptr; pm <= pe; pm++) {
+		switch (pm->w_wc) {
+		case LBRACKET:
+			/* Ignore everything between [] */
+			for (pl = pm++; pm->w_wc != RBRACKET && pm->w_wc != EOS;
+			    pm++)
+				;
+			if (pm->w_wc == EOS) {
+				/*
+				 * We could not find a matching RBRACKET.
+				 * Ignore and just look for RBRACE
+				 */
+				pm = pl;
+			}
+			break;
+
+		case LBRACE:
+			i++;
+			break;
+
+		case RBRACE:
+			if (i) {
+				i--;
+				break;
+			}
+			/* FALLTHROUGH */
+		case COMMA:
+			if (i && pm->w_wc == COMMA)
+				break;
+			else {
+				/* Append the current string */
+				for (lm = ls; (pl < pm); *lm++ = *pl++)
+					;
+
+				/*
+				 * Append the rest of the pattern after the
+				 * closing brace
+				 */
+				for (pl = pe + 1;
+				    (*lm++ = *pl++).w_wc != EOS; /* */)
+					;
+
+				/* Expand the current pattern */
+				rv = globexp1(patbuf, pglob, limitp, errfunc);
+				if (rv && rv != GLOB_NOMATCH)
+					return (rv);
+
+				/* move after the comma, to the next string */
+				pl = pm + 1;
+			}
+			break;
+
+		default:
+			break;
+		}
+	}
+	return (0);
+}
+
+
+
+/*
+ * expand tilde from the passwd file.
+ */
+static const wcat_t *
+globtilde(const wcat_t *pattern, wcat_t *patbuf, size_t patbuf_len,
+    glob_t *pglob)
+{
+	struct passwd *pwd;
+	char *h;
+	const wcat_t *p;
+	wcat_t *b, *eb, *q;
+	int n;
+	size_t lenh;
+	wchar_t c;
+
+	if (pattern->w_wc != TILDE || !(pglob->gl_flags & GLOB_TILDE))
+		return (pattern);
+
+	/* Copy up to the end of the string or / */
+	eb = &patbuf[patbuf_len - 1];
+	for (p = pattern + 1, q = patbuf;
+	    q < eb && p->w_wc != EOS && p->w_wc != SLASH; *q++ = *p++)
+		;
+
+	q->w_at = 0;
+	q->w_wc = EOS;
+
+	/* What to do if patbuf is full? */
+
+	if (patbuf[0].w_wc == EOS) {
+		/*
+		 * handle a plain ~ or ~/ by expanding $HOME
+		 * first and then trying the password file
+		 */
+		if (issetugid() != 0)
+			return (pattern);
+		if ((h = getenv("HOME")) == NULL) {
+			if ((pwd = getpwuid(getuid())) == NULL)
+				return (pattern);
+			else
+				h = pwd->pw_dir;
+		}
+	} else {
+		/*
+		 * Expand a ~user
+		 */
+		if ((pwd = getpwnam((char *)patbuf)) == NULL)
+			return (pattern);
+		else
+			h = pwd->pw_dir;
+	}
+
+	/* Copy the home directory */
+	lenh = strlen(h) + 1;
+	for (b = patbuf; b < eb && *h != EOS; b++) {
+		if ((n = mbtowc(&c, h, lenh)) > 0) {
+			h += n;
+			lenh -= n;
+			b->w_at = 0;
+			b->w_wc = c;
+		} else if (n < 0) {
+			return (pattern);
+		} else {
+			break;
+		}
+	}
+
+	/* Append the rest of the pattern */
+	while (b < eb && (*b++ = *p++).w_wc != EOS)
+		;
+	b->w_at = 0;
+	b->w_wc = EOS;
+
+	return (patbuf);
+}
+
+static int
+g_charclass(const wcat_t **patternp, wcat_t **bufnextp)
+{
+	const wcat_t *pattern = *patternp + 1;
+	wcat_t *bufnext = *bufnextp;
+	const wcat_t *colon;
+	char cbuf[MB_LEN_MAX + 32];
+	wctype_t cc;
+	size_t len;
+
+	if ((colon = g_strchr(pattern, COLON)) == NULL ||
+	    colon[1].w_wc != RBRACKET)
+		return (1);	/* not a character class */
+
+	len = (size_t)(colon - pattern);
+	if (len + MB_LEN_MAX + 1 > sizeof (cbuf))
+		return (-1);	/* invalid character class */
+	{
+		wchar_t w;
+		const wcat_t *s1 = pattern;
+		char *s2 = cbuf;
+		size_t n = len;
+
+		/* Copy the string. */
+		while (n > 0) {
+			w = (s1++)->w_wc;
+			/* Character class names must be ASCII. */
+			if (iswascii(w)) {
+				n--;
+				*s2++ = w;
+			} else {
+				return (-1);	/* invalid character class */
+			}
+		}
+		*s2 = EOS;
+	}
+	if ((cc = wctype(cbuf)) == 0)
+		return (-1);	/* invalid character class */
+	bufnext->w_at = M_QUOTE;
+	(bufnext++)->w_wc = M_CLASS;
+	bufnext->w_at = 0;
+	(bufnext++)->w_wc = cc;
+	*bufnextp = bufnext;
+	*patternp += len + 3;
+
+	return (0);
+}
+
+/*
+ * The main glob() routine: compiles the pattern (optionally processing
+ * quotes), calls glob1() to do the real pattern matching, and finally
+ * sorts the list (unless unsorted operation is requested).  Returns 0
+ * if things went well, nonzero if errors occurred.  It is not an error
+ * to find no matches.
+ */
+static int
+glob0(const wcat_t *pattern, glob_t *pglob, struct glob_lim *limitp,
+    int (*errfunc)(const char *, int))
+{
+	const wcat_t *qpatnext;
+	int err, oldpathc;
+	wchar_t c;
+	int a;
+	wcat_t *bufnext, patbuf[PATH_MAX];
+
+	qpatnext = globtilde(pattern, patbuf, PATH_MAX, pglob);
+	oldpathc = pglob->gl_pathc;
+	bufnext = patbuf;
+
+	/*
+	 * We don't need to check for buffer overflow any more.
+	 * The pattern has already been copied to an internal buffer.
+	 */
+	while ((a = qpatnext->w_at), (c = (qpatnext++)->w_wc) != EOS) {
+		switch (c) {
+		case LBRACKET:
+			if (a != 0) {
+				bufnext->w_at = a;
+				(bufnext++)->w_wc = c;
+				break;
+			}
+			a = qpatnext->w_at;
+			c = qpatnext->w_wc;
+			if (a == 0 && c == NOT)
+				++qpatnext;
+			if (qpatnext->w_wc == EOS ||
+			    g_strchr(qpatnext+1, RBRACKET) == NULL) {
+				bufnext->w_at = 0;
+				(bufnext++)->w_wc = LBRACKET;
+				if (a == 0 && c == NOT)
+					--qpatnext;
+				break;
+			}
+			bufnext->w_at = M_QUOTE;
+			(bufnext++)->w_wc = M_SET;
+			if (a == 0 && c == NOT) {
+				bufnext->w_at = M_QUOTE;
+				(bufnext++)->w_wc = M_NOT;
+			}
+			a = qpatnext->w_at;
+			c = (qpatnext++)->w_wc;
+			do {
+				if (a == 0 && c == LBRACKET &&
+				    qpatnext->w_wc == COLON) {
+					do {
+						err = g_charclass(&qpatnext,
+						    &bufnext);
+						if (err)
+							break;
+						a = qpatnext->w_at;
+						c = (qpatnext++)->w_wc;
+					} while (a == 0 && c == LBRACKET &&
+					    qpatnext->w_wc == COLON);
+					if (err == -1 &&
+					    !(pglob->gl_flags & GLOB_NOCHECK))
+						return (GLOB_NOMATCH);
+					if (a == 0 && c == RBRACKET)
+						break;
+				}
+				bufnext->w_at = a;
+				(bufnext++)->w_wc = c;
+				if (qpatnext->w_at == 0 &&
+				    qpatnext->w_wc == RANGE) {
+					a = qpatnext[1].w_at;
+					c = qpatnext[1].w_wc;
+					if (qpatnext[1].w_at != 0 ||
+					    qpatnext[1].w_wc != RBRACKET) {
+						bufnext->w_at = M_QUOTE;
+						(bufnext++)->w_wc = M_RNG;
+						bufnext->w_at = a;
+						(bufnext++)->w_wc = c;
+						qpatnext += 2;
+					}
+				}
+				a = qpatnext->w_at;
+				c = (qpatnext++)->w_wc;
+			} while (a != 0 || c != RBRACKET);
+			pglob->gl_flags |= GLOB_MAGCHAR;
+			bufnext->w_at = M_QUOTE;
+			(bufnext++)->w_wc = M_END;
+			break;
+		case QUESTION:
+			if (a != 0) {
+				bufnext->w_at = a;
+				(bufnext++)->w_wc = c;
+				break;
+			}
+			pglob->gl_flags |= GLOB_MAGCHAR;
+			bufnext->w_at = M_QUOTE;
+			(bufnext++)->w_wc = M_ONE;
+			break;
+		case STAR:
+			if (a != 0) {
+				bufnext->w_at = a;
+				(bufnext++)->w_wc = c;
+				break;
+			}
+			pglob->gl_flags |= GLOB_MAGCHAR;
+			/*
+			 * collapse adjacent stars to one,
+			 * to avoid exponential behavior
+			 */
+			if (bufnext == patbuf ||
+			    bufnext[-1].w_at != M_QUOTE ||
+			    bufnext[-1].w_wc != M_ALL) {
+				bufnext->w_at = M_QUOTE;
+				(bufnext++)->w_wc = M_ALL;
+			}
+			break;
+		default:
+			bufnext->w_at = a;
+			(bufnext++)->w_wc = c;
+			break;
+		}
+	}
+	bufnext->w_at = 0;
+	bufnext->w_wc = EOS;
+
+	if ((err = glob1(patbuf, patbuf+PATH_MAX-1, pglob, limitp, errfunc))
+	    != 0)
+		return (err);
+
+	/*
+	 * If there was no match we are going to append the pattern
+	 * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
+	 * and the pattern did not contain any magic characters
+	 * GLOB_NOMAGIC is there just for compatibility with csh.
+	 */
+	if (pglob->gl_pathc == oldpathc) {
+		if ((pglob->gl_flags & GLOB_NOCHECK) ||
+		    ((pglob->gl_flags & GLOB_NOMAGIC) &&
+		    !(pglob->gl_flags & GLOB_MAGCHAR)))
+			return (globextend(pattern, pglob, limitp, NULL));
+		else
+			return (GLOB_NOMATCH);
+	}
+	if (!(pglob->gl_flags & GLOB_NOSORT)) {
+		if ((pglob->gl_flags & GLOB_KEEPSTAT)) {
+			/* Keep the paths and stat info synced during sort */
+			struct glob_path_stat *path_stat;
+			int i;
+			int n = pglob->gl_pathc - oldpathc;
+			int o = pglob->gl_offs + oldpathc;
+
+			if ((path_stat = calloc(n, sizeof (*path_stat))) ==
+			    NULL)
+				return (GLOB_NOSPACE);
+			for (i = 0; i < n; i++) {
+				path_stat[i].gps_path = pglob->gl_pathv[o + i];
+				path_stat[i].gps_stat = pglob->gl_statv[o + i];
+			}
+			qsort(path_stat, n, sizeof (*path_stat), compare_gps);
+			for (i = 0; i < n; i++) {
+				pglob->gl_pathv[o + i] = path_stat[i].gps_path;
+				pglob->gl_statv[o + i] = path_stat[i].gps_stat;
+			}
+			free(path_stat);
+		} else {
+			qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
+			    pglob->gl_pathc - oldpathc, sizeof (char *),
+			    compare);
+		}
+	}
+	return (0);
+}
+
+static int
+compare(const void *p, const void *q)
+{
+	return (strcmp(*(char **)p, *(char **)q));
+}
+
+static int
+compare_gps(const void *_p, const void *_q)
+{
+	const struct glob_path_stat *p = (const struct glob_path_stat *)_p;
+	const struct glob_path_stat *q = (const struct glob_path_stat *)_q;
+
+	return (strcmp(p->gps_path, q->gps_path));
+}
+
+static int
+glob1(wcat_t *pattern, wcat_t *pattern_last, glob_t *pglob,
+    struct glob_lim *limitp, int (*errfunc)(const char *, int))
+{
+	wcat_t pathbuf[PATH_MAX];
+
+	/* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
+	if (pattern->w_wc == EOS)
+		return (0);
+	return (glob2(pathbuf, pathbuf+PATH_MAX-1,
+	    pathbuf, pathbuf+PATH_MAX-1,
+	    pattern, pattern_last, pglob, limitp, errfunc));
+}
+
+/*
+ * The functions glob2 and glob3 are mutually recursive; there is one level
+ * of recursion for each segment in the pattern that contains one or more
+ * meta characters.
+ */
+static int
+glob2(wcat_t *pathbuf, wcat_t *pathbuf_last, wcat_t *pathend,
+    wcat_t *pathend_last, wcat_t *pattern, wcat_t *pattern_last,
+    glob_t *pglob, struct glob_lim *limitp, int (*errfunc)(const char *, int))
+{
+	struct stat sb;
+	wcat_t *p, *q;
+	int anymeta;
+
+	/*
+	 * Loop over pattern segments until end of pattern or until
+	 * segment with meta character found.
+	 */
+	for (anymeta = 0; ; ) {
+		if (pattern->w_wc == EOS) {		/* End of pattern? */
+			pathend->w_at = 0;
+			pathend->w_wc = EOS;
+
+			if ((pglob->gl_flags & GLOB_LIMIT) &&
+			    limitp->glim_stat++ >= GLOB_LIMIT_STAT) {
+				errno = 0;
+				pathend->w_at = 0;
+				(pathend++)->w_wc = SEP;
+				pathend->w_at = 0;
+				pathend->w_wc = EOS;
+				return (GLOB_NOSPACE);
+			}
+			if (g_lstat(pathbuf, &sb, pglob))
+				return (0);
+
+			if (((pglob->gl_flags & GLOB_MARK) &&
+			    (pathend[-1].w_at != 0 ||
+			    pathend[-1].w_wc != SEP)) &&
+			    (S_ISDIR(sb.st_mode) ||
+			    (S_ISLNK(sb.st_mode) &&
+			    (g_stat(pathbuf, &sb, pglob) == 0) &&
+			    S_ISDIR(sb.st_mode)))) {
+				if (pathend+1 > pathend_last)
+					return (GLOB_NOSPACE);
+				pathend->w_at = 0;
+				(pathend++)->w_wc = SEP;
+				pathend->w_at = 0;
+				pathend->w_wc = EOS;
+			}
+			++pglob->gl_matchc;
+			return (globextend(pathbuf, pglob, limitp, &sb));
+		}
+
+		/* Find end of next segment, copy tentatively to pathend. */
+		q = pathend;
+		p = pattern;
+		while (p->w_wc != EOS && p->w_wc != SEP) {
+			if (ismeta(*p))
+				anymeta = 1;
+			if (q+1 > pathend_last)
+				return (GLOB_NOSPACE);
+			*q++ = *p++;
+		}
+
+		if (!anymeta) {		/* No expansion, do next segment. */
+			pathend = q;
+			pattern = p;
+			while (pattern->w_wc == SEP) {
+				if (pathend+1 > pathend_last)
+					return (GLOB_NOSPACE);
+				*pathend++ = *pattern++;
+			}
+		} else  {
+			/* Need expansion, recurse. */
+			return (glob3(pathbuf, pathbuf_last, pathend,
+			    pathend_last, pattern, p, pattern_last,
+			    pglob, limitp, errfunc));
+		}
+	}
+	/* NOTREACHED */
+}
+
+static int
+glob3(wcat_t *pathbuf, wcat_t *pathbuf_last, wcat_t *pathend,
+    wcat_t *pathend_last, wcat_t *pattern, wcat_t *restpattern,
+    wcat_t *restpattern_last, glob_t *pglob, struct glob_lim *limitp,
+    int (*errfunc)(const char *, int))
+{
+	struct dirent *dp;
+	DIR *dirp;
+	int err;
+	char buf[PATH_MAX];
+
+	/*
+	 * The readdirfunc declaration can't be prototyped, because it is
+	 * assigned, below, to two functions which are prototyped in glob.h
+	 * and dirent.h as taking pointers to differently typed opaque
+	 * structures.
+	 */
+	struct dirent *(*readdirfunc)(void *);
+
+	if (pathend > pathend_last)
+		return (GLOB_NOSPACE);
+	pathend->w_at = 0;
+	pathend->w_wc = EOS;
+	errno = 0;
+
+	if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
+		/* TODO: don't call for ENOENT or ENOTDIR? */
+		if (errfunc) {
+			if (g_Ctoc(pathbuf, buf, sizeof (buf)))
+				return (GLOB_ABORTED);
+			if (errfunc(buf, errno) ||
+			    pglob->gl_flags & GLOB_ERR)
+				return (GLOB_ABORTED);
+		}
+		return (0);
+	}
+
+	err = 0;
+
+	/* Search directory for matching names. */
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		readdirfunc = pglob->gl_readdir;
+	else
+		readdirfunc = (struct dirent *(*)(void *))readdir;
+	while ((dp = (*readdirfunc)(dirp))) {
+		char *sc;
+		wcat_t *dc;
+		int n;
+		int lensc;
+		wchar_t w;
+
+		if ((pglob->gl_flags & GLOB_LIMIT) &&
+		    limitp->glim_readdir++ >= GLOB_LIMIT_READDIR) {
+			errno = 0;
+			pathend->w_at = 0;
+			(pathend++)->w_wc = SEP;
+			pathend->w_at = 0;
+			pathend->w_wc = EOS;
+			err = GLOB_NOSPACE;
+			break;
+		}
+
+		/* Initial DOT must be matched literally. */
+		if (dp->d_name[0] == DOT && pattern->w_wc != DOT)
+			continue;
+		dc = pathend;
+		sc = dp->d_name;
+		lensc = strlen(sc) + 1;
+		while (dc < pathend_last) {
+			if ((n = mbtowc(&w, sc, lensc)) <= 0) {
+				sc += 1;
+				lensc -= 1;
+				dc->w_at = 0;
+				dc->w_wc = EOS;
+			} else {
+				sc += n;
+				lensc -= n;
+				dc->w_at = 0;
+				dc->w_wc = w;
+			}
+			dc++;
+			if (n <= 0)
+				break;
+		}
+		if (dc >= pathend_last) {
+			dc->w_at = 0;
+			dc->w_wc = EOS;
+			err = GLOB_NOSPACE;
+			break;
+		}
+		if (n < 0) {
+			err = GLOB_NOMATCH;
+			break;
+		}
+
+		if (!match(pathend, pattern, restpattern)) {
+			pathend->w_at = 0;
+			pathend->w_wc = EOS;
+			continue;
+		}
+		err = glob2(pathbuf, pathbuf_last, --dc, pathend_last,
+		    restpattern, restpattern_last, pglob, limitp,
+		    errfunc);
+		if (err)
+			break;
+	}
+
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		(*pglob->gl_closedir)(dirp);
+	else
+		(void) closedir(dirp);
+	return (err);
+}
+
+
+/*
+ * Extend the gl_pathv member of a glob_t structure to accommodate a new item,
+ * add the new item, and update gl_pathc.  Avoids excessive reallocation
+ * by doubling the number of elements each time.  Uses gl_pathn to contain
+ * the number.
+ *
+ * Return 0 if new item added, error code if memory couldn't be allocated.
+ *
+ * Invariant of the glob_t structure:
+ *	Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
+ *	gl_pathv points to (gl_offs + gl_pathc + 1) items.
+ */
+static int
+globextend(const wcat_t *path, glob_t *pglob, struct glob_lim *limitp,
+    struct stat *sb)
+{
+	char **pathv;
+	ssize_t i;
+	size_t allocn, newn, len;
+	char *copy = NULL;
+	const wcat_t *p;
+	struct stat **statv;
+	char junk[MB_LEN_MAX];
+	int n;
+
+	allocn = pglob->gl_pathn;
+	newn = 2 + pglob->gl_pathc + pglob->gl_offs;
+
+	if (newn <= allocn) {
+		pathv = pglob->gl_pathv;
+		if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0)
+			statv = pglob->gl_statv;
+	} else {
+		if (allocn == 0)
+			allocn = pglob->gl_offs + INITIAL;
+		allocn *= 2;
+		if (pglob->gl_offs >= INT_MAX ||
+		    pglob->gl_pathc >= INT_MAX ||
+		    allocn >= INT_MAX ||
+		    SIZE_MAX / sizeof (*pathv) <= allocn ||
+		    SIZE_MAX / sizeof (*statv) <= allocn) {
+		nospace:
+			for (i = pglob->gl_offs; i < (ssize_t)(newn - 2);
+			    i++) {
+				if (pglob->gl_pathv && pglob->gl_pathv[i])
+					free(pglob->gl_pathv[i]);
+				if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0 &&
+				    pglob->gl_statv && pglob->gl_statv[i])
+					free(pglob->gl_statv[i]);
+			}
+			free(pglob->gl_pathv);
+			pglob->gl_pathv = NULL;
+			if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0) {
+				free(pglob->gl_statv);
+				pglob->gl_statv = NULL;
+			}
+			return (GLOB_NOSPACE);
+		}
+		limitp->glim_malloc += allocn * sizeof (*pathv);
+		pathv = reallocarray(pglob->gl_pathv, allocn, sizeof (*pathv));
+		if (pathv == NULL)
+			goto nospace;
+		if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0) {
+			limitp->glim_malloc += allocn * sizeof (*statv);
+			statv = reallocarray(pglob->gl_statv, allocn,
+			    sizeof (*statv));
+			if (statv == NULL)
+				goto nospace;
+		}
+	}
+	pglob->gl_pathn = allocn;
+
+	if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
+		/* first time around -- clear initial gl_offs items */
+		pathv += pglob->gl_offs;
+		for (i = pglob->gl_offs; --i >= 0; )
+			*--pathv = NULL;
+	}
+	pglob->gl_pathv = pathv;
+
+	if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0) {
+		if (pglob->gl_statv == NULL && pglob->gl_offs > 0) {
+			/* first time around -- clear initial gl_offs items */
+			statv += pglob->gl_offs;
+			for (i = pglob->gl_offs; --i >= 0; )
+				*--statv = NULL;
+		}
+		pglob->gl_statv = statv;
+		if (sb == NULL)
+			statv[pglob->gl_offs + pglob->gl_pathc] = NULL;
+		else {
+			limitp->glim_malloc += sizeof (**statv);
+			if ((statv[pglob->gl_offs + pglob->gl_pathc] =
+			    malloc(sizeof (**statv))) == NULL)
+				goto copy_error;
+			(void) memcpy(statv[pglob->gl_offs + pglob->gl_pathc],
+			    sb, sizeof (*sb));
+		}
+		statv[pglob->gl_offs + pglob->gl_pathc + 1] = NULL;
+	}
+
+	len = MB_LEN_MAX;
+	p = path;
+	while ((n = wctomb(junk, p->w_wc)) > 0) {
+		len += n;
+		if ((p++)->w_wc == EOS)
+			break;
+	}
+	if (n < 0)
+		return (GLOB_NOMATCH);
+
+	limitp->glim_malloc += len;
+	if ((copy = malloc(len)) != NULL) {
+		if (g_Ctoc(path, copy, len)) {
+			free(copy);
+			return (GLOB_NOSPACE);
+		}
+		pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
+	}
+	pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
+
+	if ((pglob->gl_flags & GLOB_LIMIT) &&
+	    limitp->glim_malloc >= GLOB_LIMIT_MALLOC) {
+		errno = 0;
+		return (GLOB_NOSPACE);
+	}
+	copy_error:
+	return (copy == NULL ? GLOB_NOSPACE : 0);
+}
+
+
+/*
+ * pattern matching function for filenames.  Each occurrence of the *
+ * pattern causes an iteration.
+ *
+ * Note, this function differs from the original as per the discussion
+ * here: https://research.swtch.com/glob
+ *
+ * Basically we removed the recursion and made it use the algorithm
+ * from Russ Cox to not go quadratic on cases like a file called
+ * ("a" x 100) . "x" matched against a pattern like "a*a*a*a*a*a*a*y".
+ */
+static int
+match(wcat_t *name, wcat_t *pat, wcat_t *patend)
+{
+	int ok, negate_range;
+	wcat_t c, k;
+	wcat_t *nextp = NULL;
+	wcat_t *nextn = NULL;
+
+loop:
+	while (pat < patend) {
+		c = *pat++;
+		switch (c.w_wc) {
+		case M_ALL:
+			if (c.w_at != M_QUOTE) {
+				k = *name++;
+				if (k.w_at != c.w_at || k.w_wc != c.w_wc)
+					return (0);
+				break;
+			}
+			while (pat < patend && pat->w_at == M_QUOTE &&
+			    pat->w_wc == M_ALL)
+				pat++;	/* eat consecutive '*' */
+			if (pat == patend)
+				return (1);
+			if (name->w_wc == EOS)
+				return (0);
+			nextn = name + 1;
+			nextp = pat - 1;
+			break;
+		case M_ONE:
+			if (c.w_at != M_QUOTE) {
+				k = *name++;
+				if (k.w_at != c.w_at || k.w_wc != c.w_wc)
+					goto fail;
+				break;
+			}
+			if ((name++)->w_wc == EOS)
+				goto fail;
+			break;
+		case M_SET:
+			if (c.w_at != M_QUOTE) {
+				k = *name++;
+				if (k.w_at != c.w_at || k.w_wc != c.w_wc)
+					goto fail;
+				break;
+			}
+			ok = 0;
+			if ((k = *name++).w_wc == EOS)
+				goto fail;
+			if ((negate_range = (pat->w_at == M_QUOTE &&
+			    pat->w_wc == M_NOT)) != 0)
+				++pat;
+			while (((c = *pat++).w_at != M_QUOTE) ||
+			    c.w_wc != M_END) {
+				if (c.w_at == M_QUOTE && c.w_wc == M_CLASS) {
+					wcat_t cc;
+
+					cc.w_at = pat->w_at;
+					cc.w_wc = pat->w_wc;
+					if (iswctype(k.w_wc, cc.w_wc))
+						ok = 1;
+					++pat;
+				}
+				if (pat->w_at == M_QUOTE &&
+				    pat->w_wc == M_RNG) {
+					if (c.w_wc <= k.w_wc &&
+					    k.w_wc <= pat[1].w_wc)
+						ok = 1;
+					pat += 2;
+				} else if (c.w_wc == k.w_wc)
+					ok = 1;
+			}
+			if (ok == negate_range)
+				goto fail;
+			break;
+		default:
+			k = *name++;
+			if (k.w_at != c.w_at || k.w_wc != c.w_wc)
+				goto fail;
+			break;
+		}
+	}
+	if (name->w_wc == EOS)
+		return (1);
+
+fail:
+	if (nextn) {
+		pat = nextp;
+		name = nextn;
+		goto loop;
+	}
+	return (0);
+}
+
+/*
+ * Extended globfree() function, selected by #pragma redefine_extname
+ * in glob.h with the external name _globfree_ext() .
+ */
+void
+_globfree_ext(glob_t *pglob)
+{
+	int i;
+	char **pp;
+
+	if (pglob->gl_pathv != NULL) {
+		pp = pglob->gl_pathv + pglob->gl_offs;
+		for (i = pglob->gl_pathc; i--; ++pp)
+			free(*pp);
+		free(pglob->gl_pathv);
+		pglob->gl_pathv = NULL;
+	}
+	if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0 &&
+	    pglob->gl_statv != NULL) {
+		for (i = 0; i < pglob->gl_pathc; i++) {
+			free(pglob->gl_statv[i]);
+		}
+		free(pglob->gl_statv);
+		pglob->gl_statv = NULL;
+	}
+}
+
+static DIR *
+g_opendir(wcat_t *str, glob_t *pglob)
+{
+	char buf[PATH_MAX];
+
+	if (str->w_wc == EOS)
+		(void) strlcpy(buf, ".", sizeof (buf));
+	else {
+		if (g_Ctoc(str, buf, sizeof (buf)))
+			return (NULL);
+	}
+
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		return ((*pglob->gl_opendir)(buf));
+
+	return (opendir(buf));
+}
+
+static int
+g_lstat(wcat_t *fn, struct stat *sb, glob_t *pglob)
+{
+	char buf[PATH_MAX];
+
+	if (g_Ctoc(fn, buf, sizeof (buf)))
+		return (-1);
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		return ((*pglob->gl_lstat)(buf, sb));
+	return (lstat(buf, sb));
+}
+
+static int
+g_stat(wcat_t *fn, struct stat *sb, glob_t *pglob)
+{
+	char buf[PATH_MAX];
+
+	if (g_Ctoc(fn, buf, sizeof (buf)))
+		return (-1);
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		return ((*pglob->gl_stat)(buf, sb));
+	return (stat(buf, sb));
+}
+
+static wcat_t *
+g_strchr(const wcat_t *str, wchar_t ch)
+{
+	do {
+		if (str->w_at == 0 && str->w_wc == ch)
+			return ((wcat_t *)str);
+	} while ((str++)->w_wc != EOS);
+	return (NULL);
+}
+
+static int
+g_Ctoc(const wcat_t *str, char *buf, uint_t len)
+{
+	int n;
+	wchar_t w;
+
+	while (len >= MB_LEN_MAX) {
+		w = (str++)->w_wc;
+		if ((n = wctomb(buf, w)) > 0) {
+			len -= n;
+			buf += n;
+		}
+		if (n < 0)
+			break;
+		if (w == EOS)
+			return (0);
+	}
+	return (1);
+}
+
+#if defined(_LP64) || _FILE_OFFSET_BITS != 64
+
+/* glob() function with legacy glob structure */
+int
+old_glob(const char *pattern, int flags, int (*errfunc)(const char *, int),
+    old_glob_t *pglob)
+{
+
+	glob_t gl;
+	int rv;
+
+	flags &= GLOB_POSIX;
+
+	(void) memset(&gl, 0, sizeof (gl));
+
+	/*
+	 * Copy all the members, old to new.  There's
+	 * really no point in micro-optimizing the copying.
+	 * Other members are set to zero.
+	 */
+	gl.gl_pathc = pglob->gl_pathc;
+	gl.gl_pathv = pglob->gl_pathv;
+	gl.gl_offs = pglob->gl_offs;
+	gl.gl_pathp = pglob->gl_pathp;
+	gl.gl_pathn = pglob->gl_pathn;
+
+	rv = _glob_ext(pattern, flags, errfunc, &gl);
+
+	/*
+	 * Copy all the members, new to old.  There's
+	 * really no point in micro-optimizing the copying.
+	 */
+	pglob->gl_pathc = gl.gl_pathc;
+	pglob->gl_pathv = gl.gl_pathv;
+	pglob->gl_offs = gl.gl_offs;
+	pglob->gl_pathp = gl.gl_pathp;
+	pglob->gl_pathn = gl.gl_pathn;
+
+	return (rv);
+}
+
+/* globfree() function with legacy glob structure */
+void
+old_globfree(old_glob_t *pglob)
+{
+	glob_t gl;
+
+	(void) memset(&gl, 0, sizeof (gl));
+
+	/*
+	 * Copy all the members, old to new.  There's
+	 * really no point in micro-optimizing the copying.
+	 * Other members are set to zero.
+	 */
+	gl.gl_pathc = pglob->gl_pathc;
+	gl.gl_pathv = pglob->gl_pathv;
+	gl.gl_offs = pglob->gl_offs;
+	gl.gl_pathp = pglob->gl_pathp;
+	gl.gl_pathn = pglob->gl_pathn;
+
+	_globfree_ext(&gl);
+
+	/*
+	 * Copy all the members, new to old.  There's
+	 * really no point in micro-optimizing the copying.
+	 */
+	pglob->gl_pathc = gl.gl_pathc;
+	pglob->gl_pathv = gl.gl_pathv;
+	pglob->gl_offs = gl.gl_offs;
+	pglob->gl_pathp = gl.gl_pathp;
+	pglob->gl_pathn = gl.gl_pathn;
+
+}
+
+#endif	/* _LP64 || _FILE_OFFSET_BITS != 64 */
--- old/usr/src/lib/libc/port/regex/wordexp.c	Tue Feb 13 07:11:46 2018
+++ /dev/null	Tue Feb 13 07:04:46 2018
@@ -1,433 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-/*
- * This code is MKS code ported to Solaris originally with minimum
- * modifications so that upgrades from MKS would readily integrate.
- * The MKS basis for this modification was:
- *
- *	$Id: wordexp.c 1.22 1994/11/21 18:24:50 miked
- *
- * Additional modifications have been made to this code to make it
- * 64-bit clean.
- */
-
-/*
- * wordexp, wordfree -- POSIX.2 D11.2 word expansion routines.
- *
- * Copyright 1985, 1992 by Mortice Kern Systems Inc.  All rights reserved.
- * Modified by Roland Mainz <roland.mainz@nrubsig.org> to support ksh93.
- */
-
-#pragma	weak _wordexp = wordexp
-#pragma	weak _wordfree = wordfree
-
-#include "lint.h"
-#include <stdio.h>
-#include <unistd.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <alloca.h>
-#include <string.h>
-#include <sys/wait.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <wordexp.h>
-#include <stdio.h>
-#include <spawn.h>
-#include <errno.h>
-
-#define	INITIAL	8		/* initial pathv allocation */
-#define	BUFSZ	256		/* allocation unit of the line buffer */
-
-/*
- * Needs no locking if fetched only once.
- * See getenv()/putenv()/setenv().
- */
-extern	const char **_environ;
-
-/* Local prototypes */
-static int append(wordexp_t *, char *);
-
-/*
- * |mystpcpy| - like |strcpy()| but returns the end of the buffer
- * We'll add this later (and a matching multibyte/widechar version)
- * as normal libc function.
- *
- * Copy string s2 to s1.  s1 must be large enough.
- * return s1-1 (position of string terminator ('\0') in destination buffer).
- */
-static char *
-mystpcpy(char *s1, const char *s2)
-{
-	while (*s1++ = *s2++)
-		;
-	return (s1-1);
-}
-
-/*
- * Do word expansion.
- * We build a mini-script in |buff| which takes care of all details,
- * including stdin/stdout/stderr redirection, WRDE_NOCMD mode and
- * the word expansion itself.
- */
-int
-wordexp(const char *word, wordexp_t *wp, int flags)
-{
-	const char *path = "/usr/bin/ksh93";
-	wordexp_t wptmp;
-	size_t si;
-	pid_t pid;
-	char *line, *eob, *cp;		/* word from shell */
-	int rv = WRDE_ERRNO;
-	int status;
-	int pv[2];			/* pipe from shell stdout */
-	FILE *fp;			/* pipe read stream */
-	int tmpalloc;
-	char *wd = NULL;
-	const char **env = NULL;
-	const char **envp;
-	const char *ev;
-	int n;
-	posix_spawnattr_t attr;
-	posix_spawn_file_actions_t fact;
-	int error;
-	int cancel_state;
-	size_t bufflen; /* Length of |buff| */
-	char *buff;
-	char *currbuffp; /* Current position of '\0' in |buff| */
-	char *args[10];
-	int i;
-
-	/*
-	 * Do absolute minimum necessary for the REUSE flag. Eventually
-	 * want to be able to actually avoid excessive malloc calls.
-	 */
-	if (flags & WRDE_REUSE)
-		wordfree(wp);
-
-	/*
-	 * Initialize wordexp_t
-	 *
-	 * XPG requires that the struct pointed to by wp not be modified
-	 * unless wordexp() either succeeds, or fails on WRDE_NOSPACE.
-	 * So we work with wptmp, and only copy wptmp to wp if one of the
-	 * previously mentioned conditions is satisfied.
-	 */
-	wptmp = *wp;
-
-	/*
-	 * Man page says:
-	 * 2. All of the calls must set WRDE_DOOFFS, or all must not
-	 *    set it.
-	 * Therefore, if it's not set, we_offs will always be reset.
-	 */
-	if ((flags & WRDE_DOOFFS) == 0)
-		wptmp.we_offs = 0;
-
-	/*
-	 * If we get APPEND|REUSE, how should we do?
-	 * allocating buffer anyway to avoid segfault.
-	 */
-	tmpalloc = 0;
-	if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) {
-		wptmp.we_wordc = 0;
-		wptmp.we_wordn = wptmp.we_offs + INITIAL;
-		wptmp.we_wordv = malloc(sizeof (char *) * wptmp.we_wordn);
-		if (wptmp.we_wordv == NULL)
-			return (WRDE_NOSPACE);
-		wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs;
-		for (si = 0; si < wptmp.we_offs; si++)
-			wptmp.we_wordv[si] = NULL;
-		tmpalloc = 1;
-	}
-
-	/*
-	 * The UNIX98 Posix conformance test suite requires
-	 * |wordexp()| to not be a cancellation point.
-	 */
-	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
-
-	/*
-	 * Make sure PWD is in the environment.
-	 */
-	if ((envp = _environ) == NULL) {
-		/* can happen when processing a SunOS 4.x AOUT file */
-		ev = NULL;
-		n = 0;
-	} else {
-		for (n = 0; (ev = envp[n]) != NULL; n++) {
-			if (*ev == 'P' && strncmp(ev, "PWD=", 4) == 0)
-				break;
-		}
-	}
-	if (ev == NULL) {	/* PWD missing from the environment */
-		/* allocate a new environment */
-		if ((env = malloc((n + 2) * sizeof (char *))) == NULL ||
-		    (wd = malloc(PATH_MAX + 4)) == NULL)
-			goto cleanup;
-		for (i = 0; i < n; i++)
-			env[i] = envp[i];
-		(void) strcpy(wd, "PWD=");
-		if (getcwd(&wd[4], PATH_MAX) == NULL)
-			(void) strcpy(&wd[4], "/");
-		env[i] = wd;
-		env[i + 1] = NULL;
-		envp = env;
-	}
-
-	/*
-	 * Calculate size of required buffer (which is size of the
-	 * input string (|word|) plus all string literals below;
-	 * this value MUST be adjusted each time the literals are
-	 * changed!!).
-	 */
-	bufflen = 165 + strlen(word);
-	buff = alloca(bufflen);
-	i = 0;
-
-	/* Start filling the buffer */
-	buff[0] = '\0';
-	currbuffp = buff;
-
-	if (flags & WRDE_UNDEF)
-		currbuffp = mystpcpy(currbuffp, "set -o nounset\n");
-	if ((flags & WRDE_SHOWERR) == 0) {
-		/*
-		 * The newline ('\n') is neccesary to make sure that
-		 * the redirection to /dev/null is already active in
-		 * the case the printf below contains a syntax
-		 * error...
-		 */
-		currbuffp = mystpcpy(currbuffp, "exec 2>/dev/null\n");
-	}
-	/* Squish stdin */
-	currbuffp = mystpcpy(currbuffp, "exec 0</dev/null\n");
-
-	if (flags & WRDE_NOCMD) {
-		/*
-		 * Switch to restricted shell (rksh) mode here to
-		 * put the word expansion into a "cage" which
-		 * prevents users from executing external commands
-		 * (outside those listed by ${PATH} (which we set
-		 * explicitly to /usr/no/such/path/element/)).
-		 */
-		currbuffp = mystpcpy(currbuffp,
-		    "export PATH=/usr/no/such/path/element/ ; "
-		    "set -o restricted\n");
-	}
-
-	(void) snprintf(currbuffp, bufflen,
-	    "print -f '%%s\\000' -- %s", word);
-
-	args[i++] = strrchr(path, '/') + 1;
-	args[i++] = "-c";
-	args[i++] = buff;
-	args[i++] = NULL;
-
-	if ((error = posix_spawnattr_init(&attr)) != 0) {
-		errno = error;
-		goto cleanup;
-	}
-	if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
-		(void) posix_spawnattr_destroy(&attr);
-		errno = error;
-		goto cleanup;
-	}
-
-	/*
-	 * Set up pipe from shell stdout to "fp" for us
-	 */
-	if (pipe(pv) < 0) {
-		error = errno;
-		(void) posix_spawnattr_destroy(&attr);
-		(void) posix_spawn_file_actions_destroy(&fact);
-		errno = error;
-		goto cleanup;
-	}
-
-	/*
-	 * Spawn shell
-	 */
-	error = posix_spawnattr_setflags(&attr,
-	    POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP);
-	if (error == 0)
-		error = posix_spawn_file_actions_adddup2(&fact, pv[1], 1);
-	if (error == 0 && pv[0] != 1)
-		error = posix_spawn_file_actions_addclose(&fact, pv[0]);
-	if (error == 0 && pv[1] != 1)
-		error = posix_spawn_file_actions_addclose(&fact, pv[1]);
-	if (error == 0 && !(flags & WRDE_SHOWERR))
-		error = posix_spawn_file_actions_addopen(&fact, 2,
-		    "/dev/null", O_WRONLY, 0);
-
-	if (error == 0)
-		error = posix_spawn(&pid, path, &fact, &attr,
-		    (char *const *)args, (char *const *)envp);
-	(void) posix_spawnattr_destroy(&attr);
-	(void) posix_spawn_file_actions_destroy(&fact);
-	(void) close(pv[1]);
-	if (error) {
-		(void) close(pv[0]);
-		errno = error;
-		goto cleanup;
-	}
-
-	if ((fp = fdopen(pv[0], "rF")) == NULL) {
-		error = errno;
-		(void) close(pv[0]);
-		errno = error;
-		goto wait_cleanup;
-	}
-
-	/*
-	 * Read words from shell, separated with '\0'.
-	 * Since there is no way to disable IFS splitting,
-	 * it would be possible to separate the output with '\n'.
-	 */
-	cp = line = malloc(BUFSZ);
-	if (line == NULL) {
-		error = errno;
-		(void) fclose(fp);
-		errno = error;
-		goto wait_cleanup;
-	}
-	eob = line + BUFSZ;
-
-	rv = 0;
-	flockfile(fp);
-	while ((i = getc_unlocked(fp)) != EOF) {
-		*cp++ = (char)i;
-		if (i == '\0') {
-			cp = line;
-			if ((rv = append(&wptmp, cp)) != 0) {
-				break;
-			}
-		}
-		if (cp == eob) {
-			size_t bs = (eob - line);
-			char *nl;
-
-			if ((nl = realloc(line, bs + BUFSZ)) == NULL) {
-				rv = WRDE_NOSPACE;
-				break;
-			}
-			line = nl;
-			cp = line + bs;
-			eob = cp + BUFSZ;
-		}
-	}
-	funlockfile(fp);
-
-	wptmp.we_wordp[wptmp.we_wordc] = NULL;
-
-	free(line);
-	(void) fclose(fp);	/* kill shell if still writing */
-
-wait_cleanup:
-	while (waitpid(pid, &status, 0) == -1) {
-		if (errno != EINTR) {
-			if (rv == 0)
-				rv = WRDE_ERRNO;
-			break;
-		}
-	}
-	if (rv == 0)
-		rv = WEXITSTATUS(status); /* shell WRDE_* status */
-
-cleanup:
-	if (rv == 0)
-		*wp = wptmp;
-	else if (tmpalloc)
-		wordfree(&wptmp);
-
-	if (env)
-		free(env);
-	if (wd)
-		free(wd);
-	/*
-	 * Map ksh93 errors to |wordexp()| errors
-	 */
-	switch (rv) {
-		case 1:
-			rv = WRDE_BADVAL;
-			break;
-		case 127:
-			rv = WRDE_BADCHAR;
-			break;
-	}
-
-	(void) pthread_setcancelstate(cancel_state, NULL);
-	return (rv);
-}
-
-/*
- * Append a word to the wordexp_t structure, growing it as necessary.
- */
-static int
-append(wordexp_t *wp, char *str)
-{
-	char *cp;
-	char **nwp;
-
-	/*
-	 * We will be adding one entry and later adding
-	 * one more NULL. So we need 2 more free slots.
-	 */
-	if ((wp->we_wordp + wp->we_wordc) ==
-	    (wp->we_wordv + wp->we_wordn - 1)) {
-		nwp = realloc(wp->we_wordv,
-		    (wp->we_wordn + INITIAL) * sizeof (char *));
-		if (nwp == NULL)
-			return (WRDE_NOSPACE);
-		wp->we_wordn += INITIAL;
-		wp->we_wordv = nwp;
-		wp->we_wordp = wp->we_wordv + wp->we_offs;
-	}
-	if ((cp = strdup(str)) == NULL)
-		return (WRDE_NOSPACE);
-	wp->we_wordp[wp->we_wordc++] = cp;
-	return (0);
-}
-
-/*
- * Free all space owned by wordexp_t.
- */
-void
-wordfree(wordexp_t *wp)
-{
-	size_t i;
-
-	if (wp->we_wordv == NULL)
-		return;
-	for (i = wp->we_offs; i < wp->we_offs + wp->we_wordc; i++)
-		free(wp->we_wordv[i]);
-	free((void *)wp->we_wordv);
-	wp->we_wordc = 0;
-	wp->we_wordv = NULL;
-}
--- /dev/null	Tue Feb 13 07:04:46 2018
+++ new/usr/src/lib/libc/port/gen/wordexp.c	Tue Feb 13 07:11:46 2018
@@ -0,0 +1,433 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This code is MKS code ported to Solaris originally with minimum
+ * modifications so that upgrades from MKS would readily integrate.
+ * The MKS basis for this modification was:
+ *
+ *	$Id: wordexp.c 1.22 1994/11/21 18:24:50 miked
+ *
+ * Additional modifications have been made to this code to make it
+ * 64-bit clean.
+ */
+
+/*
+ * wordexp, wordfree -- POSIX.2 D11.2 word expansion routines.
+ *
+ * Copyright 1985, 1992 by Mortice Kern Systems Inc.  All rights reserved.
+ * Modified by Roland Mainz <roland.mainz@nrubsig.org> to support ksh93.
+ */
+
+#pragma	weak _wordexp = wordexp
+#pragma	weak _wordfree = wordfree
+
+#include "lint.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <alloca.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <wordexp.h>
+#include <stdio.h>
+#include <spawn.h>
+#include <errno.h>
+
+#define	INITIAL	8		/* initial pathv allocation */
+#define	BUFSZ	256		/* allocation unit of the line buffer */
+
+/*
+ * Needs no locking if fetched only once.
+ * See getenv()/putenv()/setenv().
+ */
+extern	const char **_environ;
+
+/* Local prototypes */
+static int append(wordexp_t *, char *);
+
+/*
+ * |mystpcpy| - like |strcpy()| but returns the end of the buffer
+ * We'll add this later (and a matching multibyte/widechar version)
+ * as normal libc function.
+ *
+ * Copy string s2 to s1.  s1 must be large enough.
+ * return s1-1 (position of string terminator ('\0') in destination buffer).
+ */
+static char *
+mystpcpy(char *s1, const char *s2)
+{
+	while (*s1++ = *s2++)
+		;
+	return (s1-1);
+}
+
+/*
+ * Do word expansion.
+ * We build a mini-script in |buff| which takes care of all details,
+ * including stdin/stdout/stderr redirection, WRDE_NOCMD mode and
+ * the word expansion itself.
+ */
+int
+wordexp(const char *word, wordexp_t *wp, int flags)
+{
+	const char *path = "/usr/bin/ksh93";
+	wordexp_t wptmp;
+	size_t si;
+	pid_t pid;
+	char *line, *eob, *cp;		/* word from shell */
+	int rv = WRDE_ERRNO;
+	int status;
+	int pv[2];			/* pipe from shell stdout */
+	FILE *fp;			/* pipe read stream */
+	int tmpalloc;
+	char *wd = NULL;
+	const char **env = NULL;
+	const char **envp;
+	const char *ev;
+	int n;
+	posix_spawnattr_t attr;
+	posix_spawn_file_actions_t fact;
+	int error;
+	int cancel_state;
+	size_t bufflen; /* Length of |buff| */
+	char *buff;
+	char *currbuffp; /* Current position of '\0' in |buff| */
+	char *args[10];
+	int i;
+
+	/*
+	 * Do absolute minimum necessary for the REUSE flag. Eventually
+	 * want to be able to actually avoid excessive malloc calls.
+	 */
+	if (flags & WRDE_REUSE)
+		wordfree(wp);
+
+	/*
+	 * Initialize wordexp_t
+	 *
+	 * XPG requires that the struct pointed to by wp not be modified
+	 * unless wordexp() either succeeds, or fails on WRDE_NOSPACE.
+	 * So we work with wptmp, and only copy wptmp to wp if one of the
+	 * previously mentioned conditions is satisfied.
+	 */
+	wptmp = *wp;
+
+	/*
+	 * Man page says:
+	 * 2. All of the calls must set WRDE_DOOFFS, or all must not
+	 *    set it.
+	 * Therefore, if it's not set, we_offs will always be reset.
+	 */
+	if ((flags & WRDE_DOOFFS) == 0)
+		wptmp.we_offs = 0;
+
+	/*
+	 * If we get APPEND|REUSE, how should we do?
+	 * allocating buffer anyway to avoid segfault.
+	 */
+	tmpalloc = 0;
+	if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) {
+		wptmp.we_wordc = 0;
+		wptmp.we_wordn = wptmp.we_offs + INITIAL;
+		wptmp.we_wordv = malloc(sizeof (char *) * wptmp.we_wordn);
+		if (wptmp.we_wordv == NULL)
+			return (WRDE_NOSPACE);
+		wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs;
+		for (si = 0; si < wptmp.we_offs; si++)
+			wptmp.we_wordv[si] = NULL;
+		tmpalloc = 1;
+	}
+
+	/*
+	 * The UNIX98 Posix conformance test suite requires
+	 * |wordexp()| to not be a cancellation point.
+	 */
+	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
+
+	/*
+	 * Make sure PWD is in the environment.
+	 */
+	if ((envp = _environ) == NULL) {
+		/* can happen when processing a SunOS 4.x AOUT file */
+		ev = NULL;
+		n = 0;
+	} else {
+		for (n = 0; (ev = envp[n]) != NULL; n++) {
+			if (*ev == 'P' && strncmp(ev, "PWD=", 4) == 0)
+				break;
+		}
+	}
+	if (ev == NULL) {	/* PWD missing from the environment */
+		/* allocate a new environment */
+		if ((env = malloc((n + 2) * sizeof (char *))) == NULL ||
+		    (wd = malloc(PATH_MAX + 4)) == NULL)
+			goto cleanup;
+		for (i = 0; i < n; i++)
+			env[i] = envp[i];
+		(void) strcpy(wd, "PWD=");
+		if (getcwd(&wd[4], PATH_MAX) == NULL)
+			(void) strcpy(&wd[4], "/");
+		env[i] = wd;
+		env[i + 1] = NULL;
+		envp = env;
+	}
+
+	/*
+	 * Calculate size of required buffer (which is size of the
+	 * input string (|word|) plus all string literals below;
+	 * this value MUST be adjusted each time the literals are
+	 * changed!!).
+	 */
+	bufflen = 165 + strlen(word);
+	buff = alloca(bufflen);
+	i = 0;
+
+	/* Start filling the buffer */
+	buff[0] = '\0';
+	currbuffp = buff;
+
+	if (flags & WRDE_UNDEF)
+		currbuffp = mystpcpy(currbuffp, "set -o nounset\n");
+	if ((flags & WRDE_SHOWERR) == 0) {
+		/*
+		 * The newline ('\n') is neccesary to make sure that
+		 * the redirection to /dev/null is already active in
+		 * the case the printf below contains a syntax
+		 * error...
+		 */
+		currbuffp = mystpcpy(currbuffp, "exec 2>/dev/null\n");
+	}
+	/* Squish stdin */
+	currbuffp = mystpcpy(currbuffp, "exec 0</dev/null\n");
+
+	if (flags & WRDE_NOCMD) {
+		/*
+		 * Switch to restricted shell (rksh) mode here to
+		 * put the word expansion into a "cage" which
+		 * prevents users from executing external commands
+		 * (outside those listed by ${PATH} (which we set
+		 * explicitly to /usr/no/such/path/element/)).
+		 */
+		currbuffp = mystpcpy(currbuffp,
+		    "export PATH=/usr/no/such/path/element/ ; "
+		    "set -o restricted\n");
+	}
+
+	(void) snprintf(currbuffp, bufflen,
+	    "print -f '%%s\\000' -- %s", word);
+
+	args[i++] = strrchr(path, '/') + 1;
+	args[i++] = "-c";
+	args[i++] = buff;
+	args[i++] = NULL;
+
+	if ((error = posix_spawnattr_init(&attr)) != 0) {
+		errno = error;
+		goto cleanup;
+	}
+	if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
+		(void) posix_spawnattr_destroy(&attr);
+		errno = error;
+		goto cleanup;
+	}
+
+	/*
+	 * Set up pipe from shell stdout to "fp" for us
+	 */
+	if (pipe(pv) < 0) {
+		error = errno;
+		(void) posix_spawnattr_destroy(&attr);
+		(void) posix_spawn_file_actions_destroy(&fact);
+		errno = error;
+		goto cleanup;
+	}
+
+	/*
+	 * Spawn shell
+	 */
+	error = posix_spawnattr_setflags(&attr,
+	    POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP);
+	if (error == 0)
+		error = posix_spawn_file_actions_adddup2(&fact, pv[1], 1);
+	if (error == 0 && pv[0] != 1)
+		error = posix_spawn_file_actions_addclose(&fact, pv[0]);
+	if (error == 0 && pv[1] != 1)
+		error = posix_spawn_file_actions_addclose(&fact, pv[1]);
+	if (error == 0 && !(flags & WRDE_SHOWERR))
+		error = posix_spawn_file_actions_addopen(&fact, 2,
+		    "/dev/null", O_WRONLY, 0);
+
+	if (error == 0)
+		error = posix_spawn(&pid, path, &fact, &attr,
+		    (char *const *)args, (char *const *)envp);
+	(void) posix_spawnattr_destroy(&attr);
+	(void) posix_spawn_file_actions_destroy(&fact);
+	(void) close(pv[1]);
+	if (error) {
+		(void) close(pv[0]);
+		errno = error;
+		goto cleanup;
+	}
+
+	if ((fp = fdopen(pv[0], "rF")) == NULL) {
+		error = errno;
+		(void) close(pv[0]);
+		errno = error;
+		goto wait_cleanup;
+	}
+
+	/*
+	 * Read words from shell, separated with '\0'.
+	 * Since there is no way to disable IFS splitting,
+	 * it would be possible to separate the output with '\n'.
+	 */
+	cp = line = malloc(BUFSZ);
+	if (line == NULL) {
+		error = errno;
+		(void) fclose(fp);
+		errno = error;
+		goto wait_cleanup;
+	}
+	eob = line + BUFSZ;
+
+	rv = 0;
+	flockfile(fp);
+	while ((i = getc_unlocked(fp)) != EOF) {
+		*cp++ = (char)i;
+		if (i == '\0') {
+			cp = line;
+			if ((rv = append(&wptmp, cp)) != 0) {
+				break;
+			}
+		}
+		if (cp == eob) {
+			size_t bs = (eob - line);
+			char *nl;
+
+			if ((nl = realloc(line, bs + BUFSZ)) == NULL) {
+				rv = WRDE_NOSPACE;
+				break;
+			}
+			line = nl;
+			cp = line + bs;
+			eob = cp + BUFSZ;
+		}
+	}
+	funlockfile(fp);
+
+	wptmp.we_wordp[wptmp.we_wordc] = NULL;
+
+	free(line);
+	(void) fclose(fp);	/* kill shell if still writing */
+
+wait_cleanup:
+	while (waitpid(pid, &status, 0) == -1) {
+		if (errno != EINTR) {
+			if (rv == 0)
+				rv = WRDE_ERRNO;
+			break;
+		}
+	}
+	if (rv == 0)
+		rv = WEXITSTATUS(status); /* shell WRDE_* status */
+
+cleanup:
+	if (rv == 0)
+		*wp = wptmp;
+	else if (tmpalloc)
+		wordfree(&wptmp);
+
+	if (env)
+		free(env);
+	if (wd)
+		free(wd);
+	/*
+	 * Map ksh93 errors to |wordexp()| errors
+	 */
+	switch (rv) {
+		case 1:
+			rv = WRDE_BADVAL;
+			break;
+		case 127:
+			rv = WRDE_BADCHAR;
+			break;
+	}
+
+	(void) pthread_setcancelstate(cancel_state, NULL);
+	return (rv);
+}
+
+/*
+ * Append a word to the wordexp_t structure, growing it as necessary.
+ */
+static int
+append(wordexp_t *wp, char *str)
+{
+	char *cp;
+	char **nwp;
+
+	/*
+	 * We will be adding one entry and later adding
+	 * one more NULL. So we need 2 more free slots.
+	 */
+	if ((wp->we_wordp + wp->we_wordc) ==
+	    (wp->we_wordv + wp->we_wordn - 1)) {
+		nwp = realloc(wp->we_wordv,
+		    (wp->we_wordn + INITIAL) * sizeof (char *));
+		if (nwp == NULL)
+			return (WRDE_NOSPACE);
+		wp->we_wordn += INITIAL;
+		wp->we_wordv = nwp;
+		wp->we_wordp = wp->we_wordv + wp->we_offs;
+	}
+	if ((cp = strdup(str)) == NULL)
+		return (WRDE_NOSPACE);
+	wp->we_wordp[wp->we_wordc++] = cp;
+	return (0);
+}
+
+/*
+ * Free all space owned by wordexp_t.
+ */
+void
+wordfree(wordexp_t *wp)
+{
+	size_t i;
+
+	if (wp->we_wordv == NULL)
+		return;
+	for (i = wp->we_offs; i < wp->we_offs + wp->we_wordc; i++)
+		free(wp->we_wordv[i]);
+	free((void *)wp->we_wordv);
+	wp->we_wordc = 0;
+	wp->we_wordv = NULL;
+}
--- old/usr/src/lib/libc/port/locale/collate.c	Tue Feb 13 07:11:46 2018
+++ new/usr/src/lib/libc/port/locale/collate.c	Tue Feb 13 07:11:46 2018
@@ -650,3 +650,54 @@
 		free(tr);
 	return ((size_t)(-1));
 }
+
+/*
+ * __collate_equiv_value returns the primary collation value for the given
+ * collating symbol specified by str and len.  Zero or negative is returned
+ * if the collating symbol was not found.
+ */
+int
+__collate_equiv_value(locale_t loc, const wchar_t *str, size_t len)
+{
+	const struct lc_collate *lcc = loc->collate;
+	int32_t e;
+
+	if (len < 1 || len >= COLLATE_STR_LEN)
+		return (-1);
+
+	if (lcc->lc_is_posix)
+		return ((len == 1 && *str <= UCHAR_MAX) ? *str : -1);
+
+	if (len == 1) {
+		e = -1;
+		if (*str <= UCHAR_MAX)
+			e = lcc->lc_char_table[*str].pri[0];
+		else if (lcc->lc_large_count > 0) {
+			collate_large_t *match_large;
+
+			match_large = largesearch(lcc, *str);
+			if (match_large != NULL)
+				e = match_large->pri.pri[0];
+		}
+		if (e == 0)
+			return (1);
+		return (e > 0 ? e : 0);
+	}
+	if (lcc->lc_chain_count > 0) {
+		wchar_t name[COLLATE_STR_LEN];
+		collate_chain_t *match_chain;
+		int clen;
+
+		wcsncpy(name, str, len);
+		name[len] = 0;
+		match_chain = chainsearch(lcc, name, &clen);
+		if (match_chain != NULL) {
+			e = match_chain->pri[0];
+			if (e == 0)
+				return (1);
+			return (e < 0 ? -e : e);
+		}
+	}
+
+	return (0);
+}
--- old/usr/src/lib/libc/port/locale/collate.h	Tue Feb 13 07:11:47 2018
+++ new/usr/src/lib/libc/port/locale/collate.h	Tue Feb 13 07:11:47 2018
@@ -68,4 +68,6 @@
 size_t	_collate_sxfrm(const wchar_t *, char *, size_t, locale_t);
 int	_collate_range_cmp(wchar_t, wchar_t, locale_t);
 
+int	__collate_equiv_value(locale_t, const wchar_t *, size_t);
+
 #endif /* !_COLLATE_H_ */
--- /dev/null	Tue Feb 13 07:11:47 2018
+++ new/usr/src/lib/libc/port/regex/README.tre	Tue Feb 13 07:11:47 2018
@@ -0,0 +1,13 @@
+This is TRE regex implementation with modifications from DragonFlyBSD.
+
+Original source can be downloaded from:
+http://laurikari.net/tre/
+
+file = tre-0.8.0.tar.bz2
+date = Sep 20 2009
+size = 380714
+sha1 = a41692e64b40ebae3cffe83931ddbf8420a10ae3
+
+Local modifications applied to all files.  The collation, equivalence,
+and legacy character-name table support came from modifications by Apple
+(opensource.apple.com)
--- /dev/null	Tue Feb 13 07:11:47 2018
+++ new/usr/src/lib/libc/port/regex/bitstring.h	Tue Feb 13 07:11:47 2018
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 1989, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Paul Vixie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef	_BITSTRING_H
+#define	_BITSTRING_H
+
+typedef	unsigned char bitstr_t;
+
+/* byte of the bitstring bit is in */
+#define	_bit_byte(bit) \
+	((bit) >> 3)
+/* mask for the bit within its byte */
+#define	_bit_mask(bit) \
+	(1 << ((bit)&0x7))
+/* bytes in a bitstring of nbits bits */
+#define	bitstr_size(nbits) \
+	(((nbits) + 7) >> 3)
+/* is bit N of bitstring name set? */
+#define	bit_test(name, bit) \
+	((name)[_bit_byte(bit)] & _bit_mask(bit))
+/* set bit N of bitstring name */
+#define	bit_set(name, bit) \
+	((name)[_bit_byte(bit)] |= _bit_mask(bit))
+/* find first bit set in name */
+#define	bit_ffs(name, nbits, value) { \
+	bitstr_t *_name = name; \
+	int _byte, _nbits = nbits; \
+	int _stopbyte = _bit_byte(_nbits), _value = -1; \
+	for (_byte = 0; _byte <= _stopbyte; ++_byte) \
+		if (_name[_byte]) { \
+			_value = _byte << 3; \
+			for (_stopbyte = _name[_byte]; !(_stopbyte&0x1); \
+			    ++_value, _stopbyte >>= 1); \
+				break; \
+		} \
+		*(value) = _value; \
+	}
+
+#endif	/* _BITSTRING_H */
--- old/usr/src/lib/libc/port/regex/cname.h	Tue Feb 13 07:11:47 2018
+++ new/usr/src/lib/libc/port/regex/cname.h	Tue Feb 13 07:11:47 2018
@@ -32,104 +32,106 @@
  */
 
 /* character-name table */
-static struct cname {
-	char *name;
-	char code;
+static const struct cname {
+	const wchar_t *name;
+	wchar_t code;
 } cnames[] = {
-	{"NUL",			'\0'},
-	{"SOH",			'\001'},
-	{"STX",			'\002'},
-	{"ETX",			'\003'},
-	{"EOT",			'\004'},
-	{"ENQ",			'\005'},
-	{"ACK",			'\006'},
-	{"BEL",			'\007'},
-	{"alert",		'\007'},
-	{"BS",			'\010'},
-	{"backspace",		'\b'},
-	{"HT",			'\011'},
-	{"tab",			'\t'},
-	{"LF",			'\012'},
-	{"newline",		'\n'},
-	{"VT",			'\013'},
-	{"vertical-tab",	'\v'},
-	{"FF",			'\014'},
-	{"form-feed",		'\f'},
-	{"CR",			'\015'},
-	{"carriage-return",	'\r'},
-	{"SO",			'\016'},
-	{"SI",			'\017'},
-	{"DLE",			'\020'},
-	{"DC1",			'\021'},
-	{"DC2",			'\022'},
-	{"DC3",			'\023'},
-	{"DC4",			'\024'},
-	{"NAK",			'\025'},
-	{"SYN",			'\026'},
-	{"ETB",			'\027'},
-	{"CAN",			'\030'},
-	{"EM",			'\031'},
-	{"SUB",			'\032'},
-	{"ESC",			'\033'},
-	{"IS4",			'\034'},
-	{"FS",			'\034'},
-	{"IS3",			'\035'},
-	{"GS",			'\035'},
-	{"IS2",			'\036'},
-	{"RS",			'\036'},
-	{"IS1",			'\037'},
-	{"US",			'\037'},
-	{"space",		' '},
-	{"exclamation-mark",	'!'},
-	{"quotation-mark",	'"'},
-	{"number-sign",		'#'},
-	{"dollar-sign",		'$'},
-	{"percent-sign",	'%'},
-	{"ampersand",		'&'},
-	{"apostrophe",		'\''},
-	{"left-parenthesis",	'('},
-	{"right-parenthesis",	')'},
-	{"asterisk",		'*'},
-	{"plus-sign",		'+'},
-	{"comma",		','},
-	{"hyphen",		'-'},
-	{"hyphen-minus",	'-'},
-	{"period",		'.'},
-	{"full-stop",		'.'},
-	{"slash",		'/'},
-	{"solidus",		'/'},
-	{"zero",		'0'},
-	{"one",			'1'},
-	{"two",			'2'},
-	{"three",		'3'},
-	{"four",		'4'},
-	{"five",		'5'},
-	{"six",			'6'},
-	{"seven",		'7'},
-	{"eight",		'8'},
-	{"nine",		'9'},
-	{"colon",		':'},
-	{"semicolon",		';'},
-	{"less-than-sign",	'<'},
-	{"equals-sign",		'='},
-	{"greater-than-sign",	'>'},
-	{"question-mark",	'?'},
-	{"commercial-at",	'@'},
-	{"left-square-bracket",	'['},
-	{"backslash",		'\\'},
-	{"reverse-solidus",	'\\'},
-	{"right-square-bracket", ']'},
-	{"circumflex",		'^'},
-	{"circumflex-accent",	'^'},
-	{"underscore",		'_'},
-	{"low-line",		'_'},
-	{"grave-accent",	'`'},
-	{"left-brace",		'{'},
-	{"left-curly-bracket",	'{'},
-	{"vertical-line",	'|'},
-	{"right-brace",		'}'},
-	{"right-curly-bracket",	'}'},
-	{"tilde",		'~'},
-	{"DEL",	'\177'},
-	{NULL,	0}
+	{L"",				(wchar_t)-1}, /* sentinel */
+	{L"ACK",			L'\006'},
+	{L"BEL",			L'\007'},
+	{L"BS",				L'\010'},
+	{L"CAN",			L'\030'},
+	{L"CR",				L'\015'},
+	{L"DC1",			L'\021'},
+	{L"DC2",			L'\022'},
+	{L"DC3",			L'\023'},
+	{L"DC4",			L'\024'},
+	{L"DEL",			L'\177'},
+	{L"DLE",			L'\020'},
+	{L"EM",				L'\031'},
+	{L"ENQ",			L'\005'},
+	{L"EOT",			L'\004'},
+	{L"ESC",			L'\033'},
+	{L"ETB",			L'\027'},
+	{L"ETX",			L'\003'},
+	{L"FF",				L'\014'},
+	{L"FS",				L'\034'},
+	{L"GS",				L'\035'},
+	{L"HT",				L'\011'},
+	{L"IS1",			L'\037'},
+	{L"IS2",			L'\036'},
+	{L"IS3",			L'\035'},
+	{L"IS4",			L'\034'},
+	{L"LF",				L'\012'},
+	{L"NAK",			L'\025'},
+	{L"NUL",			L'\0'},
+	{L"RS",				L'\036'},
+	{L"SI",				L'\017'},
+	{L"SO",				L'\016'},
+	{L"SOH",			L'\001'},
+	{L"STX",			L'\002'},
+	{L"SUB",			L'\032'},
+	{L"SYN",			L'\026'},
+	{L"US",				L'\037'},
+	{L"VT",				L'\013'},
+	{L"alert",			L'\007'},
+	{L"ampersand",			L'&'},
+	{L"apostrophe",			L'\''},
+	{L"asterisk",			L'*'},
+	{L"backslash",			L'\\'},
+	{L"backspace",			L'\b'},
+	{L"carriage-return",		L'\r'},
+	{L"circumflex",			L'^'},
+	{L"circumflex-accent",		L'^'},
+	{L"colon",			L':'},
+	{L"comma",			L','},
+	{L"commercial-at",		L'@'},
+	{L"dollar-sign",		L'$'},
+	{L"eight",			L'8'},
+	{L"equals-sign",		L'='},
+	{L"exclamation-mark",		L'!'},
+	{L"five",			L'5'},
+	{L"form-feed",			L'\f'},
+	{L"four",			L'4'},
+	{L"full-stop",			L'.'},
+	{L"grave-accent",		L'`'},
+	{L"greater-than-sign",		L'>'},
+	{L"hyphen",			L'-'},
+	{L"hyphen-minus",		L'-'},
+	{L"left-brace",			L'{'},
+	{L"left-curly-bracket",		L'{'},
+	{L"left-parenthesis",		L'('},
+	{L"left-square-bracket",	L'['},
+	{L"less-than-sign",		L'<'},
+	{L"low-line",			L'_'},
+	{L"newline",			L'\n'},
+	{L"nine",			L'9'},
+	{L"number-sign",		L'#'},
+	{L"one",			L'1'},
+	{L"percent-sign",		L'%'},
+	{L"period",			L'.'},
+	{L"plus-sign",			L'+'},
+	{L"question-mark",		L'?'},
+	{L"quotation-mark",		L'"'},
+	{L"reverse-solidus",		L'\\'},
+	{L"right-brace",		L'}'},
+	{L"right-curly-bracket",	L'}'},
+	{L"right-parenthesis",		L')'},
+	{L"right-square-bracket",	L']'},
+	{L"semicolon",			L';'},
+	{L"seven",			L'7'},
+	{L"six",			L'6'},
+	{L"slash",			L'/'},
+	{L"solidus",			L'/'},
+	{L"space",			L' '},
+	{L"tab",			L'\t'},
+	{L"three",			L'3'},
+	{L"tilde",			L'~'},
+	{L"two",			L'2'},
+	{L"underscore",			L'_'},
+	{L"vertical-line",		L'|'},
+	{L"vertical-tab",		L'\v'},
+	{L"zero",			L'0'},
 };
+
+#define	NCNAMES		(sizeof (cnames)/sizeof (*cnames))
--- old/usr/src/lib/libc/port/regex/engine.c	Tue Feb 13 07:11:48 2018
+++ /dev/null	Tue Feb 13 07:11:47 2018
@@ -1,1134 +0,0 @@
-/*
- * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
- * Copyright 2012 Milan Jurik. All rights reserved.
- * Copyright (c) 2016 by Delphix. All rights reserved.
- * Copyright (c) 1992, 1993, 1994 Henry Spencer.
- * Copyright (c) 1992, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Henry Spencer.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * The matching engine and friends.  This file is #included by regexec.c
- * after suitable #defines of a variety of macros used herein, so that
- * different state representations can be used without duplicating masses
- * of code.
- */
-
-#ifdef SNAMES
-#define	matcher	smatcher
-#define	fast	sfast
-#define	slow	sslow
-#define	dissect	sdissect
-#define	backref	sbackref
-#define	step	sstep
-#define	print	sprint
-#define	at	sat
-#define	match	smat
-#endif
-#ifdef LNAMES
-#define	matcher	lmatcher
-#define	fast	lfast
-#define	slow	lslow
-#define	dissect	ldissect
-#define	backref	lbackref
-#define	step	lstep
-#define	print	lprint
-#define	at	lat
-#define	match	lmat
-#endif
-#ifdef MNAMES
-#define	matcher	mmatcher
-#define	fast	mfast
-#define	slow	mslow
-#define	dissect	mdissect
-#define	backref	mbackref
-#define	step	mstep
-#define	print	mprint
-#define	at	mat
-#define	match	mmat
-#endif
-
-/* another structure passed up and down to avoid zillions of parameters */
-struct match {
-	struct re_guts *g;
-	int eflags;
-	regmatch_t *pmatch;	/* [nsub+1] (0 element unused) */
-	const char *offp;	/* offsets work from here */
-	const char *beginp;	/* start of string -- virtual NUL precedes */
-	const char *endp;	/* end of string -- virtual NUL here */
-	const char *coldp;	/* can be no match starting before here */
-	const char **lastpos;	/* [nplus+1] */
-	STATEVARS;
-	states st;		/* current states */
-	states fresh;		/* states for a fresh start */
-	states tmp;		/* temporary */
-	states empty;		/* empty set of states */
-	mbstate_t mbs;		/* multibyte conversion state */
-};
-
-/* ========= begin header generated by ./mkh ========= */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* === engine.c === */
-static int matcher(struct re_guts *, const char *, size_t, regmatch_t[], int);
-static const char *dissect(struct match *, const char *, const char *,
-    sopno, sopno);
-static const char *backref(struct match *, const char *, const char *, sopno,
-    sopno, sopno, int);
-static const char *fast(struct match *, const char *, const char *, sopno,
-    sopno);
-static const char *slow(struct match *, const char *, const char *, sopno,
-    sopno);
-static states step(struct re_guts *, sopno, sopno, states, wint_t, states);
-#define	MAX_RECURSION	100
-#define	BOL	(OUT-1)
-#define	EOL	(BOL-1)
-#define	BOLEOL	(BOL-2)
-#define	NOTHING	(BOL-3)
-#define	BOW	(BOL-4)
-#define	EOW	(BOL-5)
-#define	BADCHAR	(BOL-6)
-#define	NONCHAR(c)	((c) <= OUT)
-#ifdef REDEBUG
-static void print(struct match *, const char *, states, int, FILE *);
-#endif
-#ifdef REDEBUG
-static void at(struct match *, const char *, const char *, const char *,
-    sopno, sopno);
-#endif
-#ifdef REDEBUG
-static const char *pchar(int ch);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-/* ========= end header generated by ./mkh ========= */
-
-#ifdef REDEBUG
-#define	SP(t, s, c)	print(m, t, s, c, stdout)
-#define	AT(t, p1, p2, s1, s2)	at(m, t, p1, p2, s1, s2)
-#define	NOTE(str)	{ if (m->eflags&REG_TRACE) printf("=%s\n", (str)); }
-#else
-#define	SP(t, s, c)	/* nothing */
-#define	AT(t, p1, p2, s1, s2)	/* nothing */
-#define	NOTE(s)	/* nothing */
-#endif
-
-/*
- * matcher - the actual matching engine
- */
-static int			/* 0 success, REG_NOMATCH failure */
-matcher(struct re_guts *g, const char *string, size_t nmatch,
-    regmatch_t pmatch[], int eflags)
-{
-	const char *endp;
-	size_t i;
-	struct match mv;
-	struct match *m = &mv;
-	const char *dp = NULL;
-	const sopno gf = g->firststate+1;	/* +1 for OEND */
-	const sopno gl = g->laststate;
-	const char *start;
-	const char *stop;
-	/* Boyer-Moore algorithms variables */
-	const char *pp;
-	int cj, mj;
-	const char *mustfirst;
-	const char *mustlast;
-	int *matchjump;
-	int *charjump;
-
-	/* simplify the situation where possible */
-	if (g->cflags&REG_NOSUB)
-		nmatch = 0;
-
-	if (eflags&REG_STARTEND) {
-		start = string + pmatch[0].rm_so;
-		stop = string + pmatch[0].rm_eo;
-	} else {
-		start = string;
-		stop = start + strlen(start);
-	}
-
-	if (stop < start)
-		return (REG_EFATAL);
-
-	/* prescreening; this does wonders for this rather slow code */
-	if (g->must != NULL) {
-		if (g->charjump != NULL && g->matchjump != NULL) {
-			mustfirst = g->must;
-			mustlast = g->must + g->mlen - 1;
-			charjump = g->charjump;
-			matchjump = g->matchjump;
-			pp = mustlast;
-			for (dp = start+g->mlen-1; dp < stop; ) {
-				/* Fast skip non-matches */
-				while (dp < stop && charjump[(int)*dp])
-					dp += charjump[(int)*dp];
-
-				if (dp >= stop)
-					break;
-
-				/* Greedy matcher */
-				/*
-				 * We depend on not being used for
-				 * for strings of length 1
-				 */
-				while (*--dp == *--pp && pp != mustfirst)
-					;
-
-				if (*dp == *pp)
-					break;
-
-				/* Jump to next possible match */
-				mj = matchjump[pp - mustfirst];
-				cj = charjump[(int)*dp];
-				dp += (cj < mj ? mj : cj);
-				pp = mustlast;
-			}
-			if (pp != mustfirst)
-				return (REG_NOMATCH);
-		} else {
-			for (dp = start; dp < stop; dp++)
-				if (*dp == g->must[0] &&
-				    stop - dp >= g->mlen &&
-				    memcmp(dp, g->must, (size_t)g->mlen) == 0)
-					break;
-			if (dp == stop)		/* we didn't find g->must */
-				return (REG_NOMATCH);
-		}
-	}
-
-	/* match struct setup */
-	m->g = g;
-	m->eflags = eflags;
-	m->pmatch = NULL;
-	m->lastpos = NULL;
-	m->offp = string;
-	m->beginp = start;
-	m->endp = stop;
-	STATESETUP(m, 4);
-	SETUP(m->st);
-	SETUP(m->fresh);
-	SETUP(m->tmp);
-	SETUP(m->empty);
-	CLEAR(m->empty);
-	ZAPSTATE(&m->mbs);
-
-	/* Adjust start according to moffset, to speed things up */
-	if (dp != NULL && g->moffset > -1)
-		start = ((dp - g->moffset) < start) ? start : dp - g->moffset;
-
-	SP("mloop", m->st, *start);
-
-	/* this loop does only one repetition except for backrefs */
-	for (;;) {
-		endp = fast(m, start, stop, gf, gl);
-		if (endp == NULL) {		/* a miss */
-			if (m->pmatch != NULL)
-				free((char *)m->pmatch);
-			if (m->lastpos != NULL)
-				free((char *)m->lastpos);
-			STATETEARDOWN(m);
-			return (REG_NOMATCH);
-		}
-		if (nmatch == 0 && !g->backrefs)
-			break;		/* no further info needed */
-
-		/* where? */
-		assert(m->coldp != NULL);
-		for (;;) {
-			NOTE("finding start");
-			endp = slow(m, m->coldp, stop, gf, gl);
-			if (endp != NULL)
-				break;
-			assert(m->coldp < m->endp);
-			m->coldp += XMBRTOWC(NULL, m->coldp,
-			    m->endp - m->coldp, &m->mbs, 0);
-		}
-		if (nmatch == 1 && !g->backrefs)
-			break;		/* no further info needed */
-
-		/* oh my, it wants the subexpressions... */
-		if (m->pmatch == NULL)
-			m->pmatch = (regmatch_t *)malloc((m->g->nsub + 1) *
-			    sizeof (regmatch_t));
-		if (m->pmatch == NULL) {
-			STATETEARDOWN(m);
-			return (REG_ESPACE);
-		}
-		for (i = 1; i <= m->g->nsub; i++)
-			m->pmatch[i].rm_so = m->pmatch[i].rm_eo = -1;
-		if (!g->backrefs && !(m->eflags&REG_BACKR)) {
-			NOTE("dissecting");
-			dp = dissect(m, m->coldp, endp, gf, gl);
-		} else {
-			if (g->nplus > 0 && m->lastpos == NULL)
-				m->lastpos = malloc((g->nplus+1) *
-				    sizeof (const char *));
-			if (g->nplus > 0 && m->lastpos == NULL) {
-				free(m->pmatch);
-				STATETEARDOWN(m);
-				return (REG_ESPACE);
-			}
-			NOTE("backref dissect");
-			dp = backref(m, m->coldp, endp, gf, gl, (sopno)0, 0);
-		}
-		if (dp != NULL)
-			break;
-
-		/* uh-oh... we couldn't find a subexpression-level match */
-		assert(g->backrefs);	/* must be back references doing it */
-		assert(g->nplus == 0 || m->lastpos != NULL);
-		for (;;) {
-			if (dp != NULL || endp <= m->coldp)
-				break;		/* defeat */
-			NOTE("backoff");
-			endp = slow(m, m->coldp, endp-1, gf, gl);
-			if (endp == NULL)
-				break;		/* defeat */
-			/* try it on a shorter possibility */
-#ifndef NDEBUG
-			for (i = 1; i <= m->g->nsub; i++) {
-				assert(m->pmatch[i].rm_so == -1);
-				assert(m->pmatch[i].rm_eo == -1);
-			}
-#endif
-			NOTE("backoff dissect");
-			dp = backref(m, m->coldp, endp, gf, gl, (sopno)0, 0);
-		}
-		assert(dp == NULL || dp == endp);
-		if (dp != NULL)		/* found a shorter one */
-			break;
-
-		/* despite initial appearances, there is no match here */
-		NOTE("false alarm");
-		/* recycle starting later */
-		start = m->coldp + XMBRTOWC(NULL, m->coldp,
-		    stop - m->coldp, &m->mbs, 0);
-		assert(start <= stop);
-	}
-
-	/* fill in the details if requested */
-	if (nmatch > 0) {
-		pmatch[0].rm_so = m->coldp - m->offp;
-		pmatch[0].rm_eo = endp - m->offp;
-	}
-	if (nmatch > 1) {
-		assert(m->pmatch != NULL);
-		for (i = 1; i < nmatch; i++)
-			if (i <= m->g->nsub)
-				pmatch[i] = m->pmatch[i];
-			else {
-				pmatch[i].rm_so = -1;
-				pmatch[i].rm_eo = -1;
-			}
-	}
-
-	if (m->pmatch != NULL)
-		free((char *)m->pmatch);
-	if (m->lastpos != NULL)
-		free((char *)m->lastpos);
-	STATETEARDOWN(m);
-	return (0);
-}
-
-/*
- * dissect - figure out what matched what, no back references
- */
-static const char *
-dissect(struct match *m, const char *start, const char *stop, sopno startst,
-    sopno stopst)
-{
-	int i;
-	sopno ss;		/* start sop of current subRE */
-	sopno es;		/* end sop of current subRE */
-	const char *sp;		/* start of string matched by it */
-	const char *stp;	/* string matched by it cannot pass here */
-	const char *rest;	/* start of rest of string */
-	const char *tail;	/* string unmatched by rest of RE */
-	sopno ssub;		/* start sop of subsubRE */
-	sopno esub;		/* end sop of subsubRE */
-	const char *ssp;	/* start of string matched by subsubRE */
-	const char *sep;	/* end of string matched by subsubRE */
-	const char *oldssp;	/* previous ssp */
-	const char *dp __unused;
-
-	AT("diss", start, stop, startst, stopst);
-	sp = start;
-	for (ss = startst; ss < stopst; ss = es) {
-		/* identify end of subRE */
-		es = ss;
-		switch (OP(m->g->strip[es])) {
-		case OPLUS_:
-		case OQUEST_:
-			es += OPND(m->g->strip[es]);
-			break;
-		case OCH_:
-			while (OP(m->g->strip[es]) != O_CH)
-				es += OPND(m->g->strip[es]);
-			break;
-		}
-		es++;
-
-		/* figure out what it matched */
-		switch (OP(m->g->strip[ss])) {
-		case OEND:
-			assert(0);
-			break;
-		case OCHAR:
-			sp += XMBRTOWC(NULL, sp, stop - start, &m->mbs, 0);
-			break;
-		case OBOL:
-		case OEOL:
-		case OBOW:
-		case OEOW:
-			break;
-		case OANY:
-		case OANYOF:
-			sp += XMBRTOWC(NULL, sp, stop - start, &m->mbs, 0);
-			break;
-		case OBACK_:
-		case O_BACK:
-			assert(0);
-			break;
-		/* cases where length of match is hard to find */
-		case OQUEST_:
-			stp = stop;
-			for (;;) {
-				/* how long could this one be? */
-				rest = slow(m, sp, stp, ss, es);
-				assert(rest != NULL);	/* it did match */
-				/* could the rest match the rest? */
-				tail = slow(m, rest, stop, es, stopst);
-				if (tail == stop)
-					break;		/* yes! */
-				/* no -- try a shorter match for this one */
-				stp = rest - 1;
-				assert(stp >= sp);	/* it did work */
-			}
-			ssub = ss + 1;
-			esub = es - 1;
-			/* did innards match? */
-			if (slow(m, sp, rest, ssub, esub) != NULL) {
-				dp = dissect(m, sp, rest, ssub, esub);
-				assert(dp == rest);
-#if defined(__lint)
-				(void) dp;
-#endif
-			} else		/* no */
-				assert(sp == rest);
-			sp = rest;
-			break;
-		case OPLUS_:
-			stp = stop;
-			for (;;) {
-				/* how long could this one be? */
-				rest = slow(m, sp, stp, ss, es);
-				assert(rest != NULL);	/* it did match */
-				/* could the rest match the rest? */
-				tail = slow(m, rest, stop, es, stopst);
-				if (tail == stop)
-					break;		/* yes! */
-				/* no -- try a shorter match for this one */
-				stp = rest - 1;
-				assert(stp >= sp);	/* it did work */
-			}
-			ssub = ss + 1;
-			esub = es - 1;
-			ssp = sp;
-			oldssp = ssp;
-			for (;;) {	/* find last match of innards */
-				sep = slow(m, ssp, rest, ssub, esub);
-				if (sep == NULL || sep == ssp)
-					break;	/* failed or matched null */
-				oldssp = ssp;	/* on to next try */
-				ssp = sep;
-			}
-			if (sep == NULL) {
-				/* last successful match */
-				sep = ssp;
-				ssp = oldssp;
-			}
-			assert(sep == rest);	/* must exhaust substring */
-			assert(slow(m, ssp, sep, ssub, esub) == rest);
-			dp = dissect(m, ssp, sep, ssub, esub);
-			assert(dp == sep);
-			sp = rest;
-			break;
-		case OCH_:
-			stp = stop;
-			for (;;) {
-				/* how long could this one be? */
-				rest = slow(m, sp, stp, ss, es);
-				assert(rest != NULL);	/* it did match */
-				/* could the rest match the rest? */
-				tail = slow(m, rest, stop, es, stopst);
-				if (tail == stop)
-					break;		/* yes! */
-				/* no -- try a shorter match for this one */
-				stp = rest - 1;
-				assert(stp >= sp);	/* it did work */
-			}
-			ssub = ss + 1;
-			esub = ss + OPND(m->g->strip[ss]) - 1;
-			assert(OP(m->g->strip[esub]) == OOR1);
-			for (;;) {	/* find first matching branch */
-				if (slow(m, sp, rest, ssub, esub) == rest)
-					break;	/* it matched all of it */
-				/* that one missed, try next one */
-				assert(OP(m->g->strip[esub]) == OOR1);
-				esub++;
-				assert(OP(m->g->strip[esub]) == OOR2);
-				ssub = esub + 1;
-				esub += OPND(m->g->strip[esub]);
-				if (OP(m->g->strip[esub]) == OOR2)
-					esub--;
-				else
-					assert(OP(m->g->strip[esub]) == O_CH);
-			}
-			dp = dissect(m, sp, rest, ssub, esub);
-			assert(dp == rest);
-			sp = rest;
-			break;
-		case O_PLUS:
-		case O_QUEST:
-		case OOR1:
-		case OOR2:
-		case O_CH:
-			assert(0);
-			break;
-		case OLPAREN:
-			i = OPND(m->g->strip[ss]);
-			assert(0 < i && i <= m->g->nsub);
-			m->pmatch[i].rm_so = sp - m->offp;
-			break;
-		case ORPAREN:
-			i = OPND(m->g->strip[ss]);
-			assert(0 < i && i <= m->g->nsub);
-			m->pmatch[i].rm_eo = sp - m->offp;
-			break;
-		default:		/* uh oh */
-			assert(0);
-			break;
-		}
-	}
-
-	assert(sp == stop);
-	return (sp);
-}
-
-/*
- * backref - figure out what matched what, figuring in back references
- */
-static const char *
-backref(struct match *m, const char *start, const char *stop, sopno startst,
-    sopno stopst, sopno lev,		/* PLUS nesting level */
-    int rec)
-{
-	int i;
-	sopno ss;		/* start sop of current subRE */
-	const char *sp;		/* start of string matched by it */
-	sopno ssub;		/* start sop of subsubRE */
-	sopno esub;		/* end sop of subsubRE */
-	const char *ssp;	/* start of string matched by subsubRE */
-	const char *dp;
-	size_t len;
-	int hard;
-	sop s;
-	regoff_t offsave;
-	cset *cs;
-	wint_t wc;
-
-	AT("back", start, stop, startst, stopst);
-	sp = start;
-
-	/* get as far as we can with easy stuff */
-	hard = 0;
-	for (ss = startst; !hard && ss < stopst; ss++)
-		switch (OP(s = m->g->strip[ss])) {
-		case OCHAR:
-			if (sp == stop)
-				return (NULL);
-			sp += XMBRTOWC(&wc, sp, stop - sp, &m->mbs, BADCHAR);
-			if (wc != OPND(s))
-				return (NULL);
-			break;
-		case OANY:
-			if (sp == stop)
-				return (NULL);
-			sp += XMBRTOWC(&wc, sp, stop - sp, &m->mbs, BADCHAR);
-			if (wc == BADCHAR)
-				return (NULL);
-			break;
-		case OANYOF:
-			if (sp == stop)
-				return (NULL);
-			cs = &m->g->sets[OPND(s)];
-			sp += XMBRTOWC(&wc, sp, stop - sp, &m->mbs, BADCHAR);
-			if (wc == BADCHAR || !CHIN(cs, wc))
-				return (NULL);
-			break;
-		case OBOL:
-			if ((sp == m->beginp && !(m->eflags&REG_NOTBOL)) ||
-			    (sp > m->offp && sp < m->endp &&
-			    *(sp-1) == '\n' && (m->g->cflags&REG_NEWLINE))) {
-				break;
-			}
-			return (NULL);
-		case OEOL:
-			if ((sp == m->endp && !(m->eflags&REG_NOTEOL)) ||
-			    (sp < m->endp && *sp == '\n' &&
-			    (m->g->cflags&REG_NEWLINE))) {
-				break;
-			}
-			return (NULL);
-		case OBOW:
-			if (sp < m->endp && ISWORD(*sp) &&
-			    ((sp == m->beginp && !(m->eflags&REG_NOTBOL)) ||
-			    (sp > m->offp && !ISWORD(*(sp-1))))) {
-				break;
-			}
-			return (NULL);
-		case OEOW:
-			if (((sp == m->endp && !(m->eflags&REG_NOTEOL)) ||
-			    (sp < m->endp && *sp == '\n' &&
-			    (m->g->cflags&REG_NEWLINE)) ||
-			    (sp < m->endp && !ISWORD(*sp))) &&
-			    (sp > m->beginp && ISWORD(*(sp-1)))) {
-				break;
-			}
-			return (NULL);
-		case O_QUEST:
-			break;
-		case OOR1:	/* matches null but needs to skip */
-			ss++;
-			s = m->g->strip[ss];
-			do {
-				assert(OP(s) == OOR2);
-				ss += OPND(s);
-			} while (OP(s = m->g->strip[ss]) != O_CH);
-			/* note that the ss++ gets us past the O_CH */
-			break;
-		default:	/* have to make a choice */
-			hard = 1;
-			break;
-		}
-	if (!hard) {		/* that was it! */
-		if (sp != stop)
-			return (NULL);
-		return (sp);
-	}
-	ss--;			/* adjust for the for's final increment */
-
-	/* the hard stuff */
-	AT("hard", sp, stop, ss, stopst);
-	s = m->g->strip[ss];
-	switch (OP(s)) {
-	case OBACK_:		/* the vilest depths */
-		i = OPND(s);
-		assert(0 < i && i <= m->g->nsub);
-		if (m->pmatch[i].rm_eo == -1)
-			return (NULL);
-		assert(m->pmatch[i].rm_so != -1);
-		len = m->pmatch[i].rm_eo - m->pmatch[i].rm_so;
-		if (len == 0 && rec++ > MAX_RECURSION)
-			return (NULL);
-		assert(stop - m->beginp >= len);
-		if (sp > stop - len)
-			return (NULL);	/* not enough left to match */
-		ssp = m->offp + m->pmatch[i].rm_so;
-		if (memcmp(sp, ssp, len) != 0)
-			return (NULL);
-		while (m->g->strip[ss] != SOP(O_BACK, i))
-			ss++;
-		return (backref(m, sp+len, stop, ss+1, stopst, lev, rec));
-	case OQUEST_:		/* to null or not */
-		dp = backref(m, sp, stop, ss+1, stopst, lev, rec);
-		if (dp != NULL)
-			return (dp);	/* not */
-		return (backref(m, sp, stop, ss+OPND(s)+1, stopst, lev, rec));
-	case OPLUS_:
-		assert(m->lastpos != NULL);
-		assert(lev+1 <= m->g->nplus);
-		m->lastpos[lev+1] = sp;
-		return (backref(m, sp, stop, ss+1, stopst, lev+1, rec));
-	case O_PLUS:
-		if (sp == m->lastpos[lev])	/* last pass matched null */
-			return (backref(m, sp, stop, ss+1, stopst, lev-1, rec));
-		/* try another pass */
-		m->lastpos[lev] = sp;
-		dp = backref(m, sp, stop, ss-OPND(s)+1, stopst, lev, rec);
-		if (dp == NULL)
-			return (backref(m, sp, stop, ss+1, stopst, lev-1, rec));
-		return (dp);
-	case OCH_:		/* find the right one, if any */
-		ssub = ss + 1;
-		esub = ss + OPND(s) - 1;
-		assert(OP(m->g->strip[esub]) == OOR1);
-		for (;;) {	/* find first matching branch */
-			dp = backref(m, sp, stop, ssub, esub, lev, rec);
-			if (dp != NULL)
-				return (dp);
-			/* that one missed, try next one */
-			if (OP(m->g->strip[esub]) == O_CH)
-				return (NULL);	/* there is none */
-			esub++;
-			assert(OP(m->g->strip[esub]) == OOR2);
-			ssub = esub + 1;
-			esub += OPND(m->g->strip[esub]);
-			if (OP(m->g->strip[esub]) == OOR2)
-				esub--;
-			else
-				assert(OP(m->g->strip[esub]) == O_CH);
-		}
-		/* NOTREACHED */
-		break;
-	case OLPAREN:		/* must undo assignment if rest fails */
-		i = OPND(s);
-		assert(0 < i && i <= m->g->nsub);
-		offsave = m->pmatch[i].rm_so;
-		m->pmatch[i].rm_so = sp - m->offp;
-		dp = backref(m, sp, stop, ss+1, stopst, lev, rec);
-		if (dp != NULL)
-			return (dp);
-		m->pmatch[i].rm_so = offsave;
-		return (NULL);
-	case ORPAREN:		/* must undo assignment if rest fails */
-		i = OPND(s);
-		assert(0 < i && i <= m->g->nsub);
-		offsave = m->pmatch[i].rm_eo;
-		m->pmatch[i].rm_eo = sp - m->offp;
-		dp = backref(m, sp, stop, ss+1, stopst, lev, rec);
-		if (dp != NULL)
-			return (dp);
-		m->pmatch[i].rm_eo = offsave;
-		return (NULL);
-	default:		/* uh oh */
-		assert(0);
-		break;
-	}
-
-	/* "can't happen" */
-	assert(0);
-	return (NULL);
-}
-
-/*
- * fast - step through the string at top speed
- */
-static const char *
-fast(struct match *m, const char *start, const char *stop, sopno startst,
-    sopno stopst)
-{
-	states st = m->st;
-	states fresh = m->fresh;
-	states tmp = m->tmp;
-	const char *p = start;
-	wint_t c;
-	wint_t lastc;		/* previous c */
-	wint_t flagch;
-	int i;
-	const char *coldp;	/* last p after which no match was underway */
-	size_t clen;
-
-	CLEAR(st);
-	SET1(st, startst);
-	SP("fast", st, *p);
-	st = step(m->g, startst, stopst, st, NOTHING, st);
-	ASSIGN(fresh, st);
-	SP("start", st, *p);
-	coldp = NULL;
-	if (start == m->offp || (start == m->beginp && !(m->eflags&REG_NOTBOL)))
-		c = OUT;
-	else {
-		/*
-		 * XXX Wrong if the previous character was multi-byte.
-		 * Newline never is (in encodings supported by FreeBSD),
-		 * so this only breaks the ISWORD tests below.
-		 */
-		c = (uch)*(start - 1);
-	}
-	for (;;) {
-		/* next character */
-		lastc = c;
-		if (p == m->endp) {
-			clen = 0;
-			c = OUT;
-		} else
-			clen = XMBRTOWC(&c, p, m->endp - p, &m->mbs, BADCHAR);
-		if (EQ(st, fresh))
-			coldp = p;
-
-		/* is there an EOL and/or BOL between lastc and c? */
-		flagch = '\0';
-		i = 0;
-		if ((lastc == '\n' && m->g->cflags&REG_NEWLINE) ||
-		    (lastc == OUT && !(m->eflags&REG_NOTBOL))) {
-			flagch = BOL;
-			i = m->g->nbol;
-		}
-		if ((c == '\n' && m->g->cflags&REG_NEWLINE) ||
-		    (c == OUT && !(m->eflags&REG_NOTEOL))) {
-			flagch = (flagch == BOL) ? BOLEOL : EOL;
-			i += m->g->neol;
-		}
-		if (i != 0) {
-			for (; i > 0; i--)
-				st = step(m->g, startst, stopst, st,
-				    flagch, st);
-			SP("boleol", st, c);
-		}
-
-		/* how about a word boundary? */
-		if ((flagch == BOL || (lastc != OUT && !ISWORD(lastc))) &&
-		    (c != OUT && ISWORD(c))) {
-			flagch = BOW;
-		}
-		if ((lastc != OUT && ISWORD(lastc)) &&
-		    (flagch == EOL || (c != OUT && !ISWORD(c)))) {
-			flagch = EOW;
-		}
-		if (flagch == BOW || flagch == EOW) {
-			st = step(m->g, startst, stopst, st, flagch, st);
-			SP("boweow", st, c);
-		}
-
-		/* are we done? */
-		if (ISSET(st, stopst) || p == stop || clen > stop - p)
-			break;		/* NOTE BREAK OUT */
-
-		/* no, we must deal with this character */
-		ASSIGN(tmp, st);
-		ASSIGN(st, fresh);
-		assert(c != OUT);
-		st = step(m->g, startst, stopst, tmp, c, st);
-		SP("aft", st, c);
-		assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st));
-		p += clen;
-	}
-
-	assert(coldp != NULL);
-	m->coldp = coldp;
-	if (ISSET(st, stopst))
-		return (p+XMBRTOWC(NULL, p, stop - p, &m->mbs, 0));
-	else
-		return (NULL);
-}
-
-/*
- * slow - step through the string more deliberately
- */
-static const char *
-slow(struct match *m, const char *start, const char *stop, sopno startst,
-    sopno stopst)
-{
-	states st = m->st;
-	states empty = m->empty;
-	states tmp = m->tmp;
-	const char *p = start;
-	wint_t c;
-	wint_t lastc;		/* previous c */
-	wint_t flagch;
-	int i;
-	const char *matchp;	/* last p at which a match ended */
-	size_t clen;
-
-	AT("slow", start, stop, startst, stopst);
-	CLEAR(st);
-	SET1(st, startst);
-	SP("sstart", st, *p);
-	st = step(m->g, startst, stopst, st, NOTHING, st);
-	matchp = NULL;
-	if (start == m->offp || (start == m->beginp && !(m->eflags&REG_NOTBOL)))
-		c = OUT;
-	else {
-		/*
-		 * XXX Wrong if the previous character was multi-byte.
-		 * Newline never is (in encodings supported by FreeBSD),
-		 * so this only breaks the ISWORD tests below.
-		 */
-		c = (uch)*(start - 1);
-	}
-	for (;;) {
-		/* next character */
-		lastc = c;
-		if (p == m->endp) {
-			c = OUT;
-			clen = 0;
-		} else
-			clen = XMBRTOWC(&c, p, m->endp - p, &m->mbs, BADCHAR);
-
-		/* is there an EOL and/or BOL between lastc and c? */
-		flagch = '\0';
-		i = 0;
-		if ((lastc == '\n' && m->g->cflags&REG_NEWLINE) ||
-		    (lastc == OUT && !(m->eflags&REG_NOTBOL))) {
-			flagch = BOL;
-			i = m->g->nbol;
-		}
-		if ((c == '\n' && m->g->cflags&REG_NEWLINE) ||
-		    (c == OUT && !(m->eflags&REG_NOTEOL))) {
-			flagch = (flagch == BOL) ? BOLEOL : EOL;
-			i += m->g->neol;
-		}
-		if (i != 0) {
-			for (; i > 0; i--)
-				st = step(m->g, startst, stopst, st,
-				    flagch, st);
-			SP("sboleol", st, c);
-		}
-
-		/* how about a word boundary? */
-		if ((flagch == BOL || (lastc != OUT && !ISWORD(lastc))) &&
-		    (c != OUT && ISWORD(c))) {
-			flagch = BOW;
-		}
-		if ((lastc != OUT && ISWORD(lastc)) &&
-		    (flagch == EOL || (c != OUT && !ISWORD(c)))) {
-			flagch = EOW;
-		}
-		if (flagch == BOW || flagch == EOW) {
-			st = step(m->g, startst, stopst, st, flagch, st);
-			SP("sboweow", st, c);
-		}
-
-		/* are we done? */
-		if (ISSET(st, stopst))
-			matchp = p;
-		if (EQ(st, empty) || p == stop || clen > stop - p)
-			break;		/* NOTE BREAK OUT */
-
-		/* no, we must deal with this character */
-		ASSIGN(tmp, st);
-		ASSIGN(st, empty);
-		assert(c != OUT);
-		st = step(m->g, startst, stopst, tmp, c, st);
-		SP("saft", st, c);
-		assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st));
-		p += clen;
-	}
-
-	return (matchp);
-}
-
-
-/*
- * step - map set of states reachable before char to set reachable after
- */
-static states
-step(struct re_guts *g,
-    sopno start,	/* start state within strip */
-    sopno stop,		/* state after stop state within strip */
-    states bef,		/* states reachable before */
-    wint_t ch,		/* character or NONCHAR code */
-    states aft)		/* states already known reachable after */
-{
-	cset *cs;
-	sop s;
-	sopno pc;
-	onestate here;		/* note, macros know this name */
-	sopno look;
-	int i;
-
-	for (pc = start, INIT(here, pc); pc != stop; pc++, INC(here)) {
-		s = g->strip[pc];
-		switch (OP(s)) {
-		case OEND:
-			assert(pc == stop-1);
-			break;
-		case OCHAR:
-			/* only characters can match */
-			assert(!NONCHAR(ch) || ch != OPND(s));
-			if (ch == OPND(s))
-				FWD(aft, bef, 1);
-			break;
-		case OBOL:
-			if (ch == BOL || ch == BOLEOL)
-				FWD(aft, bef, 1);
-			break;
-		case OEOL:
-			if (ch == EOL || ch == BOLEOL)
-				FWD(aft, bef, 1);
-			break;
-		case OBOW:
-			if (ch == BOW)
-				FWD(aft, bef, 1);
-			break;
-		case OEOW:
-			if (ch == EOW)
-				FWD(aft, bef, 1);
-			break;
-		case OANY:
-			if (!NONCHAR(ch))
-				FWD(aft, bef, 1);
-			break;
-		case OANYOF:
-			cs = &g->sets[OPND(s)];
-			if (!NONCHAR(ch) && CHIN(cs, ch))
-				FWD(aft, bef, 1);
-			break;
-		case OBACK_:		/* ignored here */
-		case O_BACK:
-			FWD(aft, aft, 1);
-			break;
-		case OPLUS_:		/* forward, this is just an empty */
-			FWD(aft, aft, 1);
-			break;
-		case O_PLUS:		/* both forward and back */
-			FWD(aft, aft, 1);
-			i = ISSETBACK(aft, OPND(s));
-			BACK(aft, aft, OPND(s));
-			if (!i && ISSETBACK(aft, OPND(s))) {
-				/* oho, must reconsider loop body */
-				pc -= OPND(s) + 1;
-				INIT(here, pc);
-			}
-			break;
-		case OQUEST_:		/* two branches, both forward */
-			FWD(aft, aft, 1);
-			FWD(aft, aft, OPND(s));
-			break;
-		case O_QUEST:		/* just an empty */
-			FWD(aft, aft, 1);
-			break;
-		case OLPAREN:		/* not significant here */
-		case ORPAREN:
-			FWD(aft, aft, 1);
-			break;
-		case OCH_:		/* mark the first two branches */
-			FWD(aft, aft, 1);
-			assert(OP(g->strip[pc+OPND(s)]) == OOR2);
-			FWD(aft, aft, OPND(s));
-			break;
-		case OOR1:		/* done a branch, find the O_CH */
-			if (ISSTATEIN(aft, here)) {
-				for (look = 1;
-				    OP(s = g->strip[pc+look]) != O_CH;
-				    look += OPND(s))
-					assert(OP(s) == OOR2);
-				FWD(aft, aft, look + 1);
-			}
-			break;
-		case OOR2:		/* propagate OCH_'s marking */
-			FWD(aft, aft, 1);
-			if (OP(g->strip[pc+OPND(s)]) != O_CH) {
-				assert(OP(g->strip[pc+OPND(s)]) == OOR2);
-				FWD(aft, aft, OPND(s));
-			}
-			break;
-		case O_CH:		/* just empty */
-			FWD(aft, aft, 1);
-			break;
-		default:		/* ooooops... */
-			assert(0);
-			break;
-		}
-	}
-
-	return (aft);
-}
-
-#ifdef REDEBUG
-/*
- * print - print a set of states
- */
-static void
-print(struct match *m, const char *caption, states st, int ch, FILE *d)
-{
-	struct re_guts *g = m->g;
-	sopno i;
-	int first = 1;
-
-	if (!(m->eflags&REG_TRACE))
-		return;
-
-	(void) fprintf(d, "%s", caption);
-	if (ch != '\0')
-		(void) fprintf(d, " %s", pchar(ch));
-	for (i = 0; i < g->nstates; i++)
-		if (ISSET(st, i)) {
-			(void) fprintf(d, "%s%d", (first) ? "\t" : ", ", i);
-			first = 0;
-		}
-	(void) fprintf(d, "\n");
-}
-
-/*
- * at - print current situation
- */
-static void
-at(struct match *m, const char *title, const char *start, const char *stop,
-    sopno startst, sopno stopst)
-{
-	if (!(m->eflags&REG_TRACE))
-		return;
-
-	(void) printf("%s %s-", title, pchar(*start));
-	(void) printf("%s ", pchar(*stop));
-	(void) printf("%ld-%ld\n", (long)startst, (long)stopst);
-}
-
-#ifndef PCHARDONE
-#define	PCHARDONE	/* never again */
-/*
- * pchar - make a character printable
- *
- * Is this identical to regchar() over in debug.c?  Well, yes.  But a
- * duplicate here avoids having a debugging-capable regexec.o tied to
- * a matching debug.o, and this is convenient.  It all disappears in
- * the non-debug compilation anyway, so it doesn't matter much.
- */
-static const char *
-pchar(int ch)
-{
-	static char pbuf[10];
-
-	if (isprint((uch)ch) || ch == ' ')
-		(void) sprintf(pbuf, "%c", ch);
-	else
-		(void) sprintf(pbuf, "\\%o", ch);
-	return (pbuf);
-}
-#endif
-#endif
-
-#undef	matcher
-#undef	fast
-#undef	slow
-#undef	dissect
-#undef	backref
-#undef	step
-#undef	print
-#undef	at
-#undef	match
--- old/usr/src/lib/libc/port/regex/regcomp.c	Tue Feb 13 07:11:48 2018
+++ new/usr/src/lib/libc/port/regex/regcomp.c	Tue Feb 13 07:11:48 2018
@@ -1,1783 +1,141 @@
 /*
- * Copyright 2013 Garrett D'Amore <garrett@damore.org>
- * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
- * Copyright 2012 Milan Jurik. All rights reserved.
- * Copyright (c) 1992, 1993, 1994 Henry Spencer.
- * Copyright (c) 1992, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
  *
- * This code is derived from software contributed to Berkeley by
- * Henry Spencer.
- *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
+ *
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
+ *
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "lint.h"
-#include "file64.h"
-#include <sys/types.h>
-#include <stdio.h>
+/*
+  tre_regcomp.c - TRE POSIX compatible regex compilation functions.
+*/
+
 #include <string.h>
-#include <ctype.h>
-#include <limits.h>
+#include <errno.h>
 #include <stdlib.h>
-#include <regex.h>
-#include <wchar.h>
-#include <wctype.h>
 
-#include "../locale/runetype.h"
-#include "../locale/collate.h"
+#include "tre.h"
+#include "tre-internal.h"
 
-#include "utils.h"
-#include "regex2.h"
-
-#include "cname.h"
-#include "../locale/mblocal.h"
-
-/*
- * parse structure, passed up and down to avoid global variables and
- * other clumsinesses
- */
-struct parse {
-	const char *next;	/* next character in RE */
-	const char *end;	/* end of string (-> NUL normally) */
-	int error;		/* has an error been seen? */
-	sop *strip;		/* malloced strip */
-	sopno ssize;		/* malloced strip size (allocated) */
-	sopno slen;		/* malloced strip length (used) */
-	int ncsalloc;		/* number of csets allocated */
-	struct re_guts *g;
-#define	NPAREN	10		/* we need to remember () 1-9 for back refs */
-	sopno pbegin[NPAREN];	/* -> ( ([0] unused) */
-	sopno pend[NPAREN];	/* -> ) ([0] unused) */
-};
-
-/* ========= begin header generated by ./mkh ========= */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* === regcomp.c === */
-static void p_ere(struct parse *p, int stop);
-static void p_ere_exp(struct parse *p);
-static void p_str(struct parse *p);
-static void p_bre(struct parse *p, int end1, int end2);
-static int p_simp_re(struct parse *p, int starordinary);
-static int p_count(struct parse *p);
-static void p_bracket(struct parse *p);
-static void p_b_term(struct parse *p, cset *cs);
-static void p_b_cclass(struct parse *p, cset *cs);
-static void p_b_eclass(struct parse *p, cset *cs);
-static wint_t p_b_symbol(struct parse *p);
-static wint_t p_b_coll_elem(struct parse *p, wint_t endc);
-static wint_t othercase(wint_t ch);
-static void bothcases(struct parse *p, wint_t ch);
-static void ordinary(struct parse *p, wint_t ch);
-static void nonnewline(struct parse *p);
-static void repeat(struct parse *p, sopno start, int from, int to);
-static int seterr(struct parse *p, int e);
-static cset *allocset(struct parse *p);
-static void freeset(struct parse *p, cset *cs);
-static void CHadd(struct parse *p, cset *cs, wint_t ch);
-static void CHaddrange(struct parse *p, cset *cs, wint_t min, wint_t max);
-static void CHaddtype(struct parse *p, cset *cs, wctype_t wct);
-static wint_t singleton(cset *cs);
-static sopno dupl(struct parse *p, sopno start, sopno finish);
-static void doemit(struct parse *p, sop op, size_t opnd);
-static void doinsert(struct parse *p, sop op, size_t opnd, sopno pos);
-static void dofwd(struct parse *p, sopno pos, sop value);
-static int enlarge(struct parse *p, sopno size);
-static void stripsnug(struct parse *p, struct re_guts *g);
-static void findmust(struct parse *p, struct re_guts *g);
-static int altoffset(sop *scan, int offset);
-static void computejumps(struct parse *p, struct re_guts *g);
-static void computematchjumps(struct parse *p, struct re_guts *g);
-static sopno pluscount(struct parse *p, struct re_guts *g);
-static wint_t wgetnext(struct parse *p);
-
-#ifdef __cplusplus
-}
-#endif
-/* ========= end header generated by ./mkh ========= */
-
-static char nuls[10];		/* place to point scanner in event of error */
-
-/*
- * macros for use with parse structure
- * BEWARE:  these know that the parse structure is named `p' !!!
- */
-#define	PEEK()	(*p->next)
-#define	PEEK2()	(*(p->next+1))
-#define	MORE()	(p->next < p->end)
-#define	MORE2()	(p->next+1 < p->end)
-#define	SEE(c)	(MORE() && PEEK() == (c))
-#define	SEETWO(a, b)	(MORE() && MORE2() && PEEK() == (a) && PEEK2() == (b))
-#define	EAT(c)	((SEE(c)) ? (NEXT(), 1) : 0)
-#define	EATTWO(a, b)	((SEETWO(a, b)) ? (NEXT2(), 1) : 0)
-#define	NEXT()	(p->next++)
-#define	NEXT2()	(p->next += 2)
-#define	NEXTn(n)	(p->next += (n))
-#define	GETNEXT()	(*p->next++)
-#define	WGETNEXT()	wgetnext(p)
-#define	SETERROR(e)	((void)seterr(p, (e)))
-#define	REQUIRE(co, e)	((co) || seterr(p, e))
-#define	MUSTSEE(c, e)	(REQUIRE(MORE() && PEEK() == (c), e))
-#define	MUSTEAT(c, e)	(REQUIRE(MORE() && GETNEXT() == (c), e))
-#define	MUSTNOTSEE(c, e)	(REQUIRE(!MORE() || PEEK() != (c), e))
-#define	EMIT(op, sopnd)	doemit(p, (sop)(op), (size_t)(sopnd))
-#define	INSERT(op, pos)	doinsert(p, (sop)(op), HERE()-(pos)+1, pos)
-#define	AHEAD(pos)		dofwd(p, pos, HERE()-(pos))
-#define	ASTERN(sop, pos)	EMIT(sop, HERE()-pos)
-#define	HERE()		(p->slen)
-#define	THERE()		(p->slen - 1)
-#define	THERETHERE()	(p->slen - 2)
-#define	DROP(n)	(p->slen -= (n))
-
-#ifndef NDEBUG
-static int never = 0;		/* for use in asserts; shuts lint up */
-#else
-#define	never	0		/* some <assert.h>s have bugs too */
-#endif
-
-/*
- * regcomp - interface for parser and compilation
- */
-int				/* 0 success, otherwise REG_something */
-regcomp(regex_t *_RESTRICT_KYWD preg, const char *_RESTRICT_KYWD pattern,
-    int cflags)
+int
+tre_regncomp_l(regex_t *preg, const char *regex, size_t n, int cflags,
+    locale_t loc)
 {
-	struct parse pa;
-	struct re_guts *g;
-	struct parse *p = &pa;
-	int i;
-	size_t len;
-	size_t maxlen;
-#ifdef REDEBUG
-#define	GOODFLAGS(f)	(f)
-#else
-#define	GOODFLAGS(f)	((f)&~REG_DUMP)
-#endif
+  int ret;
+  tre_char_t *wregex;
+  size_t wlen;
 
-	/* We had REG_INVARG, but we don't have that on Solaris. */
-	cflags = GOODFLAGS(cflags);
-	if ((cflags&REG_EXTENDED) && (cflags&REG_NOSPEC))
-		return (REG_EFATAL);
+  wregex = malloc(sizeof(tre_char_t) * (n + 1));
+  if (wregex == NULL)
+    return REG_ESPACE;
 
-	if (cflags&REG_PEND) {
-		if (preg->re_endp < pattern)
-			return (REG_EFATAL);
-		len = preg->re_endp - pattern;
-	} else
-		len = strlen(pattern);
+  /* If the current locale uses the standard single byte encoding of
+     characters, we don't do a multibyte string conversion.  If we did,
+     many applications which use the default locale would break since
+     the default "C" locale uses the 7-bit ASCII character set, and
+     all characters with the eighth bit set would be considered invalid. */
+  if (TRE_MB_CUR_MAX_L(loc) == 1)
+    {
+      unsigned int i;
+      const unsigned char *str = (const unsigned char *)regex;
+      tre_char_t *wstr = wregex;
 
-	/* do the mallocs early so failure handling is easy */
-	g = (struct re_guts *)malloc(sizeof (struct re_guts));
-	if (g == NULL)
-		return (REG_ESPACE);
-	/*
-	 * Limit the pattern space to avoid a 32-bit overflow on buffer
-	 * extension.  Also avoid any signed overflow in case of conversion
-	 * so make the real limit based on a 31-bit overflow.
-	 *
-	 * Likely not applicable on 64-bit systems but handle the case
-	 * generically (who are we to stop people from using ~715MB+
-	 * patterns?).
-	 */
-	maxlen = ((size_t)-1 >> 1) / sizeof (sop) * 2 / 3;
-	if (len >= maxlen) {
-		free((char *)g);
-		return (REG_ESPACE);
-	}
-	p->ssize = len/(size_t)2*(size_t)3 + (size_t)1;	/* ugh */
-	assert(p->ssize >= len);
+      for (i = 0; i < n; i++)
+	*(wstr++) = *(str++);
+      wlen = n;
+    }
+  else
+    {
+      size_t consumed;
+      tre_char_t *wcptr = wregex;
+      mbstate_t state;
+      memset(&state, '\0', sizeof(state));
+      while (n > 0)
+	{
+	  consumed = tre_mbrtowc_l(wcptr, regex, n, &state, loc);
 
-	p->strip = (sop *)malloc(p->ssize * sizeof (sop));
-	p->slen = 0;
-	if (p->strip == NULL) {
-		free((char *)g);
-		return (REG_ESPACE);
-	}
-
-	/* set things up */
-	p->g = g;
-	p->next = pattern;	/* convenience; we do not modify it */
-	p->end = p->next + len;
-	p->error = 0;
-	p->ncsalloc = 0;
-	for (i = 0; i < NPAREN; i++) {
-		p->pbegin[i] = 0;
-		p->pend[i] = 0;
-	}
-	g->sets = NULL;
-	g->ncsets = 0;
-	g->cflags = cflags;
-	g->iflags = 0;
-	g->nbol = 0;
-	g->neol = 0;
-	g->must = NULL;
-	g->moffset = -1;
-	g->charjump = NULL;
-	g->matchjump = NULL;
-	g->mlen = 0;
-	g->nsub = 0;
-	g->backrefs = 0;
-
-	/* do it */
-	EMIT(OEND, 0);
-	g->firststate = THERE();
-	if (cflags&REG_EXTENDED)
-		p_ere(p, OUT);
-	else if (cflags&REG_NOSPEC)
-		p_str(p);
-	else
-		p_bre(p, OUT, OUT);
-	EMIT(OEND, 0);
-	g->laststate = THERE();
-
-	/* tidy up loose ends and fill things in */
-	stripsnug(p, g);
-	findmust(p, g);
-	/*
-	 * only use Boyer-Moore algorithm if the pattern is bigger
-	 * than three characters
-	 */
-	if (g->mlen > 3) {
-		computejumps(p, g);
-		computematchjumps(p, g);
-		if (g->matchjump == NULL && g->charjump != NULL) {
-			free(g->charjump);
-			g->charjump = NULL;
+	  switch (consumed)
+	    {
+	    case 0:
+	      if (*regex == '\0')
+		consumed = 1;
+	      else
+		{
+		  free(wregex);
+		  return REG_BADPAT;
 		}
+	      break;
+	    case (size_t)-1:
+	    case (size_t)-2:
+	      DPRINT(("mbrtowc: error %d: %s.\n", errno, strerror(errno)));
+	      free(wregex);
+	      return REG_ILLSEQ;
+	    }
+	  regex += consumed;
+	  n -= consumed;
+	  wcptr++;
 	}
-	g->nplus = pluscount(p, g);
-	g->magic = MAGIC2;
-	preg->re_nsub = g->nsub;
-	preg->re_g = g;
-	preg->re_magic = MAGIC1;
-#ifndef REDEBUG
-	/* not debugging, so can't rely on the assert() in regexec() */
-	if (g->iflags&BAD)
-		SETERROR(REG_EFATAL);
-#endif
+      wlen = wcptr - wregex;
+    }
 
-	/* win or lose, we're done */
-	if (p->error != 0)	/* lose */
-		regfree(preg);
-	return (p->error);
-}
+  wregex[wlen] = L'\0';
+  ret = tre_compile(preg, wregex, wlen, cflags, loc);
+  free(wregex);
 
-/*
- * p_ere - ERE parser top level, concatenation and alternation
- */
-static void
-p_ere(struct parse *p,
-    int stop)		/* character this ERE should end at */
-{
-	char c;
-	sopno prevback;
-	sopno prevfwd;
-	sopno conc;
-	int first = 1;		/* is this the first alternative? */
-
-	for (;;) {
-		/* do a bunch of concatenated expressions */
-		conc = HERE();
-		while (MORE() && (c = PEEK()) != '|' && c != stop)
-			p_ere_exp(p);
-		/* require nonempty */
-		(void) REQUIRE(HERE() != conc, REG_BADPAT);
-
-		if (!EAT('|'))
-			break;		/* NOTE BREAK OUT */
-
-		if (first) {
-			INSERT(OCH_, conc);	/* offset is wrong */
-			prevfwd = conc;
-			prevback = conc;
-			first = 0;
-		}
-		ASTERN(OOR1, prevback);
-		prevback = THERE();
-		AHEAD(prevfwd);			/* fix previous offset */
-		prevfwd = HERE();
-		EMIT(OOR2, 0);			/* offset is very wrong */
-	}
-
-	if (!first) {		/* tail-end fixups */
-		AHEAD(prevfwd);
-		ASTERN(O_CH, prevback);
-	}
-
-	assert(!MORE() || SEE(stop));
+  return ret;
 }
 
-/*
- * p_ere_exp - parse one subERE, an atom possibly followed by a repetition op
- */
-static void
-p_ere_exp(struct parse *p)
+int
+tre_regncomp(regex_t *preg, const char *regex, size_t n, int cflags)
 {
-	char c;
-	wint_t wc;
-	sopno pos;
-	int count;
-	int count2;
-	sopno subno;
-	int wascaret = 0;
-
-	assert(MORE());		/* caller should have ensured this */
-	c = GETNEXT();
-
-	pos = HERE();
-	switch (c) {
-	case '(':
-		(void) REQUIRE(MORE(), REG_EPAREN);
-		p->g->nsub++;
-		subno = p->g->nsub;
-		if (subno < NPAREN)
-			p->pbegin[subno] = HERE();
-		EMIT(OLPAREN, subno);
-		if (!SEE(')'))
-			p_ere(p, ')');
-		if (subno < NPAREN) {
-			p->pend[subno] = HERE();
-			assert(p->pend[subno] != 0);
-		}
-		EMIT(ORPAREN, subno);
-		(void) MUSTEAT(')', REG_EPAREN);
-		break;
-#ifndef POSIX_MISTAKE
-	case ')':		/* happens only if no current unmatched ( */
-		/*
-		 * You may ask, why the ifndef?  Because I didn't notice
-		 * this until slightly too late for 1003.2, and none of the
-		 * other 1003.2 regular-expression reviewers noticed it at
-		 * all.  So an unmatched ) is legal POSIX, at least until
-		 * we can get it fixed.
-		 */
-		SETERROR(REG_EPAREN);
-		break;
-#endif
-	case '^':
-		EMIT(OBOL, 0);
-		p->g->iflags |= USEBOL;
-		p->g->nbol++;
-		wascaret = 1;
-		break;
-	case '$':
-		EMIT(OEOL, 0);
-		p->g->iflags |= USEEOL;
-		p->g->neol++;
-		break;
-	case '|':
-		SETERROR(REG_BADPAT);
-		break;
-	case '*':
-	case '+':
-	case '?':
-	case '{':
-		SETERROR(REG_BADRPT);
-		break;
-	case '.':
-		if (p->g->cflags&REG_NEWLINE)
-			nonnewline(p);
-		else
-			EMIT(OANY, 0);
-		break;
-	case '[':
-		p_bracket(p);
-		break;
-	case '\\':
-		(void) REQUIRE(MORE(), REG_EESCAPE);
-		wc = WGETNEXT();
-		switch (wc) {
-		case '<':
-			EMIT(OBOW, 0);
-			break;
-		case '>':
-			EMIT(OEOW, 0);
-			break;
-		default:
-			ordinary(p, wc);
-			break;
-		}
-		break;
-	default:
-		if (p->error != 0)
-			return;
-		p->next--;
-		wc = WGETNEXT();
-		ordinary(p, wc);
-		break;
-	}
-
-	if (!MORE())
-		return;
-	c = PEEK();
-	/* we call { a repetition if followed by a digit */
-	if (!(c == '*' || c == '+' || c == '?' || c == '{'))
-		return;		/* no repetition, we're done */
-	else if (c == '{')
-		(void) REQUIRE(MORE2() && \
-		    (isdigit((uch)PEEK2()) || PEEK2() == ','), REG_BADRPT);
-	NEXT();
-
-	(void) REQUIRE(!wascaret, REG_BADRPT);
-	switch (c) {
-	case '*':	/* implemented as +? */
-		/* this case does not require the (y|) trick, noKLUDGE */
-		INSERT(OPLUS_, pos);
-		ASTERN(O_PLUS, pos);
-		INSERT(OQUEST_, pos);
-		ASTERN(O_QUEST, pos);
-		break;
-	case '+':
-		INSERT(OPLUS_, pos);
-		ASTERN(O_PLUS, pos);
-		break;
-	case '?':
-		/* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
-		INSERT(OCH_, pos);		/* offset slightly wrong */
-		ASTERN(OOR1, pos);		/* this one's right */
-		AHEAD(pos);			/* fix the OCH_ */
-		EMIT(OOR2, 0);			/* offset very wrong... */
-		AHEAD(THERE());			/* ...so fix it */
-		ASTERN(O_CH, THERETHERE());
-		break;
-	case '{':
-		count = p_count(p);
-		if (EAT(',')) {
-			if (isdigit((uch)PEEK())) {
-				count2 = p_count(p);
-				(void) REQUIRE(count <= count2, REG_BADBR);
-			} else		/* single number with comma */
-				count2 = INFINITY;
-		} else		/* just a single number */
-			count2 = count;
-		repeat(p, pos, count, count2);
-		if (!EAT('}')) {	/* error heuristics */
-			while (MORE() && PEEK() != '}')
-				NEXT();
-			(void) REQUIRE(MORE(), REG_EBRACE);
-			SETERROR(REG_BADBR);
-		}
-		break;
-	}
-
-	if (!MORE())
-		return;
-	c = PEEK();
-	if (!(c == '*' || c == '+' || c == '?' ||
-	    (c == '{' && MORE2() && isdigit((uch)PEEK2()))))
-		return;
-	SETERROR(REG_BADRPT);
+  return tre_regncomp_l(preg, regex, n, cflags, uselocale((locale_t)0));
 }
 
-/*
- * p_str - string (no metacharacters) "parser"
- */
-static void
-p_str(struct parse *p)
+int
+tre_regcomp_l(regex_t *preg, const char *regex, int cflags, locale_t loc)
 {
-	(void) REQUIRE(MORE(), REG_BADPAT);
-	while (MORE())
-		ordinary(p, WGETNEXT());
-}
+  size_t len;
 
-/*
- * p_bre - BRE parser top level, anchoring and concatenation
- * Giving end1 as OUT essentially eliminates the end1/end2 check.
- *
- * This implementation is a bit of a kludge, in that a trailing $ is first
- * taken as an ordinary character and then revised to be an anchor.
- * The amount of lookahead needed to avoid this kludge is excessive.
- */
-static void
-p_bre(struct parse *p,
-    int end1,		/* first terminating character */
-    int end2)		/* second terminating character */
-{
-	sopno start = HERE();
-	int first = 1;			/* first subexpression? */
-	int wasdollar = 0;
-
-	if (EAT('^')) {
-		EMIT(OBOL, 0);
-		p->g->iflags |= USEBOL;
-		p->g->nbol++;
-	}
-	while (MORE() && !SEETWO(end1, end2)) {
-		wasdollar = p_simp_re(p, first);
-		first = 0;
-	}
-	if (wasdollar) {	/* oops, that was a trailing anchor */
-		DROP(1);
-		EMIT(OEOL, 0);
-		p->g->iflags |= USEEOL;
-		p->g->neol++;
-	}
-
-	(void) REQUIRE(HERE() != start, REG_BADPAT);	/* require nonempty */
+  if (cflags & REG_PEND)
+    {
+      if ((const char *)(preg->re_endp) < regex)
+	return REG_INVARG;
+      len = (const char *)(preg->re_endp) - regex;
+    }
+  else
+    len = strlen(regex);
+  return tre_regncomp_l(preg, regex, len, cflags, loc);
 }
 
-/*
- * p_simp_re - parse a simple RE, an atom possibly followed by a repetition
- */
-static int			/* was the simple RE an unbackslashed $? */
-p_simp_re(struct parse *p,
-    int starordinary)	/* is a leading * an ordinary character? */
+int
+regcomp(regex_t *_RESTRICT_KYWD preg, const char *_RESTRICT_KYWD regex,
+    int cflags)
 {
-	int c;
-	int count;
-	int count2;
-	sopno pos;
-	int i;
-	wint_t wc;
-	sopno subno;
-#define	BACKSL	(1<<CHAR_BIT)
-
-	pos = HERE();		/* repetition op, if any, covers from here */
-
-	assert(MORE());		/* caller should have ensured this */
-	c = GETNEXT();
-	if (c == '\\') {
-		(void) REQUIRE(MORE(), REG_EESCAPE);
-		c = BACKSL | GETNEXT();
-	}
-	switch (c) {
-	case '.':
-		if (p->g->cflags&REG_NEWLINE)
-			nonnewline(p);
-		else
-			EMIT(OANY, 0);
-		break;
-	case '[':
-		p_bracket(p);
-		break;
-	case BACKSL|'<':
-		EMIT(OBOW, 0);
-		break;
-	case BACKSL|'>':
-		EMIT(OEOW, 0);
-		break;
-	case BACKSL|'{':
-		SETERROR(REG_BADRPT);
-		break;
-	case BACKSL|'(':
-		p->g->nsub++;
-		subno = p->g->nsub;
-		if (subno < NPAREN)
-			p->pbegin[subno] = HERE();
-		EMIT(OLPAREN, subno);
-		/* the MORE here is an error heuristic */
-		if (MORE() && !SEETWO('\\', ')'))
-			p_bre(p, '\\', ')');
-		if (subno < NPAREN) {
-			p->pend[subno] = HERE();
-			assert(p->pend[subno] != 0);
-		}
-		EMIT(ORPAREN, subno);
-		(void) REQUIRE(EATTWO('\\', ')'), REG_EPAREN);
-		break;
-	case BACKSL|')':	/* should not get here -- must be user */
-		SETERROR(REG_EPAREN);
-		break;
-	case BACKSL|'1':
-	case BACKSL|'2':
-	case BACKSL|'3':
-	case BACKSL|'4':
-	case BACKSL|'5':
-	case BACKSL|'6':
-	case BACKSL|'7':
-	case BACKSL|'8':
-	case BACKSL|'9':
-		i = (c&~BACKSL) - '0';
-		assert(i < NPAREN);
-		if (p->pend[i] != 0) {
-			assert(i <= p->g->nsub);
-			EMIT(OBACK_, i);
-			assert(p->pbegin[i] != 0);
-			assert(OP(p->strip[p->pbegin[i]]) == OLPAREN);
-			assert(OP(p->strip[p->pend[i]]) == ORPAREN);
-			(void) dupl(p, p->pbegin[i]+1, p->pend[i]);
-			EMIT(O_BACK, i);
-		} else
-			SETERROR(REG_ESUBREG);
-		p->g->backrefs = 1;
-		break;
-	case '*':
-		(void) REQUIRE(starordinary, REG_BADRPT);
-		/* FALLTHROUGH */
-	default:
-		if (p->error != 0)
-			return (0);	/* Definitely not $... */
-		p->next--;
-		wc = WGETNEXT();
-		ordinary(p, wc);
-		break;
-	}
-
-	if (EAT('*')) {		/* implemented as +? */
-		/* this case does not require the (y|) trick, noKLUDGE */
-		INSERT(OPLUS_, pos);
-		ASTERN(O_PLUS, pos);
-		INSERT(OQUEST_, pos);
-		ASTERN(O_QUEST, pos);
-	} else if (EATTWO('\\', '{')) {
-		count = p_count(p);
-		if (EAT(',')) {
-			if (MORE() && isdigit((uch)PEEK())) {
-				count2 = p_count(p);
-				(void) REQUIRE(count <= count2, REG_BADBR);
-			} else		/* single number with comma */
-				count2 = INFINITY;
-		} else		/* just a single number */
-			count2 = count;
-		repeat(p, pos, count, count2);
-		if (!EATTWO('\\', '}')) {	/* error heuristics */
-			while (MORE() && !SEETWO('\\', '}'))
-				NEXT();
-			(void) REQUIRE(MORE(), REG_EBRACE);
-			SETERROR(REG_BADBR);
-		}
-	} else if (c == '$')	/* $ (but not \$) ends it */
-		return (1);
-
-	return (0);
+  return tre_regcomp_l(preg, regex, cflags, uselocale((locale_t)0));
 }
 
-/*
- * p_count - parse a repetition count
- */
-static int			/* the value */
-p_count(struct parse *p)
+void
+regfree(regex_t *preg)
 {
-	int count = 0;
-	int ndigits = 0;
-
-	while (MORE() && isdigit((uch)PEEK()) && count <= DUPMAX) {
-		count = count*10 + (GETNEXT() - '0');
-		ndigits++;
-	}
-
-	(void) REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR);
-	return (count);
-}
-
-/*
- * p_bracket - parse a bracketed character list
- */
-static void
-p_bracket(struct parse *p)
-{
-	cset *cs;
-	wint_t ch;
-
-	/* Dept of Truly Sickening Special-Case Kludges */
-	if (p->next + 5 < p->end && strncmp(p->next, "[:<:]]", 6) == 0) {
-		EMIT(OBOW, 0);
-		NEXTn(6);
-		return;
-	}
-	if (p->next + 5 < p->end && strncmp(p->next, "[:>:]]", 6) == 0) {
-		EMIT(OEOW, 0);
-		NEXTn(6);
-		return;
-	}
-
-	if ((cs = allocset(p)) == NULL)
-		return;
-
-	if (p->g->cflags&REG_ICASE)
-		cs->icase = 1;
-	if (EAT('^'))
-		cs->invert = 1;
-	if (EAT(']'))
-		CHadd(p, cs, ']');
-	else if (EAT('-'))
-		CHadd(p, cs, '-');
-	while (MORE() && PEEK() != ']' && !SEETWO('-', ']'))
-		p_b_term(p, cs);
-	if (EAT('-'))
-		CHadd(p, cs, '-');
-	(void) MUSTEAT(']', REG_EBRACK);
-
-	if (p->error != 0)	/* don't mess things up further */
-		return;
-
-	if (cs->invert && p->g->cflags&REG_NEWLINE)
-		cs->bmp['\n' >> 3] |= 1 << ('\n' & 7);
-
-	if ((ch = singleton(cs)) != OUT) {	/* optimize singleton sets */
-		ordinary(p, ch);
-		freeset(p, cs);
-	} else
-		EMIT(OANYOF, (int)(cs - p->g->sets));
-}
-
-/*
- * p_b_term - parse one term of a bracketed character list
- */
-static void
-p_b_term(struct parse *p, cset *cs)
-{
-	char c;
-	wint_t start, finish;
-	wint_t i;
-	locale_t loc = uselocale(NULL);
-
-	/* classify what we've got */
-	switch ((MORE()) ? PEEK() : '\0') {
-	case '[':
-		c = (MORE2()) ? PEEK2() : '\0';
-		break;
-	case '-':
-		SETERROR(REG_ERANGE);
-		return;			/* NOTE RETURN */
-	default:
-		c = '\0';
-		break;
-	}
-
-	switch (c) {
-	case ':':		/* character class */
-		NEXT2();
-		(void) REQUIRE(MORE(), REG_EBRACK);
-		c = PEEK();
-		(void) REQUIRE(c != '-' && c != ']', REG_ECTYPE);
-		p_b_cclass(p, cs);
-		(void) REQUIRE(MORE(), REG_EBRACK);
-		(void) REQUIRE(EATTWO(':', ']'), REG_ECTYPE);
-		break;
-	case '=':		/* equivalence class */
-		NEXT2();
-		(void) REQUIRE(MORE(), REG_EBRACK);
-		c = PEEK();
-		(void) REQUIRE(c != '-' && c != ']', REG_ECOLLATE);
-		p_b_eclass(p, cs);
-		(void) REQUIRE(MORE(), REG_EBRACK);
-		(void) REQUIRE(EATTWO('=', ']'), REG_ECOLLATE);
-		break;
-	default:		/* symbol, ordinary character, or range */
-		start = p_b_symbol(p);
-		if (SEE('-') && MORE2() && PEEK2() != ']') {
-			/* range */
-			NEXT();
-			if (EAT('-'))
-				finish = '-';
-			else
-				finish = p_b_symbol(p);
-		} else
-			finish = start;
-		if (start == finish)
-			CHadd(p, cs, start);
-		else {
-			if (loc->collate->lc_is_posix) {
-				(void) REQUIRE((uch)start <= (uch)finish,
-				    REG_ERANGE);
-				CHaddrange(p, cs, start, finish);
-			} else {
-				(void) REQUIRE(_collate_range_cmp(start,
-				    finish, loc) <= 0, REG_ERANGE);
-				for (i = 0; i <= UCHAR_MAX; i++) {
-					if (_collate_range_cmp(start, i, loc)
-					    <= 0 &&
-					    _collate_range_cmp(i, finish, loc)
-					    <= 0)
-						CHadd(p, cs, i);
-				}
-			}
-		}
-		break;
-	}
-}
-
-/*
- * p_b_cclass - parse a character-class name and deal with it
- */
-static void
-p_b_cclass(struct parse *p, cset *cs)
-{
-	const char *sp = p->next;
-	size_t len;
-	wctype_t wct;
-	char clname[16];
-
-	while (MORE() && isalpha((uch)PEEK()))
-		NEXT();
-	len = p->next - sp;
-	if (len >= sizeof (clname) - 1) {
-		SETERROR(REG_ECTYPE);
-		return;
-	}
-	(void) memcpy(clname, sp, len);
-	clname[len] = '\0';
-	if ((wct = wctype(clname)) == 0) {
-		SETERROR(REG_ECTYPE);
-		return;
-	}
-	CHaddtype(p, cs, wct);
-}
-
-/*
- * p_b_eclass - parse an equivalence-class name and deal with it
- *
- * This implementation is incomplete. xxx
- */
-static void
-p_b_eclass(struct parse *p, cset *cs)
-{
-	wint_t c;
-
-	c = p_b_coll_elem(p, '=');
-	CHadd(p, cs, c);
-}
-
-/*
- * p_b_symbol - parse a character or [..]ed multicharacter collating symbol
- */
-static wint_t			/* value of symbol */
-p_b_symbol(struct parse *p)
-{
-	wint_t value;
-
-	(void) REQUIRE(MORE(), REG_EBRACK);
-	if (!EATTWO('[', '.'))
-		return (WGETNEXT());
-
-	/* collating symbol */
-	value = p_b_coll_elem(p, '.');
-	(void) REQUIRE(EATTWO('.', ']'), REG_ECOLLATE);
-	return (value);
-}
-
-/*
- * p_b_coll_elem - parse a collating-element name and look it up
- */
-static wint_t			/* value of collating element */
-p_b_coll_elem(struct parse *p,
-    wint_t endc)		/* name ended by endc,']' */
-{
-	const char *sp = p->next;
-	struct cname *cp;
-	mbstate_t mbs;
-	wchar_t wc;
-	size_t clen, len;
-
-	while (MORE() && !SEETWO(endc, ']'))
-		NEXT();
-	if (!MORE()) {
-		SETERROR(REG_EBRACK);
-		return (0);
-	}
-	len = p->next - sp;
-	for (cp = cnames; cp->name != NULL; cp++)
-		if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0')
-			return (cp->code);	/* known name */
-	(void) memset(&mbs, 0, sizeof (mbs));
-	if ((clen = mbrtowc(&wc, sp, len, &mbs)) == len)
-		return (wc);			/* single character */
-	else if (clen == (size_t)-1 || clen == (size_t)-2)
-		SETERROR(REG_ECHAR);
-	else
-		SETERROR(REG_ECOLLATE);		/* neither */
-	return (0);
-}
-
-/*
- * othercase - return the case counterpart of an alphabetic
- */
-static wint_t			/* if no counterpart, return ch */
-othercase(wint_t ch)
-{
-	assert(iswalpha(ch));
-	if (iswupper(ch))
-		return (towlower(ch));
-	else if (iswlower(ch))
-		return (towupper(ch));
-	else			/* peculiar, but could happen */
-		return (ch);
-}
-
-/*
- * bothcases - emit a dualcase version of a two-case character
- *
- * Boy, is this implementation ever a kludge...
- */
-static void
-bothcases(struct parse *p, wint_t ch)
-{
-	const char *oldnext = p->next;
-	const char *oldend = p->end;
-	char bracket[3 + MB_LEN_MAX];
-	size_t n;
-	mbstate_t mbs;
-
-	assert(othercase(ch) != ch);	/* p_bracket() would recurse */
-	p->next = bracket;
-	(void) memset(&mbs, 0, sizeof (mbs));
-	n = wcrtomb(bracket, ch, &mbs);
-	assert(n != (size_t)-1);
-	bracket[n] = ']';
-	bracket[n + 1] = '\0';
-	p->end = bracket+n+1;
-	p_bracket(p);
-	assert(p->next == p->end);
-	p->next = oldnext;
-	p->end = oldend;
-}
-
-/*
- * ordinary - emit an ordinary character
- */
-static void
-ordinary(struct parse *p, wint_t ch)
-{
-	cset *cs;
-
-	if ((p->g->cflags&REG_ICASE) && iswalpha(ch) && othercase(ch) != ch)
-		bothcases(p, ch);
-	else if ((ch & OPDMASK) == ch)
-		EMIT(OCHAR, ch);
-	else {
-		/*
-		 * Kludge: character is too big to fit into an OCHAR operand.
-		 * Emit a singleton set.
-		 */
-		if ((cs = allocset(p)) == NULL)
-			return;
-		CHadd(p, cs, ch);
-		EMIT(OANYOF, (int)(cs - p->g->sets));
-	}
-}
-
-/*
- * nonnewline - emit REG_NEWLINE version of OANY
- *
- * Boy, is this implementation ever a kludge...
- */
-static void
-nonnewline(struct parse *p)
-{
-	const char *oldnext = p->next;
-	const char *oldend = p->end;
-	char bracket[4];
-
-	p->next = bracket;
-	p->end = bracket+3;
-	bracket[0] = '^';
-	bracket[1] = '\n';
-	bracket[2] = ']';
-	bracket[3] = '\0';
-	p_bracket(p);
-	assert(p->next == bracket+3);
-	p->next = oldnext;
-	p->end = oldend;
-}
-
-/*
- * repeat - generate code for a bounded repetition, recursively if needed
- */
-static void
-repeat(struct parse *p,
-    sopno start,		/* operand from here to end of strip */
-    int from,			/* repeated from this number */
-    int to)			/* to this number of times (maybe INFINITY) */
-{
-	sopno finish = HERE();
-#define	N	2
-#define	INF	3
-#define	REP(f, t)	((f)*8 + (t))
-#define	MAP(n)	(((n) <= 1) ? (n) : ((n) == INFINITY) ? INF : N)
-	sopno copy;
-
-	if (p->error != 0)	/* head off possible runaway recursion */
-		return;
-
-	assert(from <= to);
-
-	switch (REP(MAP(from), MAP(to))) {
-	case REP(0, 0):			/* must be user doing this */
-		DROP(finish-start);	/* drop the operand */
-		break;
-	case REP(0, 1):			/* as x{1,1}? */
-	case REP(0, N):			/* as x{1,n}? */
-	case REP(0, INF):		/* as x{1,}? */
-		/* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
-		INSERT(OCH_, start);		/* offset is wrong... */
-		repeat(p, start+1, 1, to);
-		ASTERN(OOR1, start);
-		AHEAD(start);			/* ... fix it */
-		EMIT(OOR2, 0);
-		AHEAD(THERE());
-		ASTERN(O_CH, THERETHERE());
-		break;
-	case REP(1, 1):			/* trivial case */
-		/* done */
-		break;
-	case REP(1, N):			/* as x?x{1,n-1} */
-		/* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
-		INSERT(OCH_, start);
-		ASTERN(OOR1, start);
-		AHEAD(start);
-		EMIT(OOR2, 0);			/* offset very wrong... */
-		AHEAD(THERE());			/* ...so fix it */
-		ASTERN(O_CH, THERETHERE());
-		copy = dupl(p, start+1, finish+1);
-		assert(copy == finish+4);
-		repeat(p, copy, 1, to-1);
-		break;
-	case REP(1, INF):		/* as x+ */
-		INSERT(OPLUS_, start);
-		ASTERN(O_PLUS, start);
-		break;
-	case REP(N, N):			/* as xx{m-1,n-1} */
-		copy = dupl(p, start, finish);
-		repeat(p, copy, from-1, to-1);
-		break;
-	case REP(N, INF):		/* as xx{n-1,INF} */
-		copy = dupl(p, start, finish);
-		repeat(p, copy, from-1, to);
-		break;
-	default:			/* "can't happen" */
-		SETERROR(REG_EFATAL);	/* just in case */
-		break;
-	}
-}
-
-/*
- * wgetnext - helper function for WGETNEXT() macro. Gets the next wide
- * character from the parse struct, signals a REG_ILLSEQ error if the
- * character can't be converted. Returns the number of bytes consumed.
- */
-static wint_t
-wgetnext(struct parse *p)
-{
-	mbstate_t mbs;
-	wchar_t wc;
-	size_t n;
-
-	(void) memset(&mbs, 0, sizeof (mbs));
-	n = mbrtowc(&wc, p->next, p->end - p->next, &mbs);
-	if (n == (size_t)-1 || n == (size_t)-2) {
-		SETERROR(REG_ECHAR);
-		return (0);
-	}
-	if (n == 0)
-		n = 1;
-	p->next += n;
-	return (wc);
-}
-
-/*
- * seterr - set an error condition
- */
-static int			/* useless but makes type checking happy */
-seterr(struct parse *p, int e)
-{
-	if (p->error == 0)	/* keep earliest error condition */
-		p->error = e;
-	p->next = nuls;		/* try to bring things to a halt */
-	p->end = nuls;
-	return (0);		/* make the return value well-defined */
-}
-
-/*
- * allocset - allocate a set of characters for []
- */
-static cset *
-allocset(struct parse *p)
-{
-	cset *cs, *ncs;
-
-	ncs = realloc(p->g->sets, (p->g->ncsets + 1) * sizeof (*ncs));
-	if (ncs == NULL) {
-		SETERROR(REG_ESPACE);
-		return (NULL);
-	}
-	p->g->sets = ncs;
-	cs = &p->g->sets[p->g->ncsets++];
-	(void) memset(cs, 0, sizeof (*cs));
-
-	return (cs);
-}
-
-/*
- * freeset - free a now-unused set
- */
-static void
-freeset(struct parse *p, cset *cs)
-{
-	cset *top = &p->g->sets[p->g->ncsets];
-
-	free(cs->wides);
-	free(cs->ranges);
-	free(cs->types);
-	(void) memset(cs, 0, sizeof (*cs));
-	if (cs == top-1)	/* recover only the easy case */
-		p->g->ncsets--;
-}
-
-/*
- * singleton - Determine whether a set contains only one character,
- * returning it if so, otherwise returning OUT.
- */
-static wint_t
-singleton(cset *cs)
-{
-	wint_t i, s, n;
-
-	for (i = n = 0; i < NC; i++)
-		if (CHIN(cs, i)) {
-			n++;
-			s = i;
-		}
-	if (n == 1)
-		return (s);
-	if (cs->nwides == 1 && cs->nranges == 0 && cs->ntypes == 0 &&
-	    cs->icase == 0)
-		return (cs->wides[0]);
-	/* Don't bother handling the other cases. */
-	return (OUT);
-}
-
-/*
- * CHadd - add character to character set.
- */
-static void
-CHadd(struct parse *p, cset *cs, wint_t ch)
-{
-	wint_t nch, *newwides;
-	assert(ch >= 0);
-	if (ch < NC)
-		cs->bmp[ch >> 3] |= 1 << (ch & 7);
-	else {
-		newwides = realloc(cs->wides, (cs->nwides + 1) *
-		    sizeof (*cs->wides));
-		if (newwides == NULL) {
-			SETERROR(REG_ESPACE);
-			return;
-		}
-		cs->wides = newwides;
-		cs->wides[cs->nwides++] = ch;
-	}
-	if (cs->icase) {
-		if ((nch = towlower(ch)) < NC)
-			cs->bmp[nch >> 3] |= 1 << (nch & 7);
-		if ((nch = towupper(ch)) < NC)
-			cs->bmp[nch >> 3] |= 1 << (nch & 7);
-	}
-}
-
-/*
- * CHaddrange - add all characters in the range [min,max] to a character set.
- */
-static void
-CHaddrange(struct parse *p, cset *cs, wint_t min, wint_t max)
-{
-	crange *newranges;
-
-	for (; min < NC && min <= max; min++)
-		CHadd(p, cs, min);
-	if (min >= max)
-		return;
-	newranges = realloc(cs->ranges, (cs->nranges + 1) *
-	    sizeof (*cs->ranges));
-	if (newranges == NULL) {
-		SETERROR(REG_ESPACE);
-		return;
-	}
-	cs->ranges = newranges;
-	cs->ranges[cs->nranges].min = min;
-	cs->ranges[cs->nranges].max = max;
-	cs->nranges++;
-}
-
-/*
- * CHaddtype - add all characters of a certain type to a character set.
- */
-static void
-CHaddtype(struct parse *p, cset *cs, wctype_t wct)
-{
-	wint_t i;
-	wctype_t *newtypes;
-
-	for (i = 0; i < NC; i++)
-		if (iswctype(i, wct))
-			CHadd(p, cs, i);
-	newtypes = realloc(cs->types, (cs->ntypes + 1) *
-	    sizeof (*cs->types));
-	if (newtypes == NULL) {
-		SETERROR(REG_ESPACE);
-		return;
-	}
-	cs->types = newtypes;
-	cs->types[cs->ntypes++] = wct;
-}
-
-/*
- * dupl - emit a duplicate of a bunch of sops
- */
-static sopno			/* start of duplicate */
-dupl(struct parse *p,
-    sopno start,		/* from here */
-    sopno finish)		/* to this less one */
-{
-	sopno ret = HERE();
-	sopno len = finish - start;
-
-	assert(finish >= start);
-	if (len == 0)
-		return (ret);
-	if (!enlarge(p, p->ssize + len)) /* this many unexpected additions */
-		return (ret);
-	assert(p->ssize >= p->slen + len);
-	(void) memcpy((char *)(p->strip + p->slen),
-	    (char *)(p->strip + start), (size_t)len*sizeof (sop));
-	p->slen += len;
-	return (ret);
-}
-
-/*
- * doemit - emit a strip operator
- *
- * It might seem better to implement this as a macro with a function as
- * hard-case backup, but it's just too big and messy unless there are
- * some changes to the data structures.  Maybe later.
- */
-static void
-doemit(struct parse *p, sop op, size_t opnd)
-{
-	/* avoid making error situations worse */
-	if (p->error != 0)
-		return;
-
-	/* deal with oversize operands ("can't happen", more or less) */
-	assert(opnd < 1<<OPSHIFT);
-
-	/* deal with undersized strip */
-	if (p->slen >= p->ssize)
-		if (!enlarge(p, (p->ssize+1) / 2 * 3))	/* +50% */
-			return;
-
-	/* finally, it's all reduced to the easy case */
-	p->strip[p->slen++] = SOP(op, opnd);
-}
-
-/*
- * doinsert - insert a sop into the strip
- */
-static void
-doinsert(struct parse *p, sop op, size_t opnd, sopno pos)
-{
-	sopno sn;
-	sop s;
-	int i;
-
-	/* avoid making error situations worse */
-	if (p->error != 0)
-		return;
-
-	sn = HERE();
-	EMIT(op, opnd);		/* do checks, ensure space */
-	assert(HERE() == sn+1);
-	s = p->strip[sn];
-
-	/* adjust paren pointers */
-	assert(pos > 0);
-	for (i = 1; i < NPAREN; i++) {
-		if (p->pbegin[i] >= pos) {
-			p->pbegin[i]++;
-		}
-		if (p->pend[i] >= pos) {
-			p->pend[i]++;
-		}
-	}
-
-	(void) memmove((char *)&p->strip[pos+1], (char *)&p->strip[pos],
-	    (HERE()-pos-1)*sizeof (sop));
-	p->strip[pos] = s;
-}
-
-/*
- * dofwd - complete a forward reference
- */
-static void
-dofwd(struct parse *p, sopno pos, sop value)
-{
-	/* avoid making error situations worse */
-	if (p->error != 0)
-		return;
-
-	assert(value < 1<<OPSHIFT);
-	p->strip[pos] = OP(p->strip[pos]) | value;
-}
-
-/*
- * enlarge - enlarge the strip
- */
-static int
-enlarge(struct parse *p, sopno size)
-{
-	sop *sp;
-
-	if (p->ssize >= size)
-		return (1);
-
-	sp = (sop *)realloc(p->strip, size*sizeof (sop));
-	if (sp == NULL) {
-		SETERROR(REG_ESPACE);
-		return (0);
-	}
-	p->strip = sp;
-	p->ssize = size;
-	return (1);
-}
-
-/*
- * stripsnug - compact the strip
- */
-static void
-stripsnug(struct parse *p, struct re_guts *g)
-{
-	g->nstates = p->slen;
-	g->strip = (sop *)realloc((char *)p->strip, p->slen * sizeof (sop));
-	if (g->strip == NULL) {
-		SETERROR(REG_ESPACE);
-		g->strip = p->strip;
-	}
-}
-
-/*
- * findmust - fill in must and mlen with longest mandatory literal string
- *
- * This algorithm could do fancy things like analyzing the operands of |
- * for common subsequences.  Someday.  This code is simple and finds most
- * of the interesting cases.
- *
- * Note that must and mlen got initialized during setup.
- */
-static void
-findmust(struct parse *p, struct re_guts *g)
-{
-	sop *scan;
-	sop *start = NULL;
-	sop *newstart = NULL;
-	sopno newlen;
-	sop s;
-	char *cp;
-	int offset;
-	char buf[MB_LEN_MAX];
-	size_t clen;
-	mbstate_t mbs;
-	locale_t loc = uselocale(NULL);
-
-	/* avoid making error situations worse */
-	if (p->error != 0)
-		return;
-
-	/*
-	 * It's not generally safe to do a ``char'' substring search on
-	 * multibyte character strings, but it's safe for at least
-	 * UTF-8 (see RFC 3629).
-	 */
-	if (MB_CUR_MAX > 1 &&
-	    strcmp(loc->runelocale->__encoding, "UTF-8") != 0)
-		return;
-
-	/* find the longest OCHAR sequence in strip */
-	newlen = 0;
-	offset = 0;
-	g->moffset = 0;
-	scan = g->strip + 1;
-	do {
-		s = *scan++;
-		switch (OP(s)) {
-		case OCHAR:		/* sequence member */
-			if (newlen == 0) {		/* new sequence */
-				(void) memset(&mbs, 0, sizeof (mbs));
-				newstart = scan - 1;
-			}
-			clen = wcrtomb(buf, OPND(s), &mbs);
-			if (clen == (size_t)-1)
-				goto toohard;
-			newlen += clen;
-			break;
-		case OPLUS_:		/* things that don't break one */
-		case OLPAREN:
-		case ORPAREN:
-			break;
-		case OQUEST_:		/* things that must be skipped */
-		case OCH_:
-			offset = altoffset(scan, offset);
-			scan--;
-			do {
-				scan += OPND(s);
-				s = *scan;
-				/* assert() interferes w debug printouts */
-				if (OP(s) != O_QUEST && OP(s) != O_CH &&
-				    OP(s) != OOR2) {
-					g->iflags |= BAD;
-					return;
-				}
-			} while (OP(s) != O_QUEST && OP(s) != O_CH);
-			/* FALLTHROUGH */
-		case OBOW:		/* things that break a sequence */
-		case OEOW:
-		case OBOL:
-		case OEOL:
-		case O_QUEST:
-		case O_CH:
-		case OEND:
-			if (newlen > g->mlen) {		/* ends one */
-				start = newstart;
-				g->mlen = newlen;
-				if (offset > -1) {
-					g->moffset += offset;
-					offset = newlen;
-				} else
-					g->moffset = offset;
-			} else {
-				if (offset > -1)
-					offset += newlen;
-			}
-			newlen = 0;
-			break;
-		case OANY:
-			if (newlen > g->mlen) {		/* ends one */
-				start = newstart;
-				g->mlen = newlen;
-				if (offset > -1) {
-					g->moffset += offset;
-					offset = newlen;
-				} else
-					g->moffset = offset;
-			} else {
-				if (offset > -1)
-					offset += newlen;
-			}
-			if (offset > -1)
-				offset++;
-			newlen = 0;
-			break;
-		case OANYOF:		/* may or may not invalidate offset */
-			/* First, everything as OANY */
-			if (newlen > g->mlen) {		/* ends one */
-				start = newstart;
-				g->mlen = newlen;
-				if (offset > -1) {
-					g->moffset += offset;
-					offset = newlen;
-				} else
-					g->moffset = offset;
-			} else {
-				if (offset > -1)
-					offset += newlen;
-			}
-			if (offset > -1)
-				offset++;
-			newlen = 0;
-			break;
-		toohard:
-		default:
-			/*
-			 * Anything here makes it impossible or too hard
-			 * to calculate the offset -- so we give up;
-			 * save the last known good offset, in case the
-			 * must sequence doesn't occur later.
-			 */
-			if (newlen > g->mlen) {		/* ends one */
-				start = newstart;
-				g->mlen = newlen;
-				if (offset > -1)
-					g->moffset += offset;
-				else
-					g->moffset = offset;
-			}
-			offset = -1;
-			newlen = 0;
-			break;
-		}
-	} while (OP(s) != OEND);
-
-	if (g->mlen == 0) {		/* there isn't one */
-		g->moffset = -1;
-		return;
-	}
-
-	/* turn it into a character string */
-	g->must = malloc((size_t)g->mlen + 1);
-	if (g->must == NULL) {		/* argh; just forget it */
-		g->mlen = 0;
-		g->moffset = -1;
-		return;
-	}
-	cp = g->must;
-	scan = start;
-	(void) memset(&mbs, 0, sizeof (mbs));
-	while (cp < g->must + g->mlen) {
-		while (OP(s = *scan++) != OCHAR)
-			continue;
-		clen = wcrtomb(cp, OPND(s), &mbs);
-		assert(clen != (size_t)-1);
-		cp += clen;
-	}
-	assert(cp == g->must + g->mlen);
-	*cp++ = '\0';		/* just on general principles */
-}
-
-/*
- * altoffset - choose biggest offset among multiple choices
- *
- * Compute, recursively if necessary, the largest offset among multiple
- * re paths.
- */
-static int
-altoffset(sop *scan, int offset)
-{
-	int largest;
-	int try;
-	sop s;
-
-	/* If we gave up already on offsets, return */
-	if (offset == -1)
-		return (-1);
-
-	largest = 0;
-	try = 0;
-	s = *scan++;
-	while (OP(s) != O_QUEST && OP(s) != O_CH) {
-		switch (OP(s)) {
-		case OOR1:
-			if (try > largest)
-				largest = try;
-			try = 0;
-			break;
-		case OQUEST_:
-		case OCH_:
-			try = altoffset(scan, try);
-			if (try == -1)
-				return (-1);
-			scan--;
-			do {
-				scan += OPND(s);
-				s = *scan;
-				if (OP(s) != O_QUEST && OP(s) != O_CH &&
-				    OP(s) != OOR2)
-					return (-1);
-			} while (OP(s) != O_QUEST && OP(s) != O_CH);
-			/*
-			 * We must skip to the next position, or we'll
-			 * leave altoffset() too early.
-			 */
-			scan++;
-			break;
-		case OANYOF:
-		case OCHAR:
-		case OANY:
-			try++;
-			/*FALLTHRU*/
-		case OBOW:
-		case OEOW:
-		case OLPAREN:
-		case ORPAREN:
-		case OOR2:
-			break;
-		default:
-			try = -1;
-			break;
-		}
-		if (try == -1)
-			return (-1);
-		s = *scan++;
-	}
-
-	if (try > largest)
-		largest = try;
-
-	return (largest+offset);
-}
-
-/*
- * computejumps - compute char jumps for BM scan
- *
- * This algorithm assumes g->must exists and is has size greater than
- * zero. It's based on the algorithm found on Computer Algorithms by
- * Sara Baase.
- *
- * A char jump is the number of characters one needs to jump based on
- * the value of the character from the text that was mismatched.
- */
-static void
-computejumps(struct parse *p, struct re_guts *g)
-{
-	int ch;
-	int mindex;
-
-	/* Avoid making errors worse */
-	if (p->error != 0)
-		return;
-
-	g->charjump = (int *)malloc((NC + 1) * sizeof (int));
-	if (g->charjump == NULL)	/* Not a fatal error */
-		return;
-	/* Adjust for signed chars, if necessary */
-	g->charjump = &g->charjump[-(CHAR_MIN)];
-
-	/*
-	 * If the character does not exist in the pattern, the jump
-	 * is equal to the number of characters in the pattern.
-	 */
-	for (ch = CHAR_MIN; ch < (CHAR_MAX + 1); ch++)
-		g->charjump[ch] = g->mlen;
-
-	/*
-	 * If the character does exist, compute the jump that would
-	 * take us to the last character in the pattern equal to it
-	 * (notice that we match right to left, so that last character
-	 * is the first one that would be matched).
-	 */
-	for (mindex = 0; mindex < g->mlen; mindex++)
-		g->charjump[(int)g->must[mindex]] = g->mlen - mindex - 1;
-}
-
-/*
- * computematchjumps - compute match jumps for BM scan
- *
- * This algorithm assumes g->must exists and is has size greater than
- * zero. It's based on the algorithm found on Computer Algorithms by
- * Sara Baase.
- *
- * A match jump is the number of characters one needs to advance based
- * on the already-matched suffix.
- * Notice that all values here are minus (g->mlen-1), because of the way
- * the search algorithm works.
- */
-static void
-computematchjumps(struct parse *p, struct re_guts *g)
-{
-	int mindex;		/* General "must" iterator */
-	int suffix;		/* Keeps track of matching suffix */
-	int ssuffix;		/* Keeps track of suffixes' suffix */
-	int *pmatches;
-				/*
-				 * pmatches[k] points to the next i
-				 * such that i+1...mlen is a substring
-				 * of k+1...k+mlen-i-1
-				 */
-
-	/* Avoid making errors worse */
-	if (p->error != 0)
-		return;
-
-	pmatches = (int *)malloc(g->mlen * sizeof (unsigned int));
-	if (pmatches == NULL) {
-		g->matchjump = NULL;
-		return;
-	}
-
-	g->matchjump = (int *)malloc(g->mlen * sizeof (unsigned int));
-	if (g->matchjump == NULL) {	/* Not a fatal error */
-		free(pmatches);
-		return;
-	}
-
-	/* Set maximum possible jump for each character in the pattern */
-	for (mindex = 0; mindex < g->mlen; mindex++)
-		g->matchjump[mindex] = 2*g->mlen - mindex - 1;
-
-	/* Compute pmatches[] */
-	for (mindex = g->mlen - 1, suffix = g->mlen; mindex >= 0;
-	    mindex--, suffix--) {
-		pmatches[mindex] = suffix;
-
-		/*
-		 * If a mismatch is found, interrupting the substring,
-		 * compute the matchjump for that position. If no
-		 * mismatch is found, then a text substring mismatched
-		 * against the suffix will also mismatch against the
-		 * substring.
-		 */
-		while (suffix < g->mlen && g->must[mindex] != g->must[suffix]) {
-			g->matchjump[suffix] = MIN(g->matchjump[suffix],
-			    g->mlen - mindex - 1);
-			suffix = pmatches[suffix];
-		}
-	}
-
-	/*
-	 * Compute the matchjump up to the last substring found to jump
-	 * to the beginning of the largest must pattern prefix matching
-	 * it's own suffix.
-	 */
-	for (mindex = 0; mindex <= suffix; mindex++)
-		g->matchjump[mindex] = MIN(g->matchjump[mindex],
-		    g->mlen + suffix - mindex);
-
-	ssuffix = pmatches[suffix];
-	while (suffix < g->mlen) {
-		while (suffix <= ssuffix && suffix < g->mlen) {
-			g->matchjump[suffix] = MIN(g->matchjump[suffix],
-			    g->mlen + ssuffix - suffix);
-			suffix++;
-		}
-		if (suffix < g->mlen)
-			ssuffix = pmatches[ssuffix];
-	}
-
-	free(pmatches);
-}
-
-/*
- * pluscount - count + nesting
- */
-static sopno			/* nesting depth */
-pluscount(struct parse *p, struct re_guts *g)
-{
-	sop *scan;
-	sop s;
-	sopno plusnest = 0;
-	sopno maxnest = 0;
-
-	if (p->error != 0)
-		return (0);	/* there may not be an OEND */
-
-	scan = g->strip + 1;
-	do {
-		s = *scan++;
-		switch (OP(s)) {
-		case OPLUS_:
-			plusnest++;
-			break;
-		case O_PLUS:
-			if (plusnest > maxnest)
-				maxnest = plusnest;
-			plusnest--;
-			break;
-		}
-	} while (OP(s) != OEND);
-	if (plusnest != 0)
-		g->iflags |= BAD;
-	return (maxnest);
+  tre_free(preg);
 }
--- old/usr/src/lib/libc/port/regex/regerror.c	Tue Feb 13 07:11:49 2018
+++ new/usr/src/lib/libc/port/regex/regerror.c	Tue Feb 13 07:11:49 2018
@@ -1,5 +1,4 @@
 /*
- * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994 Henry Spencer.
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
@@ -32,20 +31,21 @@
  * SUCH DAMAGE.
  */
 
+/*
+ * Copyright 2018 Nexenta Systems, Inc.
+ */
+
 #include "lint.h"
-#include "file64.h"
+
 #include <sys/types.h>
-#include <stdio.h>
-#include <string.h>
+
 #include <limits.h>
-#include <stdlib.h>
 #include <regex.h>
+#include <stdlib.h>
+#include <string.h>
 
-#include "utils.h"
 #include "../gen/_libc_gettext.h"
 
-static const char *regatoi(const regex_t *preg, char *localbuf);
-
 #define	RERR(x, msg)	{ x, #x, msg }
 
 static struct rerr {
@@ -66,24 +66,15 @@
 	RERR(REG_ERANGE,	"invalid character range"),
 	RERR(REG_ESPACE,	"out of memory"),
 	RERR(REG_BADRPT,	"repetition-operator operand invalid"),
-#ifdef	REG_EMPTY
 	RERR(REG_EMPTY,		"empty (sub)expression"),
-#endif
-	RERR(REG_EFATAL,	"fatal internal error"),
-#ifdef	REG_INVARG
 	RERR(REG_INVARG,	"invalid argument to regex routine"),
-#endif
-	RERR(REG_ECHAR,		"illegal byte sequence"),
-	RERR(REG_ENOSYS,	"function not supported"),
-	RERR(REG_STACK,		"backtrack stack overflow"),
-	RERR(REG_ENSUB,		"more than 9 \\( \\) pairs"),
-	RERR(REG_ENEWLINE,	"\n found before end of pattern"),
+	RERR(REG_ILLSEQ,	"illegal byte sequence"),
 	{0,	"",		"*** unknown regexp error code ***"}
 };
 
 
 /*
- * regerror - the interface to error numbers
+ * The interface to error numbers
  */
 /* ARGSUSED */
 size_t
@@ -92,58 +83,24 @@
 {
 	struct rerr *r;
 	size_t len;
-	int target = errcode &~ REG_ITOA;
 	const char *s;
-	char convbuf[50];
 
-	if (errcode == REG_ATOI) {
-		s = regatoi(preg, convbuf);
-	} else {
-		for (r = rerrs; r->code != 0; r++) {
-			if (r->code == target)
-				break;
-		}
-
-		if (errcode&REG_ITOA) {
-			if (r->code != 0)
-				(void) strcpy(convbuf, r->name);
-			else
-				(void) sprintf(convbuf, "REG_0x%x", target);
-			assert(strlen(convbuf) < sizeof (convbuf));
-			s = convbuf;
-		} else {
-			s = _libc_gettext(r->explain);
-		}
+	for (r = rerrs; r->code != 0; r++) {
+		if (r->code == errcode)
+			break;
 	}
 
+	s = _libc_gettext(r->explain);
+
 	len = strlen(s) + 1;
-	if (errbuf_size > 0) {
+	if (errbuf != NULL && errbuf_size > 0) {
 		if (errbuf_size > len) {
 			(void) strcpy(errbuf, s);
 		} else {
-			(void) strncpy(errbuf, s, errbuf_size-1);
-			errbuf[errbuf_size-1] = '\0';
+			(void) strncpy(errbuf, s, errbuf_size - 1);
+			errbuf[errbuf_size - 1] = '\0';
 		}
 	}
 
 	return (len);
-}
-
-/*
- * regatoi - internal routine to implement REG_ATOI
- */
-static const char *
-regatoi(const regex_t *preg, char *localbuf)
-{
-	struct rerr *r;
-
-	for (r = rerrs; r->code != 0; r++) {
-		if (strcmp(r->name, preg->re_endp) == 0)
-			break;
-	}
-	if (r->code == 0)
-		return ("0");
-
-	(void) sprintf(localbuf, "%d", r->code);
-	return (localbuf);
 }
--- old/usr/src/lib/libc/port/regex/regex2.h	Tue Feb 13 07:11:49 2018
+++ /dev/null	Tue Feb 13 07:11:47 2018
@@ -1,189 +0,0 @@
-/*
- * Copyright (c) 1992, 1993, 1994 Henry Spencer.
- * Copyright (c) 1992, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Henry Spencer.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * First, the stuff that ends up in the outside-world include file
- * typedef off_t regoff_t;
- * typedef struct {
- * 	int re_magic;
- * 	size_t re_nsub;		// number of parenthesized subexpressions
- * 	const char *re_endp;	// end pointer for REG_PEND
- * 	struct re_guts *re_g;	// none of your business :-)
- * } regex_t;
- * typedef struct {
- * 	regoff_t rm_so;		// start of match
- * 	regoff_t rm_eo;		// end of match
- * } regmatch_t;
- */
-/*
- * internals of regex_t
- */
-#define	MAGIC1	((('r'^0200)<<8) | 'e')
-
-/*
- * The internal representation is a *strip*, a sequence of
- * operators ending with an endmarker.  (Some terminology etc. is a
- * historical relic of earlier versions which used multiple strips.)
- * Certain oddities in the representation are there to permit running
- * the machinery backwards; in particular, any deviation from sequential
- * flow must be marked at both its source and its destination.  Some
- * fine points:
- *
- * - OPLUS_ and O_PLUS are *inside* the loop they create.
- * - OQUEST_ and O_QUEST are *outside* the bypass they create.
- * - OCH_ and O_CH are *outside* the multi-way branch they create, while
- *   OOR1 and OOR2 are respectively the end and the beginning of one of
- *   the branches.  Note that there is an implicit OOR2 following OCH_
- *   and an implicit OOR1 preceding O_CH.
- *
- * In state representations, an operator's bit is on to signify a state
- * immediately *preceding* "execution" of that operator.
- */
-typedef unsigned int sop;	/* strip operator */
-typedef unsigned int sopno;
-#define	OPRMASK	0xf8000000U
-#define	OPDMASK	0x07ffffffU
-#define	OPSHIFT	((unsigned)27)
-#define	OP(n)	((n)&OPRMASK)
-#define	OPND(n)	((n)&OPDMASK)
-#define	SOP(op, opnd)	((op)|(opnd))
-/* operators			   meaning	operand			*/
-/*						(back, fwd are offsets)	*/
-#define	OEND	(1U<<OPSHIFT)	/* endmarker	-			*/
-#define	OCHAR	(2U<<OPSHIFT)	/* character	wide character		*/
-#define	OBOL	(3U<<OPSHIFT)	/* left anchor	-			*/
-#define	OEOL	(4U<<OPSHIFT)	/* right anchor	-			*/
-#define	OANY	(5U<<OPSHIFT)	/* .		-			*/
-#define	OANYOF	(6U<<OPSHIFT)	/* [...]	set number		*/
-#define	OBACK_	(7U<<OPSHIFT)	/* begin \d	paren number		*/
-#define	O_BACK	(8U<<OPSHIFT)	/* end \d	paren number		*/
-#define	OPLUS_	(9U<<OPSHIFT)	/* + prefix	fwd to suffix		*/
-#define	O_PLUS	(10U<<OPSHIFT)	/* + suffix	back to prefix		*/
-#define	OQUEST_	(11U<<OPSHIFT)	/* ? prefix	fwd to suffix		*/
-#define	O_QUEST	(12U<<OPSHIFT)	/* ? suffix	back to prefix		*/
-#define	OLPAREN	(13U<<OPSHIFT)	/* (		fwd to )		*/
-#define	ORPAREN	(14U<<OPSHIFT)	/* )		back to (		*/
-#define	OCH_	(15U<<OPSHIFT)	/* begin choice	fwd to OOR2		*/
-#define	OOR1	(16U<<OPSHIFT)	/* | pt. 1	back to OOR1 or OCH_	*/
-#define	OOR2	(17U<<OPSHIFT)	/* | pt. 2	fwd to OOR2 or O_CH	*/
-#define	O_CH	(18U<<OPSHIFT)	/* end choice	back to OOR1		*/
-#define	OBOW	(19U<<OPSHIFT)	/* begin word	-			*/
-#define	OEOW	(20U<<OPSHIFT)	/* end word	-			*/
-
-/*
- * Structures for [] character-set representation.
- */
-typedef struct {
-	wint_t		min;
-	wint_t		max;
-} crange;
-typedef struct {
-	unsigned char	bmp[NC / 8];
-	wctype_t	*types;
-	unsigned int	ntypes;
-	wint_t		*wides;
-	unsigned int	nwides;
-	crange		*ranges;
-	unsigned int	nranges;
-	int		invert;
-	int		icase;
-} cset;
-
-static int
-CHIN1(cset *cs, wint_t ch)
-{
-	unsigned int i;
-
-	assert(ch >= 0);
-	if (ch < NC)
-		return (((cs->bmp[ch >> 3] & (1 << (ch & 7))) != 0) ^
-		    cs->invert);
-	for (i = 0; i < cs->nwides; i++)
-		if (ch == cs->wides[i])
-			return (!cs->invert);
-	for (i = 0; i < cs->nranges; i++)
-		if (cs->ranges[i].min <= ch && ch <= cs->ranges[i].max)
-			return (!cs->invert);
-	for (i = 0; i < cs->ntypes; i++)
-		if (iswctype(ch, cs->types[i]))
-			return (!cs->invert);
-	return (cs->invert);
-}
-
-static int
-CHIN(cset *cs, wint_t ch)
-{
-
-	assert(ch >= 0);
-	if (ch < NC)
-		return (((cs->bmp[ch >> 3] & (1 << (ch & 7))) != 0) ^
-		    cs->invert);
-	else if (cs->icase)
-		return (CHIN1(cs, ch) || CHIN1(cs, towlower(ch)) ||
-		    CHIN1(cs, towupper(ch)));
-	else
-		return (CHIN1(cs, ch));
-}
-
-/*
- * main compiled-expression structure
- */
-struct re_guts {
-	int magic;
-#define	MAGIC2	((('R'^0200)<<8)|'E')
-	sop *strip;		/* malloced area for strip */
-	unsigned int ncsets;	/* number of csets in use */
-	cset *sets;		/* -> cset [ncsets] */
-	int cflags;		/* copy of regcomp() cflags argument */
-	sopno nstates;		/* = number of sops */
-	sopno firststate;	/* the initial OEND (normally 0) */
-	sopno laststate;	/* the final OEND */
-	int iflags;		/* internal flags */
-#define	USEBOL	01	/* used ^ */
-#define	USEEOL	02	/* used $ */
-#define	BAD	04	/* something wrong */
-	int nbol;		/* number of ^ used */
-	int neol;		/* number of $ used */
-	char *must;		/* match must contain this string */
-	int moffset;		/* latest point at which must may be located */
-	int *charjump;		/* Boyer-Moore char jump table */
-	int *matchjump;		/* Boyer-Moore match jump table */
-	int mlen;		/* length of must */
-	size_t nsub;		/* copy of re_nsub */
-	int backrefs;		/* does it use back references? */
-	sopno nplus;		/* how deep does it nest +s? */
-};
-
-/* misc utilities */
-#define	OUT	(CHAR_MIN - 1)	/* a non-character value */
-#define	ISWORD(c)	(iswalnum((uch)(c)) || (c) == '_')
--- old/usr/src/lib/libc/port/regex/regexec.c	Tue Feb 13 07:11:49 2018
+++ new/usr/src/lib/libc/port/regex/regexec.c	Tue Feb 13 07:11:49 2018
@@ -1,225 +1,303 @@
 /*
- * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
- * Copyright (c) 1992, 1993, 1994 Henry Spencer.
- * Copyright (c) 1992, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
  *
- * This code is derived from software contributed to Berkeley by
- * Henry Spencer.
- *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
+ *
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
+ *
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 /*
- * the outer shell of regexec()
- *
- * This file includes engine.c three times, after muchos fiddling with the
- * macros that code uses.  This lets the same code operate on two different
- * representations for state sets and characters.
- */
-#include "lint.h"
-#include "file64.h"
-#include <sys/types.h>
-#include <stdio.h>
+  tre_regexec.c - TRE POSIX compatible matching functions (and more).
+*/
+
+#include <assert.h>
+#include <limits.h>
 #include <stdlib.h>
 #include <string.h>
-#include <limits.h>
-#include <ctype.h>
-#include <regex.h>
 #include <wchar.h>
 #include <wctype.h>
-#include <note.h>
-#include <assert.h>
 
-#include "utils.h"
-#include "regex2.h"
+#include "tre-internal.h"
+#include "tre-match-utils.h"
+#include "tre.h"
 
-/* we want _NOTE, but not NOTE (which collides with our own use) */
-#undef	NOTE
-
-static size_t
-xmbrtowc(wint_t *wi, const char *s, size_t n, mbstate_t *mbs, wint_t dummy)
+/* For each tre_last_matched_t in the lm array, find the last matched branch by
+   comparing the touch value of the cmp_tag's.  For all other branches, reset
+   the corresponding tags.  If reset_all is non-zero, reset all tags in all
+   branches.  Recurse into the nested last matched structures, clearing tags as
+   apprpriate. */
+static void
+tre_reset_last_matched_branches(tre_tag_t *tags, const tre_last_matched_t *lm,
+			        int n, int start_tag, int reset_all)
 {
-	size_t nr;
-	wchar_t wc;
+  int max, i, reset;
+  tre_last_matched_branch_t *b;
 
-	nr = mbrtowc(&wc, s, n, mbs);
-	if (wi != NULL)
-		*wi = wc;
-	if (nr == 0)
-		return (1);
-	else if (nr == (size_t)-1 || nr == (size_t)-2) {
-		(void) memset(mbs, 0, sizeof (*mbs));
-		if (wi != NULL)
-			*wi = dummy;
-		return (1);
-	} else
-		return (nr);
+  DPRINT(("tre_reset_last_matched_branches: n=%d start_tag=%d reset_all=%d\n",
+	  n, start_tag, reset_all));
+  for (; n-- > 0; lm++)
+    {
+      if (lm->n_branches == 1)
+	{
+	  b = lm->branches;
+	  if (start_tag > 0)
+	    {
+	      DPRINT(("  b->cmp_tag=%d %d <? %d\n", b->cmp_tag,
+		      tre_tag_touch_get(tags, b->cmp_tag),
+		      tre_tag_touch_get(tags, start_tag)));
+	      reset = (reset_all || tre_tag_touch_get(tags, b->cmp_tag) <
+		       tre_tag_touch_get(tags, start_tag));
+	    }
+	  else
+	    reset = 0;
+
+	  if (reset)
+	    {
+	      int *t;
+
+	      for (i = b->n_tags, t = b->tags; i > 0; i--, t++)
+		{
+		  DPRINT(("  Resetting t%d\n", *t));
+		  tre_tag_reset(tags, *t);
+		}
+	    }
+	  if (b->n_last_matched > 0)
+	    tre_reset_last_matched_branches(tags, b->last_matched,
+						b->n_last_matched, 
+						lm->start_tag, reset);
+	}
+      else
+	{
+	  if (!reset_all)
+	    {
+#ifdef TRE_DEBUG
+	      int last;
+#endif /* TRE_DEBUG */
+	      max = 0;
+	      for (i = lm->n_branches, b = lm->branches; i > 0; i--, b++)
+		{
+		  int t = b->cmp_tag;
+		  int touch = tre_tag_touch_get(tags, t);
+		  if (touch > max)
+		    {
+		      max = touch;
+#ifdef TRE_DEBUG
+		      last = t;
+#endif /* TRE_DEBUG */
+		    }
+		}
+	      DPRINT(("  Last touched end tag t%d=%d\n", last, max));
+	    }
+
+	  for (i = lm->n_branches, b = lm->branches; i > 0; i--, b++)
+	    {
+	      reset = (reset_all || tre_tag_touch_get(tags, b->cmp_tag) < max);
+	      if (reset)
+		{
+		  int j;
+		  int *t;
+
+		  for (j = b->n_tags, t = b->tags; j > 0; j--, t++)
+		    {
+		      DPRINT(("  Resetting t%d\n", *t));
+		      tre_tag_reset(tags, *t);
+		    }
+		}
+	      if (b->n_last_matched > 0)
+		tre_reset_last_matched_branches(tags, b->last_matched,
+						b->n_last_matched,
+						lm->start_tag, reset);
+	    }
+	}
+    }
 }
 
-static size_t
-xmbrtowc_dummy(wint_t *wi, const char *s, size_t n, mbstate_t *mbs,
-    wint_t dummy)
+/* Fills the POSIX.2 regmatch_t array according to the TNFA tag and match
+   endpoint values. */
+reg_errcode_t
+tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags,
+		const tre_tnfa_t *tnfa, const tre_tag_t *intags, int match_eo)
 {
-	_NOTE(ARGUNUSED(n));
-	_NOTE(ARGUNUSED(mbs));
-	_NOTE(ARGUNUSED(dummy));
+  unsigned int i;
 
-	if (wi != NULL)
-		*wi = (unsigned char)*s;
-	return (1);
-}
+  if (cflags & REG_NOSUB) return REG_OK;
 
-/* macros for manipulating states, small version */
-#define	states	long
-#define	states1	states		/* for later use in regexec() decision */
-#define	CLEAR(v)	((v) = 0)
-#define	SET0(v, n)	((v) &= ~((unsigned long)1 << (n)))
-#define	SET1(v, n)	((v) |= (unsigned long)1 << (n))
-#define	ISSET(v, n)	(((v) & ((unsigned long)1 << (n))) != 0)
-#define	ASSIGN(d, s)	((d) = (s))
-#define	EQ(a, b)	((a) == (b))
-#define	STATEVARS	long dummy	/* dummy version */
-#define	STATESETUP(m, n)	/* nothing */
-#define	STATETEARDOWN(m)	/* nothing */
-#define	SETUP(v)	((v) = 0)
-#define	onestate	long
-#define	INIT(o, n)	((o) = (unsigned long)1 << (n))
-#define	INC(o)	((o) <<= 1)
-#define	ISSTATEIN(v, o)	(((v) & (o)) != 0)
-/* some abbreviations; note that some of these know variable names! */
-/* do "if I'm here, I can also be there" etc without branches */
-#define	FWD(dst, src, n)	((dst) |= ((unsigned long)(src)&(here)) << (n))
-#define	BACK(dst, src, n)	((dst) |= ((unsigned long)(src)&(here)) >> (n))
-#define	ISSETBACK(v, n)	(((v) & ((unsigned long)here >> (n))) != 0)
-/* no multibyte support */
-#define	XMBRTOWC	xmbrtowc_dummy
-#define	ZAPSTATE(mbs)	((void)(mbs))
-/* function names */
-#define	SNAMES			/* engine.c looks after details */
+  i = 0;
+  if (match_eo >= 0 && intags)
+    {
+      const tre_tag_t *tags = intags;
+      tre_submatch_data_t *submatch_data;
 
-#include "engine.c"
+      if (tnfa->last_matched_branch &&
+	  tnfa->last_matched_branch->n_last_matched > 0)
+	{
+	  tre_tag_t *t;
+	  t = malloc(sizeof(*t) * tnfa->num_tags);
+	  if (!t) return REG_ESPACE;
+	  memcpy(t, intags, tnfa->num_tags * sizeof(tre_tag_t));
+	  tre_reset_last_matched_branches(t,
+				    tnfa->last_matched_branch->last_matched,
+				    tnfa->last_matched_branch->n_last_matched,
+				    0, 0);
+	  tags = t;
+	}
+      /* Construct submatch offsets from the tags. */
+      DPRINT(("end tag = t%d = %d\n", tnfa->end_tag, match_eo));
+      submatch_data = tnfa->submatch_data;
+      while (i < tnfa->num_submatches && i < nmatch)
+	{
+	  if (submatch_data[i].so_tag == tnfa->end_tag)
+	    pmatch[i].rm_so = match_eo;
+	  else
+	    pmatch[i].rm_so = tre_tag_get(tags, submatch_data[i].so_tag);
 
-/* now undo things */
-#undef	states
-#undef	CLEAR
-#undef	SET0
-#undef	SET1
-#undef	ISSET
-#undef	ASSIGN
-#undef	EQ
-#undef	STATEVARS
-#undef	STATESETUP
-#undef	STATETEARDOWN
-#undef	SETUP
-#undef	onestate
-#undef	INIT
-#undef	INC
-#undef	ISSTATEIN
-#undef	FWD
-#undef	BACK
-#undef	ISSETBACK
-#undef	SNAMES
-#undef	XMBRTOWC
-#undef	ZAPSTATE
+	  if (submatch_data[i].eo_tag == tnfa->end_tag)
+	    pmatch[i].rm_eo = match_eo;
+	  else
+	    pmatch[i].rm_eo = tre_tag_get(tags, submatch_data[i].eo_tag);
 
-/* macros for manipulating states, large version */
-#define	states	char *
-#define	CLEAR(v)	(void) memset(v, 0, m->g->nstates)
-#define	SET0(v, n)	((v)[n] = 0)
-#define	SET1(v, n)	((v)[n] = 1)
-#define	ISSET(v, n)	((v)[n])
-#define	ASSIGN(d, s)	(void) memcpy(d, s, m->g->nstates)
-#define	EQ(a, b)	(memcmp(a, b, m->g->nstates) == 0)
-#define	STATEVARS	long vn; char *space
-#define	STATESETUP(m, nv) { (m)->space = malloc((nv)*(m)->g->nstates); \
-	if ((m)->space == NULL) \
-		return (REG_ESPACE); \
-	(m)->vn = 0; }
-#define	STATETEARDOWN(m)	{ free((m)->space); }
-#define	SETUP(v)	((v) = &m->space[m->vn++ * m->g->nstates])
-#define	onestate	long
-#define	INIT(o, n)	((o) = (n))
-#define	INC(o)	((o)++)
-#define	ISSTATEIN(v, o)	((v)[o])
-/* some abbreviations; note that some of these know variable names! */
-/* do "if I'm here, I can also be there" etc without branches */
-#define	FWD(dst, src, n)	((dst)[here+(n)] |= (src)[here])
-#define	BACK(dst, src, n)	((dst)[here-(n)] |= (src)[here])
-#define	ISSETBACK(v, n)	((v)[here - (n)])
-/* no multibyte support */
-#define	XMBRTOWC	xmbrtowc_dummy
-#define	ZAPSTATE(mbs)	((void)(mbs))
-/* function names */
-#define	LNAMES			/* flag */
+	  /* If either of the endpoints were not used, this submatch
+	     was not part of the match. */
+	  if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1)
+	    pmatch[i].rm_so = pmatch[i].rm_eo = -1;
 
-#include "engine.c"
+	  DPRINT(("pmatch[%d] = {t%d = %qd, t%d = %qd}\n", i,
+		  submatch_data[i].so_tag, pmatch[i].rm_so,
+		  submatch_data[i].eo_tag, pmatch[i].rm_eo));
+	  i++;
+	}
+	if (tags != intags) free(__DECONST(tre_tag_t *,tags));
+    }
 
-/* multibyte character & large states version */
-#undef	LNAMES
-#undef	XMBRTOWC
-#undef	ZAPSTATE
-#define	XMBRTOWC	xmbrtowc
-#define	ZAPSTATE(mbs)	(void) memset((mbs), 0, sizeof (*(mbs)))
-#define	MNAMES
+  while (i < nmatch)
+    {
+      pmatch[i].rm_so = -1;
+      pmatch[i].rm_eo = -1;
+      i++;
+    }
 
-#include "engine.c"
+  return REG_OK;
+}
 
 /*
- * regexec - interface for matching
- *
- * We put this here so we can exploit knowledge of the state representation
- * when choosing which matcher to call.  Also, by this point the matchers
- * have been prototyped.
- */
-int				/* 0 success, REG_NOMATCH failure */
-regexec(const regex_t *_RESTRICT_KYWD preg, const char *_RESTRICT_KYWD string,
-    size_t nmatch, regmatch_t pmatch[_RESTRICT_KYWD], int eflags)
+  Wrapper functions for POSIX compatible regexp matching.
+*/
+
+int
+tre_have_backrefs(const regex_t *preg)
 {
-	struct re_guts *g = preg->re_g;
-#ifdef REDEBUG
-#define	GOODFLAGS(f)	(f)
+  tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+  return tnfa->have_backrefs;
+}
+
+static int
+tre_match(const tre_tnfa_t *tnfa, const void *string, size_t len,
+	  tre_str_type_t type, size_t nmatch, regmatch_t pmatch[],
+	  int eflags)
+{
+  reg_errcode_t status;
+  tre_tag_t *tags = NULL;
+  int eo;
+  size_t offset = 0, count = 0;
+  if (tnfa->num_tags > 0 && nmatch > 0)
+    {
+      tags = malloc(sizeof(*tags) * tnfa->num_tags);
+      if (tags == NULL)
+	return REG_ESPACE;
+    }
+
+  if (
+      (eflags & REG_STARTEND) && pmatch)
+    {
+      if (pmatch->rm_so < 0)
+	return REG_INVARG;
+      if (len == (size_t)-1)
+	{
+	  if (pmatch->rm_eo < 0 || pmatch->rm_so > pmatch->rm_eo)
+	    return REG_INVARG;
+	  len = pmatch->rm_eo - pmatch->rm_so;
+	}
+      count = offset = pmatch->rm_so;
+      if (type == STR_WIDE) offset *= sizeof(wchar_t);
+    }
+
+  /* Dispatch to the appropriate matcher. */
+#ifdef REG_BACKTRACKING_MATCHER
+  if (tnfa->have_backrefs || eflags & REG_BACKTRACKING_MATCHER)
 #else
-#ifdef	REG_STARTEND
-#define	GOODFLAGS(f)	((f)&(REG_NOTBOL|REG_NOTEOL|REG_STARTEND))
-#else
-#define	GOODFLAGS(f)	((f)&(REG_NOTBOL|REG_NOTEOL))
+  if (tnfa->have_backrefs)
 #endif
-#endif
+    {
+      /* The regex has back references, use the backtracking matcher. */
+      status = tre_tnfa_run_backtrack(tnfa, string + offset, (int)len, type,
+				      tags, eflags, &eo);
+    }
+  else
+    {
+      /* Exact matching, no back references, use the parallel matcher. */
+      status = tre_tnfa_run_parallel(tnfa, string + offset, (int)len, type,
+				     tags, eflags, &eo);
+    }
 
-	if (preg->re_magic != MAGIC1 || g->magic != MAGIC2)
-		return (REG_BADPAT);
-	assert(!(g->iflags&BAD));
-	if (g->iflags&BAD)		/* backstop for no-debug case */
-		return (REG_BADPAT);
-	eflags = GOODFLAGS(eflags);
+  if (status == REG_OK)
+    {
+      /* A match was found, so fill the submatch registers. */
+      status = tre_fill_pmatch(nmatch, pmatch, tnfa->cflags, tnfa, tags, eo);
+      /* If doing REG_STARTEND, adjust the pmatch array (we can't build
+         this into tre_fill_pmatch, because tre_tnfa_run_backtrack calls
+	 tre_fill_pmatch itself). */
+      if (status == REG_OK && !(tnfa->cflags & REG_NOSUB) &&
+	  (eflags & REG_STARTEND) && pmatch && nmatch > 0)
+	{
+	  size_t i;
+	  regmatch_t *p;
+	  for (i = nmatch, p = pmatch; i > 0; p++, i--)
+	    {
+	      if (p->rm_so >= 0) p->rm_so += count;
+	      if (p->rm_eo >= 0) p->rm_eo += count;
+	    }
+	}
+    }
+  if (tags)
+    free(tags);
+  return status;
+}
 
-	if (MB_CUR_MAX > 1)
-		return (mmatcher(g, string, nmatch, pmatch, eflags));
-	else if (g->nstates <= CHAR_BIT*sizeof (states1) && !(eflags&REG_LARGE))
-		return (smatcher(g, string, nmatch, pmatch, eflags));
-	else
-		return (lmatcher(g, string, nmatch, pmatch, eflags));
+int
+tre_regnexec(const regex_t *preg, const char *str, size_t len,
+	 size_t nmatch, regmatch_t pmatch[], int eflags)
+{
+  tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+  tre_str_type_t type = (TRE_MB_CUR_MAX_L(tnfa->loc) == 1) ? STR_BYTE : STR_MBS;
+
+  if (preg->re_magic != RE_MAGIC) return REG_BADPAT;
+
+  return tre_match(tnfa, str, len, type, nmatch, pmatch, eflags);
+}
+
+int
+regexec(const regex_t *_RESTRICT_KYWD preg, const char *_RESTRICT_KYWD str,
+    size_t nmatch, regmatch_t pmatch[_RESTRICT_KYWD], int eflags)
+{
+  return tre_regnexec(preg, str, (size_t)-1, nmatch, pmatch, eflags);
 }
--- old/usr/src/lib/libc/port/regex/regfree.c	Tue Feb 13 07:11:49 2018
+++ /dev/null	Tue Feb 13 07:11:47 2018
@@ -1,88 +0,0 @@
-/*
- * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
- * Copyright (c) 1992, 1993, 1994 Henry Spencer.
- * Copyright (c) 1992, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Henry Spencer.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include "lint.h"
-#include "file64.h"
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <regex.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#include "utils.h"
-#include "regex2.h"
-
-/*
- * regfree - free everything
- */
-void
-regfree(regex_t *preg)
-{
-	struct re_guts *g;
-	int i;
-
-#ifdef	__lint
-	/* shut up lint! */
-	CHIN(NULL, 0);
-#endif
-
-	if (preg->re_magic != MAGIC1)	/* oops */
-		return;			/* nice to complain, but hard */
-
-	g = preg->re_g;
-	if (g == NULL || g->magic != MAGIC2)	/* oops again */
-		return;
-	preg->re_magic = 0;		/* mark it invalid */
-	g->magic = 0;			/* mark it invalid */
-
-	if (g->strip != NULL)
-		free((char *)g->strip);
-	if (g->sets != NULL) {
-		for (i = 0; i < g->ncsets; i++) {
-			free(g->sets[i].ranges);
-			free(g->sets[i].wides);
-			free(g->sets[i].types);
-		}
-		free((char *)g->sets);
-	}
-	if (g->must != NULL)
-		free(g->must);
-	if (g->charjump != NULL)
-		free(&g->charjump[CHAR_MIN]);
-	if (g->matchjump != NULL)
-		free(g->matchjump);
-	free((char *)g);
-}
--- /dev/null	Tue Feb 13 07:11:50 2018
+++ new/usr/src/lib/libc/port/regex/tre-ast.c	Tue Feb 13 07:11:50 2018
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+  tre-ast.c - Abstract syntax tree (AST) routines
+*/
+
+#include <assert.h>
+
+#include "tre-ast.h"
+#include "tre-mem.h"
+
+tre_ast_node_t *
+tre_ast_new_node(tre_mem_t mem, tre_ast_type_t type, size_t size)
+{
+  tre_ast_node_t *node;
+
+  node = tre_mem_calloc(mem, sizeof(*node));
+  if (!node)
+    return NULL;
+  node->obj = tre_mem_calloc(mem, size);
+  if (!node->obj)
+    return NULL;
+  node->type = type;
+  node->nullable = -1;
+  node->submatch_id = -1;
+
+  return node;
+}
+
+tre_ast_node_t *
+tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position)
+{
+  tre_ast_node_t *node;
+  tre_literal_t *lit;
+
+  node = tre_ast_new_node(mem, LITERAL, sizeof(tre_literal_t));
+  if (!node)
+    return NULL;
+  lit = node->obj;
+  lit->code_min = code_min;
+  lit->code_max = code_max;
+  lit->position = position;
+
+  return node;
+}
+
+tre_ast_node_t *
+tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max,
+		 int minimal)
+{
+  tre_ast_node_t *node;
+  tre_iteration_t *iter;
+
+  node = tre_ast_new_node(mem, ITERATION, sizeof(tre_iteration_t));
+  if (!node)
+    return NULL;
+  iter = node->obj;
+  iter->arg = arg;
+  iter->min = min;
+  iter->max = max;
+  iter->minimal = minimal;
+  node->num_submatches = arg->num_submatches;
+
+  return node;
+}
+
+tre_ast_node_t *
+tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right)
+{
+  tre_ast_node_t *node;
+
+  node = tre_ast_new_node(mem, UNION, sizeof(tre_union_t));
+  if (node == NULL)
+    return NULL;
+  ((tre_union_t *)node->obj)->left = left;
+  ((tre_union_t *)node->obj)->right = right;
+  node->num_submatches = left->num_submatches + right->num_submatches;
+
+  return node;
+}
+
+tre_ast_node_t *
+tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left,
+		       tre_ast_node_t *right)
+{
+  tre_ast_node_t *node;
+
+  node = tre_ast_new_node(mem, CATENATION, sizeof(tre_catenation_t));
+  if (node == NULL)
+    return NULL;
+  ((tre_catenation_t *)node->obj)->left = left;
+  ((tre_catenation_t *)node->obj)->right = right;
+  node->num_submatches = left->num_submatches + right->num_submatches;
+
+  return node;
+}
+
+#ifdef TRE_DEBUG
+
+static void
+tre_findent(FILE *stream, int i)
+{
+  while (i-- > 0)
+    fputc(' ', stream);
+}
+
+void
+tre_print_params(int *params)
+{
+  int i;
+  if (params)
+    {
+      DPRINT(("params ["));
+      for (i = 0; i < TRE_PARAM_LAST; i++)
+	{
+	  if (params[i] == TRE_PARAM_UNSET)
+	    DPRINT(("unset"));
+	  else if (params[i] == TRE_PARAM_DEFAULT)
+	    DPRINT(("default"));
+	  else
+	    DPRINT(("%d", params[i]));
+	  if (i < TRE_PARAM_LAST - 1)
+	    DPRINT((", "));
+	}
+      DPRINT(("]"));
+    }
+}
+
+static void
+tre_do_print(FILE *stream, tre_ast_node_t *ast, int indent)
+{
+  int code_min, code_max, pos;
+  int num_tags = ast->num_tags;
+  tre_literal_t *lit;
+  tre_iteration_t *iter;
+
+  tre_findent(stream, indent);
+  switch (ast->type)
+    {
+    case LITERAL:
+      lit = ast->obj;
+      code_min = lit->code_min;
+      code_max = lit->code_max;
+      pos = lit->position;
+      if (IS_EMPTY(lit))
+	{
+	  fprintf(stream, "literal empty\n");
+	}
+      else if (IS_ASSERTION(lit))
+	{
+	  int i;
+	  char *assertions[] = { "bol", "eol", "bracket",
+				 "bow", "eow", "wb", "!wb", "backref" };
+	  if (code_max >= ASSERT_LAST << 1)
+	    assert(0);
+	  fprintf(stream, "assertions: ");
+	  for (i = 0; (1 << i) <= ASSERT_LAST; i++)
+	    if (code_max & (1 << i))
+	      fprintf(stream, "%s ", assertions[i]);
+	  fprintf(stream, "\n");
+	}
+      else if (IS_TAG(lit))
+	{
+	  fprintf(stream, "tag %d\n", code_max);
+	}
+      else if (IS_BACKREF(lit))
+	{
+	  fprintf(stream, "backref %d, pos %d\n", code_max, pos);
+	}
+      else if (IS_PARAMETER(lit))
+	{
+	  tre_print_params(lit->u.params);
+	  fprintf(stream, "\n");
+	}
+      else
+	{
+	  fprintf(stream, "literal (%c, %c) (%d, %d), pos %d, sub %d, "
+		  "%d tags\n", code_min, code_max, code_min, code_max, pos,
+		  ast->submatch_id, num_tags);
+	}
+      break;
+    case ITERATION:
+      iter = ast->obj;
+      fprintf(stream, "iteration {%d, %d}, sub %d, %d tags, %s\n",
+	      iter->min, iter->max, ast->submatch_id, num_tags,
+	      iter->minimal ? "minimal" : "greedy");
+      tre_do_print(stream, iter->arg, indent + 2);
+      break;
+    case UNION:
+      fprintf(stream, "union, sub %d, %d tags\n", ast->submatch_id, num_tags);
+      tre_do_print(stream, ((tre_union_t *)ast->obj)->left, indent + 2);
+      tre_do_print(stream, ((tre_union_t *)ast->obj)->right, indent + 2);
+      break;
+    case CATENATION:
+      fprintf(stream, "catenation, sub %d, %d tags\n", ast->submatch_id,
+	      num_tags);
+      tre_do_print(stream, ((tre_catenation_t *)ast->obj)->left, indent + 2);
+      tre_do_print(stream, ((tre_catenation_t *)ast->obj)->right, indent + 2);
+      break;
+    default:
+      assert(0);
+      break;
+    }
+}
+
+static void
+tre_ast_fprint(FILE *stream, tre_ast_node_t *ast)
+{
+  tre_do_print(stream, ast, 0);
+}
+
+void
+tre_ast_print(tre_ast_node_t *tree)
+{
+  printf("AST:\n");
+  tre_ast_fprint(stdout, tree);
+}
+
+#endif /* TRE_DEBUG */
--- /dev/null	Tue Feb 13 07:11:50 2018
+++ new/usr/src/lib/libc/port/regex/tre-ast.h	Tue Feb 13 07:11:50 2018
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+  tre-ast.h - Abstract syntax tree (AST) definitions
+*/
+
+#ifndef	_TRE_AST_H
+#define	_TRE_AST_H
+
+#include <limits.h>
+
+#include "tre-mem.h"
+#include "tre-internal.h"
+#include "tre-compile.h"
+
+/* The different AST node types. */
+typedef enum {
+  LITERAL,
+  CATENATION,
+  ITERATION,
+  UNION
+} tre_ast_type_t;
+
+/* Special subtypes of TRE_LITERAL. */
+#define EMPTY	  -1   /* Empty leaf (denotes empty string). */
+#define ASSERTION -2   /* Assertion leaf. */
+#define TAG	  -3   /* Tag leaf. */
+#define BACKREF	  -4   /* Back reference leaf. */
+#define PARAMETER -5   /* Parameter. */
+
+#define IS_SPECIAL(x)	((x)->code_min < 0)
+#define IS_EMPTY(x)	((x)->code_min == EMPTY)
+#define IS_ASSERTION(x) ((x)->code_min == ASSERTION)
+#define IS_TAG(x)	((x)->code_min == TAG)
+#define IS_BACKREF(x)	((x)->code_min == BACKREF)
+#define IS_PARAMETER(x) ((x)->code_min == PARAMETER)
+
+#define SUBMATCH_ID_INVISIBLE_START	(INT_MAX / 2 + 1)
+
+/* A generic AST node.  All AST nodes consist of this node on the top
+   level with `obj' pointing to the actual content. */
+typedef struct _tre_ast_node {
+  void *obj;             /* Pointer to actual node. */
+  tre_last_matched_branch_pre_t *last_matched_branch;
+  tre_last_matched_pre_t *last_matched_in_progress;
+  tre_pos_and_tags_t *firstpos;
+  tre_pos_and_tags_t *lastpos;
+  /* The original pointer is used to point to the original node, when a
+   * CATENATION and tag are added.  If NULL, this is node is the original */
+  struct _tre_ast_node *original;
+  tre_ast_type_t type;   /* Type of the node. */
+  int submatch_id;
+  int num_submatches;
+  int num_tags;
+  short nullable;
+  short make_branches;
+} tre_ast_node_t;
+
+/* A "literal" node.  These are created for assertions, back references,
+   tags, matching parameter settings, and all expressions that match one
+   character. */
+typedef struct {
+  tre_cint_t code_min;
+  tre_cint_t code_max;
+  int position;
+  union {
+    tre_bracket_match_list_t *bracket_match_list;
+    int *params;
+  } u;
+} tre_literal_t;
+
+/* A "catenation" node.	 These are created when two regexps are concatenated.
+   If there are more than one subexpressions in sequence, the `left' part
+   holds all but the last, and `right' part holds the last subexpression
+   (catenation is left associative). */
+typedef struct {
+  tre_ast_node_t *left;
+  tre_ast_node_t *right;
+} tre_catenation_t;
+
+/* An "iteration" node.	 These are created for the "*", "+", "?", and "{m,n}"
+   operators. */
+typedef struct {
+  /* Subexpression to match. */
+  tre_ast_node_t *arg;
+  /* Minimum number of consecutive matches. */
+  int min;
+  /* Maximum number of consecutive matches. */
+  int max;
+  /* If 0, match as many characters as possible, if 1 match as few as
+     possible.	Note that this does not always mean the same thing as
+     matching as many/few repetitions as possible. */
+  unsigned int minimal:1;
+  /* Approximate matching parameters (or NULL). */
+  int *params;
+} tre_iteration_t;
+
+/* An "union" node.  These are created for the "|" operator. */
+typedef struct {
+  tre_ast_node_t *left;
+  tre_ast_node_t *right;
+  /* The left end right end tags if non-zero */
+  int left_tag;
+  int right_tag;
+} tre_union_t;
+
+tre_ast_node_t *
+tre_ast_new_node(tre_mem_t mem, tre_ast_type_t type, size_t size);
+
+tre_ast_node_t *
+tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position);
+
+tre_ast_node_t *
+tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max,
+		 int minimal);
+
+tre_ast_node_t *
+tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right);
+
+tre_ast_node_t *
+tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left,
+		       tre_ast_node_t *right);
+
+#ifdef TRE_DEBUG
+void
+tre_ast_print(tre_ast_node_t *tree);
+
+/* XXX - rethink AST printing API */
+void
+tre_print_params(int *params);
+#endif /* TRE_DEBUG */
+
+#endif	/* _TRE_AST_H */
--- /dev/null	Tue Feb 13 07:11:50 2018
+++ new/usr/src/lib/libc/port/regex/tre-compile.c	Tue Feb 13 07:11:50 2018
@@ -0,0 +1,3268 @@
+/*
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+  tre-compile.c - TRE regex compiler
+*/
+
+/*
+  TODO:
+   - Fix tre_ast_to_tnfa() to recurse using a stack instead of recursive
+     function calls.
+*/
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <limits.h>
+
+#include "tre-internal.h"
+#include "tre-mem.h"
+#include "tre-stack.h"
+#include "tre-ast.h"
+#include "tre-parse.h"
+#include "tre-compile.h"
+#include "tre.h"
+
+/*
+  Algorithms to setup tags so that submatch addressing can be done.
+*/
+
+#ifdef TRE_DEBUG
+static const char *tag_dir_str[] = {
+  "minimize",
+  "maximize",
+  "left-maximize"
+};
+
+static const char _indent[] = "  ";
+
+static void
+print_indent(int indent)
+{
+  while (indent-- > 0)
+    DPRINT((_indent));
+}
+
+static void print_last_matched_pre(tre_last_matched_pre_t *lm, int indent,
+				   int num_tags);
+static void
+print_last_match_branch_pre(tre_last_matched_branch_pre_t *branch, int indent,
+			    int num_tags)
+{
+  tre_last_matched_pre_t *u = branch->last_matched;
+  int n_last_matched = 0;
+
+  while (u)
+    {
+      n_last_matched++;
+      u = u->next;
+    }
+
+  print_indent(indent);
+  DPRINT(("BRANCH: tot_branches=%d tot_last_matched=%d tot_tags=%d\n",
+	  branch->tot_branches, branch->tot_last_matched, branch->tot_tags));
+  print_indent(indent);
+  DPRINT(("..n_last_matched=%d last_matched=%d\n", branch->n_last_matched,
+	  n_last_matched));
+  if (branch->n_last_matched != n_last_matched)
+    DPRINT(("*** mismatch between n_last_matched and unions ***\n"));
+  if (branch->cmp_tag > 0)
+    {
+      int i;
+      const char *sep = " tags=";
+      print_indent(indent);
+      DPRINT(("..cmp_tag=%d n_tags=%d", branch->cmp_tag, branch->n_tags));
+      for (i = 0; i < num_tags; i++)
+	if (bit_test(branch->tags, i))
+	  {
+	    DPRINT(("%s%d", sep, i));
+	    sep = ",";
+	  }
+      DPRINT(("\n"));
+    }
+
+  u = branch->last_matched;
+  indent++;
+  while (u)
+    {
+      print_last_matched_pre(u, indent, num_tags);
+      u = u->next;
+    }
+}
+
+static void
+print_last_matched_pre(tre_last_matched_pre_t *lm, int indent, int num_tags)
+{
+  tre_last_matched_branch_pre_t *b = lm->branches;
+  int n_branches = 0;
+
+  while (b)
+    {
+      n_branches++;
+      b = b->next;
+    }
+
+  print_indent(indent);
+  DPRINT(("LAST_MATCHED: tot_branches=%d tot_last_matched=%d tot_tags=%d\n",
+	  lm->tot_branches, lm->tot_last_matched, lm->tot_tags));
+  print_indent(indent);
+  DPRINT(("..start_tag=%d n_branches=%d branches=%d\n", lm->start_tag,
+	  lm->n_branches, n_branches));
+  if (lm->n_branches != n_branches)
+    DPRINT(("*** mismatch between n and branches ***\n"));
+
+  b = lm->branches;
+  indent++;
+  while (b)
+    {
+      print_last_match_branch_pre(b, indent, num_tags);
+      b = b->next;
+    }
+}
+
+static void print_last_matched(tre_last_matched_t *lm, int indent);
+static void
+print_last_match_branch(tre_last_matched_branch_t *branch, int indent)
+{
+  tre_last_matched_t *u;
+  int i;
+
+  print_indent(indent);
+  DPRINT(("BRANCH: n_last_matched=%d\n", branch->n_last_matched));
+  if (branch->cmp_tag > 0)
+    {
+      print_indent(indent);
+      DPRINT(("..cmp_tag=%d n_tags=%d", branch->cmp_tag, branch->n_tags));
+      if (branch->n_tags > 0)
+	{
+	  const char *sep = " tags=";
+	  for (i = 0; i < branch->n_tags; i++)
+	    {
+	      DPRINT(("%s%d", sep, branch->tags[i]));
+	      sep = ",";
+	    }
+	}
+      DPRINT(("\n"));
+    }
+
+  u = branch->last_matched;
+  indent++;
+  for (i = branch->n_last_matched; i > 0; i--, u++)
+    print_last_matched(u, indent);
+}
+
+static void
+print_last_matched(tre_last_matched_t *lm, int indent)
+{
+  int i;
+  tre_last_matched_branch_t *b;
+
+  print_indent(indent);
+  DPRINT(("LAST_MATCHED: n_branches=%d start_tag=%d\n", lm->n_branches,
+	  lm->start_tag));
+
+  b = lm->branches;
+  indent++;
+  for (i = lm->n_branches; i > 0; i--, b++)
+    print_last_match_branch(b, indent);
+}
+#endif /* TRE_DEBUG */
+
+/* Merge the tre_last_matched_branch_pre_t of src into dst, creating a new
+   one if needed.  If tag_id > 0, add that tag as well (a negative tag_id will
+   create an unset tre_last_matched_branch_pre_t. */
+static reg_errcode_t
+tre_merge_branches(tre_mem_t mem, tre_ast_node_t *dst, tre_ast_node_t *src,
+		   int tag_id, int num_tags)
+{
+  tre_last_matched_branch_pre_t *db = dst->last_matched_branch;
+  tre_last_matched_branch_pre_t *sb = (src ? src->last_matched_branch : NULL);
+
+  if (db)
+    {
+      if (sb)
+	{
+	  bitstr_t *l = db->tags;
+	  bitstr_t *r = sb->tags;
+	  int i = bitstr_size(num_tags);
+
+	  while(i-- > 0)
+	    *l++ |= *r++;
+	  /* db and sb are the info from two parallel sub-trees, so the tags
+	     must be mutually exclusive, and we can just add their numbers */
+	  db->n_tags += sb->n_tags;
+	  db->tot_tags += sb->tot_tags;
+	  if (db->last_matched)
+	    {
+	      if (sb->last_matched)
+		{
+		  tre_last_matched_pre_t *u = db->last_matched;
+
+		  while(u->next)
+		    u = u->next;
+		  u->next = sb->last_matched;
+		  db->n_last_matched += sb->n_last_matched;
+		  db->tot_branches += sb->tot_branches;
+		  db->tot_last_matched += sb->tot_last_matched;
+		}
+	    }
+	    else if (sb->last_matched)
+	      {
+		db->last_matched = sb->last_matched;
+		db->n_last_matched = sb->n_last_matched;
+		db->tot_branches = sb->tot_branches;
+		db->tot_last_matched = sb->tot_last_matched;
+	      }
+	}
+    }
+  else
+    db = sb;
+
+  if (tag_id != 0)
+    {
+      if (!db)
+	{
+	  db = tre_mem_calloc(mem, sizeof(tre_last_matched_branch_pre_t)
+			      + bitstr_size(num_tags));
+	  if (db == NULL)
+	    return REG_ESPACE;
+	  db->tot_branches = 1;
+	}
+      if (tag_id > 0)
+	{
+	  /* tag_id is a new tag, and shouldn't exist in db's tags,
+	     so we can always increment n_tags */
+	  bit_set(db->tags, tag_id);
+	  db->n_tags++;
+	  db->tot_tags++;
+	}
+    }
+  dst->last_matched_branch = db;
+  return REG_OK;
+}
+
+/* Inserts a catenation node to the root of the tree given in `node'.
+   As the left child a new tag with number `tag_id' to `node' is added,
+   and the right child is the old root. */
+static reg_errcode_t
+tre_add_tag_left(tre_mem_t mem, tre_ast_node_t *node, int tag_id)
+{
+  tre_catenation_t *c;
+
+  DPRINT(("add_tag_left: tag %d\n", tag_id));
+
+  c = tre_mem_alloc(mem, sizeof(*c));
+  if (c == NULL)
+    return REG_ESPACE;
+  c->left = tre_ast_new_literal(mem, TAG, tag_id, -1);
+  if (c->left == NULL)
+    return REG_ESPACE;
+  c->right = tre_mem_calloc(mem, sizeof(tre_ast_node_t));
+  if (c->right == NULL)
+    return REG_ESPACE;
+
+  c->right->obj = node->obj;
+  c->right->type = node->type;
+  c->right->last_matched_branch = node->last_matched_branch;
+  c->right->nullable = -1;
+  c->right->submatch_id = -1;
+  node->obj = c;
+  node->type = CATENATION;
+  node->original = c->right;
+  return REG_OK;
+}
+
+/* Inserts a catenation node to the root of the tree given in `node'.
+   As the right child a new tag with number `tag_id' to `node' is added,
+   and the left child is the old root. */
+static reg_errcode_t
+tre_add_tag_right(tre_mem_t mem, tre_ast_node_t *node, int tag_id)
+{
+  tre_catenation_t *c;
+
+  DPRINT(("tre_add_tag_right: tag %d\n", tag_id));
+
+  c = tre_mem_alloc(mem, sizeof(*c));
+  if (c == NULL)
+    return REG_ESPACE;
+  c->right = tre_ast_new_literal(mem, TAG, tag_id, -1);
+  if (c->right == NULL)
+    return REG_ESPACE;
+  c->left = tre_mem_calloc(mem, sizeof(tre_ast_node_t));
+  if (c->left == NULL)
+    return REG_ESPACE;
+
+  c->left->obj = node->obj;
+  c->left->type = node->type;
+  c->left->last_matched_branch = node->last_matched_branch;
+  c->left->nullable = -1;
+  c->left->submatch_id = -1;
+  node->obj = c;
+  node->type = CATENATION;
+  node->original = c->left;
+  return REG_OK;
+}
+
+typedef enum {
+  ADDTAGS_RECURSE,
+  ADDTAGS_RECURSE_NOT_TOP_UNION,
+  ADDTAGS_AFTER_ITERATION,
+  ADDTAGS_AFTER_UNION_LEFT,
+  ADDTAGS_AFTER_UNION_RIGHT,
+  ADDTAGS_AFTER_CAT_LEFT,
+  ADDTAGS_AFTER_CAT_RIGHT,
+  ADDTAGS_SET_SUBMATCH_END,
+  ADDTAGS_UNION_RECURSE,
+  ADDTAGS_UNION_RIGHT_RECURSE,
+  ADDTAGS_AFTER_UNION_TOP,
+} tre_addtags_symbol_t;
+
+enum {
+  COPY_LAST_MATCHED_BRANCH,
+  COPY_LAST_MATCHED_BRANCH_NEXT,
+  COPY_LAST_MATCHED,
+  COPY_LAST_MATCHED_NEXT,
+};
+
+#define REGSET_UNSET		((unsigned)-1)
+
+/* Go through `regset' and set submatch data for submatches that are
+   using this tag. */
+static void
+tre_purge_regset(unsigned *regset, tre_tnfa_t *tnfa, int tag)
+{
+  int i;
+
+  for (i = 0; regset[i] != REGSET_UNSET; i++)
+    {
+      int id = regset[i] / 2;
+      int start = !(regset[i] % 2);
+      if (id >= SUBMATCH_ID_INVISIBLE_START)
+	continue;
+      DPRINT(("  Using tag %d for %s offset of "
+	      "submatch %d\n", tag,
+	      start ? "start" : "end", id));
+      if (start)
+	tnfa->submatch_data[id].so_tag = tag;
+      else
+	tnfa->submatch_data[id].eo_tag = tag;
+    }
+  regset[0] = -1;
+}
+
+#define REGSET_HAS_STARTS	0x1
+#define REGSET_HAS_ENDS		0x2
+
+/* Adds tags to appropriate locations in the parse tree in `tree', so that
+   subexpressions marked for submatch addressing can be traced. */
+static reg_errcode_t
+tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
+	     tre_tnfa_t *tnfa)
+{
+  reg_errcode_t status = REG_OK;
+  tre_addtags_symbol_t symbol;
+  tre_ast_node_t *node = tree; /* Tree node we are currently looking at. */
+  int bottom = tre_stack_num_objects(stack);
+  /* True for first pass (counting number of needed tags) */
+  int first_pass = (mem == NULL || tnfa == NULL);
+  unsigned *regset, *orig_regset;
+  int regset_contains = 0;
+  int num_tags = 0; /* Total number of tags. */
+  int num_minimals = 0;	 /* Number of special minimal tags. */
+  int tag = 0;	    /* The tag that is to be added next. */
+  int next_tag = 1; /* Next tag to use after this one. */
+  int minimal_tag = -1; /* Tag that marks the beginning of a minimal match. */
+  int *reorder_tags = NULL; /* Tag reorder array: a pair for each reorder,
+		             * the first is the tag to reorder, the second
+		             * is the tag after which the first is reordered */
+  int *rtp;		    /* Pointer used to fill in reorder_tags and
+			     * tag_order */
+  int *to_reorder;          /* Transform array converting sequential order to
+		             * that specified by reorder_tags */
+  int id;
+
+  tre_tag_direction_t direction = TRE_TAG_LEFT_MAXIMIZE;
+  if (!first_pass)
+    {
+      DPRINT(("Initializing direction to %s\n", tag_dir_str[direction]));
+      tnfa->end_tag = 0;
+      tnfa->minimal_tags[0] = -1;
+    }
+
+  regset = malloc(sizeof(*regset) * ((tnfa->num_submatches
+		   + tnfa->num_submatches_invisible + 1) * 2));
+  if (regset == NULL)
+    {
+      status = REG_ESPACE;
+      goto error_regset;
+    }
+  regset[0] = REGSET_UNSET;
+  orig_regset = regset;
+
+  if (!first_pass)
+    {
+      /* Allocate all memory for reorder_tags, tag_order, to_seq_order and
+       * to_reorder in one batch (assuming all are the same type) */
+      rtp = reorder_tags = malloc(sizeof(*reorder_tags) *
+				   ((2 * tnfa->num_reorder_tags + 1) +
+				   tnfa->num_tags));
+      if (reorder_tags == NULL)
+	{
+	  status = REG_ESPACE;
+	  goto error_reorder_tags;
+	}
+      to_reorder = reorder_tags + (2 * tnfa->num_reorder_tags + 1);
+    }
+
+  STACK_PUSH(stack, voidptr, node);
+  STACK_PUSH(stack, int, ADDTAGS_RECURSE);
+
+  while (tre_stack_num_objects(stack) > bottom)
+    {
+      if (status != REG_OK)
+	break;
+
+      symbol = (tre_addtags_symbol_t)tre_stack_pop_int(stack);
+      switch (symbol)
+	{
+	  int top_union;
+
+	case ADDTAGS_SET_SUBMATCH_END:
+	  {
+	    int i;
+
+	    id = tre_stack_pop_int(stack);
+	    node = tre_stack_pop_voidptr(stack);
+	    /* Add end of this submatch to regset. */
+	    for (i = 0; regset[i] != REGSET_UNSET; i++);
+	    regset[i] = id * 2 + 1;
+	    regset[i + 1] = -1;
+	    regset_contains |= REGSET_HAS_ENDS;
+
+	    /* Always put a tag after a minimal iterator. */
+	    if (minimal_tag >= 0)
+	      {
+		if (first_pass)
+		  {
+		    node->num_tags++;
+		    DPRINT(("  ADDTAGS_SET_SUBMATCH_END: node->num_tags = %d\n",
+			    node->num_tags));
+		  }
+		else
+		  {
+		    int i;
+		    status = tre_merge_branches(mem, node, NULL, tag,
+						tnfa->num_tags);
+		    if (status != REG_OK)
+		      break;
+		    status = tre_add_tag_right(mem, node, tag);
+		    if (status != REG_OK)
+		      break;
+		    tnfa->tag_directions[tag] = TRE_TAG_MINIMIZE;
+		    DPRINT(("Setting t%d direction to %s\n", tag,
+			    tag_dir_str[tnfa->tag_directions[tag]]));
+		    DPRINT(("Minimal %d, %d\n", minimal_tag, tag));
+		    for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+		    tnfa->minimal_tags[i] = tag;
+		    tnfa->minimal_tags[i + 1] = minimal_tag;
+		    tnfa->minimal_tags[i + 2] = -1;
+
+		    DPRINT(("  Minimal end: t%d reordered to "
+			    "after t%d\n", tag, minimal_tag));
+		    /* Append to tag_order, move "tag" after
+		     * "minimal_tag" */
+		    *rtp++ = tag;
+		    *rtp++ = minimal_tag;
+
+		    num_minimals++;
+		    tre_purge_regset(regset, tnfa, tag);
+		  }
+
+		minimal_tag = -1;
+		DPRINT(("  ADDTAGS_SET_SUBMATCH_END num_tags++ tag=%d\n", tag));
+		regset[0] = REGSET_UNSET;
+		regset_contains = 0;
+		tag = next_tag;
+		num_tags++;
+		next_tag++;
+	      }
+	    break;
+	  }
+
+	case ADDTAGS_RECURSE_NOT_TOP_UNION:
+	  /* Like ADDTAGS_RECURSE, except that top_union is set to zero,
+	   * indicating that if a union is being processed, it is not the
+	   * top-most of a series */
+	  top_union = 0;
+	  goto do_addtags_recurse;
+
+	case ADDTAGS_RECURSE:
+	  /* Setting top_union to 1 means that if a union is begin processed,
+	   * it is the top-most of a series, and should recurse through the
+	   * series to set the left_tag and right_tag values */
+	  top_union = 1;
+
+do_addtags_recurse:
+	  node = tre_stack_pop_voidptr(stack);
+
+	  id = node->submatch_id;
+	  if (id >= 0)
+	    {
+	      int i;
+
+	      /* Add start of this submatch to regset. */
+	      for (i = 0; regset[i] != REGSET_UNSET; i++);
+	      regset[i] = id * 2;
+	      regset[i + 1] = -1;
+	      regset_contains |= REGSET_HAS_STARTS;
+
+	      /* Add end of this submatch to regset after processing this
+		 node. */
+	      STACK_PUSH(stack, voidptr, node);
+	      STACK_PUSHX(stack, int, id);
+	      STACK_PUSHX(stack, int, ADDTAGS_SET_SUBMATCH_END);
+	    }
+
+	  switch (node->type)
+	    {
+	    case LITERAL:
+	      {
+		tre_literal_t *lit = node->obj;
+
+		if (!IS_SPECIAL(lit) || IS_BACKREF(lit) || IS_EMPTY(lit) || IS_ASSERTION(lit))
+		  {
+		    DPRINT(("Literal %d-%d\n",
+			    (int)lit->code_min, (int)lit->code_max));
+		    if (regset_contains)
+		      {
+			/* Regset is not empty, so add a tag before the
+			   literal or backref. */
+			if (first_pass)
+			  {
+			    DPRINT(("  ADDTAGS_RECURSE:LITERAL node->num_tags = 1\n"));
+			    node->num_tags = 1;
+			  }
+			else
+			  {
+			    status = tre_merge_branches(mem, node, NULL, tag,
+							tnfa->num_tags);
+			    if (status != REG_OK)
+			      break;
+			    status = tre_add_tag_left(mem, node, tag);
+			    if (status != REG_OK)
+			      break;
+			    if (regset_contains == REGSET_HAS_STARTS)
+			      tnfa->tag_directions[tag] = TRE_TAG_LEFT_MAXIMIZE;
+			    else
+			      tnfa->tag_directions[tag] = direction;
+			    DPRINT(("Setting t%d direction to %s\n", tag,
+				    tag_dir_str[tnfa->tag_directions[tag]]));
+			    tre_purge_regset(regset, tnfa, tag);
+
+			    if (IS_BACKREF(lit))
+			      {
+				int b = lit->code_max;
+				int t = tnfa->submatch_data[b].so_tag;
+				/* Fail if the referenced submatch hasn't been
+				 * completed yet */
+				if (tnfa->submatch_data[b].eo_tag < 0)
+				  {
+				    status = REG_ESUBREG;
+				    break;
+				  }
+				if (t < tag)
+				  {
+				    DPRINT(("  Backref %d start: "
+					    "t%d reordered to before t%d\n",
+					    b, tag, t));
+				    if(t > 0)
+				      t--;
+				    /* Append to tag_order, move "tag" after
+				     * "t" */
+				    *rtp++ = tag;
+				    *rtp++ = t;
+				  }
+#if TRE_DEBUG
+				else
+				  DPRINT(("  Backref %d start: "
+					  "(t%d already before t%d)\n",
+					    b, tag, t));
+#endif /* TRE_DEBUG */
+			      }
+			  }
+
+			DPRINT(("  ADDTAGS_RECURSE:LITERAL num_tags++ tag=%d\n",
+				tag));
+			regset[0] = REGSET_UNSET;
+			regset_contains = 0;
+			tag = next_tag;
+			num_tags++;
+			next_tag++;
+		      }
+		  }
+		else
+		  {
+		    assert(!IS_TAG(lit));
+		  }
+		break;
+	      }
+	    case CATENATION:
+	      {
+		tre_catenation_t *cat = node->obj;
+		tre_ast_node_t *left = cat->left;
+		tre_ast_node_t *right = cat->right;
+		int reserved_tag = -1;
+		DPRINT(("Catenation, next_tag = %d\n", next_tag));
+
+		/* After processing right child. */
+		STACK_PUSHX(stack, voidptr, node);
+		STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_RIGHT);
+
+		/* Process right child. */
+		STACK_PUSHX(stack, voidptr, right);
+		STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+		/* After processing left child. */
+		STACK_PUSHX(stack, int, next_tag + left->num_tags);
+		DPRINT(("  Pushing %d for after left\n",
+			next_tag + left->num_tags));
+		if (left->num_tags > 0 && right->num_tags > 0)
+		  {
+		    /* Reserve the next tag to the right child. */
+		    DPRINT(("  ADDTAGS_RECURSE:CATENATION num_tags++ "
+			    "Reserving next_tag %d to right child\n",
+			    next_tag));
+		    reserved_tag = next_tag;
+		    next_tag++;
+		  }
+		STACK_PUSHX(stack, int, reserved_tag);
+		STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_LEFT);
+
+		/* Process left child. */
+		STACK_PUSHX(stack, voidptr, left);
+		STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+		}
+	      break;
+	    case ITERATION:
+	      {
+		tre_iteration_t *iter = node->obj;
+		DPRINT(("Iteration\n"));
+
+		if (first_pass)
+		  STACK_PUSHX(stack, int, regset_contains != 0);
+		STACK_PUSHX(stack, int, tag);
+		STACK_PUSHX(stack, voidptr, node);
+		STACK_PUSHX(stack, int, ADDTAGS_AFTER_ITERATION);
+
+		STACK_PUSHX(stack, voidptr, iter->arg);
+		STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+		/* Regset is not empty, so add a tag here (this always happens
+		   because iterators always get submatch id, even if in the
+		   invisible range) */
+		if (regset_contains)
+		  {
+		    if (!first_pass)
+		      {
+			status = tre_merge_branches(mem, node, NULL, tag,
+						    tnfa->num_tags);
+			if (status != REG_OK)
+			  break;
+			status = tre_add_tag_left(mem, node, tag);
+			if (status != REG_OK)
+			  break;
+			if (regset_contains == REGSET_HAS_STARTS && tag != 0)
+			  tnfa->tag_directions[tag] = iter->minimal ?
+						      TRE_TAG_MINIMIZE :
+						      TRE_TAG_LEFT_MAXIMIZE;
+			else
+			  tnfa->tag_directions[tag] = direction;
+			DPRINT(("Setting t%d direction to %s\n", tag,
+				tag_dir_str[tnfa->tag_directions[tag]]));
+			tre_purge_regset(regset, tnfa, tag);
+		      }
+
+		    DPRINT(("  ADDTAGS_RECURSE:ITERATION num_tags++ tag=%d\n",
+			    tag));
+		    regset[0] = REGSET_UNSET;
+		    regset_contains = 0;
+		    tag = next_tag;
+		    num_tags++;
+		    next_tag++;
+		  }
+		direction = TRE_TAG_LEFT_MAXIMIZE;
+		DPRINT(("  Setting direction to %s\n", tag_dir_str[direction]));
+	      }
+	      break;
+	    case UNION:
+	      {
+		tre_union_t *uni;
+		tre_ast_node_t *left;
+		tre_ast_node_t *right;
+		int front_tag = -1;
+
+		DPRINT(("Union\n"));
+
+		if (regset_contains)
+		  {
+		    DPRINT(("  UNION num_tags++ tag=%d\n", tag));
+		    front_tag = tag;
+		    tag = next_tag;
+		    num_tags++;
+		    next_tag++;
+		  }
+
+		/* For the top union, walk the tree of consecutive unions,
+		 * setting the left_tag and right_tag values in increasing
+		 * order (left to right priority) */
+		if (top_union &&
+		    (node->num_submatches -
+		    (node->submatch_id >= 0 &&
+		    node->submatch_id < SUBMATCH_ID_INVISIBLE_START)) > 0)
+		  {
+		    tre_ast_node_t *n;
+		    int last = tre_stack_num_objects(stack);
+
+		    STACK_PUSH(stack, voidptr, node);
+		    STACK_PUSH(stack, int, ADDTAGS_UNION_RECURSE);
+
+		    while (tre_stack_num_objects(stack) > last)
+		      {
+			symbol = (tre_addtags_symbol_t)tre_stack_pop_int(stack);
+			switch (symbol)
+			  {
+			  case ADDTAGS_UNION_RECURSE:
+			    n = tre_stack_pop_voidptr(stack);
+			    uni = n->obj;
+			    left = uni->left;
+
+			    /* Since the top union has num_submatches > 0,
+			     * we set all the consecutive union's
+			     * make_branches to 1 to force the generation
+			     * of end tags for each union branch. */
+			    n->make_branches = 1;
+
+			    STACK_PUSH(stack, voidptr, n);
+			    STACK_PUSH(stack, int,
+				       ADDTAGS_UNION_RIGHT_RECURSE);
+
+			    if (left->type == UNION)
+			      {
+				STACK_PUSH(stack, voidptr, left);
+				STACK_PUSH(stack, int,
+					   ADDTAGS_UNION_RECURSE);
+			      }
+			    else
+			      {
+				DPRINT(("  ADDTAGS_UNION_RECURSE "
+					"num_tags++ tag=%d\n", tag));
+				uni->left_tag = tag;
+				tag = next_tag;
+				num_tags++;
+				next_tag++;
+			      }
+			    break;
+
+			  case ADDTAGS_UNION_RIGHT_RECURSE:
+			    n = tre_stack_pop_voidptr(stack);
+			    uni = n->obj;
+			    right = uni->right;
+
+			    if (right->type == UNION)
+			      {
+				STACK_PUSH(stack, voidptr, right);
+				STACK_PUSH(stack, int,
+					   ADDTAGS_UNION_RECURSE);
+			      }
+			    else
+			      {
+				DPRINT(("  ADDTAGS_UNION_RIGHT_RECURSE "
+					"num_tags++ tag=%d\n", tag));
+				uni->right_tag = tag;
+				tag = next_tag;
+				num_tags++;
+				next_tag++;
+			      }
+
+			    break;
+
+			  default:
+			    assert(0);
+			    break;
+
+			  } /* end switch(symbol) */
+		      } /* end while(tre_stack_num_objects(stack) > last */
+		    if (!first_pass)
+		      {
+			STACK_PUSHX(stack, int, front_tag);
+			STACK_PUSHX(stack, voidptr, node);
+			STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_TOP);
+		      }
+		  } /* end if (top_union && ...) */
+
+		uni = node->obj;
+		left = uni->left;
+		right = uni->right;
+
+		/* After processing right child. */
+		STACK_PUSHX(stack, voidptr, regset);
+		STACK_PUSHX(stack, int, regset_contains != 0);
+		STACK_PUSHX(stack, voidptr, node);
+		STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_RIGHT);
+
+		/* Process right child. */
+		STACK_PUSHX(stack, voidptr, right);
+		STACK_PUSHX(stack, int, ADDTAGS_RECURSE_NOT_TOP_UNION);
+
+		/* After processing left child. */
+		STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_LEFT);
+
+		/* Process left child. */
+		STACK_PUSHX(stack, voidptr, left);
+		STACK_PUSHX(stack, int, ADDTAGS_RECURSE_NOT_TOP_UNION);
+
+		/* Regset is not empty, so add a tag here. */
+		if (regset_contains)
+		  {
+		    if (!first_pass)
+		      {
+			status = tre_merge_branches(mem, node, NULL, front_tag,
+						    tnfa->num_tags);
+			if (status != REG_OK)
+			  break;
+			status = tre_add_tag_left(mem, node, front_tag);
+			if (status != REG_OK)
+			  break;
+			if (regset_contains == REGSET_HAS_STARTS)
+			  tnfa->tag_directions[front_tag] = TRE_TAG_LEFT_MAXIMIZE;
+			else
+			  tnfa->tag_directions[front_tag] = direction;
+			DPRINT(("Setting t%d direction to %s\n", front_tag,
+				tag_dir_str[tnfa->tag_directions[front_tag]]));
+			tre_purge_regset(regset, tnfa, front_tag);
+		      }
+
+		    regset[0] = REGSET_UNSET;
+		    regset_contains = 0;
+		  }
+
+		break;
+	      }
+	    } /* end switch (node->type) */
+
+	  break; /* end case: ADDTAGS_RECURSE */
+
+	case ADDTAGS_AFTER_ITERATION:
+	  {
+	    tre_iteration_t *iter;
+	    tre_ast_node_t *orig;
+	    int enter_tag;
+
+	    node = tre_stack_pop_voidptr(stack);
+	    orig = node->original ? node->original : node;
+	    iter = (tre_iteration_t *)orig->obj;
+	    enter_tag = tre_stack_pop_int(stack);
+	    if (iter->minimal)
+	      minimal_tag = enter_tag;
+
+	    DPRINT(("After iteration\n"));
+	    if (first_pass)
+	      {
+		node->num_tags = iter->arg->num_tags + tre_stack_pop_int(stack);
+		DPRINT(("  ADDTAGS_AFTER_ITERATION: node->num_tags = %d\n",
+			node->num_tags));
+	      }
+	    else
+	      {
+		/* node->last_matched_branch will have the start tag (the tag
+		   just *before* the iteration).  iter->arg->last_matched_branch
+		   will have the tag(s) inside the iteration, the ones that
+		   may need to be reset if the iteration doesn't match.  So
+		   before we merge iter->arg into node, we need to set up
+		   a new tre_last_matched_t and tre_last_matched_branch_t,
+		   using any of the inside tags as cmp_tag (we choose the first
+		   tag found by bit_ffs).  If there are no inside tags, we
+		   don't bother creating the extra structures. */
+		tre_last_matched_branch_pre_t *b =
+						iter->arg->last_matched_branch;
+
+		if (b && b->n_tags > 0)
+		  {
+		    tre_last_matched_pre_t *u;
+
+		    bit_ffs(b->tags, num_tags, &b->cmp_tag);
+		    DPRINT(("  ADDTAGS_AFTER_ITERATION: n_tags=%d "
+			    "cmp_tag = %d\n", b->n_tags, b->cmp_tag));
+
+		    u = tre_mem_calloc(mem, sizeof(tre_last_matched_pre_t) +
+				       sizeof(tre_last_matched_branch_pre_t)
+				       + bitstr_size(tnfa->num_tags));
+		    if (!u)
+		      {
+			status = REG_ESPACE;
+			break;
+		      }
+		    u->branches = b;
+		    u->n_branches = 1;
+		    u->start_tag = b->cmp_tag;
+		    u->tot_branches = b->tot_branches;
+		    u->tot_last_matched = 1 + b->tot_last_matched;
+		    u->tot_tags = b->tot_tags;
+
+		    b = (tre_last_matched_branch_pre_t *)(u + 1);
+		    b->last_matched = u;
+		    b->n_last_matched = 1;
+		    b->tot_branches = 1 + u->tot_branches;
+		    b->tot_last_matched = u->tot_last_matched;
+		    b->tot_tags = u->tot_tags;
+
+		    iter->arg->last_matched_branch = b;
+		  }
+		status = tre_merge_branches(mem, node, iter->arg, 0,
+					    tnfa->num_tags);
+		if (status != REG_OK)
+		  break;
+
+		if (iter->minimal)
+		  {
+		    /* Add a union with a left EMPTY literal and the right
+		       being iter->arg.  This should force the tags inside
+		       the minimal iteration to prefer being unset */
+		    if (iter->min == 0 && iter->max <= 1)
+		      {
+			tre_ast_node_t *u, *e;
+
+			e = tre_ast_new_literal(mem, EMPTY, -1, -1);
+			if (e == NULL)
+			  {
+			    status = REG_ESPACE;
+			    break;
+			  }
+			u = tre_ast_new_union(mem, e, iter->arg);
+			if (u == NULL)
+			  {
+			    status = REG_ESPACE;
+			    break;
+			  }
+			iter->arg = u;
+		      }
+
+		    direction = TRE_TAG_MINIMIZE;
+		  }
+		else
+		  direction = TRE_TAG_MAXIMIZE;
+		DPRINT(("  Setting direction to %s\n", tag_dir_str[direction]));
+	      }
+	    break;
+	  }
+
+	case ADDTAGS_AFTER_CAT_LEFT:
+	  {
+	    int new_tag = tre_stack_pop_int(stack);
+	    next_tag = tre_stack_pop_int(stack);
+	    DPRINT(("After cat left, tag = %d, next_tag = %d\n",
+		    tag, next_tag));
+	    if (new_tag >= 0)
+	      {
+		DPRINT(("  Setting tag to %d\n", new_tag));
+		tag = new_tag;
+	      }
+	    break;
+	  }
+
+	case ADDTAGS_AFTER_CAT_RIGHT:
+	  {
+	    tre_catenation_t *cat;
+
+	    DPRINT(("After cat right\n"));
+	    node = tre_stack_pop_voidptr(stack);
+	    cat = node->obj;
+	    if (first_pass)
+	      {
+		node->num_tags = cat->left->num_tags + cat->right->num_tags;
+		DPRINT(("  ADDTAGS_AFTER_CAT_RIGHT: node->num_tags = %d\n",
+			node->num_tags));
+	      }
+	    else
+	      {
+		status = tre_merge_branches(mem, cat->left, cat->right, 0,
+					    tnfa->num_tags);
+		if (status != REG_OK)
+		  break;
+		status = tre_merge_branches(mem, node, cat->left, 0,
+					    tnfa->num_tags);
+	      }
+	    break;
+	  }
+
+	case ADDTAGS_AFTER_UNION_LEFT:
+	  DPRINT(("After union left\n"));
+	  /* Lift the bottom of the `regset' array so that when processing
+	     the right operand the items currently in the array are
+	     invisible.	 The original bottom was saved at ADDTAGS_UNION and
+	     will be restored at ADDTAGS_AFTER_UNION_RIGHT below. */
+	  while (*regset != REGSET_UNSET)
+	    regset++;
+	  regset_contains = 0;
+	  break;
+
+	case ADDTAGS_AFTER_UNION_RIGHT:
+	  {
+	    int added_tags;
+	    tre_ast_node_t *orig;
+	    tre_union_t *uni;
+	    /* Note: node may not be a UNION, but a CATENATION with a left
+	     * tag.  So that is why we pass the original node->obj on the
+	     * stack, to get the union's true values. */
+
+	    DPRINT(("After union right\n"));
+	    node = tre_stack_pop_voidptr(stack);
+	    orig = node->original ? node->original : node;
+	    uni = (tre_union_t *)orig->obj;
+	    added_tags = tre_stack_pop_int(stack);
+	    if (first_pass)
+	      {
+		node->num_tags = uni->left->num_tags + uni->right->num_tags
+				 + added_tags;
+		if (uni->left_tag > 0)
+		  node->num_tags++;
+		if (uni->right_tag > 0)
+		  node->num_tags++;
+		DPRINT(("  ADDTAGS_AFTER_UNION_RIGHT: node->num_tags = %d\n",
+			node->num_tags));
+	      }
+	    regset = tre_stack_pop_voidptr(stack);
+
+	    /* Add tags after both children, the left child gets a smaller
+	       tag than the right child.  This guarantees that we prefer
+	       the left child over the right child. */
+	    /* XXX - This is not always necessary (if the children have
+	       tags which must be seen for every match of that child). */
+	    if (!first_pass && node->make_branches)
+	      {
+		tre_last_matched_branch_pre_t *lb =
+		  uni->left->last_matched_branch;
+		tre_last_matched_branch_pre_t *rb =
+		  uni->right->last_matched_branch;
+		tre_last_matched_pre_t *lu =
+		  uni->left->last_matched_in_progress;
+		tre_last_matched_pre_t *ru =
+		  uni->right->last_matched_in_progress;
+		tre_last_matched_pre_t *u;
+		/* We don't need to call tre_merge_branches because these
+		 * tags don't participate in submatch ranges, so don't need
+		 * to be recorded.  But we do set the cmp_tag entry of the
+		 * tre_last_matched_branch_pre_t, so we might call
+		 * tre_merge_branches if we need to create an empty
+		 * tre_last_matched_branch_pre_t. */
+		if (uni->left_tag > 0)
+		  {
+		    DPRINT(("Setting t%d direction to maximize\n",
+			    uni->left_tag));
+		    status = tre_add_tag_right(mem, uni->left, uni->left_tag);
+		    if (status != REG_OK)
+		      break;
+		    tnfa->tag_directions[uni->left_tag] = TRE_TAG_MAXIMIZE;
+		    if (!lb)
+		      {
+			status = tre_merge_branches(mem, uni->left, NULL, -1,
+						    tnfa->num_tags);
+			if (status != REG_OK)
+			  break;
+			lb = uni->left->last_matched_branch;
+		      }
+		    lb->cmp_tag = uni->left_tag;
+		  }
+		if (uni->right_tag > 0)
+		  {
+		    DPRINT(("Setting t%d direction to maximize\n",
+			    uni->right_tag));
+		    status = tre_add_tag_right(mem, uni->right, uni->right_tag);
+		    if (status != REG_OK)
+		      break;
+		    tnfa->tag_directions[uni->right_tag] = TRE_TAG_MAXIMIZE;
+		    if (!rb)
+		      {
+			status = tre_merge_branches(mem, uni->right, NULL, -1,
+						    tnfa->num_tags);
+			if (status != REG_OK)
+			  break;
+			rb = uni->right->last_matched_branch;
+		      }
+		    rb->cmp_tag = uni->right_tag;
+		  }
+		/* Now merge the tre_last_matched_branch_pre_t into a
+		   tre_last_matched_pre_t */
+		if (lu == NULL)
+		  {
+		    if (ru == NULL)
+		      {
+			/* Create a new tre_last_matched_pre_t */
+			u = tre_mem_calloc(mem, sizeof(tre_last_matched_pre_t));
+			if (!u)
+			  {
+			    status = REG_ESPACE;
+			    break;
+			  }
+			u->tot_last_matched = 1;
+
+			if (lb)
+			  {
+			    u->branches = lb;
+			    u->n_branches = 1;
+			    u->tot_branches += lb->tot_branches;
+			    u->tot_last_matched += lb->tot_last_matched;
+			    u->tot_tags += lb->tot_tags;
+			    if (rb)
+			      {
+				lb->next = rb;
+				u->n_branches++;
+				u->tot_branches += rb->tot_branches;
+				u->tot_last_matched += rb->tot_last_matched;
+				u->tot_tags += rb->tot_tags;
+			      }
+			  }
+			else if (rb)
+			  {
+			    u->branches = rb;
+			    u->n_branches = 1;
+			    u->tot_branches += rb->tot_branches;
+			    u->tot_last_matched += rb->tot_last_matched;
+			    u->tot_tags += rb->tot_tags;
+			  }
+		      }
+		    else
+		      {
+			/* Use ru, and add lb */
+			u = ru;
+			if (lb)
+			  {
+			    lb->next = u->branches;
+			    u->branches = lb;
+			    u->n_branches++;
+			    u->tot_branches += lb->tot_branches;
+			    u->tot_last_matched += lb->tot_last_matched;
+			    u->tot_tags += lb->tot_tags;
+			  }
+		      }
+		  }
+		else if (ru == NULL)
+		  {
+		    /* Use lu, and add rb */
+		    u = lu;
+		    if (rb)
+		      {
+			rb->next = u->branches;
+			u->branches = rb;
+			u->n_branches++;
+			u->tot_branches += rb->tot_branches;
+			u->tot_last_matched += rb->tot_last_matched;
+			u->tot_tags += rb->tot_tags;
+		      }
+		  }
+		else
+		  {
+		    /* Merge lu and ru into lu */
+		    if (lu->branches)
+		      {
+			if (ru->branches)
+			  {
+			    tre_last_matched_branch_pre_t *b = lu->branches;
+			    while (b->next) b = b->next;
+			    b->next = ru->branches;
+			    lu->n_branches += ru->n_branches;
+			  }
+		      }
+		    else if (ru->branches)
+		      {
+			lu->branches = ru->branches;
+			lu->n_branches = ru->n_branches;
+		      }
+		    lu->tot_branches += ru->tot_branches;
+		    lu->tot_last_matched += ru->tot_last_matched - 1;
+		    lu->tot_tags += ru->tot_tags;
+		    u = lu;
+		  }
+		node->last_matched_in_progress = u;
+	      }
+	    direction = TRE_TAG_MAXIMIZE;
+	    break;
+	  }
+
+	case ADDTAGS_AFTER_UNION_TOP: /* only called when not first_pass */
+	  {
+	    tre_last_matched_branch_pre_t *b;
+	    tre_last_matched_pre_t *u;
+	    int start_tag;
+
+	    DPRINT(("After union top\n"));
+	    node = tre_stack_pop_voidptr(stack);
+	    start_tag = tre_stack_pop_int(stack);
+	    b = tre_mem_calloc(mem, sizeof(tre_last_matched_branch_pre_t)
+			       + bitstr_size(tnfa->num_tags));
+	    if (!b)
+	      {
+		status = REG_ESPACE;
+		break;
+	      }
+
+	    u = node->last_matched_in_progress;
+	    u->start_tag = start_tag;
+	    b->tot_branches = 1 + u->tot_branches;
+	    b->tot_last_matched = u->tot_last_matched;
+	    b->tot_tags = u->tot_tags;
+	    b->last_matched = u;
+	    b->n_last_matched = 1;
+	    node->last_matched_branch = b;
+	    node->last_matched_in_progress = NULL;
+	    break;
+	  }
+
+	default:
+	  assert(0);
+	  break;
+
+	} /* end switch(symbol) */
+    } /* end while(tre_stack_num_objects(stack) > bottom) */
+
+  if (status != REG_OK)
+    {
+      DPRINT(("Error during %s pass\n", first_pass ? "first" : "second"));
+      goto error_post_compile;
+    }
+
+  if (!first_pass)
+    {
+      int i;
+      if (num_tags != tnfa->num_tags)
+	{
+	  DPRINT(("num_tags(%d) != tnfa->num_tags(%d)\n", num_tags,
+		  tnfa->num_tags));
+	  status = REG_BADPAT;
+	  goto error_post_compile;
+	}
+
+      tre_purge_regset(regset, tnfa, tag);
+      DPRINT(("Setting t%d to %s\n", num_tags,
+	      tag_dir_str[direction]));
+      tnfa->tag_directions[num_tags] = direction;
+
+      if (rtp > reorder_tags + 2 * tnfa->num_reorder_tags)
+	{
+	  DPRINT(("Processed %d reorder tags instead of %d\n",
+		  (int)(rtp - reorder_tags) / 2, tnfa->num_reorder_tags));
+	  status = REG_BADPAT;
+	  goto error_post_compile;
+	}
+    *rtp = -1;
+#if TRE_DEBUG
+      if (reorder_tags[0] >= 0)
+	{
+	  DPRINT(("reorder_tags:\n"));
+	  for (rtp = reorder_tags; *rtp >= 0;)
+	    {
+	      DPRINT(("%d after ", *rtp++));
+	      DPRINT(("%d\n", *rtp++));
+	    }
+	}
+	else
+	  DPRINT(("No reorder_tags\n"));
+#endif /* TRE_DEBUG */
+
+      /* Initialize to_reorder */
+      for (i = 0; i < num_tags; i++)
+	to_reorder[i] = i;
+      /* Use to_seq_order to convert reorder_tags values, and use those to
+       * reorder to_reorder */
+      for (rtp = reorder_tags; *rtp >= 0;)
+	{
+	  int j, high, low;
+	  int ti = *rtp++;
+
+	  /* Skip reordering the final tag */
+	  if (ti >= num_tags)
+	    {
+	      DPRINT(("Skipping reorder of %d\n", ti));
+	      rtp++;
+	      continue;
+	    }
+	  /* The number of the tag to reorder */
+	  high = to_reorder[ti];
+	  /* Reorder after this tag */
+	  low = to_reorder[*rtp++];
+
+	  DPRINT(("ti=%d high=%d low=%d\n", ti, high, low));
+	  if (low > high)
+	    {
+	      DPRINT(("Tag %d already before %d\n", high, low));
+	      continue;
+	    }
+	  for (j = 0; j < num_tags; j++)
+	    if (to_reorder[j] > low && to_reorder[j] < high)
+	      to_reorder[j]++;
+	  to_reorder[ti] = low + 1;
+#ifdef TRE_DEBUG
+	  DPRINT(("to_reorder=("));
+	  for (j = 0; j < num_tags; j++)
+	    {
+	      DPRINT(("%d", to_reorder[j]));
+	      if (j < num_tags - 1)
+		DPRINT((","));
+	    }
+	  DPRINT((")\n"));
+#endif /* TRE_DEBUG */
+	}
+      /* Determine if reordering in really necessary */
+      {
+	int need_reorder = 0;
+	for (i = 0; i < num_tags; i++)
+	  if(to_reorder[i] != i)
+	    {
+	      need_reorder = 1;
+	      break;
+	    }
+	/* If need_reorder is not set, free reorder_tags, and set to NULL,
+	 * indicating no reordering is needed */
+	if (!need_reorder)
+	  {
+	    DPRINT(("Don't need to reorder\n"));
+	    free(reorder_tags);
+	    reorder_tags = NULL;
+	  }
+      }
+    }
+
+  if (reorder_tags)
+    {
+      int i;
+      tre_tag_direction_t *new_tag_directions;
+#if TRE_DEBUG
+      DPRINT(("to_reorder:"));
+      for (i = 0; i < num_tags; i++)
+	DPRINT((" %d->%d", i, to_reorder[i]));
+      DPRINT(("\n"));
+#endif /* TRE_DEBUG */
+
+      DPRINT(("Reordering submatch_data\n"));
+      for (i = 0; i < tnfa->num_submatches; i++)
+	{
+#if TRE_DEBUG
+	  int so = tnfa->submatch_data[i].so_tag;
+	  int eo = tnfa->submatch_data[i].eo_tag;
+#endif /* TRE_DEBUG */
+	  tnfa->submatch_data[i].so_tag =
+	    to_reorder[tnfa->submatch_data[i].so_tag];
+	  tnfa->submatch_data[i].eo_tag =
+	    tnfa->submatch_data[i].eo_tag < num_tags ?
+	    to_reorder[tnfa->submatch_data[i].eo_tag] :
+	    tnfa->submatch_data[i].eo_tag;
+	  DPRINT(("pmatch[%d]: {%d, %d}->{%d, %d}\n", i, so, eo,
+		  tnfa->submatch_data[i].so_tag,
+		  tnfa->submatch_data[i].eo_tag));
+	}
+
+      DPRINT(("Reordering tag_directions\n"));
+      /* We only allocate num_tags directions and reorder them.  The
+       * num_tags-th direction (end tag) is left unchanged. */
+      new_tag_directions = malloc(sizeof(*new_tag_directions) * num_tags);
+      if (new_tag_directions == NULL)
+	{
+	  status = REG_ESPACE;
+	  goto error_post_compile;
+	}
+      for (i = 0; i < num_tags; i++)
+	{
+	  new_tag_directions[to_reorder[i]] = tnfa->tag_directions[i];
+	}
+#if TRE_DEBUG
+      for (i = 0; i < num_tags; i++)
+	{
+	  DPRINT(("t%d %s->%s\n", i,
+		  tag_dir_str[tnfa->tag_directions[i]],
+		  tag_dir_str[new_tag_directions[i]]));
+	}
+	DPRINT(("t%d %s->%s\n", num_tags,
+		tag_dir_str[tnfa->tag_directions[num_tags]],
+		tag_dir_str[tnfa->tag_directions[num_tags]]));
+#endif /* TRE_DEBUG */
+      memcpy(tnfa->tag_directions, new_tag_directions, sizeof(*new_tag_directions) * num_tags);
+      free(new_tag_directions);
+
+      DPRINT(("Reordering minimal_tags\n"));
+      for (i = 0; tnfa->minimal_tags[i] >= 0; i++)
+	tnfa->minimal_tags[i] = tnfa->minimal_tags[i] < num_tags ?
+				to_reorder[tnfa->minimal_tags[i]] :
+				tnfa->minimal_tags[i];
+
+      DPRINT(("Reordering AST tags\n"));
+      STACK_PUSH(stack, voidptr, tree);
+      while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
+	{
+	  node = tre_stack_pop_voidptr(stack);
+
+	  switch (node->type)
+	    {
+	    case LITERAL:
+	      {
+		tre_literal_t *lit = (tre_literal_t *)node->obj;
+		if (IS_TAG(lit))
+		  lit->code_max = to_reorder[lit->code_max];
+		break;
+	      }
+
+	    case UNION:
+	      {
+		tre_union_t *uni = (tre_union_t *)node->obj;
+		STACK_PUSHX(stack, voidptr, uni->right);
+		STACK_PUSHX(stack, voidptr, uni->left);
+		break;
+	      }
+
+	    case CATENATION:
+	      {
+		tre_catenation_t *cat = (tre_catenation_t *)node->obj;
+		STACK_PUSHX(stack, voidptr, cat->right);
+		STACK_PUSHX(stack, voidptr, cat->left);
+		break;
+	      }
+
+	    case ITERATION:
+	      {
+		tre_iteration_t *iter = (tre_iteration_t *)node->obj;
+		STACK_PUSHX(stack, voidptr, iter->arg);
+		break;
+	      }
+
+	    default:
+	      assert(0);
+	      break;
+	    }
+	}
+	if (status != REG_OK)
+	  {
+	    DPRINT(("Error while reordering tags\n"));
+	    goto error_post_compile;
+	  }
+    }
+
+  if (!first_pass)
+    {
+      if (tree->last_matched_branch)
+	{
+	  tre_last_matched_branch_t *buf, *b, *bb;
+	  tre_last_matched_branch_pre_t *bp;
+	  tre_last_matched_t *u, *uu;
+	  tre_last_matched_pre_t *up;
+	  int *t;
+	  int i;
+#ifdef TRE_DEBUG
+	  tre_last_matched_branch_t *_b;
+	  tre_last_matched_t *_u;
+	  int *_t;
+
+	  DPRINT(("last_match_branch_pre:\n"));
+	  print_last_match_branch_pre(tree->last_matched_branch, 0, num_tags);
+#endif /* TRE_DEBUG */
+	  buf = (tre_last_matched_branch_t *)calloc(1,
+				     tree->last_matched_branch->tot_branches
+				     * sizeof(tre_last_matched_branch_t) +
+				     tree->last_matched_branch->tot_last_matched
+				     * sizeof(tre_last_matched_t) +
+				     tree->last_matched_branch->tot_tags *
+				     sizeof(int));
+	  if (!buf)
+	    {
+	      status = REG_ESPACE;
+	      goto error_post_compile;
+	    }
+
+	  b = buf;
+	  u = (tre_last_matched_t *)(b +
+	      tree->last_matched_branch->tot_branches);
+	  t = (int *)(u + tree->last_matched_branch->tot_last_matched);
+#ifdef TRE_DEBUG
+	  _b = b;
+	  _u = u;
+	  _t = t;
+#endif /* TRE_DEBUG */
+	  DPRINT(("Copying info_pre to info\n"));
+	  STACK_PUSH(stack, voidptr, tree->last_matched_branch);
+	  STACK_PUSH(stack, int, 1);
+	  STACK_PUSH(stack, int, COPY_LAST_MATCHED_BRANCH);
+
+	  while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
+	    {
+	      switch (tre_stack_pop_int(stack))
+		{
+		case COPY_LAST_MATCHED_BRANCH:
+		  i = tre_stack_pop_int(stack);
+		  /* The tre_last_matched_branch_pre_t * is still on the
+		     stack */
+		  STACK_PUSHX(stack, voidptr, b);
+		  STACK_PUSHX(stack, int, COPY_LAST_MATCHED_BRANCH_NEXT);
+		  b += i;
+		  break;
+
+		case COPY_LAST_MATCHED_BRANCH_NEXT:
+		  bb = tre_stack_pop_voidptr(stack);
+		  bp = tre_stack_pop_voidptr(stack);
+		  bb->n_last_matched = bp->n_last_matched;
+		  bb->cmp_tag = bp->cmp_tag;
+		  if (bp->n_tags > 0)
+		    {
+		      int n;
+		      n = bb->n_tags = bp->n_tags;
+		      bb->tags = t;
+		      for (i = 0; i < num_tags; i++)
+			if (bit_test(bp->tags, i))
+			  {
+			    *t++ = i;
+			    if (--n <= 0)
+			      break;
+			  }
+		    }
+		  if (bp->next)
+		    {
+		      STACK_PUSHX(stack, voidptr, bp->next);
+		      STACK_PUSHX(stack, voidptr, bb + 1);
+		      STACK_PUSHX(stack, int, COPY_LAST_MATCHED_BRANCH_NEXT);
+		    }
+		  if (bp->n_last_matched > 0)
+		    {
+		      bb->last_matched = u;
+		      STACK_PUSHX(stack, voidptr, bp->last_matched);
+		      STACK_PUSHX(stack, int, bp->n_last_matched);
+		      STACK_PUSHX(stack, int, COPY_LAST_MATCHED);
+		    }
+		  break;
+
+		case COPY_LAST_MATCHED:
+		  i = tre_stack_pop_int(stack);
+		  /* The tre_last_matched_pre_t * is still on the stack */
+		  STACK_PUSHX(stack, voidptr, u);
+		  STACK_PUSHX(stack, int, COPY_LAST_MATCHED_NEXT);
+		  u += i;
+		  break;
+
+		case COPY_LAST_MATCHED_NEXT:
+		  uu = tre_stack_pop_voidptr(stack);
+		  up = tre_stack_pop_voidptr(stack);
+		  uu->n_branches = up->n_branches;
+		  uu->branches = b;
+		  uu->start_tag = up->start_tag;
+		  if (up->next)
+		    {
+		      STACK_PUSHX(stack, voidptr, up->next);
+		      STACK_PUSHX(stack, voidptr, uu + 1);
+		      STACK_PUSHX(stack, int, COPY_LAST_MATCHED_NEXT);
+		    }
+		  STACK_PUSHX(stack, voidptr, up->branches);
+		  STACK_PUSHX(stack, int, up->n_branches);
+		  STACK_PUSHX(stack, int, COPY_LAST_MATCHED_BRANCH);
+		  break;
+		}
+	    }
+	  if (status != REG_OK)
+	    goto error_post_compile;
+#ifdef TRE_DEBUG
+	  DPRINT(("last_matched_branch:\n"));
+	  print_last_match_branch(buf, 0);
+	  if (b != _b + tree->last_matched_branch->tot_branches)
+	    DPRINT(("b/%p != _b + tree->last_matched_branch->tot_branches/%p\n",
+		    b, _b + tree->last_matched_branch->tot_branches));
+	  if (u != _u + tree->last_matched_branch->tot_last_matched)
+	    DPRINT(("u/%p != _u + "
+		    "tree->last_matched_branch->tot_last_matched/%p\n",
+		    u, _u + tree->last_matched_branch->tot_last_matched));
+	  if (t != _t + tree->last_matched_branch->tot_tags)
+	    DPRINT(("t/%p != _t + tree->last_matched_branch->tot_tags/%p\n",
+		    t, _t + tree->last_matched_branch->tot_tags));
+#endif /* TRE_DEBUG */
+	  tnfa->last_matched_branch = buf;
+	}
+#ifdef TRE_DEBUG
+      else
+	DPRINT(("No last_match_branch_pre\n"));
+#endif /* TRE_DEBUG */
+    }
+
+  DPRINT(("tre_add_tags: %s complete.  Number of tags %d.\n",
+	  first_pass? "First pass" : "Second pass", num_tags));
+#ifdef TRE_DEBUG
+  tre_ast_print(tree);
+#endif /* TRE_DEBUG */
+  DPRINT(("tre_add_tags: tree->num_tags=%d num_tags=%d\n", tree->num_tags,
+	  num_tags));
+  assert(tree->num_tags == num_tags);
+  tnfa->end_tag = num_tags;
+  tnfa->num_tags = num_tags;
+  tnfa->num_minimals = num_minimals;
+error_post_compile:
+  free(reorder_tags);
+error_reorder_tags:
+  free(orig_regset);
+error_regset:
+  return status;
+}
+
+/*
+  AST to TNFA compilation routines.
+*/
+
+typedef enum {
+  COPY_RECURSE,
+  COPY_SET_RESULT_PTR
+} tre_copyast_symbol_t;
+
+/* Flags for tre_copy_ast(). */
+#define COPY_REMOVE_TAGS	 1
+#define COPY_MAXIMIZE_FIRST_TAG	 2
+
+static reg_errcode_t
+tre_copy_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
+	     int flags, int *pos_add, tre_tag_direction_t *tag_directions,
+	     tre_ast_node_t **copy, int *max_pos)
+{
+  reg_errcode_t status = REG_OK;
+  int bottom = tre_stack_num_objects(stack);
+  int num_copied = 0;
+  int first_tag = 1;
+  tre_ast_node_t **result = copy;
+  tre_copyast_symbol_t symbol;
+
+  STACK_PUSH(stack, voidptr, ast);
+  STACK_PUSH(stack, int, COPY_RECURSE);
+
+  while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
+    {
+      tre_ast_node_t *node;
+      if (status != REG_OK)
+	break;
+
+      symbol = (tre_copyast_symbol_t)tre_stack_pop_int(stack);
+      switch (symbol)
+	{
+	case COPY_SET_RESULT_PTR:
+	  result = tre_stack_pop_voidptr(stack);
+	  break;
+	case COPY_RECURSE:
+	  node = tre_stack_pop_voidptr(stack);
+	  switch (node->type)
+	    {
+	    case LITERAL:
+	      {
+		tre_literal_t *lit = node->obj;
+		int pos = lit->position;
+		int min = lit->code_min;
+		int max = lit->code_max;
+		tre_bracket_match_list_t *list = !IS_SPECIAL(lit) ?
+						  lit->u.bracket_match_list :
+						  NULL;
+		if (!IS_SPECIAL(lit) || IS_BACKREF(lit))
+		  {
+		    /* XXX - e.g. [ab] has only one position but two
+		       nodes, so we are creating holes in the state space
+		       here.  Not fatal, just wastes memory. */
+		    pos += *pos_add;
+		    num_copied++;
+		  }
+		else if (IS_TAG(lit) && (flags & COPY_REMOVE_TAGS))
+		  {
+		    /* Change this tag to empty. */
+		    min = EMPTY;
+		    max = pos = -1;
+		  }
+		else if (IS_TAG(lit) && (flags & COPY_MAXIMIZE_FIRST_TAG)
+			 && first_tag)
+		  {
+		    /* Maximize the first tag. */
+		    if (tag_directions[max] == TRE_TAG_LEFT_MAXIMIZE)
+		      tag_directions[max] = TRE_TAG_MAXIMIZE;
+		    first_tag = 0;
+		  }
+		*result = tre_ast_new_literal(mem, min, max, pos);
+		if (*result == NULL)
+		  status = REG_ESPACE;
+
+		if (pos > *max_pos)
+		  *max_pos = pos;
+
+		if (!IS_SPECIAL(lit))
+		  ((tre_literal_t *)(*result)->obj)->u.bracket_match_list
+		      = list;
+		break;
+	      }
+	    case UNION:
+	      {
+		tre_union_t *uni = node->obj;
+		tre_union_t *tmp;
+		*result = tre_ast_new_union(mem, uni->left, uni->right);
+		if (*result == NULL)
+		  {
+		    status = REG_ESPACE;
+		    break;
+		  }
+		tmp = (*result)->obj;
+		result = &tmp->left;
+		STACK_PUSHX(stack, voidptr, uni->right);
+		STACK_PUSHX(stack, int, COPY_RECURSE);
+		STACK_PUSHX(stack, voidptr, &tmp->right);
+		STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR);
+		STACK_PUSHX(stack, voidptr, uni->left);
+		STACK_PUSHX(stack, int, COPY_RECURSE);
+		break;
+	      }
+	    case CATENATION:
+	      {
+		tre_catenation_t *cat = node->obj;
+		tre_catenation_t *tmp;
+		*result = tre_ast_new_catenation(mem, cat->left, cat->right);
+		if (*result == NULL)
+		  {
+		    status = REG_ESPACE;
+		    break;
+		  }
+		tmp = (*result)->obj;
+		tmp->left = NULL;
+		tmp->right = NULL;
+		result = &tmp->left;
+
+		STACK_PUSHX(stack, voidptr, cat->right);
+		STACK_PUSHX(stack, int, COPY_RECURSE);
+		STACK_PUSHX(stack, voidptr, &tmp->right);
+		STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR);
+		STACK_PUSHX(stack, voidptr, cat->left);
+		STACK_PUSHX(stack, int, COPY_RECURSE);
+		break;
+	      }
+	    case ITERATION:
+	      {
+		tre_iteration_t *iter = node->obj;
+		STACK_PUSHX(stack, voidptr, iter->arg);
+		STACK_PUSHX(stack, int, COPY_RECURSE);
+		*result = tre_ast_new_iter(mem, iter->arg, iter->min,
+					   iter->max, iter->minimal);
+		if (*result == NULL)
+		  {
+		    status = REG_ESPACE;
+		    break;
+		  }
+		iter = (*result)->obj;
+		result = &iter->arg;
+		break;
+	      }
+	    default:
+	      assert(0);
+	      break;
+	    }
+	  break;
+	}
+    }
+  *pos_add += num_copied;
+  return status;
+}
+
+typedef enum {
+  EXPAND_RECURSE,
+  EXPAND_AFTER_ITER
+} tre_expand_ast_symbol_t;
+
+/* Expands each iteration node that has a finite nonzero minimum or maximum
+   iteration count to a catenated sequence of copies of the node. */
+static reg_errcode_t
+tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
+	       int *position, tre_tag_direction_t *tag_directions,
+	       int *max_depth)
+{
+  reg_errcode_t status = REG_OK;
+  int bottom = tre_stack_num_objects(stack);
+  int pos_add = 0;
+  int pos_add_total = 0;
+  int max_pos = 0;
+  int iter_depth = 0;
+
+  STACK_PUSHR(stack, voidptr, ast);
+  STACK_PUSHR(stack, int, EXPAND_RECURSE);
+  while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
+    {
+      tre_ast_node_t *node;
+      tre_expand_ast_symbol_t symbol;
+
+      if (status != REG_OK)
+	break;
+
+      DPRINT(("pos_add %d\n", pos_add));
+
+      symbol = (tre_expand_ast_symbol_t)tre_stack_pop_int(stack);
+      node = tre_stack_pop_voidptr(stack);
+      switch (symbol)
+	{
+	case EXPAND_RECURSE:
+	  switch (node->type)
+	    {
+	    case LITERAL:
+	      {
+		tre_literal_t *lit= node->obj;
+		if (!IS_SPECIAL(lit) || IS_BACKREF(lit))
+		  {
+		    lit->position += pos_add;
+		    if (lit->position > max_pos)
+		      max_pos = lit->position;
+		  }
+		break;
+	      }
+	    case UNION:
+	      {
+		tre_union_t *uni = node->obj;
+		STACK_PUSHX(stack, voidptr, uni->right);
+		STACK_PUSHX(stack, int, EXPAND_RECURSE);
+		STACK_PUSHX(stack, voidptr, uni->left);
+		STACK_PUSHX(stack, int, EXPAND_RECURSE);
+		break;
+	      }
+	    case CATENATION:
+	      {
+		tre_catenation_t *cat = node->obj;
+		STACK_PUSHX(stack, voidptr, cat->right);
+		STACK_PUSHX(stack, int, EXPAND_RECURSE);
+		STACK_PUSHX(stack, voidptr, cat->left);
+		STACK_PUSHX(stack, int, EXPAND_RECURSE);
+		break;
+	      }
+	    case ITERATION:
+	      {
+		tre_iteration_t *iter = node->obj;
+		STACK_PUSHX(stack, int, pos_add);
+		STACK_PUSHX(stack, voidptr, node);
+		STACK_PUSHX(stack, int, EXPAND_AFTER_ITER);
+		STACK_PUSHX(stack, voidptr, iter->arg);
+		STACK_PUSHX(stack, int, EXPAND_RECURSE);
+		/* If we are going to expand this node at EXPAND_AFTER_ITER
+		   then don't increase the `pos' fields of the nodes now, it
+		   will get done when expanding. */
+		if (iter->min > 1 || iter->max > 1)
+		  pos_add = 0;
+		iter_depth++;
+		DPRINT(("iter\n"));
+		break;
+	      }
+	    default:
+	      assert(0);
+	      break;
+	    }
+	  break;
+	case EXPAND_AFTER_ITER:
+	  {
+	    tre_iteration_t *iter = node->obj;
+	    int pos_add_last;
+	    pos_add = tre_stack_pop_int(stack);
+	    pos_add_last = pos_add;
+	    /* Originally (in tre_parse_bound), if min == 0 && max == 0, we
+	       immediate 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 left it there and replace with EMPTY here. */
+	    if (iter->min == 0 && iter->max == 0)
+	      {
+		tre_ast_node_t *empty = tre_ast_new_literal(mem, EMPTY, -1, -1);
+		if (empty == NULL)
+		  return REG_ESPACE;
+		node->obj = empty->obj;
+		node->type = empty->type;
+	      }
+	    else if (iter->min > 1 || iter->max > 1)
+	      {
+		tre_ast_node_t *seq1 = NULL, *seq2 = NULL;
+		int j;
+		int pos_add_save = pos_add;
+
+		/* Create a catenated sequence of copies of the node. */
+		for (j = 0; j < iter->min; j++)
+		  {
+		    tre_ast_node_t *copy;
+		    /* Remove tags from all but the last copy. */
+		    int flags = ((j + 1 < iter->min)
+				 ? COPY_REMOVE_TAGS
+				 : COPY_MAXIMIZE_FIRST_TAG);
+		    DPRINT(("  pos_add %d\n", pos_add));
+		    pos_add_save = pos_add;
+		    status = tre_copy_ast(mem, stack, iter->arg, flags,
+					  &pos_add, tag_directions, &copy,
+					  &max_pos);
+		    if (status != REG_OK)
+		      return status;
+		    if (seq1 != NULL)
+		      seq1 = tre_ast_new_catenation(mem, seq1, copy);
+		    else
+		      seq1 = copy;
+		    if (seq1 == NULL)
+		      return REG_ESPACE;
+		  }
+
+		if (iter->max == -1)
+		  {
+		    /* No upper limit. */
+		    pos_add_save = pos_add;
+		    status = tre_copy_ast(mem, stack, iter->arg, 0,
+					  &pos_add, NULL, &seq2, &max_pos);
+		    if (status != REG_OK)
+		      return status;
+		    seq2 = tre_ast_new_iter(mem, seq2, 0, -1, 0);
+		    if (seq2 == NULL)
+		      return REG_ESPACE;
+		  }
+		else
+		  {
+		    for (j = iter->min; j < iter->max; j++)
+		      {
+			tre_ast_node_t *tmp, *copy;
+			pos_add_save = pos_add;
+			status = tre_copy_ast(mem, stack, iter->arg, 0,
+					      &pos_add, NULL, &copy, &max_pos);
+			if (status != REG_OK)
+			  return status;
+			if (seq2 != NULL)
+			  seq2 = tre_ast_new_catenation(mem, copy, seq2);
+			else
+			  seq2 = copy;
+			if (seq2 == NULL)
+			  return REG_ESPACE;
+			tmp = tre_ast_new_literal(mem, EMPTY, -1, -1);
+			if (tmp == NULL)
+			  return REG_ESPACE;
+			seq2 = tre_ast_new_union(mem, tmp, seq2);
+			if (seq2 == NULL)
+			  return REG_ESPACE;
+		      }
+		  }
+
+		pos_add = pos_add_save;
+		if (seq1 == NULL)
+		  seq1 = seq2;
+		else if (seq2 != NULL)
+		  seq1 = tre_ast_new_catenation(mem, seq1, seq2);
+		if (seq1 == NULL)
+		  return REG_ESPACE;
+		node->obj = seq1->obj;
+		node->type = seq1->type;
+	      }
+
+	    iter_depth--;
+	    pos_add_total += pos_add - pos_add_last;
+	    if (iter_depth == 0)
+	      pos_add = pos_add_total;
+
+	    break;
+	  }
+	default:
+	  assert(0);
+	  break;
+	}
+    }
+
+  *position += pos_add_total;
+
+  /* `max_pos' should never be larger than `*position' if the above
+     code works, but just an extra safeguard let's make sure
+     `*position' is set large enough so enough memory will be
+     allocated for the transition table. */
+  if (max_pos > *position)
+    *position = max_pos;
+
+#ifdef TRE_DEBUG
+  DPRINT(("Expanded AST:\n"));
+  tre_ast_print(ast);
+  DPRINT(("*position %d, max_pos %d\n", *position, max_pos));
+#endif
+
+  return status;
+}
+
+static tre_pos_and_tags_t *
+tre_set_empty(tre_mem_t mem)
+{
+  tre_pos_and_tags_t *new_set;
+
+  new_set = tre_mem_calloc(mem, sizeof(*new_set));
+  if (new_set == NULL)
+    return NULL;
+
+  new_set[0].position = -1;
+  new_set[0].code_min = -1;
+  new_set[0].code_max = -1;
+
+  return new_set;
+}
+
+static tre_pos_and_tags_t *
+tre_set_one(tre_mem_t mem, int position, int code_min, int code_max,
+	    tre_bracket_match_list_t *bracket_match_list, int backref)
+{
+  tre_pos_and_tags_t *new_set;
+
+  new_set = tre_mem_calloc(mem, sizeof(*new_set) * 2);
+  if (new_set == NULL)
+    return NULL;
+
+  new_set[0].position = position;
+  new_set[0].code_min = code_min;
+  new_set[0].code_max = code_max;
+  new_set[0].bracket_match_list = bracket_match_list;
+  new_set[0].backref = backref;
+  new_set[1].position = -1;
+  new_set[1].code_min = -1;
+  new_set[1].code_max = -1;
+
+  return new_set;
+}
+
+static tre_pos_and_tags_t *
+tre_set_union(tre_mem_t mem, tre_pos_and_tags_t *set1, tre_pos_and_tags_t *set2,
+	      int *tags, int assertions, int *params)
+{
+  int s1, s2, i, j;
+  tre_pos_and_tags_t *new_set;
+  int *new_tags;
+  int num_tags;
+
+  for (num_tags = 0; tags != NULL && tags[num_tags] >= 0; num_tags++);
+  for (s1 = 0; set1[s1].position >= 0; s1++);
+  for (s2 = 0; set2[s2].position >= 0; s2++);
+  new_set = tre_mem_calloc(mem, sizeof(*new_set) * (s1 + s2 + 1));
+  if (!new_set )
+    return NULL;
+
+  for (s1 = 0; set1[s1].position >= 0; s1++)
+    {
+      new_set[s1].position = set1[s1].position;
+      new_set[s1].code_min = set1[s1].code_min;
+      new_set[s1].code_max = set1[s1].code_max;
+      new_set[s1].assertions = set1[s1].assertions | assertions;
+      new_set[s1].bracket_match_list = set1[s1].bracket_match_list;
+      new_set[s1].backref = set1[s1].backref;
+      if (set1[s1].tags == NULL && tags == NULL)
+	new_set[s1].tags = NULL;
+      else
+	{
+	  for (i = 0; set1[s1].tags != NULL && set1[s1].tags[i] >= 0; i++);
+	  new_tags = tre_mem_alloc(mem, (sizeof(*new_tags)
+					 * (i + num_tags + 1)));
+	  if (new_tags == NULL)
+	    return NULL;
+	  for (j = 0; j < i; j++)
+	    new_tags[j] = set1[s1].tags[j];
+	  for (i = 0; i < num_tags; i++)
+	    new_tags[j + i] = tags[i];
+	  new_tags[j + i] = -1;
+	  new_set[s1].tags = new_tags;
+	}
+      if (set1[s1].params)
+	new_set[s1].params = set1[s1].params;
+      if (params)
+	{
+	  if (!new_set[s1].params)
+	    new_set[s1].params = params;
+	  else
+	    {
+	      new_set[s1].params = tre_mem_alloc(mem, sizeof(*params) *
+						 TRE_PARAM_LAST);
+	      if (!new_set[s1].params)
+		return NULL;
+	      for (i = 0; i < TRE_PARAM_LAST; i++)
+		if (params[i] != TRE_PARAM_UNSET)
+		  new_set[s1].params[i] = params[i];
+	    }
+	}
+    }
+
+  for (s2 = 0; set2[s2].position >= 0; s2++)
+    {
+      new_set[s1 + s2].position = set2[s2].position;
+      new_set[s1 + s2].code_min = set2[s2].code_min;
+      new_set[s1 + s2].code_max = set2[s2].code_max;
+      /* XXX - why not | assertions here as well? */
+      new_set[s1 + s2].assertions = set2[s2].assertions;
+      new_set[s1 + s2].bracket_match_list = set2[s2].bracket_match_list;
+      new_set[s1 + s2].backref = set2[s2].backref;
+      if (set2[s2].tags == NULL)
+	new_set[s1 + s2].tags = NULL;
+      else
+	{
+	  for (i = 0; set2[s2].tags[i] >= 0; i++);
+	  new_tags = tre_mem_alloc(mem, sizeof(*new_tags) * (i + 1));
+	  if (new_tags == NULL)
+	    return NULL;
+	  for (j = 0; j < i; j++)
+	    new_tags[j] = set2[s2].tags[j];
+	  new_tags[j] = -1;
+	  new_set[s1 + s2].tags = new_tags;
+	}
+      if (set2[s2].params)
+	new_set[s1 + s2].params = set2[s2].params;
+      if (params)
+	{
+	  if (!new_set[s1 + s2].params)
+	    new_set[s1 + s2].params = params;
+	  else
+	    {
+	      new_set[s1 + s2].params = tre_mem_alloc(mem, sizeof(*params) *
+						      TRE_PARAM_LAST);
+	      if (!new_set[s1 + s2].params)
+		return NULL;
+	      for (i = 0; i < TRE_PARAM_LAST; i++)
+		if (params[i] != TRE_PARAM_UNSET)
+		  new_set[s1 + s2].params[i] = params[i];
+	    }
+	}
+    }
+  new_set[s1 + s2].position = -1;
+  return new_set;
+}
+
+/* Finds the empty path through `node' which is the one that should be
+   taken according to POSIX.2 rules, and adds the tags on that path to
+   `tags'.   `tags' may be NULL.  If `num_tags_seen' is not NULL, it is
+   set to the number of tags seen on the path. */
+static reg_errcode_t
+tre_match_empty(tre_stack_t *stack, tre_ast_node_t *node, int *tags,
+		int *assertions, int *params, int *num_tags_seen,
+		int *params_seen)
+{
+  tre_literal_t *lit;
+  tre_union_t *uni;
+  tre_catenation_t *cat;
+  tre_iteration_t *iter;
+  int i;
+  int bottom = tre_stack_num_objects(stack);
+  reg_errcode_t status = REG_OK;
+  if (num_tags_seen)
+    *num_tags_seen = 0;
+  if (params_seen)
+    *params_seen = 0;
+
+  status = tre_stack_push_voidptr(stack, node);
+
+  /* Walk through the tree recursively. */
+  while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
+    {
+      node = tre_stack_pop_voidptr(stack);
+
+      switch (node->type)
+	{
+	case LITERAL:
+	  lit = (tre_literal_t *)node->obj;
+	  switch (lit->code_min)
+	    {
+	    case TAG:
+	      if (lit->code_max >= 0)
+		{
+		  if (tags != NULL)
+		    {
+		      /* Add the tag to `tags'. */
+		      for (i = 0; tags[i] >= 0; i++)
+			if (tags[i] == lit->code_max)
+			  break;
+		      if (tags[i] < 0)
+			{
+			  tags[i] = lit->code_max;
+			  tags[i + 1] = -1;
+			}
+		    }
+		  if (num_tags_seen)
+		    (*num_tags_seen)++;
+		}
+	      break;
+	    case ASSERTION:
+	      assert(lit->code_max >= 1
+		     || lit->code_max <= ASSERT_LAST);
+	      if (assertions != NULL)
+		*assertions |= lit->code_max;
+	      break;
+	    case PARAMETER:
+	      if (params != NULL)
+		for (i = 0; i < TRE_PARAM_LAST; i++)
+		  params[i] = lit->u.params[i];
+	      if (params_seen != NULL)
+		*params_seen = 1;
+	      break;
+	    case EMPTY:
+	      break;
+	    default:
+	      assert(0);
+	      break;
+	    }
+	  break;
+
+	case UNION:
+	  /* Subexpressions starting earlier take priority over ones
+	     starting later, so we prefer the left subexpression over the
+	     right subexpression. */
+	  uni = (tre_union_t *)node->obj;
+	  if (uni->left->nullable)
+	    STACK_PUSHX(stack, voidptr, uni->left)
+	  else if (uni->right->nullable)
+	    STACK_PUSHX(stack, voidptr, uni->right)
+	  else
+	    assert(0);
+	  break;
+
+	case CATENATION:
+	  /* The path must go through both children. */
+	  cat = (tre_catenation_t *)node->obj;
+	  assert(cat->left->nullable);
+	  assert(cat->right->nullable);
+	  STACK_PUSHX(stack, voidptr, cat->left);
+	  STACK_PUSHX(stack, voidptr, cat->right);
+	  break;
+
+	case ITERATION:
+	  /* A match with an empty string is preferred over no match at
+	     all, so we go through the argument if possible. */
+	  iter = (tre_iteration_t *)node->obj;
+	  if (iter->arg->nullable)
+	    STACK_PUSHX(stack, voidptr, iter->arg);
+	  break;
+
+	default:
+	  assert(0);
+	  break;
+	}
+    }
+
+  return status;
+}
+
+typedef enum {
+  NFL_RECURSE,
+  NFL_POST_UNION,
+  NFL_POST_CATENATION,
+  NFL_POST_ITERATION
+} tre_nfl_stack_symbol_t;
+
+/* Computes and fills in the fields `nullable', `firstpos', and `lastpos' for
+   the nodes of the AST `tree'. */
+static reg_errcode_t
+tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree)
+{
+  int bottom = tre_stack_num_objects(stack);
+
+  STACK_PUSHR(stack, voidptr, tree);
+  STACK_PUSHR(stack, int, NFL_RECURSE);
+
+  while (tre_stack_num_objects(stack) > bottom)
+    {
+      tre_nfl_stack_symbol_t symbol;
+      tre_ast_node_t *node;
+
+      symbol = (tre_nfl_stack_symbol_t)tre_stack_pop_int(stack);
+      node = tre_stack_pop_voidptr(stack);
+      switch (symbol)
+	{
+	case NFL_RECURSE:
+	  switch (node->type)
+	    {
+	    case LITERAL:
+	      {
+		tre_literal_t *lit = (tre_literal_t *)node->obj;
+		if (IS_BACKREF(lit))
+		  {
+		    /* Back references: nullable = false, firstpos = {i},
+		       lastpos = {i}. */
+		    node->nullable = 0;
+		    node->firstpos = tre_set_one(mem, lit->position, 0,
+					     TRE_CHAR_MAX, NULL, -1);
+		    if (!node->firstpos)
+		      return REG_ESPACE;
+		    node->lastpos = tre_set_one(mem, lit->position, 0,
+						TRE_CHAR_MAX, NULL,
+						(int)lit->code_max);
+		    if (!node->lastpos)
+		      return REG_ESPACE;
+		  }
+		else if (lit->code_min < 0)
+		  {
+		    /* Tags, empty strings, params, and zero width assertions:
+		       nullable = true, firstpos = {}, and lastpos = {}. */
+		    node->nullable = 1;
+		    node->firstpos = tre_set_empty(mem);
+		    if (!node->firstpos)
+		      return REG_ESPACE;
+		    node->lastpos = tre_set_empty(mem);
+		    if (!node->lastpos)
+		      return REG_ESPACE;
+		  }
+		else
+		  {
+		    /* Literal at position i: nullable = false, firstpos = {i},
+		       lastpos = {i}. */
+		    node->nullable = 0;
+		    node->firstpos =
+		      tre_set_one(mem, lit->position, (int)lit->code_min,
+				  (int)lit->code_max, NULL, -1);
+		    if (!node->firstpos)
+		      return REG_ESPACE;
+		    node->lastpos = tre_set_one(mem, lit->position,
+						(int)lit->code_min,
+						(int)lit->code_max,
+						lit->u.bracket_match_list,
+						-1);
+		    if (!node->lastpos)
+		      return REG_ESPACE;
+		  }
+		break;
+	      }
+
+	    case UNION:
+	      /* Compute the attributes for the two subtrees, and after that
+		 for this node. */
+	      STACK_PUSHR(stack, voidptr, node);
+	      STACK_PUSHR(stack, int, NFL_POST_UNION);
+	      STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->right);
+	      STACK_PUSHR(stack, int, NFL_RECURSE);
+	      STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->left);
+	      STACK_PUSHR(stack, int, NFL_RECURSE);
+	      break;
+
+	    case CATENATION:
+	      /* Compute the attributes for the two subtrees, and after that
+		 for this node. */
+	      STACK_PUSHR(stack, voidptr, node);
+	      STACK_PUSHR(stack, int, NFL_POST_CATENATION);
+	      STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->right);
+	      STACK_PUSHR(stack, int, NFL_RECURSE);
+	      STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->left);
+	      STACK_PUSHR(stack, int, NFL_RECURSE);
+	      break;
+
+	    case ITERATION:
+	      /* Compute the attributes for the subtree, and after that for
+		 this node. */
+	      STACK_PUSHR(stack, voidptr, node);
+	      STACK_PUSHR(stack, int, NFL_POST_ITERATION);
+	      STACK_PUSHR(stack, voidptr, ((tre_iteration_t *)node->obj)->arg);
+	      STACK_PUSHR(stack, int, NFL_RECURSE);
+	      break;
+	    }
+	  break; /* end case: NFL_RECURSE */
+
+	case NFL_POST_UNION:
+	  {
+	    tre_union_t *uni = (tre_union_t *)node->obj;
+	    node->nullable = uni->left->nullable || uni->right->nullable;
+	    node->firstpos = tre_set_union(mem, uni->left->firstpos,
+					   uni->right->firstpos, NULL, 0, NULL);
+	    if (!node->firstpos)
+	      return REG_ESPACE;
+	    node->lastpos = tre_set_union(mem, uni->left->lastpos,
+					  uni->right->lastpos, NULL, 0, NULL);
+	    if (!node->lastpos)
+	      return REG_ESPACE;
+	    break;
+	  }
+
+	case NFL_POST_ITERATION:
+	  {
+	    int num_tags, *tags, assertions, params_seen;
+	    int *params;
+	    reg_errcode_t status;
+	    tre_iteration_t *iter = (tre_iteration_t *)node->obj;
+
+	    /* From Ville Laurikari's original 2001 Master's thesis, the
+	       firstpos(n) and lastpos(n) of an iteration is just the
+	       corresponding values of the iteration's argument.  Unfortunately,
+	       this isn't sufficient for the following BRE:
+
+	           \(a*\)*b\(\1\)    matched against    ab
+
+	       The backreference wants to force the first subexpression to
+	       be the empty string, but there is no transition for this.  So
+	       we need to modify the lastpos(n) of an iteration to be the
+	       equivalent of that of catentation.  Using the same notation as
+	       in the thesis, lastpos(n) is redefined as:
+
+	           if nullable(c1) then
+		       lastpos(c1) U
+		       addtags(lastpos(c1),
+		               emptymatch(c1))
+		   else
+		       lastpos(c1)
+
+	       where c1 is the argument node.  firstpos(n) remains the same. */
+
+	    /* Compute lastpos. */
+	    if (iter->min == 0 || iter->arg->nullable)
+	      {
+		node->nullable = 1;
+		if (iter->arg->nullable)
+		  {
+		    /* The arg matches the empty string.  Make a first pass
+		       with tre_match_empty() to get the number of tags and
+		       parameters. */
+		    status = tre_match_empty(stack, iter->arg,
+					     NULL, NULL, NULL, &num_tags,
+					     &params_seen);
+		    if (status != REG_OK)
+		      return status;
+		    /* Allocate arrays for the tags and parameters. */
+		    tags = malloc(sizeof(int) * (num_tags + 1));
+		    if (!tags)
+		      return REG_ESPACE;
+		    tags[0] = -1;
+		    assertions = 0;
+		    params = NULL;
+		    if (params_seen)
+		      {
+			params = tre_mem_alloc(mem, sizeof(*params)
+					       * TRE_PARAM_LAST);
+			if (!params)
+			  {
+			    free(tags);
+			    return REG_ESPACE;
+			  }
+		      }
+		    /* Second pass with tre_mach_empty() to get the list of
+		       tags and parameters. */
+		    status = tre_match_empty(stack, iter->arg, tags,
+					     &assertions, params, NULL, NULL);
+		    if (status != REG_OK)
+		      {
+			free(tags);
+			return status;
+		      }
+		    node->lastpos =
+		      tre_set_union(mem, iter->arg->lastpos, iter->arg->lastpos,
+				    tags, assertions, params);
+		    free(tags);
+		    if (!node->lastpos)
+		      return REG_ESPACE;
+		  }
+		else
+		  node->lastpos = iter->arg->lastpos;
+	      }
+	    else
+	      {
+		node->nullable = 0;
+		node->lastpos = iter->arg->lastpos;
+	      }
+	    node->firstpos = iter->arg->firstpos;
+	    break;
+	  }
+
+	case NFL_POST_CATENATION:
+	  {
+	    int num_tags, *tags, assertions, params_seen;
+	    int *params;
+	    reg_errcode_t status;
+	    tre_catenation_t *cat = node->obj;
+	    node->nullable = cat->left->nullable && cat->right->nullable;
+
+	    /* Compute firstpos. */
+	    if (cat->left->nullable)
+	      {
+		/* The left side matches the empty string.  Make a first pass
+		   with tre_match_empty() to get the number of tags and
+		   parameters. */
+		status = tre_match_empty(stack, cat->left,
+					 NULL, NULL, NULL, &num_tags,
+					 &params_seen);
+		if (status != REG_OK)
+		  return status;
+		/* Allocate arrays for the tags and parameters. */
+		tags = malloc(sizeof(*tags) * (num_tags + 1));
+		if (!tags)
+		  return REG_ESPACE;
+		tags[0] = -1;
+		assertions = 0;
+		params = NULL;
+		if (params_seen)
+		  {
+		    params = tre_mem_alloc(mem, sizeof(*params)
+					   * TRE_PARAM_LAST);
+		    if (!params)
+		      {
+			free(tags);
+			return REG_ESPACE;
+		      }
+		  }
+		/* Second pass with tre_mach_empty() to get the list of
+		   tags and parameters. */
+		status = tre_match_empty(stack, cat->left, tags,
+					 &assertions, params, NULL, NULL);
+		if (status != REG_OK)
+		  {
+		    free(tags);
+		    return status;
+		  }
+		node->firstpos =
+		  tre_set_union(mem, cat->right->firstpos, cat->left->firstpos,
+				tags, assertions, params);
+		free(tags);
+		if (!node->firstpos)
+		  return REG_ESPACE;
+	      }
+	    else
+	      {
+		node->firstpos = cat->left->firstpos;
+	      }
+
+	    /* Compute lastpos. */
+	    if (cat->right->nullable)
+	      {
+		/* The right side matches the empty string.  Make a first pass
+		   with tre_match_empty() to get the number of tags and
+		   parameters. */
+		status = tre_match_empty(stack, cat->right,
+					 NULL, NULL, NULL, &num_tags,
+					 &params_seen);
+		if (status != REG_OK)
+		  return status;
+		/* Allocate arrays for the tags and parameters. */
+		tags = malloc(sizeof(int) * (num_tags + 1));
+		if (!tags)
+		  return REG_ESPACE;
+		tags[0] = -1;
+		assertions = 0;
+		params = NULL;
+		if (params_seen)
+		  {
+		    params = tre_mem_alloc(mem, sizeof(*params)
+					   * TRE_PARAM_LAST);
+		    if (!params)
+		      {
+			free(tags);
+			return REG_ESPACE;
+		      }
+		  }
+		/* Second pass with tre_mach_empty() to get the list of
+		   tags and parameters. */
+		status = tre_match_empty(stack, cat->right, tags,
+					 &assertions, params, NULL, NULL);
+		if (status != REG_OK)
+		  {
+		    free(tags);
+		    return status;
+		  }
+		node->lastpos =
+		  tre_set_union(mem, cat->left->lastpos, cat->right->lastpos,
+				tags, assertions, params);
+		free(tags);
+		if (!node->lastpos)
+		  return REG_ESPACE;
+	      }
+	    else
+	      {
+		node->lastpos = cat->right->lastpos;
+	      }
+	    break;
+	  }
+
+	default:
+	  assert(0);
+	  break;
+	}
+    }
+
+  return REG_OK;
+}
+
+/* Adds a transition from each position in `p1' to each position in `p2'. */
+static reg_errcode_t
+tre_make_trans(tre_pos_and_tags_t *p1, tre_pos_and_tags_t *p2,
+	       tre_tnfa_transition_t *transitions,
+	       int *counts, int *offs)
+{
+  tre_pos_and_tags_t *orig_p2 = p2;
+  tre_tnfa_transition_t *trans;
+  int i, j, k, l, dup, prev_p2_pos;
+
+  if (transitions != NULL)
+    while (p1->position >= 0)
+      {
+	p2 = orig_p2;
+	prev_p2_pos = -1;
+	while (p2->position >= 0)
+	  {
+	    /* Optimization: if this position was already handled, skip it. */
+	    if (p2->position == prev_p2_pos)
+	      {
+		p2++;
+		continue;
+	      }
+	    prev_p2_pos = p2->position;
+	    /* Set `trans' to point to the next unused transition from
+	       position `p1->position'. */
+	    trans = transitions + offs[p1->position];
+	    while (trans->state != NULL)
+	      {
+#if 0
+		/* If we find a previous transition from `p1->position' to
+		   `p2->position', it is overwritten.  This can happen only
+		   if there are nested loops in the regexp, like in "((a)*)*".
+		   In POSIX.2 repetition using the outer loop is always
+		   preferred over using the inner loop.	 Therefore the
+		   transition for the inner loop is useless and can be thrown
+		   away. */
+		/* XXX - The same position is used for all nodes in a bracket
+		   expression, so this optimization cannot be used (it will
+		   break bracket expressions) unless I figure out a way to
+		   detect it here. */
+		if (trans->state_id == p2->position)
+		  {
+		    DPRINT(("*"));
+		    break;
+		  }
+#endif
+		trans++;
+	      }
+
+	    if (trans->state == NULL)
+	      (trans + 1)->state = NULL;
+	    /* Use the character ranges, assertions, etc. from `p1' for
+	       the transition from `p1' to `p2'. */
+	    trans->code_min = p1->code_min;
+	    trans->code_max = p1->code_max;
+	    trans->state = transitions + offs[p2->position];
+	    trans->state_id = p2->position;
+	    trans->assertions = p1->assertions | p2->assertions
+	      | (p1->bracket_match_list != NULL ? ASSERT_BRACKET_MATCH : 0);
+	    if (p1->backref >= 0)
+	      {
+		assert((trans->assertions & ASSERT_BRACKET_MATCH) == 0);
+		assert(p2->backref < 0);
+		trans->u.backref = p1->backref;
+		trans->assertions |= ASSERT_BACKREF;
+	      }
+	    if (p1->bracket_match_list != NULL)
+	      {
+		trans->u.bracket_match_list =
+		  malloc(SIZEOF_BRACKET_MATCH_LIST(p1->bracket_match_list));
+		if (trans->u.bracket_match_list == NULL)
+		  return REG_ESPACE;
+		memcpy(trans->u.bracket_match_list, p1->bracket_match_list,
+		       SIZEOF_BRACKET_MATCH_LIST(p1->bracket_match_list));
+	      }
+
+	    /* Find out how many tags this transition has. */
+	    i = 0;
+	    if (p1->tags != NULL)
+	      while(p1->tags[i] >= 0)
+		i++;
+	    j = 0;
+	    if (p2->tags != NULL)
+	      while(p2->tags[j] >= 0)
+		j++;
+
+	    /* If we are overwriting a transition, free the old tag array. */
+	    if (trans->tags != NULL)
+	      free(trans->tags);
+	    trans->tags = NULL;
+
+	    /* If there were any tags, allocate an array and fill it. */
+	    if (i + j > 0)
+	      {
+		trans->tags = malloc(sizeof(*trans->tags) * (i + j + 1));
+		if (!trans->tags)
+		  return REG_ESPACE;
+		i = 0;
+		if (p1->tags != NULL)
+		  while(p1->tags[i] >= 0)
+		    {
+		      trans->tags[i] = p1->tags[i];
+		      i++;
+		    }
+		l = i;
+		j = 0;
+		if (p2->tags != NULL)
+		  while (p2->tags[j] >= 0)
+		    {
+		      /* Don't add duplicates. */
+		      dup = 0;
+		      for (k = 0; k < i; k++)
+			if (trans->tags[k] == p2->tags[j])
+			  {
+			    dup = 1;
+			    break;
+			  }
+		      if (!dup)
+			trans->tags[l++] = p2->tags[j];
+		      j++;
+		    }
+		trans->tags[l] = -1;
+	      }
+
+	    /* Set the parameter array.	 If both `p2' and `p1' have same
+	       parameters, the values in `p2' override those in `p1'. */
+	    if (p1->params || p2->params)
+	      {
+		if (!trans->params)
+		  trans->params = malloc(sizeof(*trans->params)
+					  * TRE_PARAM_LAST);
+		if (!trans->params)
+		  return REG_ESPACE;
+		for (i = 0; i < TRE_PARAM_LAST; i++)
+		  {
+		    trans->params[i] = TRE_PARAM_UNSET;
+		    if (p1->params && p1->params[i] != TRE_PARAM_UNSET)
+		      trans->params[i] = p1->params[i];
+		    if (p2->params && p2->params[i] != TRE_PARAM_UNSET)
+		      trans->params[i] = p2->params[i];
+		  }
+	      }
+	    else
+	      {
+		if (trans->params)
+		  free(trans->params);
+		trans->params = NULL;
+	      }
+
+#ifdef TRE_DEBUG
+	    {
+	      int *tags;
+
+	      DPRINT(("	 %2d -> %2d on %3d", p1->position, p2->position,
+		      p1->code_min));
+	      if (p1->code_max != p1->code_min)
+		DPRINT(("-%3d", p1->code_max));
+	      tags = trans->tags;
+	      if (tags)
+		{
+		  DPRINT((", tags ["));
+		  while (*tags >= 0)
+		    {
+		      DPRINT(("%d", *tags));
+		      tags++;
+		      if (*tags >= 0)
+			DPRINT((","));
+		    }
+		  DPRINT(("]"));
+		}
+	      if (trans->assertions)
+		DPRINT((", assert %d", trans->assertions));
+	      if (trans->assertions & ASSERT_BACKREF)
+		DPRINT((", backref %d", trans->u.backref));
+	      else if (trans->assertions & ASSERT_BRACKET_MATCH)
+		DPRINT((", bracket_match_list %p",
+			trans->u.bracket_match_list));
+	      if (trans->params)
+		{
+		  DPRINT((", "));
+		  tre_print_params(trans->params);
+		}
+	      DPRINT(("\n"));
+	    }
+#endif /* TRE_DEBUG */
+	    p2++;
+	  }
+	p1++;
+      }
+  else
+    /* Compute a maximum limit for the number of transitions leaving
+       from each state. */
+    while (p1->position >= 0)
+      {
+	p2 = orig_p2;
+	while (p2->position >= 0)
+	  {
+	    counts[p1->position]++;
+	    p2++;
+	  }
+	p1++;
+      }
+  return REG_OK;
+}
+
+/* Converts the syntax tree to a TNFA.	All the transitions in the TNFA are
+   labelled with one character range (there are no transitions on empty
+   strings).  The TNFA takes O(n^2) space in the worst case, `n' is size of
+   the regexp. */
+static reg_errcode_t
+tre_ast_to_tnfa(tre_ast_node_t *node, tre_tnfa_transition_t *transitions,
+		int *counts, int *offs)
+{
+  tre_union_t *uni;
+  tre_catenation_t *cat;
+  tre_iteration_t *iter;
+  reg_errcode_t errcode = REG_OK;
+
+  /* XXX - recurse using a stack!. */
+  switch (node->type)
+    {
+    case LITERAL:
+      break;
+    case UNION:
+      uni = (tre_union_t *)node->obj;
+      errcode = tre_ast_to_tnfa(uni->left, transitions, counts, offs);
+      if (errcode != REG_OK)
+	return errcode;
+      errcode = tre_ast_to_tnfa(uni->right, transitions, counts, offs);
+      break;
+
+    case CATENATION:
+      cat = (tre_catenation_t *)node->obj;
+      /* Add a transition from each position in cat->left->lastpos
+	 to each position in cat->right->firstpos. */
+      errcode = tre_make_trans(cat->left->lastpos, cat->right->firstpos,
+			       transitions, counts, offs);
+      if (errcode != REG_OK)
+	return errcode;
+      errcode = tre_ast_to_tnfa(cat->left, transitions, counts, offs);
+      if (errcode != REG_OK)
+	return errcode;
+      errcode = tre_ast_to_tnfa(cat->right, transitions, counts, offs);
+      break;
+
+    case ITERATION:
+      iter = (tre_iteration_t *)node->obj;
+      assert(iter->max == -1 || iter->max == 1);
+
+      if (iter->max == -1)
+	{
+	  assert(iter->min == 0 || iter->min == 1);
+	  /* Add a transition from each last position in the iterated
+	     expression to each first position. */
+	  errcode = tre_make_trans(iter->arg->lastpos, iter->arg->firstpos,
+				   transitions, counts, offs);
+	  if (errcode != REG_OK)
+	    return errcode;
+	}
+      errcode = tre_ast_to_tnfa(iter->arg, transitions, counts, offs);
+      break;
+    }
+  return errcode;
+}
+
+#define ERROR_EXIT(err)		  \
+  do				  \
+    {				  \
+      errcode = err;		  \
+      if (/*CONSTCOND*/1)	  \
+      	goto error_exit;	  \
+    }				  \
+ while (/*CONSTCOND*/0)
+
+int
+tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags,
+	    locale_t loc)
+{
+  tre_stack_t *stack;
+  tre_ast_node_t *tree, *tmp_ast_l, *tmp_ast_r;
+  tre_pos_and_tags_t *p;
+  int *counts = NULL, *offs = NULL;
+  int i, add = 0;
+  tre_tnfa_transition_t *transitions, *initial;
+  tre_tnfa_t *tnfa = NULL;
+  tre_submatch_data_t *submatch_data = NULL;
+  tre_tag_direction_t *tag_directions = NULL;
+  reg_errcode_t errcode;
+  tre_mem_t mem;
+
+  /* Parse context. */
+  tre_parse_ctx_t parse_ctx;
+
+  /* Allocate a stack used throughout the compilation process for various
+     purposes. */
+  stack = tre_stack_new(512, 10240, 128);
+  if (!stack)
+    return REG_ESPACE;
+  /* Allocate a fast memory allocator. */
+  mem = tre_mem_new();
+  if (!mem)
+    {
+      tre_stack_destroy(stack);
+      return REG_ESPACE;
+    }
+
+  /* Parse the regexp. */
+  memset(&parse_ctx, 0, sizeof(parse_ctx));
+  parse_ctx.mem = mem;
+  parse_ctx.stack = stack;
+  parse_ctx.re = regex;
+  parse_ctx.len = n;
+  /* Only allow REG_UNGREEDY to be set if both REG_ENHANCED and REG_EXTENDED
+     are also set */
+  if ((cflags & (REG_ENHANCED | REG_EXTENDED)) != (REG_ENHANCED | REG_EXTENDED))
+    cflags &= ~REG_UNGREEDY;
+  parse_ctx.cflags = cflags;
+  parse_ctx.max_backref = -1;
+  parse_ctx.loc = loc;
+  parse_ctx.submatch_id_invisible = SUBMATCH_ID_INVISIBLE_START;
+
+  DPRINT(("tre_compile: parsing '%.*" STRF "'\n", (int)n, regex));
+  errcode = tre_parse(&parse_ctx);
+  if (errcode != REG_OK)
+    ERROR_EXIT(errcode);
+  preg->re_nsub = parse_ctx.submatch_id - 1;
+  tree = parse_ctx.result;
+
+  /* Back references and approximate matching cannot currently be used
+     in the same regexp. */
+  if (parse_ctx.max_backref >= 0 && parse_ctx.have_approx)
+    ERROR_EXIT(REG_BADPAT);
+
+#ifdef TRE_DEBUG
+  tre_ast_print(tree);
+#endif /* TRE_DEBUG */
+
+  /* Referring to nonexistent subexpressions is illegal. */
+  if (parse_ctx.max_backref > (int)preg->re_nsub)
+    ERROR_EXIT(REG_ESUBREG);
+
+  /* Allocate the TNFA struct. */
+  tnfa = calloc(1, sizeof(tre_tnfa_t));
+  if (tnfa == NULL)
+    ERROR_EXIT(REG_ESPACE);
+  tnfa->have_backrefs = parse_ctx.max_backref >= 0;
+  tnfa->have_approx = parse_ctx.have_approx;
+  tnfa->num_submatches = parse_ctx.submatch_id;
+  tnfa->num_submatches_invisible = parse_ctx.submatch_id_invisible
+				   - SUBMATCH_ID_INVISIBLE_START;
+  tnfa->num_reorder_tags = parse_ctx.num_reorder_tags;
+  tnfa->loc = duplocale(parse_ctx.loc);
+  if (tnfa->loc == (locale_t)0)
+    ERROR_EXIT(REG_ESPACE);
+
+  /* Set up tags for submatch addressing.  If REG_NOSUB is set and the
+     regexp does not have back references, this can be skipped. */
+  if (tnfa->num_reorder_tags > 0 || !(cflags & REG_NOSUB))
+    {
+      DPRINT(("tre_compile: setting up tags\n"));
+
+      /* Figure out how many tags we will need. */
+      errcode = tre_add_tags(NULL, stack, tree, tnfa);
+      if (errcode != REG_OK)
+	ERROR_EXIT(errcode);
+#ifdef TRE_DEBUG
+      tre_ast_print(tree);
+#endif /* TRE_DEBUG */
+
+      if (tnfa->num_tags > 0)
+	{
+	  tag_directions = malloc(sizeof(*tag_directions)
+				   * (tnfa->num_tags + 1));
+	  if (tag_directions == NULL)
+	    ERROR_EXIT(REG_ESPACE);
+	  tnfa->tag_directions = tag_directions;
+	  memset(tag_directions, -1,
+		 sizeof(*tag_directions) * (tnfa->num_tags + 1));
+	}
+      tnfa->minimal_tags = calloc((unsigned)tnfa->num_tags * 2 + 3,
+				   sizeof(tnfa->minimal_tags));
+      if (tnfa->minimal_tags == NULL)
+	ERROR_EXIT(REG_ESPACE);
+
+      submatch_data = calloc((unsigned)parse_ctx.submatch_id,
+			      sizeof(*submatch_data));
+      if (submatch_data == NULL)
+	ERROR_EXIT(REG_ESPACE);
+      /* Set the eo_tag value to -1 to indicate that that corresponding
+       * submatch has not be completed yet */
+      for (i = 0; i < parse_ctx.submatch_id; i++)
+	{
+	  submatch_data[i].eo_tag = -1;
+	}
+      tnfa->submatch_data = submatch_data;
+
+      errcode = tre_add_tags(mem, stack, tree, tnfa);
+      if (errcode != REG_OK)
+	ERROR_EXIT(errcode);
+
+#ifdef TRE_DEBUG
+      for (i = 0; i < parse_ctx.submatch_id; i++)
+	DPRINT(("pmatch[%d] = {t%d, t%d}\n",
+		i, submatch_data[i].so_tag, submatch_data[i].eo_tag));
+      for (i = 0; i <= tnfa->num_tags; i++)
+	DPRINT(("t%d is %s\n", i, tag_dir_str[tag_directions[i]]));
+#endif /* TRE_DEBUG */
+    }
+
+  /* Expand iteration nodes. */
+  errcode = tre_expand_ast(mem, stack, tree, &parse_ctx.position,
+			   tag_directions, &tnfa->params_depth);
+  if (errcode != REG_OK)
+    ERROR_EXIT(errcode);
+
+  /* Add a dummy node for the final state.
+     XXX - For certain patterns this dummy node can be optimized away,
+	   for example "a*" or "ab*".	Figure out a simple way to detect
+	   this possibility. */
+  tmp_ast_l = tree;
+  tmp_ast_r = tre_ast_new_literal(mem, 0, 0, parse_ctx.position++);
+  if (tmp_ast_r == NULL)
+    ERROR_EXIT(REG_ESPACE);
+
+  tree = tre_ast_new_catenation(mem, tmp_ast_l, tmp_ast_r);
+  if (tree == NULL)
+    ERROR_EXIT(REG_ESPACE);
+
+#ifdef TRE_DEBUG
+  tre_ast_print(tree);
+  DPRINT(("Number of states: %d\n", parse_ctx.position));
+  if (submatch_data)
+    for (i = 0; i < parse_ctx.submatch_id; i++)
+      DPRINT(("pmatch[%d] = {t%d, t%d}\n",
+	      i, submatch_data[i].so_tag, submatch_data[i].eo_tag));
+  if (tag_directions)
+    for (i = 0; i <= tnfa->num_tags; i++)
+      DPRINT(("t%d is %s\n", i, tag_dir_str[tag_directions[i]]));
+#endif /* TRE_DEBUG */
+
+  errcode = tre_compute_nfl(mem, stack, tree);
+  if (errcode != REG_OK)
+    ERROR_EXIT(errcode);
+
+  counts = malloc(sizeof(int) * parse_ctx.position);
+  if (counts == NULL)
+    ERROR_EXIT(REG_ESPACE);
+
+  offs = malloc(sizeof(int) * parse_ctx.position);
+  if (offs == NULL)
+    ERROR_EXIT(REG_ESPACE);
+
+  for (i = 0; i < parse_ctx.position; i++)
+    counts[i] = 0;
+  tre_ast_to_tnfa(tree, NULL, counts, NULL);
+
+  add = 0;
+  for (i = 0; i < parse_ctx.position; i++)
+    {
+      offs[i] = add;
+      add += counts[i] + 1;
+      counts[i] = 0;
+    }
+  transitions = calloc((unsigned)add + 1, sizeof(*transitions));
+  if (transitions == NULL)
+    ERROR_EXIT(REG_ESPACE);
+  tnfa->transitions = transitions;
+  tnfa->num_transitions = add;
+
+  DPRINT(("Converting to TNFA:\n"));
+  errcode = tre_ast_to_tnfa(tree, transitions, counts, offs);
+  if (errcode != REG_OK)
+    ERROR_EXIT(errcode);
+
+  /* Set first_char only if there is only one character that can be the
+     first character of a match */
+  tnfa->first_char = -1;
+  if (!tmp_ast_l->nullable)
+    {
+      int scanning = 1;
+      for (p = tree->firstpos; scanning && p->position >= 0; p++)
+	{
+	  tre_tnfa_transition_t *j = transitions + offs[p->position];
+	  while (j->state != NULL)
+	    {
+	      if (j->code_min <= j->code_max)
+		{
+		  if (j->code_max != j->code_min || j->code_min == -1 || tnfa->first_char != -1)
+		    {
+		      tnfa->first_char = -1;
+		      scanning = 0;
+		      break;
+		    }
+		  tnfa->first_char = j->code_min;
+		}
+	      j++;
+	    }
+	}
+#ifdef TRE_DEBUG
+      if (tnfa->first_char >= 0)
+	DPRINT(("first char must be %d\n", tnfa->first_char));
+#endif /* TRE_DEBUG */
+    }
+
+  p = tree->firstpos;
+  i = 0;
+  while (p->position >= 0)
+    {
+      i++;
+
+#ifdef TRE_DEBUG
+      {
+	int *tags;
+	DPRINT(("initial: %d", p->position));
+	tags = p->tags;
+	if (tags != NULL)
+	  {
+	    if (*tags >= 0)
+	      DPRINT(("/"));
+	    while (*tags >= 0)
+	      {
+		DPRINT(("%d", *tags));
+		tags++;
+		if (*tags >= 0)
+		  DPRINT((","));
+	      }
+	  }
+	DPRINT((", assert %d", p->assertions));
+	if (p->params)
+	  {
+	    DPRINT((", "));
+	    tre_print_params(p->params);
+	  }
+	DPRINT(("\n"));
+      }
+#endif /* TRE_DEBUG */
+
+      p++;
+    }
+
+  initial = calloc((unsigned)i + 1, sizeof(tre_tnfa_transition_t));
+  if (initial == NULL)
+    ERROR_EXIT(REG_ESPACE);
+  tnfa->initial = initial;
+
+  i = 0;
+  for (p = tree->firstpos; p->position >= 0; p++)
+    {
+      initial[i].state = transitions + offs[p->position];
+      initial[i].state_id = p->position;
+      initial[i].tags = NULL;
+      /* Copy the arrays p->tags, and p->params, they are allocated
+	 from a tre_mem object. */
+      if (p->tags)
+	{
+	  int j;
+	  for (j = 0; p->tags[j] >= 0; j++);
+	  initial[i].tags = malloc(sizeof(*p->tags) * (j + 1));
+	  if (!initial[i].tags)
+	    ERROR_EXIT(REG_ESPACE);
+	  memcpy(initial[i].tags, p->tags, sizeof(*p->tags) * (j + 1));
+	}
+      initial[i].params = NULL;
+      if (p->params)
+	{
+	  initial[i].params = malloc(sizeof(*p->params) * TRE_PARAM_LAST);
+	  if (!initial[i].params)
+	    ERROR_EXIT(REG_ESPACE);
+	  memcpy(initial[i].params, p->params,
+		 sizeof(*p->params) * TRE_PARAM_LAST);
+	}
+      initial[i].assertions = p->assertions;
+      i++;
+    }
+  initial[i].state = NULL;
+
+  tnfa->num_transitions = add;
+  tnfa->final = transitions + offs[tree->lastpos[0].position];
+  tnfa->num_states = parse_ctx.position;
+  tnfa->cflags = cflags;
+
+  DPRINT(("final state %d (%p)\n", tree->lastpos[0].position,
+	  (void *)tnfa->final));
+
+  tre_mem_destroy(mem);
+  tre_stack_destroy(stack);
+  free(counts);
+  free(offs);
+
+  preg->re_magic = RE_MAGIC;
+  preg->TRE_REGEX_T_FIELD = (void *)tnfa;
+  return REG_OK;
+
+ error_exit:
+  /* Free everything that was allocated and return the error code. */
+  tre_mem_destroy(mem);
+  if (stack != NULL)
+    tre_stack_destroy(stack);
+  if (counts != NULL)
+    free(counts);
+  if (offs != NULL)
+    free(offs);
+
+  /* Set tnfa into preg, so that calling tre_free() will free the contents
+     of tnfa.  NULL out the loc field since we never retained the locale. */
+  preg->TRE_REGEX_T_FIELD = (void *)tnfa;
+  if(tnfa)
+    {
+      if (tnfa->loc != (locale_t)0)
+	freelocale(tnfa->loc);
+      tnfa->loc = NULL;
+    }
+
+  tre_free(preg);
+  return errcode;
+}
+
+void
+tre_free(regex_t *preg)
+{
+  tre_tnfa_t *tnfa;
+  unsigned int i;
+  tre_tnfa_transition_t *trans;
+
+  preg->re_magic = 0;
+  tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+  if (!tnfa)
+    return;
+  preg->TRE_REGEX_T_FIELD = NULL;
+
+  for (i = 0; i < tnfa->num_transitions; i++)
+    if (tnfa->transitions[i].state)
+      {
+	if (tnfa->transitions[i].tags)
+	  free(tnfa->transitions[i].tags);
+	if (tnfa->transitions[i].assertions & ASSERT_BRACKET_MATCH)
+	  free(tnfa->transitions[i].u.bracket_match_list);
+	if (tnfa->transitions[i].params)
+	  free(tnfa->transitions[i].params);
+      }
+  if (tnfa->transitions)
+    free(tnfa->transitions);
+
+  if (tnfa->initial)
+    {
+      for (trans = tnfa->initial; trans->state; trans++)
+	{
+	  if (trans->tags)
+	    free(trans->tags);
+	  if (trans->params)
+	    free(trans->params);
+	}
+      free(tnfa->initial);
+    }
+
+  if (tnfa->submatch_data)
+    {
+      free(tnfa->submatch_data);
+    }
+
+  if (tnfa->tag_directions)
+    free(tnfa->tag_directions);
+  if (tnfa->minimal_tags)
+    free(tnfa->minimal_tags);
+
+  if (tnfa->loc != (locale_t)0)
+    freelocale(tnfa->loc);
+
+  if (tnfa->last_matched_branch)
+    free(tnfa->last_matched_branch);
+
+  free(tnfa);
+}
--- /dev/null	Tue Feb 13 07:11:50 2018
+++ new/usr/src/lib/libc/port/regex/tre-compile.h	Tue Feb 13 07:11:50 2018
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+  tre-compile.h: Regex compilation definitions
+*/
+
+#ifndef	_TRE_COMPILE_H
+#define	_TRE_COMPILE_H
+
+typedef struct {
+  int position;
+  int code_min;
+  int code_max;
+  int *tags;
+  int assertions;
+  tre_bracket_match_list_t *bracket_match_list;
+  int backref;
+  int *params;
+  locale_t loc;
+} tre_pos_and_tags_t;
+
+#endif	/* _TRE_COMPILE_H */
--- /dev/null	Tue Feb 13 07:11:51 2018
+++ new/usr/src/lib/libc/port/regex/tre-internal.h	Tue Feb 13 07:11:51 2018
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+  tre-internal.h - TRE internal definitions
+*/
+
+#ifndef	_TRE_INTERNAL_H
+#define	_TRE_INTERNAL_H
+
+#include <sys/sysmacros.h>
+
+#include <bitstring.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "tre.h"
+
+#define	TRE_REGEX_T_FIELD value
+
+#ifdef TRE_DEBUG
+#include <stdio.h>
+#define DPRINT(msg) do {printf msg; fflush(stdout);} while(/*CONSTCOND*/0)
+#else /* !TRE_DEBUG */
+#define DPRINT(msg) do { } while(/*CONSTCOND*/0)
+#endif /* !TRE_DEBUG */
+
+#define elementsof(x)	( sizeof(x) / sizeof(x[0]) )
+
+#define tre_mbrtowc(pwc, s, n, ps) (mbrtowc((pwc), (s), (n), (ps)))
+#define tre_mbrtowc_l(pwc,s,n,ps,l) (mbrtowc_l((pwc), (s), (n), (ps), (l)))
+
+typedef wint_t tre_cint_t;
+#define TRE_CHAR_MAX WCHAR_MAX
+
+#define TRE_MB_CUR_MAX MB_CUR_MAX
+#define TRE_MB_CUR_MAX_L MB_CUR_MAX_L
+
+#define tre_isalnum iswalnum
+#define tre_isalpha iswalpha
+#define tre_isblank iswblank
+#define tre_iscntrl iswcntrl
+#define tre_isdigit iswdigit
+#define tre_isgraph iswgraph
+#define tre_islower iswlower
+#define tre_isprint iswprint
+#define tre_ispunct iswpunct
+#define tre_isspace iswspace
+#define tre_isupper iswupper
+#define tre_isxdigit iswxdigit
+
+#define tre_tolower towlower
+#define tre_toupper towupper
+#define tre_strlen  wcslen
+
+#define tre_isalnum_l iswalnum_l
+#define tre_isdigit_l iswdigit_l
+#define tre_islower_l iswlower_l
+#define tre_isupper_l iswupper_l
+#define tre_isxdigit_l iswxdigit_l
+#define tre_tolower_l towlower_l
+#define tre_toupper_l towupper_l
+
+typedef wctype_t tre_ctype_t;
+#define tre_isctype iswctype
+#define tre_ctype   wctype
+#define tre_isctype_l iswctype_l
+#define tre_ctype_l   wctype_l
+
+typedef enum { STR_WIDE, STR_BYTE, STR_MBS } tre_str_type_t;
+
+/* Returns number of bytes to add to (char *)ptr to make it
+   properly aligned for the type. */
+#define ALIGN(ptr, type) \
+  ((((long)ptr) % sizeof(type)) \
+   ? (sizeof(type) - (((long)ptr) % sizeof(type))) \
+   : 0)
+
+#define STRF "ls"
+
+/* Types to handle bracket expressions. */
+typedef enum {
+  TRE_BRACKET_MATCH_TYPE_UNUSED = 0,
+  TRE_BRACKET_MATCH_TYPE_CHAR,		/* Single character value */
+  TRE_BRACKET_MATCH_TYPE_RANGE_BEGIN,	/* Collation range begin */
+  TRE_BRACKET_MATCH_TYPE_RANGE_END,	/* Collation range end */
+  TRE_BRACKET_MATCH_TYPE_CLASS,		/* Character class */
+  TRE_BRACKET_MATCH_TYPE_EQUIVALENCE,	/* Collation equivalence value */
+} tre_bracket_match_type_t;
+
+typedef struct {
+  tre_bracket_match_type_t type;
+  tre_cint_t value;
+} tre_bracket_match_t;
+
+#define TRE_BRACKET_MATCH_FLAG_NEGATE	1
+
+typedef struct {
+  int num_bracket_matches;
+  int flags;
+  tre_bracket_match_t bracket_matches[0];
+} tre_bracket_match_list_t;
+
+#define SIZEOF_BRACKET_MATCH_LIST_N(n)	(sizeof(tre_bracket_match_list_t) + \
+					 sizeof(tre_bracket_match_t) * (n))
+#define SIZEOF_BRACKET_MATCH_LIST(l)	SIZEOF_BRACKET_MATCH_LIST_N( \
+					 (l)->num_bracket_matches)
+
+/* The "count" field is the number of time the tag was set, initially zero.
+   The "first" field contains the first set value (when "count" equals 1).
+   The "value" field contains the current value of the tag, if "count" is
+   greater than zero (the tag's current value is -1 if "count" is zero).
+   The "touch" field is the touch value, a montonically increasing value
+   (maintained by the caller) set each time the tag itself is set. */
+typedef struct {
+  int count;
+  int first;
+  int value;
+  int touch;
+} tre_tag_t;
+
+/* TNFA transition type. A TNFA state is an array of transitions,
+   the terminator is a transition with NULL `state'. */
+typedef struct tnfa_transition tre_tnfa_transition_t;
+
+struct tnfa_transition {
+  /* Range of accepted characters. */
+  tre_cint_t code_min;
+  tre_cint_t code_max;
+  /* Pointer to the destination state. */
+  tre_tnfa_transition_t *state;
+  /* ID number of the destination state. */
+  int state_id;
+  /* -1 terminated array of tags (or NULL). */
+  int *tags;
+  /* Matching parameters settings (or NULL). */
+  int *params;
+  /* Assertion bitmap. */
+  int assertions;
+  /* Assertion parameters. */
+  union {
+    /* Bracket matches. */
+    tre_bracket_match_list_t *bracket_match_list;
+    /* Back reference assertion. */
+    int backref;
+  } u;
+};
+
+/* Assertions. */
+#define ASSERT_AT_BOL		  1   /* Beginning of line. */
+#define ASSERT_AT_EOL		  2   /* End of line. */
+#define ASSERT_BRACKET_MATCH	  4   /* Matches in `bracket_match_list'. */
+#define ASSERT_AT_BOW		  8   /* Beginning of word. */
+#define ASSERT_AT_EOW		 16   /* End of word. */
+#define ASSERT_AT_WB		 32   /* Word boundary. */
+#define ASSERT_AT_WB_NEG	 64   /* Not a word boundary. */
+#define ASSERT_BACKREF		128   /* A back reference in `backref'. */
+#define ASSERT_LAST		128
+
+/* Tag directions. */
+typedef enum {
+  TRE_TAG_MINIMIZE = 0,
+  TRE_TAG_MAXIMIZE,
+  TRE_TAG_LEFT_MAXIMIZE,
+} tre_tag_direction_t;
+
+/* Parameters that can be changed dynamically while matching. */
+typedef enum {
+  TRE_PARAM_COST_INS	    = 0,
+  TRE_PARAM_COST_DEL	    = 1,
+  TRE_PARAM_COST_SUBST	    = 2,
+  TRE_PARAM_COST_MAX	    = 3,
+  TRE_PARAM_MAX_INS	    = 4,
+  TRE_PARAM_MAX_DEL	    = 5,
+  TRE_PARAM_MAX_SUBST	    = 6,
+  TRE_PARAM_MAX_ERR	    = 7,
+  TRE_PARAM_DEPTH	    = 8,
+  TRE_PARAM_LAST	    = 9
+} tre_param_t;
+
+/* Unset matching parameter */
+#define TRE_PARAM_UNSET -1
+
+/* Signifies the default matching parameter value. */
+#define TRE_PARAM_DEFAULT -2
+
+/* Instructions to compute submatch register values from tag values
+   after a successful match.  */
+struct tre_submatch_data {
+  /* Tag that gives the value for rm_so (submatch start offset). */
+  int so_tag;
+  /* Tag that gives the value for rm_eo (submatch end offset). */
+  int eo_tag;
+};
+
+typedef struct tre_submatch_data tre_submatch_data_t;
+
+/* LAST MATCHED structures */
+struct __previous_type;
+struct __previous_branch_type;
+struct __current_type;
+struct __current_branch_type;
+
+typedef struct __previous_branch_type {
+  struct __previous_branch_type *next;
+  struct __previous_type *last_matched; int n_last_matched; int cmp_tag; int n_tags;
+  int tot_branches;
+  int tot_last_matched;
+  int tot_tags;
+  bitstr_t tags[0];
+} tre_last_matched_branch_pre_t;
+
+typedef struct __previous_type {
+  struct __previous_type *next;
+  tre_last_matched_branch_pre_t *branches; int n_branches; int start_tag;
+  int tot_branches;
+  int tot_last_matched;
+  int tot_tags;
+} tre_last_matched_pre_t;
+
+typedef struct __current_branch_type {
+  int *tags;
+  struct __current_type *last_matched; int n_last_matched; int cmp_tag; int n_tags;
+} tre_last_matched_branch_t;
+
+typedef struct __current_type {
+  tre_last_matched_branch_t *branches; int n_branches; int start_tag;
+} tre_last_matched_t;
+
+/* TNFA definition. */
+typedef struct tnfa tre_tnfa_t;
+
+struct tnfa {
+  tre_tnfa_transition_t *transitions;
+  tre_tnfa_transition_t *initial;
+  tre_tnfa_transition_t *final;
+  tre_submatch_data_t *submatch_data;
+  tre_tag_direction_t *tag_directions;
+  int *minimal_tags;
+  tre_last_matched_branch_t *last_matched_branch;
+  locale_t loc;
+  unsigned int num_transitions;
+  int first_char;
+  unsigned int num_submatches;
+  unsigned int num_submatches_invisible;
+  int num_tags;
+  int num_minimals;
+  int end_tag;
+  int num_states;
+  int cflags;
+  int have_backrefs;
+  int num_reorder_tags;
+  int have_approx;
+  int params_depth;
+};
+
+int
+tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags,
+	    locale_t loc);
+
+void
+tre_free(regex_t *preg);
+
+reg_errcode_t
+tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags,
+		const tre_tnfa_t *tnfa, const tre_tag_t *tags, int match_eo);
+
+reg_errcode_t
+tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, int len,
+		      tre_str_type_t type, tre_tag_t *match_tags, int eflags,
+		      int *match_end_ofs);
+
+reg_errcode_t
+tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, const void *string,
+		       int len, tre_str_type_t type, tre_tag_t *match_tags,
+		       int eflags, int *match_end_ofs);
+
+#endif	/* _TRE_INTERNAL_H */
--- /dev/null	Tue Feb 13 07:11:51 2018
+++ new/usr/src/lib/libc/port/regex/tre-match-backtrack.c	Tue Feb 13 07:11:51 2018
@@ -0,0 +1,681 @@
+/*
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+  tre-match-backtrack.c - TRE backtracking regex matching engine
+*/
+
+/*
+  This matcher is for regexps that use back referencing.  Regexp matching
+  with back referencing is an NP-complete problem on the number of back
+  references.  The easiest way to match them is to use a backtracking
+  routine which basically goes through all possible paths in the TNFA
+  and chooses the one which results in the best (leftmost and longest)
+  match.  This can be spectacularly expensive and may run out of stack
+  space, but there really is no better known generic algorithm.	 Quoting
+  Henry Spencer from comp.compilers:
+  <URL: http://compilers.iecc.com/comparch/article/93-03-102>
+
+    POSIX.2 REs require longest match, which is really exciting to
+    implement since the obsolete ("basic") variant also includes
+    \<digit>.  I haven't found a better way of tackling this than doing
+    a preliminary match using a DFA (or simulation) on a modified RE
+    that just replicates subREs for \<digit>, and then doing a
+    backtracking match to determine whether the subRE matches were
+    right.  This can be rather slow, but I console myself with the
+    thought that people who use \<digit> deserve very slow execution.
+    (Pun unintentional but very appropriate.)
+
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "tre-internal.h"
+#include "tre-mem.h"
+#include "tre-match-utils.h"
+#include "tre.h"
+#include "malloc.h"
+
+typedef struct {
+  int pos;
+  unsigned int pos_add_next;
+  const char *str_byte;
+  const wchar_t *str_wide;
+  tre_tnfa_transition_t *state;
+  int state_id;
+  int next_c;
+  tre_tag_t *tags;
+  mbstate_t mbstate;
+} tre_backtrack_item_t;
+
+typedef struct tre_backtrack_struct {
+  tre_backtrack_item_t item;
+  struct tre_backtrack_struct *prev;
+  struct tre_backtrack_struct *next;
+} *tre_backtrack_t;
+
+#define BT_STACK_WIDE_IN(_str_wide)	stack->item.str_wide = (_str_wide)
+#define BT_STACK_WIDE_OUT		(str_wide) = stack->item.str_wide
+
+#define BT_STACK_MBSTATE_IN  stack->item.mbstate = (mbstate)
+#define BT_STACK_MBSTATE_OUT (mbstate) = stack->item.mbstate
+
+#define tre_bt_mem_new		  tre_mem_new
+#define tre_bt_mem_alloc	  tre_mem_alloc
+#define tre_bt_mem_destroy	  tre_mem_destroy
+
+#define BT_STACK_PUSH(_pos, _pos_add_next, _str_byte, _str_wide, _state, _state_id, _next_c, _tags, _mbstate) \
+  do									      \
+    {									      \
+      if (!stack->next)							      \
+	{								      \
+	  tre_backtrack_t s;						      \
+	  s = tre_bt_mem_alloc(mem, sizeof(*s));			      \
+	  if (!s)							      \
+	    {								      \
+	      tre_bt_mem_destroy(mem);					      \
+	      if (tags)							      \
+		free(tags);						      \
+	      if (pmatch)						      \
+		free(pmatch);						      \
+	      if (states_seen)						      \
+		free(states_seen);					      \
+	      return REG_ESPACE;					      \
+	    }								      \
+	  s->prev = stack;						      \
+	  s->next = NULL;						      \
+	  s->item.tags = tre_bt_mem_alloc(mem,				      \
+					  num_tags * sizeof(*tags));          \
+	  if (!s->item.tags)						      \
+	    {								      \
+	      tre_bt_mem_destroy(mem);					      \
+	      if (tags)							      \
+		free(tags);						      \
+	      if (pmatch)						      \
+		free(pmatch);						      \
+	      if (states_seen)						      \
+		free(states_seen);					      \
+	      return REG_ESPACE;					      \
+	    }								      \
+	  stack->next = s;						      \
+	  stack = s;							      \
+	}								      \
+      else								      \
+	stack = stack->next;						      \
+      stack->item.pos = (_pos);						      \
+      stack->item.pos_add_next = (_pos_add_next);			      \
+      stack->item.str_byte = (_str_byte);				      \
+      BT_STACK_WIDE_IN(_str_wide);					      \
+      stack->item.state = (_state);					      \
+      stack->item.state_id = (_state_id);				      \
+      stack->item.next_c = (_next_c);					      \
+      memcpy(stack->item.tags, (_tags), num_tags * sizeof(*(_tags)));         \
+      BT_STACK_MBSTATE_IN;						      \
+    }									      \
+  while (/*CONSTCOND*/0)
+
+#define BT_STACK_POP()							      \
+  do									      \
+    {									      \
+      assert(stack->prev);						      \
+      pos = stack->item.pos;						      \
+      pos_add_next = stack->item.pos_add_next;				      \
+      str_byte = stack->item.str_byte;					      \
+      BT_STACK_WIDE_OUT;						      \
+      state = stack->item.state;					      \
+      next_c = stack->item.next_c;					      \
+      memcpy(tags, stack->item.tags, num_tags * sizeof(*tags));               \
+      BT_STACK_MBSTATE_OUT;						      \
+      stack = stack->prev;						      \
+    }									      \
+  while (/*CONSTCOND*/0)
+
+#undef MIN
+#define MIN(a, b) ((a) <= (b) ? (a) : (b))
+
+reg_errcode_t
+tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, const void *string,
+		       int len, tre_str_type_t type, tre_tag_t *match_tags,
+		       int eflags, int *match_end_ofs)
+{
+  /* State variables required by GET_NEXT_WCHAR. */
+  tre_char_t prev_c = 0, next_c = 0;
+  const char *str_byte = string;
+  int pos = 0;
+  unsigned int pos_add_next = 1;
+  const wchar_t *str_wide = string;
+  mbstate_t mbstate;
+  int reg_notbol = eflags & REG_NOTBOL;
+  int reg_noteol = eflags & REG_NOTEOL;
+  int reg_newline = tnfa->cflags & REG_NEWLINE;
+  int i;
+
+  /* These are used to remember the necessary values of the above
+     variables to return to the position where the current search
+     started from. */
+  int next_c_start;
+  const char *str_byte_start;
+  int pos_start = -1;
+  const wchar_t *str_wide_start;
+  mbstate_t mbstate_start;
+
+  /* End offset of best match so far, or -1 if no match found yet. */
+  int match_eo = -1;
+  /* Tag arrays. */
+  int *next_tags;
+  tre_tag_t *tags = NULL;
+  /* Current TNFA state. */
+  tre_tnfa_transition_t *state;
+  int *states_seen = NULL;
+
+  /* Memory allocator to for allocating the backtracking stack. */
+  tre_mem_t mem = tre_bt_mem_new();
+
+  /* The backtracking stack. */
+  tre_backtrack_t stack;
+
+  tre_tnfa_transition_t *trans_i;
+  regmatch_t *pmatch = NULL;
+  reg_errcode_t ret;
+
+  int num_tags = tnfa->num_tags;
+  int touch = 1;
+  char *buf;
+  int tbytes;
+
+  memset(&mbstate, '\0', sizeof(mbstate));
+  buf = NULL;
+
+  if (!mem)
+    return REG_ESPACE;
+  stack = tre_bt_mem_alloc(mem, sizeof(*stack));
+  if (!stack)
+    {
+      ret = REG_ESPACE;
+      goto error_exit;
+    }
+  stack->prev = NULL;
+  stack->next = NULL;
+
+  DPRINT(("tnfa_execute_backtrack, input type %d\n", type));
+  DPRINT(("len = %d\n", len));
+
+  {
+    int pbytes, sbytes, total_bytes;
+    char *tmp_buf;
+    /* Compute the length of the block we need. */
+    tbytes = sizeof(*tags) * num_tags;
+    pbytes = sizeof(*pmatch) * tnfa->num_submatches;
+    sbytes = sizeof(*states_seen) * tnfa->num_states;
+    total_bytes =
+      (sizeof(long) - 1) * 2 /* for alignment paddings */
+      + tbytes + pbytes + sbytes;
+
+    DPRINT(("tre_tnfa_run_backtrack, allocate %d bytes\n", total_bytes));
+    /* Allocate the memory. */
+    buf = malloc((unsigned)total_bytes);
+    if (buf == NULL)
+      return REG_ESPACE;
+
+    /* Get the various pointers within tmp_buf (properly aligned). */
+    tags = (void *)buf;
+    tmp_buf = buf + tbytes;
+    tmp_buf += ALIGN(tmp_buf, long);
+    pmatch = (void *)tmp_buf;
+    tmp_buf += pbytes;
+    tmp_buf += ALIGN(tmp_buf, long);
+    states_seen = (void *)tmp_buf;
+  }
+
+ retry:
+  {
+    memset(tags, 0, num_tags * sizeof(*tags));
+    if (match_tags)
+      memset(match_tags, 0, num_tags * sizeof(*match_tags));
+    for (i = 0; i < tnfa->num_states; i++)
+      states_seen[i] = 0;
+  }
+
+  state = NULL;
+  pos = pos_start;
+  GET_NEXT_WCHAR();
+  pos_start = pos;
+  next_c_start = next_c;
+  str_byte_start = str_byte;
+  str_wide_start = str_wide;
+  mbstate_start = mbstate;
+
+  /* Handle initial states. */
+  next_tags = NULL;
+  for (trans_i = tnfa->initial; trans_i->state; trans_i++)
+    {
+      DPRINT(("> init %p, prev_c %lc\n", trans_i->state, (tre_cint_t)prev_c));
+      if (trans_i->assertions && CHECK_ASSERTIONS(trans_i->assertions))
+	{
+	  DPRINT(("assert failed\n"));
+	  continue;
+	}
+      if (state == NULL)
+	{
+	  /* Start from this state. */
+	  state = trans_i->state;
+	  next_tags = trans_i->tags;
+	}
+      else
+	{
+	  /* Backtrack to this state. */
+	  DPRINT(("saving state %d for backtracking\n", trans_i->state_id));
+	  BT_STACK_PUSH(pos, pos_add_next, str_byte, str_wide, trans_i->state,
+			trans_i->state_id, next_c, tags, mbstate);
+	  {
+	    int *tmp = trans_i->tags;
+	    if (tmp)
+	      {
+		while (*tmp >= 0)
+		  tre_tag_set(stack->item.tags, *tmp++, pos, touch);
+		touch++;
+	      }
+	  }
+	}
+    }
+
+  if (next_tags)
+    {
+      for (; *next_tags >= 0; next_tags++)
+	tre_tag_set(tags, *next_tags, pos, touch);
+      touch++;
+    }
+
+  DPRINT(("entering match loop, pos %d, str_byte %p\n", pos, str_byte));
+  DPRINT(("pos:chr/code | state and tags\n"));
+  DPRINT(("-------------+------------------------------------------------\n"));
+
+  if (state == NULL)
+    goto backtrack;
+
+  while (/*CONSTCOND*/1)
+    {
+      tre_tnfa_transition_t *next_state;
+      int empty_br_match;
+
+      DPRINT(("start loop\n"));
+
+      if (match_eo >= 0 && tnfa->num_minimals)
+	{
+	  int skip = 0;
+#ifdef TRE_DEBUG
+	  DPRINT(("Checking minimal conditions: match_eo=%d match_tags=",
+		  match_eo));
+	  tre_print_tags(match_tags, tnfa->num_tags);
+	  DPRINT(("\n"));
+#endif /* TRE_DEBUG */
+	  for (i = 0; tnfa->minimal_tags[i] >= 0; i += 2)
+	    {
+	      int end = tnfa->minimal_tags[i];
+	      int start = tnfa->minimal_tags[i + 1];
+	      DPRINT(("  Minimal start %d, end %d\n", start, end));
+	      if (tre_minimal_tag_order(start, end, match_tags, tags) > 0)
+		{
+		  skip = 1;
+		  break;
+		}
+	    }
+	  if (!skip)
+	    {
+#ifdef TRE_DEBUG
+	      DPRINT(("	 Keeping tags="));
+	      tre_print_tags(tags, tnfa->num_tags);
+	      DPRINT(("\n"));
+#endif /* TRE_DEBUG */
+	    }
+	  else
+	    {
+#ifdef TRE_DEBUG
+	      DPRINT(("	 Throwing out tags="));
+	      tre_print_tags(tags, tnfa->num_tags);
+	      DPRINT(("\n"));
+#endif /* TRE_DEBUG */
+	      goto backtrack;
+	    }
+	}
+
+      if (state == tnfa->final)
+	{
+	  DPRINT(("  match found, match_eo=%d pos=%d\n", match_eo, pos));
+
+	  if (match_eo >= 0 && tnfa->num_minimals)
+	    {
+	      int compare = 0;
+#ifdef TRE_DEBUG
+	      DPRINT(("Checking minimal conditions: match_eo=%d "
+		      "match_tags=", match_eo));
+	      tre_print_tags(match_tags, tnfa->num_tags);
+	      DPRINT(("\n"));
+#endif /* TRE_DEBUG */
+	      for (i = 0; tnfa->minimal_tags[i] >= 0; i += 2)
+		{
+		  int end = tnfa->minimal_tags[i];
+		  int start = tnfa->minimal_tags[i + 1];
+		  DPRINT(("  Minimal start %d, end %d\n", start, end));
+		  if ((compare = tre_minimal_tag_order(start, end,
+					   match_tags, tags)) != 0)
+		    break;
+		}
+	      if (compare > 0)
+		{
+#ifdef TRE_DEBUG
+		  DPRINT(("	 Throwing out new match, tags="));
+		  tre_print_tags(tags, tnfa->num_tags);
+		  DPRINT(("\n"));
+#endif /* TRE_DEBUG */
+		  goto backtrack;
+		}
+	      else if (compare < 0)
+		{
+#ifdef TRE_DEBUG
+		  DPRINT(("	 Throwing out old match, tags="));
+		  tre_print_tags(match_tags, tnfa->num_tags);
+		  DPRINT(("\n"));
+#endif /* TRE_DEBUG */
+		  match_eo = -1;
+		}
+	    }
+
+	  if (match_eo < pos
+	      || (match_eo == pos
+		  && match_tags
+		  && tre_tag_order(tnfa->num_tags, tnfa->tag_directions,
+				   tags, match_tags)))
+	    {
+	      /* This match wins the previous match. */
+#ifdef TRE_DEBUG
+	      DPRINT(("	 win previous tags="));
+	      tre_print_tags(tags, tnfa->num_tags);
+	      DPRINT(("\n"));
+#endif /* TRE_DEBUG */
+	      match_eo = pos;
+	      if (match_tags)
+		memcpy(match_tags, tags, num_tags * sizeof(*tags));
+	    }
+	  /* Our TNFAs never have transitions leaving from the final state,
+	     so we jump right to backtracking. */
+	  goto backtrack;
+	}
+
+#ifdef TRE_DEBUG
+      DPRINT(("%3d:%2lc/%05d | %p ", pos, (tre_cint_t)next_c, (int)next_c,
+	      state));
+      tre_print_tags(tags, tnfa->num_tags);
+      DPRINT(("\n"));
+#endif /* TRE_DEBUG */
+
+      /* Go to the next character in the input string. */
+      empty_br_match = 0;
+      trans_i = state;
+      if (trans_i->state && trans_i->assertions & ASSERT_BACKREF)
+	{
+	  /* This is a back reference state.  All transitions leaving from
+	     this state have the same back reference "assertion".  Instead
+	     of reading the next character, we match the back reference. */
+	  int so, eo, bt = trans_i->u.backref;
+	  int bt_len;
+	  int result;
+
+	  DPRINT(("  should match back reference %d\n", bt));
+	  /* Get the substring we need to match against.  Remember to
+	     turn off REG_NOSUB temporarily. */
+	  ret = tre_fill_pmatch(bt + 1, pmatch, tnfa->cflags & ~REG_NOSUB,
+			  tnfa, tags, pos);
+	  if (ret != REG_OK) goto error_exit;
+	  so = pmatch[bt].rm_so;
+	  eo = pmatch[bt].rm_eo;
+	  bt_len = eo - so;
+
+#ifdef TRE_DEBUG
+	  {
+	    int slen;
+	    if (len < 0)
+	      slen = bt_len;
+	    else
+	      slen = MIN(bt_len, len - pos);
+
+	    if (type == STR_BYTE)
+	      {
+		DPRINT(("  substring (len %d) is [%d, %d]: '%.*s'\n",
+			bt_len, so, eo, bt_len, (char*)string + so));
+		DPRINT(("  current string is '%.*s'\n", slen, str_byte - 1));
+	      }
+	    else if (type == STR_WIDE)
+	      {
+		DPRINT(("  substring (len %d) is [%d, %d]: '%.*" STRF "'\n",
+			bt_len, so, eo, bt_len, (wchar_t*)string + so));
+		DPRINT(("  current string is '%.*" STRF "'\n",
+			slen, str_wide - 1));
+	      }
+	  }
+#endif
+
+	  if (so < 0)
+	    {
+	      result = 1; /* Back reference of nomatch doesn't match */
+	    }
+	  else if (len < 0)
+	    {
+	      if (type == STR_WIDE)
+		result = wcsncmp((const wchar_t*)string + so, str_wide - 1,
+				 (size_t)bt_len);
+	      else
+		result = strncmp((const char*)string + so, str_byte - 1,
+				 (size_t)bt_len);
+	    }
+	  else if (len - pos < bt_len)
+	    result = 1;
+	  else if (type == STR_WIDE)
+	    result = wmemcmp((const wchar_t*)string + so, str_wide - 1,
+			     (size_t)bt_len);
+	  else
+	    result = memcmp((const char*)string + so, str_byte - 1,
+			    (size_t)bt_len);
+
+	  if (result == 0)
+	    {
+	      /* Back reference matched.  Check for infinite loop. */
+	      if (bt_len == 0)
+		empty_br_match = 1;
+	      if (empty_br_match && states_seen[trans_i->state_id])
+		{
+		  DPRINT(("  avoid loop\n"));
+		  goto backtrack;
+		}
+
+	      states_seen[trans_i->state_id] = empty_br_match;
+
+	      /* Advance in input string and resync `prev_c', `next_c'
+		 and pos. */
+	      DPRINT(("	 back reference matched\n"));
+	      str_byte += bt_len - 1;
+	      str_wide += bt_len - 1;
+	      pos += bt_len - 1;
+	      GET_NEXT_WCHAR();
+	      DPRINT(("	 pos now %d\n", pos));
+	    }
+	  else
+	    {
+	      DPRINT(("	 back reference did not match\n"));
+	      goto backtrack;
+	    }
+	}
+      else
+	{
+	  /* Check for end of string. */
+	  if (len < 0)
+	    {
+	      if (next_c == L'\0')
+		goto backtrack;
+	    }
+	  else
+	    {
+	      if (pos >= len)
+		goto backtrack;
+	    }
+
+	  /* Read the next character. */
+	  GET_NEXT_WCHAR();
+	}
+
+      next_state = NULL;
+      for (trans_i = state; trans_i->state; trans_i++)
+	{
+	  DPRINT(("  transition %d-%d (%c-%c) %d to %d\n",
+		  trans_i->code_min, trans_i->code_max,
+		  trans_i->code_min, trans_i->code_max,
+		  trans_i->assertions, trans_i->state_id));
+	  if (trans_i->code_min <= (tre_cint_t)prev_c
+	      && trans_i->code_max >= (tre_cint_t)prev_c)
+	    {
+	      if (trans_i->assertions
+		  && (CHECK_ASSERTIONS(trans_i->assertions)
+		      || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags)))
+		{
+		  DPRINT(("  assertion failed\n"));
+		  continue;
+		}
+
+	      if (next_state == NULL)
+		{
+		  /* First matching transition. */
+		  DPRINT(("  Next state is %d\n", trans_i->state_id));
+		  next_state = trans_i->state;
+		  next_tags = trans_i->tags;
+		}
+	      else
+		{
+		  /* Second matching transition.  We may need to backtrack here
+		     to take this transition instead of the first one, so we
+		     push this transition in the backtracking stack so we can
+		     jump back here if needed. */
+		  DPRINT(("  saving state %d for backtracking\n",
+			  trans_i->state_id));
+		  BT_STACK_PUSH(pos, pos_add_next, str_byte, str_wide,
+				trans_i->state, trans_i->state_id, next_c,
+				tags, mbstate);
+		  {
+		    int *tmp;
+		    for (tmp = trans_i->tags; tmp && *tmp >= 0; tmp++)
+		      tre_tag_set(stack->item.tags, *tmp, pos, touch);
+		    touch++;
+		  }
+#if 0 /* XXX - it's important not to look at all transitions here to keep
+	 the stack small! */
+		  break;
+#endif
+		}
+	    }
+	}
+
+      if (next_state != NULL)
+	{
+	  /* Matching transitions were found.  Take the first one. */
+	  state = next_state;
+
+	  /* Update the tag values. */
+	  if (next_tags)
+	    {
+	      while (*next_tags >= 0)
+		tre_tag_set(tags, *next_tags++, pos, touch);
+	      touch++;
+	    }
+	}
+      else
+	{
+	backtrack:
+	  /* A matching transition was not found.  Try to backtrack. */
+	  if (stack->prev)
+	    {
+	      DPRINT(("	 backtracking\n"));
+	      if (stack->item.state->assertions & ASSERT_BACKREF)
+		{
+		  DPRINT(("  states_seen[%d] = 0\n",
+			  stack->item.state_id));
+		  states_seen[stack->item.state_id] = 0;
+		}
+
+	      BT_STACK_POP();
+	    }
+	  else if (match_eo < 0)
+	    {
+	      /* Try starting from a later position in the input string. */
+	      /* Check for end of string. */
+	      if (pos == pos_start)
+		{
+		  if (len < 0)
+		    {
+		      if (next_c == L'\0')
+			{
+			  DPRINT(("end of string.\n"));
+			  break;
+			}
+		    }
+		  else
+		    {
+		      if (pos >= len)
+			{
+			  DPRINT(("end of string.\n"));
+			  break;
+			}
+		    }
+		}
+	      DPRINT(("restarting from next start position\n"));
+	      next_c = next_c_start;
+	      mbstate = mbstate_start;
+	      str_byte = str_byte_start;
+	      str_wide = str_wide_start;
+	      goto retry;
+	    }
+	  else
+	    {
+	      DPRINT(("finished\n"));
+	      break;
+	    }
+	}
+    }
+
+  ret = match_eo >= 0 ? REG_OK : REG_NOMATCH;
+  *match_end_ofs = match_eo;
+
+ error_exit:
+  tre_bt_mem_destroy(mem);
+  if (buf)
+    free(buf);
+
+  return ret;
+}
--- /dev/null	Tue Feb 13 07:11:51 2018
+++ new/usr/src/lib/libc/port/regex/tre-match-parallel.c	Tue Feb 13 07:11:51 2018
@@ -0,0 +1,552 @@
+/*
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+  tre-match-parallel.c - TRE parallel regex matching engine
+*/
+
+/*
+  This algorithm searches for matches basically by reading characters
+  in the searched string one by one, starting at the beginning.	 All
+  matching paths in the TNFA are traversed in parallel.	 When two or
+  more paths reach the same state, exactly one is chosen according to
+  tag ordering rules; if returning submatches is not required it does
+  not matter which path is chosen.
+
+  The worst case time required for finding the leftmost and longest
+  match, or determining that there is no match, is always linearly
+  dependent on the length of the text being searched.
+
+  This algorithm cannot handle TNFAs with back referencing nodes.
+  See `tre-match-backtrack.c'.
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "tre-internal.h"
+#include "tre-match-utils.h"
+#include "tre.h"
+
+typedef struct {
+  tre_tnfa_transition_t *state;
+  tre_tag_t *tags;
+} tre_tnfa_reach_t;
+
+typedef struct {
+  int pos;
+  tre_tag_t **tags;
+} tre_reach_pos_t;
+
+#ifdef TRE_DEBUG
+static void
+tre_print_reach1(tre_tnfa_transition_t *state, tre_tag_t *tags, int num_tags)
+{
+  DPRINT((" %p", (void *)state));
+  if (num_tags > 0)
+    {
+      DPRINT(("/"));
+      tre_print_tags(tags, num_tags);
+    }
+}
+
+static void
+tre_print_reach(const tre_tnfa_t *tnfa, tre_tnfa_reach_t *reach, int num_tags)
+{
+  while (reach->state != NULL)
+    {
+      tre_print_reach1(reach->state, reach->tags, num_tags);
+      reach++;
+    }
+  DPRINT(("\n"));
+
+}
+#endif /* TRE_DEBUG */
+
+reg_errcode_t
+tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, int len,
+		      tre_str_type_t type, tre_tag_t *match_tags, int eflags,
+		      int *match_end_ofs)
+{
+  /* State variables required by GET_NEXT_WCHAR. */
+  tre_char_t prev_c = 0, next_c = 0;
+  const char *str_byte = string;
+  int pos = -1;
+  unsigned int pos_add_next = 1;
+  const wchar_t *str_wide = string;
+  mbstate_t mbstate;
+  int reg_notbol = eflags & REG_NOTBOL;
+  int reg_noteol = eflags & REG_NOTEOL;
+  int reg_newline = tnfa->cflags & REG_NEWLINE;
+
+  char *buf;
+  tre_tnfa_transition_t *trans_i;
+  tre_tnfa_reach_t *reach, *reach_next, *reach_i, *reach_next_i;
+  tre_reach_pos_t *reach_pos;
+  int *tag_i;
+  int num_tags, i;
+
+  int match_eo = -1;	   /* end offset of match (-1 if no match found yet) */
+#ifdef TRE_DEBUG
+  int once;
+#endif /* TRE_DEBUG */
+  tre_tag_t *tmp_tags = NULL;
+  tre_tag_t *tmp_iptr;
+  int tbytes;
+  int touch = 1;
+
+  memset(&mbstate, '\0', sizeof(mbstate));
+
+  DPRINT(("tre_tnfa_run_parallel, input type %d\n", type));
+
+  if (!match_tags)
+    num_tags = 0;
+  else
+    num_tags = tnfa->num_tags;
+
+  /* Allocate memory for temporary data required for matching.	This needs to
+     be done for every matching operation to be thread safe.  This allocates
+     everything in a single large block from the stack frame using alloca()
+     or with malloc() if alloca is unavailable. */
+  {
+    int rbytes, pbytes, total_bytes;
+    char *tmp_buf;
+    /* Compute the length of the block we need. */
+    tbytes = sizeof(*tmp_tags) * num_tags;
+    rbytes = sizeof(*reach_next) * (tnfa->num_states + 1);
+    pbytes = sizeof(*reach_pos) * tnfa->num_states;
+    total_bytes =
+      (sizeof(long) - 1) * 4 /* for alignment paddings */
+      + (rbytes + tbytes * tnfa->num_states) * 2 + tbytes + pbytes;
+
+    DPRINT(("tre_tnfa_run_parallel, allocate %d bytes\n", total_bytes));
+    /* Allocate the memory. */
+    buf = malloc((unsigned)total_bytes);
+    if (buf == NULL)
+      return REG_ESPACE;
+    memset(buf, 0, (size_t)total_bytes);
+
+    /* Get the various pointers within tmp_buf (properly aligned). */
+    tmp_tags = (void *)buf;
+    tmp_buf = buf + tbytes;
+    tmp_buf += ALIGN(tmp_buf, long);
+    reach_next = (void *)tmp_buf;
+    tmp_buf += rbytes;
+    tmp_buf += ALIGN(tmp_buf, long);
+    reach = (void *)tmp_buf;
+    tmp_buf += rbytes;
+    tmp_buf += ALIGN(tmp_buf, long);
+    reach_pos = (void *)tmp_buf;
+    tmp_buf += pbytes;
+    tmp_buf += ALIGN(tmp_buf, long);
+    for (i = 0; i < tnfa->num_states; i++)
+      {
+	reach[i].tags = (void *)tmp_buf;
+	tmp_buf += tbytes;
+	reach_next[i].tags = (void *)tmp_buf;
+	tmp_buf += tbytes;
+      }
+  }
+
+  for (i = 0; i < tnfa->num_states; i++)
+    reach_pos[i].pos = -1;
+
+  /* If only one character can start a match, find it first. */
+  if (tnfa->first_char >= 0 && str_byte)
+    {
+      const char *orig_str = str_byte;
+      int first = tnfa->first_char;
+      int found_high_bit = 0;
+
+      if (type == STR_BYTE)
+	{
+	  if (len >= 0)
+	    str_byte = memchr(orig_str, first, (size_t)len);
+	  else
+	    str_byte = strchr(orig_str, first);
+	}
+      else if (type == STR_MBS)
+	{
+	  /*
+	   * If the match character is ASCII, try to match the character
+	   * directly, but if a high bit character is found, we stop there.
+	   */
+	  if (first < 0x80)
+	    {
+	      if (len >= 0)
+		{
+		  int i;
+		  for (i = 0; ; str_byte++, i++)
+		    {
+		      if (i >= len)
+			{
+			  str_byte = NULL;
+			  break;
+			}
+		      if (*str_byte == first)
+			break;
+		      if (*str_byte & 0x80)
+			{
+			  found_high_bit = 1;
+			  break;
+			}
+		    }
+		}
+	      else
+		{
+		  for (; ; str_byte++)
+		    {
+		      if (!*str_byte)
+			{
+			  str_byte = NULL;
+			  break;
+			}
+		      if (*str_byte == first)
+			break;
+		      if (*str_byte & 0x80)
+			{
+			  found_high_bit = 1;
+			  break;
+			}
+		    }
+		}
+	    }
+	  else
+	    {
+	      if (len >= 0)
+		{
+		  int i;
+		  for (i = 0; ; str_byte++, i++)
+		    {
+		      if (i >= len)
+			{
+			  str_byte = NULL;
+			  break;
+			}
+		      if (*str_byte & 0x80)
+			{
+			  found_high_bit = 1;
+			  break;
+			}
+		    }
+		}
+	      else
+		{
+		  for (; ; str_byte++)
+		    {
+		      if (!*str_byte)
+			{
+			  str_byte = NULL;
+			  break;
+			}
+		      if (*str_byte & 0x80)
+			{
+			  found_high_bit = 1;
+			  break;
+			}
+		    }
+		}
+	    }
+	}
+      if (str_byte == NULL)
+	{
+	  if (buf)
+	    free(buf);
+	  return REG_NOMATCH;
+	}
+      DPRINT(("skipped %lu chars\n", (unsigned long)(str_byte - orig_str)));
+      if (!found_high_bit)
+	{
+	  if (str_byte >= orig_str + 1)
+	    prev_c = (unsigned char)*(str_byte - 1);
+	  next_c = (unsigned char)*str_byte;
+	  pos = str_byte - orig_str;
+	  if (len < 0 || pos < len)
+	    str_byte++;
+	}
+      else
+	{
+	  if (str_byte == orig_str)
+	    goto no_first_optimization;
+	  /*
+	   * Back up one character, fix up the position, then call
+	   * GET_NEXT_WCHAR() to process the multibyte character.
+	   */
+	  /* no need to set prev_c, since GET_NEXT_WCHAR will overwrite */
+	  next_c = (unsigned char)*(str_byte - 1);
+	  pos = (str_byte - 1) - orig_str;
+	  GET_NEXT_WCHAR();
+	}
+    }
+  else
+    {
+no_first_optimization:
+      GET_NEXT_WCHAR();
+      pos = 0;
+    }
+
+  DPRINT(("length: %d\n", len));
+  DPRINT(("pos:chr/code | states and tags\n"));
+  DPRINT(("-------------+------------------------------------------------\n"));
+
+  reach_next_i = reach_next;
+  while (/*CONSTCOND*/1)
+    {
+      /* If no match found yet, add the initial states to `reach_next'. */
+      if (match_eo < 0)
+	{
+	  DPRINT((" init >"));
+	  trans_i = tnfa->initial;
+	  while (trans_i->state != NULL)
+	    {
+	      if (reach_pos[trans_i->state_id].pos < pos)
+		{
+		  if (trans_i->assertions
+		      && CHECK_ASSERTIONS(trans_i->assertions))
+		    {
+		      DPRINT(("assertion failed\n"));
+		      trans_i++;
+		      continue;
+		    }
+
+		  DPRINT((" %p", (void *)trans_i->state));
+		  reach_next_i->state = trans_i->state;
+		  memset(reach_next_i->tags, 0, tbytes);
+		  tag_i = trans_i->tags;
+		  if (tag_i)
+		    {
+		      while (*tag_i >= 0)
+			{
+			  if (*tag_i < num_tags)
+			    tre_tag_set(reach_next_i->tags, *tag_i, pos, touch);
+			  tag_i++;
+			}
+			touch++;
+		    }
+		  if (reach_next_i->state == tnfa->final)
+		    {
+		      DPRINT(("	 found empty match\n"));
+		      match_eo = pos;
+		      memcpy(match_tags, reach_next_i->tags, tbytes);
+		    }
+		  reach_pos[trans_i->state_id].pos = pos;
+		  reach_pos[trans_i->state_id].tags = &reach_next_i->tags;
+		  reach_next_i++;
+		}
+	      trans_i++;
+	    }
+	  DPRINT(("\n"));
+	  reach_next_i->state = NULL;
+	}
+      else
+	{
+	  if (num_tags == 0 || reach_next_i == reach_next)
+	    /* We have found a match. */
+	    break;
+	}
+
+      /* Check for end of string. */
+      if (len < 0)
+	{
+	  if (next_c == L'\0')
+	    break;
+	}
+      else
+	{
+	  if (pos >= len)
+	    break;
+	}
+
+      GET_NEXT_WCHAR();
+
+#ifdef TRE_DEBUG
+      DPRINT(("%3d:%2lc/%05d |", pos - 1, (tre_cint_t)prev_c, (int)prev_c));
+      tre_print_reach(tnfa, reach_next, num_tags);
+#endif /* TRE_DEBUG */
+
+      /* Swap `reach' and `reach_next'. */
+      reach_i = reach;
+      reach = reach_next;
+      reach_next = reach_i;
+
+#ifdef TRE_DEBUG
+      once = 0;
+#endif /* TRE_DEBUG */
+
+      /* For each state in `reach' see if there is a transition leaving with
+	 the current input symbol to a state not yet in `reach_next', and
+	 add the destination states to `reach_next'. */
+      reach_next_i = reach_next;
+      for (reach_i = reach; reach_i->state; reach_i++)
+	{
+	  for (trans_i = reach_i->state; trans_i->state; trans_i++)
+	    {
+	      /* Does this transition match the input symbol? */
+	      if (trans_i->code_min <= (tre_cint_t)prev_c &&
+		  trans_i->code_max >= (tre_cint_t)prev_c)
+		{
+		  if (trans_i->assertions
+		      && (CHECK_ASSERTIONS(trans_i->assertions)
+			  || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags)))
+		    {
+		      DPRINT(("assertion failed\n"));
+		      continue;
+		    }
+
+		  /* Compute the tags after this transition. */
+		  memcpy(tmp_tags, reach_i->tags, tbytes);
+		  tag_i = trans_i->tags;
+		  if (tag_i != NULL)
+		    {
+		      while (*tag_i >= 0)
+			{
+			  if (*tag_i < num_tags)
+			    tre_tag_set(tmp_tags, *tag_i, pos, touch);
+			  tag_i++;
+			}
+			touch++;
+		    }
+
+		  /* For each new transition, weed out those that don't
+		     fulfill the minimal matching conditions. */
+		  if (tnfa->num_minimals && match_eo >= 0)
+		    {
+		      int skip = 0;
+#ifdef TRE_DEBUG
+		      if (!once)
+			{
+			  DPRINT(("Checking minimal conditions: match_eo=%d "
+				  "match_tags=", match_eo));
+			  tre_print_tags(match_tags, num_tags);
+			  DPRINT(("\n"));
+			  once++;
+			}
+#endif /* TRE_DEBUG */
+		      for (i = 0; tnfa->minimal_tags[i] >= 0; i += 2)
+			{
+			   int end = tnfa->minimal_tags[i];
+			   int start = tnfa->minimal_tags[i + 1];
+			   DPRINT(("  Minimal start %d, end %d\n", start, end));
+			   if (tre_minimal_tag_order(start, end, match_tags,
+						     tmp_tags) > 0)
+			     {
+				skip = 1;
+				break;
+			     }
+			}
+		      if (skip)
+			{
+#ifdef TRE_DEBUG
+			   DPRINT(("	 Throwing out"));
+			   tre_print_reach1(reach_i->state, tmp_tags,
+					     num_tags);
+			   DPRINT(("\n"));
+#endif /* TRE_DEBUG */
+			   continue;
+			}
+		    }
+
+		  if (reach_pos[trans_i->state_id].pos < pos)
+		    {
+		      /* Found an unvisited node. */
+		      reach_next_i->state = trans_i->state;
+		      tmp_iptr = reach_next_i->tags;
+		      reach_next_i->tags = tmp_tags;
+		      tmp_tags = tmp_iptr;
+		      reach_pos[trans_i->state_id].pos = pos;
+		      reach_pos[trans_i->state_id].tags = &reach_next_i->tags;
+
+		      if (reach_next_i->state == tnfa->final
+			  && (match_eo == -1
+			      || (num_tags > 0
+				  && tre_tag_get(reach_next_i->tags, 0) <=
+				  tre_tag_get(match_tags, 0))))
+			{
+#ifdef TRE_DEBUG
+			  DPRINT(("  found match"));
+			  tre_print_reach1(trans_i->state, reach_next_i->tags, num_tags);
+			  DPRINT(("\n"));
+#endif /* TRE_DEBUG */
+			  match_eo = pos;
+			  memcpy(match_tags, reach_next_i->tags, tbytes);
+			}
+		      reach_next_i++;
+
+		    }
+		  else
+		    {
+		      assert(reach_pos[trans_i->state_id].pos == pos);
+		      /* Another path has also reached this state.  We choose
+			 the winner by examining the tag values for both
+			 paths. */
+		      if (tre_tag_order(num_tags, tnfa->tag_directions,
+					tmp_tags,
+					*reach_pos[trans_i->state_id].tags))
+			{
+			  /* The new path wins. */
+			  tmp_iptr = *reach_pos[trans_i->state_id].tags;
+			  *reach_pos[trans_i->state_id].tags = tmp_tags;
+			  if (trans_i->state == tnfa->final)
+			    {
+#ifdef TRE_DEBUG
+			      DPRINT(("	 found better match"));
+			      tre_print_reach1(trans_i->state, tmp_tags, num_tags);
+			      DPRINT(("\n"));
+#endif /* TRE_DEBUG */
+			      match_eo = pos;
+			      memcpy(match_tags, tmp_tags, tbytes);
+			    }
+			  tmp_tags = tmp_iptr;
+			}
+		    }
+		}
+	    }
+	}
+      reach_next_i->state = NULL;
+    }
+
+  DPRINT(("match end offset = %d\n", match_eo));
+
+  *match_end_ofs = match_eo;
+#ifdef TRE_DEBUG
+  if (match_tags)
+    {
+      DPRINT(("Winning tags="));
+      tre_print_tags_all(match_tags, num_tags);
+      DPRINT((" touch=%d\n", touch));
+    }
+#endif /* TRE_DEBUG */
+
+  if (buf)
+    free(buf);
+
+  return match_eo >= 0 ? REG_OK : REG_NOMATCH;
+}
--- /dev/null	Tue Feb 13 07:11:51 2018
+++ new/usr/src/lib/libc/port/regex/tre-match-utils.h	Tue Feb 13 07:11:51 2018
@@ -0,0 +1,472 @@
+/*
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+  tre-match-utils.h - TRE matcher helper definitions
+*/
+
+#include "tre-internal.h"
+
+#define str_source ((const tre_str_source*)string)
+
+/*
+ * Because all multibyte encodings are exclusively single-shift encoding,
+ * with the shift codes having the high bit set, we can make an optimization
+ * for STR_MBS that only calls tre_mbrtowc_l() when a high-bit character
+ * is detected, and just do a direct copy for ASCII characters.
+ */
+#define GET_NEXT_WCHAR()						      \
+  do {									      \
+    prev_c = next_c;							      \
+    switch (type)							      \
+      {									      \
+      case STR_BYTE:							      \
+	pos++;								      \
+	if (len >= 0 && pos >= len)					      \
+	  next_c = '\0';						      \
+	else								      \
+	  next_c = (unsigned char)(*str_byte++);			      \
+	break;								      \
+      case STR_WIDE:							      \
+	pos++;								      \
+	if (len >= 0 && pos >= len)					      \
+	  next_c = L'\0';						      \
+	else								      \
+	  next_c = *str_wide++;						      \
+	break;								      \
+      case STR_MBS:							      \
+	pos += pos_add_next;					      	      \
+	if (__builtin_expect(len >= 0 && pos >= len, 0))		      \
+	  {								      \
+	    next_c = L'\0';						      \
+	    pos_add_next = 1;						      \
+	  }								      \
+	else if (__builtin_expect(!(*str_byte & 0x80), 1))		      \
+	  {								      \
+	    next_c = (unsigned char)(*str_byte++);			      \
+	    pos_add_next = 1;						      \
+	  }								      \
+	else								      \
+	  {								      \
+	    size_t w;							      \
+	    int max;							      \
+	    if (len >= 0)						      \
+	      max = len - pos;						      \
+	    else							      \
+	      max = 32;							      \
+	    w = tre_mbrtowc_l(&next_c, str_byte, (size_t)max, &mbstate,	      \
+			      tnfa->loc);				      \
+	    if (w == (size_t)-1 || w == (size_t)-2)			      \
+	      return REG_NOMATCH;					      \
+	    if (w == 0 && len >= 0)					      \
+	      {								      \
+		pos_add_next = 1;					      \
+		next_c = 0;						      \
+		str_byte++;						      \
+	      }								      \
+	    else							      \
+	      {								      \
+		pos_add_next = w;					      \
+		str_byte += w;						      \
+	      }								      \
+	  }								      \
+	break;								      \
+      }									      \
+  } while(/*CONSTCOND*/0)
+
+/* Assumes tre_tnfa_t *tnfa in scope */
+#define IS_WORD_CHAR(c)	 ((c) == L'_' || tre_isalnum_l(c, tnfa->loc))
+
+#define CHECK_ASSERTIONS(assertions)					      \
+  (((assertions & ASSERT_AT_BOL)					      \
+    && (pos > 0 || reg_notbol)						      \
+    && (prev_c != L'\n' || !reg_newline))				      \
+   || ((assertions & ASSERT_AT_EOL)					      \
+       && (next_c != L'\0' || reg_noteol)				      \
+       && (next_c != L'\n' || !reg_newline))				      \
+   || ((assertions & ASSERT_AT_BOW)					      \
+       && (IS_WORD_CHAR(prev_c) || !IS_WORD_CHAR(next_c)))	              \
+   || ((assertions & ASSERT_AT_EOW)					      \
+       && (!IS_WORD_CHAR(prev_c) || IS_WORD_CHAR(next_c)))		      \
+   || ((assertions & ASSERT_AT_WB)					      \
+       && (pos != 0 && next_c != L'\0'					      \
+	   && IS_WORD_CHAR(prev_c) == IS_WORD_CHAR(next_c)))		      \
+   || ((assertions & ASSERT_AT_WB_NEG)					      \
+       && (pos == 0 || next_c == L'\0'					      \
+	   || IS_WORD_CHAR(prev_c) != IS_WORD_CHAR(next_c))))
+
+#define CHECK_CHAR_CLASSES(trans_i, tnfa, eflags)                             \
+  ((trans_i->assertions & ASSERT_BRACKET_MATCH)                               \
+    && !tre_bracket_match(trans_i->u.bracket_match_list,(tre_cint_t)prev_c,    \
+                                      tnfa))
+
+inline static int
+tre_tag_get(const tre_tag_t *tags, int i)
+{
+  tags += i;
+  return tags->count > 0 ? tags->value : -1;
+}
+
+inline static void
+tre_tag_set(tre_tag_t *tags, int i, int val, int touch)
+{
+  tags += i;
+  if (tags->count++ == 0)
+    tags->first = val;
+  tags->value = val;
+  tags->touch = touch;
+}
+
+inline static void
+tre_tag_reset(tre_tag_t *tags, int i)
+{
+  tags[i].count = 0;
+}
+
+inline static int
+tre_tag_touch_get(const tre_tag_t *tags, int i)
+{
+  return tags[i].touch;
+}
+
+#ifdef TRE_DEBUG
+inline static void
+tre_print_tags(const tre_tag_t *tags, int num_tags)
+{
+  int i;
+  for (i = 0; i < num_tags; i++, tags++)
+    {
+      switch(tags->count)
+	{
+	case 0:
+	  DPRINT(("%d:(0,-1)", i));
+	  break;
+	case 1:
+	  DPRINT(("%d:(1,%d)", i, tags->first));
+	  break;
+	default:
+	  DPRINT(("%d:(%d,%d,%d)", i, tags->count, tags->first,
+		  tags->value));
+	  break;
+	}
+      if (i < (num_tags - 1))
+	DPRINT((" "));
+    }
+}
+
+inline static void
+tre_print_tags_all(const tre_tag_t *tags, int num_tags)
+{
+  int i;
+  for (i = 0; i < num_tags; i++, tags++)
+    {
+      switch(tags->count)
+	{
+	case 0:
+	  DPRINT(("%d:(0,-1)/%d", i, tags->touch));
+	  break;
+	case 1:
+	  DPRINT(("%d:(1,%d)/%d", i, tags->first, tags->touch));
+	  break;
+	default:
+	  DPRINT(("%d:(%d,%d,%d)/%d", i, tags->count, tags->first,
+		  tags->value, tags->touch));
+	  break;
+	}
+      if (i < (num_tags - 1))
+	DPRINT((" "));
+    }
+}
+#endif /* TRE_DEBUG */
+
+/* Return < 0, = 0 or > 0 depending on how the start/end pairs of a minimal
+ * group between t1 and t2 compare (t1 loses if < 0, t1 wins if > 0) */
+inline static int
+tre_minimal_tag_order(int start, int end, const tre_tag_t *tags1,
+		      const tre_tag_t *tags2)
+{
+  const tre_tag_t *t1, *t2;
+
+  t1 = tags1 + start;
+  t2 = tags2 + start;
+  /* We need both start tags to be set */
+  if (t1->count == 0 || t2->count == 0)
+    return 0;
+
+  /* The start tags must be equal */
+  if (t1->value != t2->value)
+    return 0;
+
+  t1 = tags1 + end;
+  t2 = tags2 + end;
+  /* For the end tags, we prefer set over unset, because unset means that
+   * the end tag is still growing */
+  if (t1->count == 0)
+    {
+      /* if t2 is set, t1 loses since it is unset */
+      if (t2->count != 0)
+	return -1;
+    }
+  /* if t2 not set, t1 wins since it is set */
+  else if (t2->count == 0)
+    return 1;
+
+  /* least current value wins */
+  return t2->value - t1->value;
+}
+
+/* Return < 0, = 0 or > 0 depending on how the i-th item of t1 and t2 compare
+ * (t1 loses if < 0, t1 wins if > 0) */
+inline static int
+tre_tag_order_1(int i, tre_tag_direction_t dir, const tre_tag_t *t1,
+		const tre_tag_t *t2)
+{
+  int diff;
+
+  t1 += i;
+  t2 += i;
+  switch (dir)
+    {
+    case TRE_TAG_MINIMIZE:
+      /* least current value wins (because tags are initialized to all zeros,
+       * unset wins over set; also, tre_minimal_tag_order() will have already
+       * been run, which checks for being unset) */
+      return t2->value - t1->value;
+
+    case TRE_TAG_MAXIMIZE:
+      /* prefer set */
+      if (t1->count == 0)
+	{
+	  /* if neither t1 and t2 are set, try next tag */
+	  if (t2->count == 0)
+	    return 0;
+	  /* t2 is set, t1 loses since it is unset */
+	  return -1;
+	}
+      /* if t2 not set, t1 wins since it is set */
+      else if (t2->count == 0)
+	return 1;
+      /* greatest initial value wins */
+      if ((diff = t1->first - t2->first) != 0)
+	return diff;
+      /* least number of times the tag was set, wins */
+      if ((diff = t2->count - t1->count) != 0)
+	return diff;
+      /* if the tags were only set once, they only have initial values */
+      if (t1->count == 1)
+	return 0;
+      /* greatest current value wins */
+      return t1->value - t2->value;
+
+    case TRE_TAG_LEFT_MAXIMIZE:
+      /* prefer set */
+      if (t1->count == 0)
+	{
+	  /* if neither t1 and t2 are set, try next tag */
+	  if (t2->count == 0)
+	    return 0;
+	  /* t2 is set, t1 loses since it is unset */
+	  return -1;
+	}
+      /* if t2 not set, t1 wins since it is set */
+      else if (t2->count == 0)
+	return 1;
+      /* least initial value wins */
+      if ((diff = t2->first - t1->first) != 0)
+	return diff;
+      /* least number of times the tag was set, wins */
+      if ((diff = t2->count - t1->count) != 0)
+	return diff;
+      /* if the tags were only set once, they only have initial values */
+      if (t1->count == 1)
+	return 0;
+      /* greatest current value wins */
+      return t1->value - t2->value;
+
+    default:
+      /* Shouldn't happen: only assert if TRE_DEBUG defined */
+      assert(0);
+      break;
+    }
+  return 0;
+}
+
+#ifdef TRE_DEBUG
+#define _MORE_DEBUGGING
+#endif /* TRE_DEBUG */
+
+/* Returns 1 if `t1' wins `t2', 0 otherwise. */
+inline static int
+#ifdef _MORE_DEBUGGING
+_tre_tag_order(int num_tags, tre_tag_direction_t *tag_directions,
+	      const tre_tag_t *t1, const tre_tag_t *t2)
+#else /* !_MORE_DEBUGGING */
+tre_tag_order(int num_tags, tre_tag_direction_t *tag_directions,
+	      const tre_tag_t *t1, const tre_tag_t *t2)
+#endif /* !_MORE_DEBUGGING */
+{
+  int i, ret;
+
+  for (i = 0; i < num_tags; i++)
+    {
+      if ((ret = tre_tag_order_1(i, tag_directions[i], t1, t2)) != 0)
+	return (ret > 0);
+    }
+  /*  assert(0);*/
+  return 0;
+}
+
+#ifdef _MORE_DEBUGGING
+inline static int
+tre_tag_order(int num_tags, tre_tag_direction_t *tag_directions,
+	      const tre_tag_t *t1, const tre_tag_t *t2)
+{
+  int ret = _tre_tag_order(num_tags, tag_directions, t1, t2);
+  DPRINT(("tre_tag_order: "));
+  tre_print_tags(t1, num_tags);
+  DPRINT((" %s ", ret ? "wins" : "doesn't win"));
+  tre_print_tags(t2, num_tags);
+  DPRINT(("\n"));
+  return ret;
+}
+#endif /* _MORE_DEBUGGING */
+
+int __collate_equiv_value(locale_t loc, const wchar_t *str, size_t len);
+
+inline static int
+tre_bracket_match(tre_bracket_match_list_t * __restrict list, tre_cint_t wc,
+		  const tre_tnfa_t * __restrict tnfa)
+{
+  int match = 0;
+  int i;
+  tre_bracket_match_t *b;
+  tre_cint_t uc, lc;
+  int we, ue, le, got_equiv = 0;
+  int icase = ((tnfa->cflags & REG_ICASE) != 0);
+
+  DPRINT(("tre_bracket_match: %p, %d, %d\n", list, wc, icase));
+  if (icase)
+    {
+      if (tre_islower_l(wc, tnfa->loc))
+	{
+	  lc = wc;
+	  uc = tre_toupper_l(wc, tnfa->loc);
+	}
+      else if (tre_isupper_l(wc, tnfa->loc))
+	{
+	  uc = wc;
+	  lc = tre_tolower_l(wc, tnfa->loc);
+	}
+      else
+	{
+	  icase = 0;
+	}
+    }
+  for (i = 0, b = list->bracket_matches; i < list->num_bracket_matches;
+       i++, b++)
+    {
+      switch (b->type)
+	{
+	case TRE_BRACKET_MATCH_TYPE_CHAR:
+	  if (icase)
+	    match = (b->value == uc || b->value == lc);
+	  else
+	    match = (b->value == wc);
+	  break;
+	case TRE_BRACKET_MATCH_TYPE_RANGE_BEGIN:
+	  {
+	    tre_cint_t start = b->value, end;
+	    if (++i >= list->num_bracket_matches ||
+		(++b)->type != TRE_BRACKET_MATCH_TYPE_RANGE_END)
+	      {
+		DPRINT(("tre_bracket_match: no following range end\n"));
+		assert(0);
+		goto error;
+	      }
+	    end = b->value;
+	    if (!got_equiv)
+	      {
+		if (icase)
+		  {
+		    ue = __collate_equiv_value(tnfa->loc, &uc, 1);
+		    le = __collate_equiv_value(tnfa->loc, &lc, 1);
+		  }
+		else
+		    we = __collate_equiv_value(tnfa->loc, &wc, 1);
+		got_equiv = 1;
+	      }
+	    if (icase)
+	      match = ((start <= ue && ue <= end) ||
+		      (start <= le && le <= end));
+	    else
+	      match = (start <= we && we <= end);
+	    break;
+	  }
+	case TRE_BRACKET_MATCH_TYPE_RANGE_END:
+	  DPRINT(("tre_bracket_match: range end without preceeding start\n"));
+	  assert(0);
+	  break;
+	case TRE_BRACKET_MATCH_TYPE_CLASS:
+	  if (icase)
+	    match = (tre_isctype_l(uc, b->value, tnfa->loc) ||
+		     tre_isctype_l(lc, b->value, tnfa->loc));
+	  else
+	    match = (tre_isctype_l(wc, b->value, tnfa->loc));
+	  break;
+	case TRE_BRACKET_MATCH_TYPE_EQUIVALENCE:
+	  if (!got_equiv)
+	    {
+	      if (icase)
+		{
+		  ue = __collate_equiv_value(tnfa->loc, &uc, 1);
+		  le = __collate_equiv_value(tnfa->loc, &lc, 1);
+		}
+	      else
+		  we = __collate_equiv_value(tnfa->loc, &wc, 1);
+	      got_equiv = 1;
+	    }
+	  if (icase)
+	    match = (b->value == ue || b->value == le);
+	  else
+	    match = (b->value == we);
+	  break;
+	default:
+	  DPRINT(("tre_bracket_match: unknown type %d\n", b->type));
+	  assert(0);
+	  break;
+	}
+	if (match)
+	  break;
+    }
+error:
+  if (list->flags & TRE_BRACKET_MATCH_FLAG_NEGATE) {
+    if ((tnfa->cflags & REG_NEWLINE) && wc == '\n') return 0;
+    match = !match;
+  }
+  return match;
+}
--- /dev/null	Tue Feb 13 07:11:51 2018
+++ new/usr/src/lib/libc/port/regex/tre-mem.c	Tue Feb 13 07:11:51 2018
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+  tre-mem.c - TRE memory allocator
+*/
+
+/*
+  This memory allocator is for allocating small memory blocks efficiently
+  in terms of memory overhead and execution speed.  The allocated blocks
+  cannot be freed individually, only all at once.  There can be multiple
+  allocators, though.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "tre-internal.h"
+#include "tre-mem.h"
+
+/* Returns a new memory allocator or NULL if out of memory. */
+tre_mem_t
+tre_mem_new_impl(int provided, void *provided_block)
+{
+  tre_mem_t mem;
+  if (provided)
+    {
+      mem = provided_block;
+      memset(mem, 0, sizeof(*mem));
+    }
+  else
+    mem = calloc(1, sizeof(*mem));
+  if (mem == NULL)
+    return NULL;
+  return mem;
+}
+
+/* Frees the memory allocator and all memory allocated with it. */
+void
+tre_mem_destroy(tre_mem_t mem)
+{
+  tre_list_t *tmp, *l = mem->blocks;
+
+  while (l != NULL)
+    {
+      free(l->data);
+      tmp = l->next;
+      free(l);
+      l = tmp;
+    }
+  free(mem);
+}
+
+/* Allocates a block of `size' bytes from `mem'.  Returns a pointer to the
+   allocated block or NULL if an underlying malloc() failed. */
+void *
+tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block,
+		   int zero, size_t size)
+{
+  void *ptr;
+
+  if (mem->failed)
+    {
+      DPRINT(("tre_mem_alloc: oops, called after failure?!\n"));
+      return NULL;
+    }
+
+  if (mem->n < size)
+    {
+      /* We need more memory than is available in the current block.
+	 Allocate a new block. */
+      tre_list_t *l;
+      if (provided)
+	{
+	  DPRINT(("tre_mem_alloc: using provided block\n"));
+	  if (provided_block == NULL)
+	    {
+	      DPRINT(("tre_mem_alloc: provided block was NULL\n"));
+	      mem->failed = 1;
+	      return NULL;
+	    }
+	  mem->ptr = provided_block;
+	  mem->n = TRE_MEM_BLOCK_SIZE;
+	}
+      else
+	{
+	  int block_size;
+	  if (size * 8 > TRE_MEM_BLOCK_SIZE)
+	    block_size = size * 8;
+	  else
+	    block_size = TRE_MEM_BLOCK_SIZE;
+	  DPRINT(("tre_mem_alloc: allocating new %d byte block\n",
+		  block_size));
+	  l = malloc(sizeof(*l));
+	  if (l == NULL)
+	    {
+	      mem->failed = 1;
+	      return NULL;
+	    }
+	  l->data = malloc(block_size);
+	  if (l->data == NULL)
+	    {
+	      free(l);
+	      mem->failed = 1;
+	      return NULL;
+	    }
+	  l->next = NULL;
+	  if (mem->current != NULL)
+	    mem->current->next = l;
+	  if (mem->blocks == NULL)
+	    mem->blocks = l;
+	  mem->current = l;
+	  mem->ptr = l->data;
+	  mem->n = block_size;
+	}
+    }
+
+  /* Make sure the next pointer will be aligned. */
+  size += ALIGN(mem->ptr + size, long);
+
+  /* Allocate from current block. */
+  ptr = mem->ptr;
+  mem->ptr += size;
+  mem->n -= size;
+
+  /* Set to zero if needed. */
+  if (zero)
+    memset(ptr, 0, size);
+
+  return ptr;
+}
--- /dev/null	Tue Feb 13 07:11:52 2018
+++ new/usr/src/lib/libc/port/regex/tre-mem.h	Tue Feb 13 07:11:52 2018
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+  tre-mem.h - TRE memory allocator interface
+*/
+
+#ifndef	_TRE_MEM_H
+#define	_TRE_MEM_H
+
+#include <stdlib.h>
+
+#define TRE_MEM_BLOCK_SIZE 1024
+
+typedef struct tre_list {
+  void *data;
+  struct tre_list *next;
+} tre_list_t;
+
+typedef struct tre_mem_struct {
+  tre_list_t *blocks;
+  tre_list_t *current;
+  char *ptr;
+  size_t n;
+  int failed;
+  void **provided;
+} *tre_mem_t;
+
+tre_mem_t tre_mem_new_impl(int provided, void *provided_block);
+void *tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block,
+			 int zero, size_t size);
+
+/* Returns a new memory allocator or NULL if out of memory. */
+#define tre_mem_new()  tre_mem_new_impl(0, NULL)
+
+/* Allocates a block of `size' bytes from `mem'.  Returns a pointer to the
+   allocated block or NULL if an underlying malloc() failed. */
+#define tre_mem_alloc(mem, size) tre_mem_alloc_impl(mem, 0, NULL, 0, size)
+
+/* Allocates a block of `size' bytes from `mem'.  Returns a pointer to the
+   allocated block or NULL if an underlying malloc() failed.  The memory
+   is set to zero. */
+#define tre_mem_calloc(mem, size) tre_mem_alloc_impl(mem, 0, NULL, 1, size)
+
+/* Frees the memory allocator and all memory allocated with it. */
+void tre_mem_destroy(tre_mem_t mem);
+
+#endif /* _TRE_MEM_H */
--- /dev/null	Tue Feb 13 07:11:52 2018
+++ new/usr/src/lib/libc/port/regex/tre-parse.c	Tue Feb 13 07:11:52 2018
@@ -0,0 +1,2012 @@
+/*
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+  tre-parse.c - Regexp parser
+*/
+
+/*
+  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.
+*/
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "malloc.h"
+#include "tre-mem.h"
+#include "tre-ast.h"
+#include "tre-stack.h"
+#include "tre-parse.h"
+
+#include "../locale/collate.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 = realloc(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;
+}
+
+#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;
+
+#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 = malloc(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 =
+				    realloc(col_syms, sizeof(*col_syms) *
+					     ((n_col_syms *= 2) + 2));
+				if (tmp == NULL)
+				  {
+				    free(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);
+				    {
+				      tre_char_t tmp_wcs[64];
+				      wcsncpy(tmp_wcs, start, (size_t)len);
+				      tmp_wcs[len] = L'\0';
+				      {
+					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);
+				      }
+				    }
+				    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)
+    free(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 = calloc(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. */
+      free(items);
+      str = malloc(sizeof(*str) * ((col_syms->start - ctx->re) + 2));
+      if (str == NULL)
+	{
+	  free(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);
+      free(str);
+      if (status != REG_OK)
+	{
+	  free(col_syms);
+	  return status;
+	}
+      ctx->re = col_syms->start;
+      ctx->position = subctx.position;
+      free(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:
+  free(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;
+  tre_char_t *endptr;
+
+  errno = 0;
+  num = (int)wcstoul(*regex, (wchar_t **)&endptr, 10);
+  if (errno != 0)
+    return -1;
+  *regex = endptr;
+  return num;
+}
+
+static reg_errcode_t
+tre_parse_bound(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
+{
+  int min, max;
+  const tre_char_t *r = ctx->re;
+  int minimal = (ctx->cflags & REG_UNGREEDY) ? 1 : 0;
+
+  /* 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);
+    if (min == -1)
+      return REG_BADBR;
+  }
+  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 */
+
+  /* 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;
+
+  /*{*//* 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 */
+
+  *result = tre_ast_new_iter(ctx->mem, *result, min, max, minimal);
+  if (!*result)
+    return REG_ESPACE;
+
+parse_bound_exit:
+  DPRINT(("tre_parse_bound: min %d, max %d\n", min, max));
+
+  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 (!(ctx->cflags & 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;
+	    if (!(ctx->cflags & 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_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;
+	  if (ctx->cflags & REG_LITERAL)
+	    break;
+	  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;
+	  if (ctx->cflags & REG_LITERAL)
+	    break;
+	  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 + 2) == 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;
+
+	    if (ctx->cflags & REG_LITERAL)
+	      goto parse_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;
+
+		/* XXX illumos: want macro expansion enabled by default (same as TRE) */
+#if 0
+		if (!(ctx->cflags & REG_ENHANCED))
+		  {
+		    DPRINT(("tre_parse:  unenhanced bleep: '%.*" STRF "'\n", REST(ctx->re)));
+		    ctx->re++;
+		    goto unenhanced_backslash;
+		  }
+#endif
+
+		/* 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;
+		    }
+		}
+
+		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;
+		  }
+
+		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. */
+		if (!(ctx->cflags & 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;
+		      }
+		  }
+
+		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;
+}
--- /dev/null	Tue Feb 13 07:11:52 2018
+++ new/usr/src/lib/libc/port/regex/tre-parse.h	Tue Feb 13 07:11:52 2018
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+  tre-parse.c - Regexp parser definitions
+*/
+
+#ifndef	_TRE_PARSE_H
+#define	_TRE_PARSE_H
+
+#include "../locale/collate.h"
+
+/* Parse context. */
+typedef struct {
+  /* Memory allocator.	The AST is allocated using this. */
+  tre_mem_t mem;
+  /* Stack used for keeping track of regexp syntax. */
+  tre_stack_t *stack;
+  /* The parse result. */
+  tre_ast_node_t *result;
+  /* The regexp to parse and its length. */
+  const tre_char_t *re;
+  /* The first character of the entire regexp. */
+  const tre_char_t *re_start;
+  /* The first character after the end of the regexp. */
+  const tre_char_t *re_end;
+  /* The current locale */
+  locale_t loc;
+  int len;
+  /* Current submatch ID. */
+  int submatch_id;
+  /* Current invisible submatch ID. */
+  int submatch_id_invisible;
+  /* Current position (number of literal). */
+  int position;
+  /* The highest back reference or -1 if none seen so far. */
+  int max_backref;
+  /* Number of tags that need reordering. */
+  int num_reorder_tags;
+  /* This flag is set if the regexp uses approximate matching. */
+  int have_approx;
+  /* Compilation flags. */
+  int cflags;
+  /* If this flag is set the top-level submatch is not captured. */
+  int nofirstsub;
+  /* The currently set approximate matching parameters. */
+  int params[TRE_PARAM_LAST];
+} tre_parse_ctx_t;
+
+/* Parses a wide character regexp pattern into a syntax tree.  This parser
+   handles both syntaxes (BRE and ERE), including the TRE extensions. */
+reg_errcode_t
+tre_parse(tre_parse_ctx_t *ctx);
+
+#endif	/* _TRE_PARSE_H */
--- /dev/null	Tue Feb 13 07:11:52 2018
+++ new/usr/src/lib/libc/port/regex/tre-stack.c	Tue Feb 13 07:11:52 2018
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+  tre-stack.c - Simple stack implementation
+*/
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "tre-internal.h"
+#include "tre-stack.h"
+
+union tre_stack_item {
+  void *voidptr_value;
+  int int_value;
+};
+
+struct tre_stack_rec {
+  int size;
+  int max_size;
+  int increment;
+  int ptr;
+  union tre_stack_item *stack;
+};
+
+tre_stack_t *
+tre_stack_new(int size, int max_size, int increment)
+{
+  tre_stack_t *s;
+
+  s = malloc(sizeof(*s));
+  if (s != NULL)
+    {
+      s->stack = malloc(sizeof(*s->stack) * size);
+      if (s->stack == NULL)
+	{
+	  free(s);
+	  return NULL;
+	}
+      s->size = size;
+      s->max_size = max_size;
+      s->increment = increment;
+      s->ptr = 0;
+    }
+  return s;
+}
+
+void
+tre_stack_destroy(tre_stack_t *s)
+{
+  free(s->stack);
+  free(s);
+}
+
+int
+tre_stack_num_objects(tre_stack_t *s)
+{
+  return s->ptr;
+}
+
+static reg_errcode_t
+tre_stack_push(tre_stack_t *s, union tre_stack_item value)
+{
+  if (s->ptr < s->size)
+    {
+      s->stack[s->ptr] = value;
+      s->ptr++;
+    }
+  else
+    {
+      if (s->size >= s->max_size)
+	{
+	  DPRINT(("tre_stack_push: stack full\n"));
+	  return REG_ESPACE;
+	}
+      else
+	{
+	  union tre_stack_item *new_buffer;
+	  int new_size;
+	  DPRINT(("tre_stack_push: trying to realloc more space\n"));
+	  new_size = s->size + s->increment;
+	  if (new_size > s->max_size)
+	    new_size = s->max_size;
+	  new_buffer = realloc(s->stack, sizeof(*new_buffer) * new_size);
+	  if (new_buffer == NULL)
+	    {
+	      DPRINT(("tre_stack_push: realloc failed.\n"));
+	      return REG_ESPACE;
+	    }
+	  DPRINT(("tre_stack_push: realloc succeeded.\n"));
+	  assert(new_size > s->size);
+	  s->size = new_size;
+	  s->stack = new_buffer;
+	  tre_stack_push(s, value);
+	}
+    }
+  return REG_OK;
+}
+
+#define define_pushf(typetag, type)  \
+  declare_pushf(typetag, type) {     \
+    union tre_stack_item item;	     \
+    item.typetag ## _value = value;  \
+    return tre_stack_push(s, item);  \
+}
+
+define_pushf(int, int)
+define_pushf(voidptr, void *)
+
+#define define_popf(typetag, type)		    \
+  declare_popf(typetag, type) {			    \
+    return s->stack[--s->ptr].typetag ## _value;    \
+  }
+
+define_popf(int, int)
+define_popf(voidptr, void *)
--- /dev/null	Tue Feb 13 07:11:52 2018
+++ new/usr/src/lib/libc/port/regex/tre-stack.h	Tue Feb 13 07:11:52 2018
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+  tre-stack.h: Stack definitions
+*/
+
+#ifndef	_TRE_STACK_H
+#define	_TRE_STACK_H
+
+#include "tre.h"
+
+typedef struct tre_stack_rec tre_stack_t;
+
+/* Creates a new stack object.	`size' is initial size in bytes, `max_size'
+   is maximum size, and `increment' specifies how much more space will be
+   allocated with realloc() if all space gets used up.	Returns the stack
+   object or NULL if out of memory. */
+tre_stack_t *
+tre_stack_new(int size, int max_size, int increment);
+
+/* Frees the stack object. */
+void
+tre_stack_destroy(tre_stack_t *s);
+
+/* Returns the current number of objects in the stack. */
+int
+tre_stack_num_objects(tre_stack_t *s);
+
+/* Each tre_stack_push_*(tre_stack_t *s, <type> value) function pushes
+   `value' on top of stack `s'.  Returns REG_ESPACE if out of memory.
+   This tries to realloc() more space before failing if maximum size
+   has not yet been reached.  Returns REG_OK if successful. */
+#define declare_pushf(typetag, type)					      \
+  reg_errcode_t tre_stack_push_ ## typetag(tre_stack_t *s, type value)
+
+declare_pushf(voidptr, void *);
+declare_pushf(int, int);
+
+/* Each tre_stack_pop_*(tre_stack_t *s) function pops the topmost
+   element off of stack `s' and returns it.  The stack must not be
+   empty. */
+#define declare_popf(typetag, type)		  \
+  type tre_stack_pop_ ## typetag(tre_stack_t *s)
+
+declare_popf(voidptr, void *);
+declare_popf(int, int);
+
+/* Just to save some typing. */
+#define STACK_PUSH(s, typetag, value)					      \
+  do									      \
+    {									      \
+      status = tre_stack_push_ ## typetag(s, value);			      \
+    }									      \
+  while (/*CONSTCOND*/0)
+
+#define STACK_PUSHX(s, typetag, value)					      \
+  {									      \
+    status = tre_stack_push_ ## typetag(s, value);			      \
+    if (status != REG_OK)						      \
+      break;								      \
+  }
+
+#define STACK_PUSHR(s, typetag, value)					      \
+  {									      \
+    reg_errcode_t _status;						      \
+    _status = tre_stack_push_ ## typetag(s, value);			      \
+    if (_status != REG_OK)						      \
+      return _status;							      \
+  }
+
+#endif	/* _TRE_STACK_H */
--- /dev/null	Tue Feb 13 07:11:52 2018
+++ new/usr/src/lib/libc/port/regex/tre.h	Tue Feb 13 07:11:52 2018
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TRE_H
+#define	_TRE_H
+
+#include <sys/types.h>
+
+#include <locale.h>
+#include <wchar.h>
+#include <xlocale.h>
+
+#include "../../../../head/regex.h"
+
+#define	RE_MAGIC ((('r'^0200)<<8) | 'e')
+
+typedef int reg_errcode_t;
+
+enum {
+  TRE_CONFIG_APPROX,
+  TRE_CONFIG_WCHAR,
+  TRE_CONFIG_MULTIBYTE,
+  TRE_CONFIG_SYSTEM_ABI,
+  TRE_CONFIG_VERSION
+};
+
+typedef wchar_t tre_char_t;
+
+/* Approximate matching parameter struct. */
+typedef struct {
+  int cost_ins;		/* Default cost of an inserted character. */
+  int cost_del;		/* Default cost of a deleted character. */
+  int cost_subst;	/* Default cost of a substituted character. */
+  int max_cost;		/* Maximum allowed cost of a match. */
+
+  int max_ins;		/* Maximum allowed number of inserts. */
+  int max_del;		/* Maximum allowed number of deletes. */
+  int max_subst;	/* Maximum allowed number of substitutes. */
+  int max_err;		/* Maximum allowed number of errors total. */
+} regaparams_t;
+
+/* Approximate matching result struct. */
+typedef struct {
+  size_t nmatch;	/* Length of pmatch[] array. */
+  regmatch_t *pmatch;	/* Submatch data. */
+  int cost;		/* Cost of the match. */
+  int num_ins;		/* Number of inserts in the match. */
+  int num_del;		/* Number of deletes in the match. */
+  int num_subst;	/* Number of substitutes in the match. */
+} regamatch_t;
+
+typedef struct {
+  int (*get_next_char)(tre_char_t *c, unsigned int *pos_add, void *context);
+  void (*rewind)(size_t pos, void *context);
+  int (*compare)(size_t pos1, size_t pos2, size_t len, void *context);
+  void *context;
+} tre_str_source;
+
+#define	REG_ENHANCED			0x100000
+#define	REG_MINIMAL			0x200000
+#define	REG_UNGREEDY			0x200000
+
+#define	__DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
+
+#ifndef	MB_CUR_MAX_L
+unsigned char __mb_cur_max_l(locale_t);
+#define	 MB_CUR_MAX_L(l) (__mb_cur_max_l(l))
+#endif
+
+#endif /* _TRE_H */
--- old/usr/src/man/man3c/regcomp.3c	Tue Feb 13 07:11:53 2018
+++ new/usr/src/man/man3c/regcomp.3c	Tue Feb 13 07:11:53 2018
@@ -74,9 +74,9 @@
 .\"
 .\" Copyright (c) 1992, X/Open Company Limited. All Rights Reserved.
 .\" Portions Copyright (c) 2003, Sun Microsystems, Inc.  All Rights Reserved.
-.\" Copyright 2017 Nexenta Systems, Inc.
+.\" Copyright 2018 Nexenta Systems, Inc.
 .\"
-.Dd June 14, 2017
+.Dd February 3, 2018
 .Dt REGCOMP 3C
 .Os
 .Sh NAME
@@ -170,6 +170,9 @@
 .Dv REG_NOSPEC
 may not be used in the same call to
 .Fn regcomp .
+.It Dv REG_LITERAL
+An alias of
+.Dv REG_NOSPEC .
 .It Dv REG_ICASE
 Compile for matching that ignores upper/lower case distinctions.
 See
@@ -482,39 +485,6 @@
 is 0,
 .Fa errbuf
 is ignored but the return value is still correct.
-.Pp
-If the
-.Fa errcode
-given to
-.Fn regerror
-is first ORed with
-.Dv REG_ITOA ,
-the
-.Qq message
-that results is the printable name of the error code, e.g.
-.Qq Dv REG_NOMATCH ,
-rather than an explanation thereof.
-If
-.Fa errcode
-is
-.Dv REG_ATOI ,
-then
-.Fa preg
-shall be non-NULL and the
-.Va re_endp
-member of the structure it points to must point to the printable name of an
-error code; in this case, the result in
-.Fa errbuf
-is the decimal digits of the numeric value of the error code
-.Pq 0 if the name is not recognized .
-.Dv REG_ITOA
-and
-.Dv REG_ATOI
-are intended primarily as debugging facilities; they are extensions,
-compatible with but not specified by
-.St -p1003.2 ,
-and should be used with caution in software intended to be portable to other
-systems.
 .Ss Fn regfree
 The
 .Fn regfree
@@ -528,76 +498,6 @@
 or
 .Fn regerror
 is undefined.
-.Sh IMPLEMENTATION NOTES
-There are a number of decisions that
-.St -p1003.2
-leaves up to the implementor,
-either by explicitly saying
-.Qq undefined
-or by virtue of them being forbidden by the RE grammar.
-This implementation treats them as follows.
-.Pp
-There is no particular limit on the length of REs, except insofar as memory is
-limited.
-Memory usage is approximately linear in RE size, and largely insensitive
-to RE complexity, except for bounded repetitions.
-.Pp
-A backslashed character other than one specifically given a magic meaning by
-.St -p1003.2
-.Pq such magic meanings occur only in BREs
-is taken as an ordinary character.
-.Pp
-Any unmatched
-.Qq \&[
-is a
-.Dv REG_EBRACK
-error.
-.Pp
-Equivalence classes cannot begin or end bracket-expression ranges.
-The endpoint of one range cannot begin another.
-.Pp
-.Dv RE_DUP_MAX ,
-the limit on repetition counts in bounded repetitions, is 255.
-.Pp
-A repetition operator
-.Po
-.Qq \&? ,
-.Qq \&* ,
-.Qq \&+ ,
-or bounds
-.Pc
-cannot follow another repetition operator.
-A repetition operator cannot begin an expression or subexpression
-or follow
-.Qq \&^
-or
-.Qq \&| .
-.Pp
-.Qq \&|
-cannot appear first or last in a (sub)expression or after another
-.Qq \&| ,
-i.e., an operand of
-.Qq \&|
-cannot be an empty subexpression.
-An empty parenthesized subexpression,
-.Qq () ,
-is legal and matches an empty (sub)string.
-An empty string is not a legal RE.
-.Pp
-A
-.Qq \&{
-followed by a digit is considered the beginning of bounds for a bounded
-repetition, which must then follow the syntax for bounds.
-A
-.Qq \&{
-.Em not
-followed by a digit is considered an ordinary character.
-.Pp
-.Qq \&^
-and
-.Qq \&$
-beginning and ending subexpressions in BREs are anchors, not ordinary
-characters.
 .Sh RETURN VALUES
 On successful completion, the
 .Fn regcomp
@@ -611,14 +511,11 @@
 function returns 0.
 Otherwise it returns
 .Dv REG_NOMATCH
-to indicate no match, or
-.Dv REG_ENOSYS
-to indicate that the function is not supported.
+to indicate no match.
 .Pp
 Upon successful completion, the
 .Fn regerror
 function returns the number of bytes needed to hold the entire generated string.
-Otherwise, it returns 0 to indicate that the function is not implemented.
 .Pp
 The
 .Fn regfree
@@ -673,6 +570,10 @@
 or
 .Qq +
 not preceded by valid regular expression.
+.It Dv REG_EMPTY
+Empty (sub)expression.
+.It Dv REG_INVARG
+Invalid argument, e.g. negative-length string.
 .El
 .Sh USAGE
 An application could use:
