| #pragma prototyped noticed | 
 |  | 
 | /* | 
 |  * regex(3) test harness | 
 |  * | 
 |  * build:	cc -o testregex testregex.c | 
 |  * help:	testregex --man | 
 |  * note:	REG_* features are detected by #ifdef; if REG_* are enums | 
 |  *		then supply #define REG_foo REG_foo for each enum REG_foo | 
 |  * | 
 |  *	Glenn Fowler <gsf@research.att.com> | 
 |  *	AT&T Research | 
 |  * | 
 |  * PLEASE: publish your tests so everyone can benefit | 
 |  * | 
 |  * The following license covers testregex.c and all associated test data. | 
 |  * | 
 |  * Permission is hereby granted, free of charge, to any person obtaining a | 
 |  * copy of THIS SOFTWARE FILE (the "Software"), to deal in the Software | 
 |  * without restriction, including without limitation the rights to use, | 
 |  * copy, modify, merge, publish, distribute, and/or sell copies of the | 
 |  * Software, and to permit persons to whom the Software is furnished to do | 
 |  * so, subject to the following disclaimer: | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY AT&T ``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 AT&T 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. | 
 |  */ | 
 |  | 
 | static const char id[] = "\n@(#)$Id: testregex (AT&T Research) 2010-06-10 $\0\n"; | 
 |  | 
 | #if _PACKAGE_ast | 
 | #include <ast.h> | 
 | #else | 
 | #include <sys/types.h> | 
 | #endif | 
 |  | 
 | #include <stdio.h> | 
 | #include <regex.h> | 
 | #include <ctype.h> | 
 | #include <setjmp.h> | 
 | #include <signal.h> | 
 | #include <string.h> | 
 | #include <unistd.h> | 
 |  | 
 | #ifdef	__STDC__ | 
 | #include <stdlib.h> | 
 | #include <locale.h> | 
 | #endif | 
 |  | 
 | #ifndef RE_DUP_MAX | 
 | #define RE_DUP_MAX	32767 | 
 | #endif | 
 |  | 
 | #if !_PACKAGE_ast | 
 | #undef	REG_DISCIPLINE | 
 | #endif | 
 |  | 
 | #ifndef REG_DELIMITED | 
 | #undef	_REG_subcomp | 
 | #endif | 
 |  | 
 | #define TEST_ARE		0x00000001 | 
 | #define TEST_BRE		0x00000002 | 
 | #define TEST_ERE		0x00000004 | 
 | #define TEST_KRE		0x00000008 | 
 | #define TEST_LRE		0x00000010 | 
 | #define TEST_SRE		0x00000020 | 
 |  | 
 | #define TEST_EXPAND		0x00000100 | 
 | #define TEST_LENIENT		0x00000200 | 
 |  | 
 | #define TEST_QUERY		0x00000400 | 
 | #define TEST_SUB		0x00000800 | 
 | #define TEST_UNSPECIFIED	0x00001000 | 
 | #define TEST_VERIFY		0x00002000 | 
 | #define TEST_AND		0x00004000 | 
 | #define TEST_OR			0x00008000 | 
 |  | 
 | #define TEST_DELIMIT		0x00010000 | 
 | #define TEST_OK			0x00020000 | 
 | #define TEST_SAME		0x00040000 | 
 |  | 
 | #define TEST_ACTUAL		0x00100000 | 
 | #define TEST_BASELINE		0x00200000 | 
 | #define TEST_FAIL		0x00400000 | 
 | #define TEST_PASS		0x00800000 | 
 | #define TEST_SUMMARY		0x01000000 | 
 |  | 
 | #define TEST_IGNORE_ERROR	0x02000000 | 
 | #define TEST_IGNORE_OVER	0x04000000 | 
 | #define TEST_IGNORE_POSITION	0x08000000 | 
 |  | 
 | #define TEST_CATCH		0x10000000 | 
 | #define TEST_VERBOSE		0x20000000 | 
 |  | 
 | #define TEST_DECOMP		0x40000000 | 
 |  | 
 | #define TEST_GLOBAL		(TEST_ACTUAL|TEST_AND|TEST_BASELINE|TEST_CATCH|TEST_FAIL|TEST_IGNORE_ERROR|TEST_IGNORE_OVER|TEST_IGNORE_POSITION|TEST_OR|TEST_PASS|TEST_SUMMARY|TEST_VERBOSE) | 
 |  | 
 | #ifdef REG_DISCIPLINE | 
 |  | 
 |  | 
 | #include <stk.h> | 
 |  | 
 | typedef struct Disc_s | 
 | { | 
 | 	regdisc_t	disc; | 
 | 	int		ordinal; | 
 | 	Sfio_t*		sp; | 
 | } Disc_t; | 
 |  | 
 | static void* | 
 | compf(const regex_t* re, const char* xstr, size_t xlen, regdisc_t* disc) | 
 | { | 
 | 	Disc_t*		dp = (Disc_t*)disc; | 
 |  | 
 | 	return (void*)((char*)0 + ++dp->ordinal); | 
 | } | 
 |  | 
 | static int | 
 | execf(const regex_t* re, void* data, const char* xstr, size_t xlen, const char* sstr, size_t slen, char** snxt, regdisc_t* disc) | 
 | { | 
 | 	Disc_t*		dp = (Disc_t*)disc; | 
 |  | 
 | 	sfprintf(dp->sp, "{%-.*s}(%lu:%d)", xlen, xstr, (char*)data - (char*)0, slen); | 
 | 	return atoi(xstr); | 
 | } | 
 |  | 
 | static void* | 
 | resizef(void* handle, void* data, size_t size) | 
 | { | 
 | 	if (!size) | 
 | 		return 0; | 
 | 	return stkalloc((Sfio_t*)handle, size); | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 | #ifndef NiL | 
 | #ifdef	__STDC__ | 
 | #define NiL		0 | 
 | #else | 
 | #define NiL		(char*)0 | 
 | #endif | 
 | #endif | 
 |  | 
 | #define H(x)		do{if(html)fprintf(stderr,x);}while(0) | 
 | #define T(x)		fprintf(stderr,x) | 
 |  | 
 | static void | 
 | help(int html) | 
 | { | 
 | H("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n"); | 
 | H("<HTML>\n"); | 
 | H("<HEAD>\n"); | 
 | H("<TITLE>testregex man document</TITLE>\n"); | 
 | H("</HEAD>\n"); | 
 | H("<BODY bgcolor=white>\n"); | 
 | H("<PRE>\n"); | 
 | T("NAME\n"); | 
 | T("  testregex - regex(3) test harness\n"); | 
 | T("\n"); | 
 | T("SYNOPSIS\n"); | 
 | T("  testregex [ options ]\n"); | 
 | T("\n"); | 
 | T("DESCRIPTION\n"); | 
 | T("  testregex reads regex(3) test specifications, one per line, from the\n"); | 
 | T("  standard input and writes one output line for each failed test. A\n"); | 
 | T("  summary line is written after all tests are done. Each successful\n"); | 
 | T("  test is run again with REG_NOSUB. Unsupported features are noted\n"); | 
 | T("  before the first test, and tests requiring these features are\n"); | 
 | T("  silently ignored.\n"); | 
 | T("\n"); | 
 | T("OPTIONS\n"); | 
 | T("  -c	catch signals and non-terminating calls\n"); | 
 | T("  -e	ignore error return mismatches\n"); | 
 | T("  -h	list help on standard error\n"); | 
 | T("  -n	do not repeat successful tests with regnexec()\n"); | 
 | T("  -o	ignore match[] overrun errors\n"); | 
 | T("  -p	ignore negative position mismatches\n"); | 
 | T("  -s	use stack instead of malloc\n"); | 
 | T("  -x	do not repeat successful tests with REG_NOSUB\n"); | 
 | T("  -v	list each test line\n"); | 
 | T("  -A	list failed test lines with actual answers\n"); | 
 | T("  -B	list all test lines with actual answers\n"); | 
 | T("  -F	list failed test lines\n"); | 
 | T("  -P	list passed test lines\n"); | 
 | T("  -S	output one summary line\n"); | 
 | T("\n"); | 
 | T("INPUT FORMAT\n"); | 
 | T("  Input lines may be blank, a comment beginning with #, or a test\n"); | 
 | T("  specification. A specification is five fields separated by one\n"); | 
 | T("  or more tabs. NULL denotes the empty string and NIL denotes the\n"); | 
 | T("  0 pointer.\n"); | 
 | T("\n"); | 
 | T("  Field 1: the regex(3) flags to apply, one character per REG_feature\n"); | 
 | T("  flag. The test is skipped if REG_feature is not supported by the\n"); | 
 | T("  implementation. If the first character is not [BEASKLP] then the\n"); | 
 | T("  specification is a global control line. One or more of [BEASKLP] may be\n"); | 
 | T("  specified; the test will be repeated for each mode.\n"); | 
 | T("\n"); | 
 | T("    B 	basic			BRE	(grep, ed, sed)\n"); | 
 | T("    E 	REG_EXTENDED		ERE	(egrep)\n"); | 
 | T("    A	REG_AUGMENTED		ARE	(egrep with negation)\n"); | 
 | T("    S	REG_SHELL		SRE	(sh glob)\n"); | 
 | T("    K	REG_SHELL|REG_AUGMENTED	KRE	(ksh glob)\n"); | 
 | T("    L	REG_LITERAL		LRE	(fgrep)\n"); | 
 | T("\n"); | 
 | T("    a	REG_LEFT|REG_RIGHT	implicit ^...$\n"); | 
 | T("    b	REG_NOTBOL		lhs does not match ^\n"); | 
 | T("    c	REG_COMMENT		ignore space and #...\\n\n"); | 
 | T("    d	REG_SHELL_DOT		explicit leading . match\n"); | 
 | T("    e	REG_NOTEOL		rhs does not match $\n"); | 
 | T("    f	REG_MULTIPLE		multiple \\n separated patterns\n"); | 
 | T("    g	FNM_LEADING_DIR		testfnmatch only -- match until /\n"); | 
 | T("    h	REG_MULTIREF		multiple digit backref\n"); | 
 | T("    i	REG_ICASE		ignore case\n"); | 
 | T("    j	REG_SPAN		. matches \\n\n"); | 
 | T("    k	REG_ESCAPE		\\ to ecape [...] delimiter\n"); | 
 | T("    l	REG_LEFT		implicit ^...\n"); | 
 | T("    m	REG_MINIMAL		minimal match\n"); | 
 | T("    n	REG_NEWLINE		explicit \\n match\n"); | 
 | T("    o	REG_ENCLOSED		(|&) magic inside [@|&](...)\n"); | 
 | T("    p	REG_SHELL_PATH		explicit / match\n"); | 
 | T("    q	REG_DELIMITED		delimited pattern\n"); | 
 | T("    r	REG_RIGHT		implicit ...$\n"); | 
 | T("    s	REG_SHELL_ESCAPED	\\ not special\n"); | 
 | T("    t	REG_MUSTDELIM		all delimiters must be specified\n"); | 
 | T("    u	standard unspecified behavior -- errors not counted\n"); | 
 | T("    v	REG_CLASS_ESCAPE	\\ special inside [...]\n"); | 
 | T("    w	REG_NOSUB		no subexpression match array\n"); | 
 | T("    x	REG_LENIENT		let some errors slide\n"); | 
 | T("    y	REG_LEFT		regexec() implicit ^...\n"); | 
 | T("    z	REG_NULL		NULL subexpressions ok\n"); | 
 | T("    $	                        expand C \\c escapes in fields 2 and 3\n"); | 
 | T("    /	                        field 2 is a regsubcomp() expression\n"); | 
 | T("    =	                        field 3 is a regdecomp() expression\n"); | 
 | T("\n"); | 
 | T("  Field 1 control lines:\n"); | 
 | T("\n"); | 
 | T("    C		set LC_COLLATE and LC_CTYPE to locale in field 2\n"); | 
 | T("\n"); | 
 | T("    ?test ...	output field 5 if passed and != EXPECTED, silent otherwise\n"); | 
 | T("    &test ...	output field 5 if current and previous passed\n"); | 
 | T("    |test ...	output field 5 if current passed and previous failed\n"); | 
 | T("    ; ...	output field 2 if previous failed\n"); | 
 | T("    {test ...	skip if failed until }\n"); | 
 | T("    }		end of skip\n"); | 
 | T("\n"); | 
 | T("    : comment		comment copied as output NOTE\n"); | 
 | T("    :comment:test	:comment: ignored\n"); | 
 | T("    N[OTE] comment	comment copied as output NOTE\n"); | 
 | T("    T[EST] comment	comment\n"); | 
 | T("\n"); | 
 | T("    number		use number for nmatch (20 by default)\n"); | 
 | T("\n"); | 
 | T("  Field 2: the regular expression pattern; SAME uses the pattern from\n"); | 
 | T("    the previous specification. RE_DUP_MAX inside {...} expands to the\n"); | 
 | T("    value from <limits.h>.\n"); | 
 | T("\n"); | 
 | T("  Field 3: the string to match. X...{RE_DUP_MAX} expands to RE_DUP_MAX\n"); | 
 | T("    copies of X.\n"); | 
 | T("\n"); | 
 | T("  Field 4: the test outcome. This is either one of the posix error\n"); | 
 | T("    codes (with REG_ omitted) or the match array, a list of (m,n)\n"); | 
 | T("    entries with m and n being first and last+1 positions in the\n"); | 
 | T("    field 3 string, or NULL if REG_NOSUB is in effect and success\n"); | 
 | T("    is expected. BADPAT is acceptable in place of any regcomp(3)\n"); | 
 | T("    error code. The match[] array is initialized to (-2,-2) before\n"); | 
 | T("    each test. All array elements from 0 to nmatch-1 must be specified\n"); | 
 | T("    in the outcome. Unspecified endpoints (offset -1) are denoted by ?.\n"); | 
 | T("    Unset endpoints (offset -2) are denoted by X. {x}(o:n) denotes a\n"); | 
 | T("    matched (?{...}) expression, where x is the text enclosed by {...},\n"); | 
 | T("    o is the expression ordinal counting from 1, and n is the length of\n"); | 
 | T("    the unmatched portion of the subject string. If x starts with a\n"); | 
 | T("    number then that is the return value of re_execf(), otherwise 0 is\n"); | 
 | T("    returned. RE_DUP_MAX[-+]N expands to the <limits.h> value -+N.\n"); | 
 | T("\n"); | 
 | T("  Field 5: optional comment appended to the report.\n"); | 
 | T("\n"); | 
 | T("CAVEAT\n"); | 
 | T("    If a regex implementation misbehaves with memory then all bets are off.\n"); | 
 | T("\n"); | 
 | T("CONTRIBUTORS\n"); | 
 | T("  Glenn Fowler    gsf@research.att.com        (ksh strmatch, regex extensions)\n"); | 
 | T("  David Korn      dgk@research.att.com        (ksh glob matcher)\n"); | 
 | T("  Doug McIlroy    mcilroy@dartmouth.edu       (ast regex/testre in C++)\n"); | 
 | T("  Tom Lord        lord@regexps.com            (rx tests)\n"); | 
 | T("  Henry Spencer   henry@zoo.toronto.edu       (original public regex)\n"); | 
 | T("  Andrew Hume     andrew@research.att.com     (gre tests)\n"); | 
 | T("  John Maddock    John_Maddock@compuserve.com (regex++ tests)\n"); | 
 | T("  Philip Hazel    ph10@cam.ac.uk              (pcre tests)\n"); | 
 | T("  Ville Laurikari vl@iki.fi                   (libtre tests)\n"); | 
 | H("</PRE>\n"); | 
 | H("</BODY>\n"); | 
 | H("</HTML>\n"); | 
 | } | 
 |  | 
 | #ifndef elementsof | 
 | #define elementsof(x)	(sizeof(x)/sizeof(x[0])) | 
 | #endif | 
 |  | 
 | #ifndef streq | 
 | #define streq(a,b)	(*(a)==*(b)&&!strcmp(a,b)) | 
 | #endif | 
 |  | 
 | #define HUNG		2 | 
 | #define NOTEST		(~0) | 
 |  | 
 | #ifndef REG_TEST_DEFAULT | 
 | #define REG_TEST_DEFAULT	0 | 
 | #endif | 
 |  | 
 | #ifndef REG_EXEC_DEFAULT | 
 | #define REG_EXEC_DEFAULT	0 | 
 | #endif | 
 |  | 
 | static const char* unsupported[] = | 
 | { | 
 | 	"BASIC", | 
 | #ifndef REG_EXTENDED | 
 | 	"EXTENDED", | 
 | #endif | 
 | #ifndef REG_AUGMENTED | 
 | 	"AUGMENTED", | 
 | #endif | 
 | #ifndef REG_SHELL | 
 | 	"SHELL", | 
 | #endif | 
 |  | 
 | #ifndef REG_CLASS_ESCAPE | 
 | 	"CLASS_ESCAPE", | 
 | #endif | 
 | #ifndef REG_COMMENT | 
 | 	"COMMENT", | 
 | #endif | 
 | #ifndef REG_DELIMITED | 
 | 	"DELIMITED", | 
 | #endif | 
 | #ifndef REG_DISCIPLINE | 
 | 	"DISCIPLINE", | 
 | #endif | 
 | #ifndef REG_ESCAPE | 
 | 	"ESCAPE", | 
 | #endif | 
 | #ifndef REG_ICASE | 
 | 	"ICASE", | 
 | #endif | 
 | #ifndef REG_LEFT | 
 | 	"LEFT", | 
 | #endif | 
 | #ifndef REG_LENIENT | 
 | 	"LENIENT", | 
 | #endif | 
 | #ifndef REG_LITERAL | 
 | 	"LITERAL", | 
 | #endif | 
 | #ifndef REG_MINIMAL | 
 | 	"MINIMAL", | 
 | #endif | 
 | #ifndef REG_MULTIPLE | 
 | 	"MULTIPLE", | 
 | #endif | 
 | #ifndef REG_MULTIREF | 
 | 	"MULTIREF", | 
 | #endif | 
 | #ifndef REG_MUSTDELIM | 
 | 	"MUSTDELIM", | 
 | #endif | 
 | #ifndef REG_NEWLINE | 
 | 	"NEWLINE", | 
 | #endif | 
 | #ifndef REG_NOTBOL | 
 | 	"NOTBOL", | 
 | #endif | 
 | #ifndef REG_NOTEOL | 
 | 	"NOTEOL", | 
 | #endif | 
 | #ifndef REG_NULL | 
 | 	"NULL", | 
 | #endif | 
 | #ifndef REG_RIGHT | 
 | 	"RIGHT", | 
 | #endif | 
 | #ifndef REG_SHELL_DOT | 
 | 	"SHELL_DOT", | 
 | #endif | 
 | #ifndef REG_SHELL_ESCAPED | 
 | 	"SHELL_ESCAPED", | 
 | #endif | 
 | #ifndef REG_SHELL_GROUP | 
 | 	"SHELL_GROUP", | 
 | #endif | 
 | #ifndef REG_SHELL_PATH | 
 | 	"SHELL_PATH", | 
 | #endif | 
 | #ifndef REG_SPAN | 
 | 	"SPAN", | 
 | #endif | 
 | #if REG_NOSUB & REG_TEST_DEFAULT | 
 | 	"SUBMATCH", | 
 | #endif | 
 | #if !_REG_nexec | 
 | 	"regnexec", | 
 | #endif | 
 | #if !_REG_subcomp | 
 | 	"regsubcomp", | 
 | #endif | 
 | #if !_REG_decomp | 
 | 	"redecomp", | 
 | #endif | 
 | 	0 | 
 | }; | 
 |  | 
 | #ifndef REG_CLASS_ESCAPE | 
 | #define REG_CLASS_ESCAPE	NOTEST | 
 | #endif | 
 | #ifndef REG_COMMENT | 
 | #define REG_COMMENT	NOTEST | 
 | #endif | 
 | #ifndef REG_DELIMITED | 
 | #define REG_DELIMITED	NOTEST | 
 | #endif | 
 | #ifndef REG_ESCAPE | 
 | #define REG_ESCAPE	NOTEST | 
 | #endif | 
 | #ifndef REG_ICASE | 
 | #define REG_ICASE	NOTEST | 
 | #endif | 
 | #ifndef REG_LEFT | 
 | #define REG_LEFT	NOTEST | 
 | #endif | 
 | #ifndef REG_LENIENT | 
 | #define REG_LENIENT	0 | 
 | #endif | 
 | #ifndef REG_MINIMAL | 
 | #define REG_MINIMAL	NOTEST | 
 | #endif | 
 | #ifndef REG_MULTIPLE | 
 | #define REG_MULTIPLE	NOTEST | 
 | #endif | 
 | #ifndef REG_MULTIREF | 
 | #define REG_MULTIREF	NOTEST | 
 | #endif | 
 | #ifndef REG_MUSTDELIM | 
 | #define REG_MUSTDELIM	NOTEST | 
 | #endif | 
 | #ifndef REG_NEWLINE | 
 | #define REG_NEWLINE	NOTEST | 
 | #endif | 
 | #ifndef REG_NOTBOL | 
 | #define REG_NOTBOL	NOTEST | 
 | #endif | 
 | #ifndef REG_NOTEOL | 
 | #define REG_NOTEOL	NOTEST | 
 | #endif | 
 | #ifndef REG_NULL | 
 | #define REG_NULL	NOTEST | 
 | #endif | 
 | #ifndef REG_RIGHT | 
 | #define REG_RIGHT	NOTEST | 
 | #endif | 
 | #ifndef REG_SHELL_DOT | 
 | #define REG_SHELL_DOT	NOTEST | 
 | #endif | 
 | #ifndef REG_SHELL_ESCAPED | 
 | #define REG_SHELL_ESCAPED	NOTEST | 
 | #endif | 
 | #ifndef REG_SHELL_GROUP | 
 | #define REG_SHELL_GROUP	NOTEST | 
 | #endif | 
 | #ifndef REG_SHELL_PATH | 
 | #define REG_SHELL_PATH	NOTEST | 
 | #endif | 
 | #ifndef REG_SPAN | 
 | #define REG_SPAN	NOTEST | 
 | #endif | 
 |  | 
 | #define REG_UNKNOWN	(-1) | 
 |  | 
 | #ifndef REG_ENEWLINE | 
 | #define REG_ENEWLINE	(REG_UNKNOWN-1) | 
 | #endif | 
 | #ifndef REG_ENULL | 
 | #ifndef REG_EMPTY | 
 | #define REG_ENULL	(REG_UNKNOWN-2) | 
 | #else | 
 | #define REG_ENULL	REG_EMPTY | 
 | #endif | 
 | #endif | 
 | #ifndef REG_ECOUNT | 
 | #define REG_ECOUNT	(REG_UNKNOWN-3) | 
 | #endif | 
 | #ifndef REG_BADESC | 
 | #define REG_BADESC	(REG_UNKNOWN-4) | 
 | #endif | 
 | #ifndef REG_EMEM | 
 | #define REG_EMEM	(REG_UNKNOWN-5) | 
 | #endif | 
 | #ifndef REG_EHUNG | 
 | #define REG_EHUNG	(REG_UNKNOWN-6) | 
 | #endif | 
 | #ifndef REG_EBUS | 
 | #define REG_EBUS	(REG_UNKNOWN-7) | 
 | #endif | 
 | #ifndef REG_EFAULT | 
 | #define REG_EFAULT	(REG_UNKNOWN-8) | 
 | #endif | 
 | #ifndef REG_EFLAGS | 
 | #define REG_EFLAGS	(REG_UNKNOWN-9) | 
 | #endif | 
 | #ifndef REG_EDELIM | 
 | #define REG_EDELIM	(REG_UNKNOWN-9) | 
 | #endif | 
 |  | 
 | static const struct { int code; char* name; } codes[] = | 
 | { | 
 | 	REG_UNKNOWN,	"UNKNOWN", | 
 | 	REG_NOMATCH,	"NOMATCH", | 
 | 	REG_BADPAT,	"BADPAT", | 
 | 	REG_ECOLLATE,	"ECOLLATE", | 
 | 	REG_ECTYPE,	"ECTYPE", | 
 | 	REG_EESCAPE,	"EESCAPE", | 
 | 	REG_ESUBREG,	"ESUBREG", | 
 | 	REG_EBRACK,	"EBRACK", | 
 | 	REG_EPAREN,	"EPAREN", | 
 | 	REG_EBRACE,	"EBRACE", | 
 | 	REG_BADBR,	"BADBR", | 
 | 	REG_ERANGE,	"ERANGE", | 
 | 	REG_ESPACE,	"ESPACE", | 
 | 	REG_BADRPT,	"BADRPT", | 
 | 	REG_ENEWLINE,	"ENEWLINE", | 
 | 	REG_ENULL,	"ENULL", | 
 | 	REG_ECOUNT,	"ECOUNT", | 
 | 	REG_BADESC,	"BADESC", | 
 | 	REG_EMEM,	"EMEM", | 
 | 	REG_EHUNG,	"EHUNG", | 
 | 	REG_EBUS,	"EBUS", | 
 | 	REG_EFAULT,	"EFAULT", | 
 | 	REG_EFLAGS,	"EFLAGS", | 
 | 	REG_EDELIM,	"EDELIM", | 
 | }; | 
 |  | 
 | static struct | 
 | { | 
 | 	regmatch_t	NOMATCH; | 
 | 	int		errors; | 
 | 	int		extracted; | 
 | 	int		ignored; | 
 | 	int		lineno; | 
 | 	int		passed; | 
 | 	int		signals; | 
 | 	int		unspecified; | 
 | 	int		verify; | 
 | 	int		warnings; | 
 | 	char*		file; | 
 | 	char*		stack; | 
 | 	char*		which; | 
 | 	jmp_buf		gotcha; | 
 | #ifdef REG_DISCIPLINE | 
 | 	Disc_t		disc; | 
 | #endif | 
 | } state; | 
 |  | 
 | static void | 
 | quote(char* s, int len, unsigned long test) | 
 | { | 
 | 	unsigned char*	u = (unsigned char*)s; | 
 | 	unsigned char*	e; | 
 | 	int		c; | 
 | #ifdef MB_CUR_MAX | 
 | 	int		w; | 
 | #endif | 
 |  | 
 | 	if (!u) | 
 | 		printf("NIL"); | 
 | 	else if (!*u && len <= 1) | 
 | 		printf("NULL"); | 
 | 	else if (test & TEST_EXPAND) | 
 | 	{ | 
 | 		if (len < 0) | 
 | 			len = strlen((char*)u); | 
 | 		e = u + len; | 
 | 		if (test & TEST_DELIMIT) | 
 | 			printf("\""); | 
 | 		while (u < e) | 
 | 			switch (c = *u++) | 
 | 			{ | 
 | 			case '\\': | 
 | 				printf("\\\\"); | 
 | 				break; | 
 | 			case '"': | 
 | 				if (test & TEST_DELIMIT) | 
 | 					printf("\\\""); | 
 | 				else | 
 | 					printf("\""); | 
 | 				break; | 
 | 			case '\a': | 
 | 				printf("\\a"); | 
 | 				break; | 
 | 			case '\b': | 
 | 				printf("\\b"); | 
 | 				break; | 
 | 			case 033: | 
 | 				printf("\\e"); | 
 | 				break; | 
 | 			case '\f': | 
 | 				printf("\\f"); | 
 | 				break; | 
 | 			case '\n': | 
 | 				printf("\\n"); | 
 | 				break; | 
 | 			case '\r': | 
 | 				printf("\\r"); | 
 | 				break; | 
 | 			case '\t': | 
 | 				printf("\\t"); | 
 | 				break; | 
 | 			case '\v': | 
 | 				printf("\\v"); | 
 | 				break; | 
 | 			default: | 
 | #ifdef MB_CUR_MAX | 
 | 				s = (char*)u - 1; | 
 | 				if ((w = mblen(s, (char*)e - s)) > 1) | 
 | 				{ | 
 | 					u += w - 1; | 
 | 					fwrite(s, 1, w, stdout); | 
 | 				} | 
 | 				else | 
 | #endif | 
 | 				if (!iscntrl(c) && isprint(c)) | 
 | 					putchar(c); | 
 | 				else | 
 | 					printf("\\x%02x", c); | 
 | 				break; | 
 | 			} | 
 | 		if (test & TEST_DELIMIT) | 
 | 			printf("\""); | 
 | 	} | 
 | 	else | 
 | 		printf("%s", s); | 
 | } | 
 |  | 
 | static void | 
 | report(char* comment, char* fun, char* re, char* s, int len, char* msg, int flags, unsigned long test) | 
 | { | 
 | 	if (state.file) | 
 | 		printf("%s:", state.file); | 
 | 	printf("%d:", state.lineno); | 
 | 	if (re) | 
 | 	{ | 
 | 		printf(" "); | 
 | 		quote(re, -1, test|TEST_DELIMIT); | 
 | 		if (s) | 
 | 		{ | 
 | 			printf(" versus "); | 
 | 			quote(s, len, test|TEST_DELIMIT); | 
 | 		} | 
 | 	} | 
 | 	if (test & TEST_UNSPECIFIED) | 
 | 	{ | 
 | 		state.unspecified++; | 
 | 		printf(" unspecified behavior"); | 
 | 	} | 
 | 	else | 
 | 		state.errors++; | 
 | 	if (state.which) | 
 | 		printf(" %s", state.which); | 
 | 	if (flags & REG_NOSUB) | 
 | 		printf(" NOSUB"); | 
 | 	if (fun) | 
 | 		printf(" %s", fun); | 
 | 	if (comment[strlen(comment)-1] == '\n') | 
 | 		printf(" %s", comment); | 
 | 	else | 
 | 	{ | 
 | 		printf(" %s: ", comment); | 
 | 		if (msg) | 
 | 			printf("%s: ", msg); | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | error(regex_t* preg, int code) | 
 | { | 
 | 	char*	msg; | 
 | 	char	buf[256]; | 
 |  | 
 | 	switch (code) | 
 | 	{ | 
 | 	case REG_EBUS: | 
 | 		msg = "bus error"; | 
 | 		break; | 
 | 	case REG_EFAULT: | 
 | 		msg = "memory fault"; | 
 | 		break; | 
 | 	case REG_EHUNG: | 
 | 		msg = "did not terminate"; | 
 | 		break; | 
 | 	default: | 
 | 		regerror(code, preg, msg = buf, sizeof buf); | 
 | 		break; | 
 | 	} | 
 | 	printf("%s\n", msg); | 
 | } | 
 |  | 
 | static void | 
 | bad(char* comment, char* re, char* s, int len, unsigned long test) | 
 | { | 
 | 	printf("bad test case "); | 
 | 	report(comment, NiL, re, s, len, NiL, 0, test); | 
 | 	exit(1); | 
 | } | 
 |  | 
 | static int | 
 | escape(char* s) | 
 | { | 
 | 	char*	b; | 
 | 	char*	t; | 
 | 	char*	q; | 
 | 	char*	e; | 
 | 	int	c; | 
 |  | 
 | 	for (b = t = s; *t = *s; s++, t++) | 
 | 		if (*s == '\\') | 
 | 			switch (*++s) | 
 | 			{ | 
 | 			case '\\': | 
 | 				break; | 
 | 			case 'a': | 
 | 				*t = '\a'; | 
 | 				break; | 
 | 			case 'b': | 
 | 				*t = '\b'; | 
 | 				break; | 
 | 			case 'c': | 
 | 				if (*t = *++s) | 
 | 					*t &= 037; | 
 | 				else | 
 | 					s--; | 
 | 				break; | 
 | 			case 'e': | 
 | 			case 'E': | 
 | 				*t = 033; | 
 | 				break; | 
 | 			case 'f': | 
 | 				*t = '\f'; | 
 | 				break; | 
 | 			case 'n': | 
 | 				*t = '\n'; | 
 | 				break; | 
 | 			case 'r': | 
 | 				*t = '\r'; | 
 | 				break; | 
 | 			case 's': | 
 | 				*t = ' '; | 
 | 				break; | 
 | 			case 't': | 
 | 				*t = '\t'; | 
 | 				break; | 
 | 			case 'v': | 
 | 				*t = '\v'; | 
 | 				break; | 
 | 			case 'u': | 
 | 			case 'x': | 
 | 				c = 0; | 
 | 				q = c == 'u' ? (s + 5) : (char*)0; | 
 | 				e = s + 1; | 
 | 				while (!e || !q || s < q) | 
 | 				{ | 
 | 					switch (*++s) | 
 | 					{ | 
 | 					case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': | 
 | 						c = (c << 4) + *s - 'a' + 10; | 
 | 						continue; | 
 | 					case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': | 
 | 						c = (c << 4) + *s - 'A' + 10; | 
 | 						continue; | 
 | 					case '0': case '1': case '2': case '3': case '4': | 
 | 					case '5': case '6': case '7': case '8': case '9': | 
 | 						c = (c << 4) + *s - '0'; | 
 | 						continue; | 
 | 					case '{': | 
 | 					case '[': | 
 | 						if (s != e) | 
 | 						{ | 
 | 							s--; | 
 | 							break; | 
 | 						} | 
 | 						e = 0; | 
 | 						continue; | 
 | 					case '}': | 
 | 					case ']': | 
 | 						if (e) | 
 | 							s--; | 
 | 						break; | 
 | 					default: | 
 | 						s--; | 
 | 						break; | 
 | 					} | 
 | 					break; | 
 | 				} | 
 | 				*t = c; | 
 | 				break; | 
 | 			case '0': case '1': case '2': case '3': | 
 | 			case '4': case '5': case '6': case '7': | 
 | 				c = *s - '0'; | 
 | 				q = s + 2; | 
 | 				while (s < q) | 
 | 				{ | 
 | 					switch (*++s) | 
 | 					{ | 
 | 					case '0': case '1': case '2': case '3': | 
 | 					case '4': case '5': case '6': case '7': | 
 | 						c = (c << 3) + *s - '0'; | 
 | 						break; | 
 | 					default: | 
 | 						q = --s; | 
 | 						break; | 
 | 					} | 
 | 				} | 
 | 				*t = c; | 
 | 				break; | 
 | 			default: | 
 | 				*(s + 1) = 0; | 
 | 				bad("invalid C \\ escape\n", s - 1, NiL, 0, 0); | 
 | 			} | 
 | 	return t - b; | 
 | } | 
 |  | 
 | static void | 
 | matchoffprint(int off) | 
 | { | 
 | 	switch (off) | 
 | 	{ | 
 | 	case -2: | 
 | 		printf("X"); | 
 | 		break; | 
 | 	case -1: | 
 | 		printf("?"); | 
 | 		break; | 
 | 	default: | 
 | 		printf("%d", off); | 
 | 		break; | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | matchprint(regmatch_t* match, int nmatch, int nsub, char* ans, unsigned long test) | 
 | { | 
 | 	int	i; | 
 |  | 
 | 	for (; nmatch > nsub + 1; nmatch--) | 
 | 		if ((match[nmatch-1].rm_so != -1 || match[nmatch-1].rm_eo != -1) && (!(test & TEST_IGNORE_POSITION) || match[nmatch-1].rm_so >= 0 && match[nmatch-1].rm_eo >= 0)) | 
 | 			break; | 
 | 	for (i = 0; i < nmatch; i++) | 
 | 	{ | 
 | 		printf("("); | 
 | 		matchoffprint(match[i].rm_so); | 
 | 		printf(","); | 
 | 		matchoffprint(match[i].rm_eo); | 
 | 		printf(")"); | 
 | 	} | 
 | 	if (!(test & (TEST_ACTUAL|TEST_BASELINE))) | 
 | 	{ | 
 | 		if (ans) | 
 | 			printf(" expected: %s", ans); | 
 | 		printf("\n"); | 
 | 	} | 
 | } | 
 |  | 
 | static int | 
 | matchcheck(regmatch_t* match, int nmatch, int nsub, char* ans, char* re, char* s, int len, int flags, unsigned long test) | 
 | { | 
 | 	char*	p; | 
 | 	int	i; | 
 | 	int	m; | 
 | 	int	n; | 
 |  | 
 | 	if (streq(ans, "OK")) | 
 | 		return test & (TEST_BASELINE|TEST_PASS|TEST_VERIFY); | 
 | 	for (i = 0, p = ans; i < nmatch && *p; i++) | 
 | 	{ | 
 | 		if (*p == '{') | 
 | 		{ | 
 | #ifdef REG_DISCIPLINE | 
 | 			char*	x; | 
 |  | 
 | 			if (!(x = sfstruse(state.disc.sp))) | 
 | 				bad("out of space [discipline string]\n", NiL, NiL, 0, 0); | 
 | 			if (strcmp(p, x)) | 
 | 			{ | 
 | 				if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) | 
 | 					return 0; | 
 | 				report("callout failed", NiL, re, s, len, NiL, flags, test); | 
 | 				quote(p, -1, test); | 
 | 				printf(" expected, "); | 
 | 				quote(x, -1, test); | 
 | 				printf(" returned\n"); | 
 | 			} | 
 | #endif | 
 | 			break; | 
 | 		} | 
 | 		if (*p++ != '(') | 
 | 			bad("improper answer\n", re, s, -1, test); | 
 | 		if (*p == '?') | 
 | 		{ | 
 | 			m = -1; | 
 | 			p++; | 
 | 		} | 
 | 		else if (*p == 'R' && !memcmp(p, "RE_DUP_MAX", 10)) | 
 | 		{ | 
 | 			m = RE_DUP_MAX; | 
 | 			p += 10; | 
 | 			if (*p == '+' || *p == '-') | 
 | 				m += strtol(p, &p, 10); | 
 | 		} | 
 | 		else | 
 | 			m = strtol(p, &p, 10); | 
 | 		if (*p++ != ',') | 
 | 			bad("improper answer\n", re, s, -1, test); | 
 | 		if (*p == '?') | 
 | 		{ | 
 | 			n = -1; | 
 | 			p++; | 
 | 		} | 
 | 		else if (*p == 'R' && !memcmp(p, "RE_DUP_MAX", 10)) | 
 | 		{ | 
 | 			n = RE_DUP_MAX; | 
 | 			p += 10; | 
 | 			if (*p == '+' || *p == '-') | 
 | 				n += strtol(p, &p, 10); | 
 | 		} | 
 | 		else | 
 | 			n = strtol(p, &p, 10); | 
 | 		if (*p++ != ')') | 
 | 			bad("improper answer\n", re, s, -1, test); | 
 | 		if (m!=match[i].rm_so || n!=match[i].rm_eo) | 
 | 		{ | 
 | 			if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY))) | 
 | 			{ | 
 | 				report("failed: match was", NiL, re, s, len, NiL, flags, test); | 
 | 				matchprint(match, nmatch, nsub, ans, test); | 
 | 			} | 
 | 			return 0; | 
 | 		} | 
 | 	} | 
 | 	for (; i < nmatch; i++) | 
 | 	{ | 
 | 		if (match[i].rm_so!=-1 || match[i].rm_eo!=-1) | 
 | 		{ | 
 | 			if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_VERIFY))) | 
 | 			{ | 
 | 				if ((test & TEST_IGNORE_POSITION) && (match[i].rm_so<0 || match[i].rm_eo<0)) | 
 | 				{ | 
 | 					state.ignored++; | 
 | 					return 0; | 
 | 				} | 
 | 				if (!(test & TEST_SUMMARY)) | 
 | 				{ | 
 | 					report("failed: match was", NiL, re, s, len, NiL, flags, test); | 
 | 					matchprint(match, nmatch, nsub, ans, test); | 
 | 				} | 
 | 			} | 
 | 			return 0; | 
 | 		} | 
 | 	} | 
 | 	if (!(test & TEST_IGNORE_OVER) && match[nmatch].rm_so != state.NOMATCH.rm_so) | 
 | 	{ | 
 | 		if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY))) | 
 | 		{ | 
 | 			report("failed: overran match array", NiL, re, s, len, NiL, flags, test); | 
 | 			matchprint(match, nmatch + 1, nsub, NiL, test); | 
 | 		} | 
 | 		return 0; | 
 | 	} | 
 | 	return 1; | 
 | } | 
 |  | 
 | static void | 
 | sigunblock(int s) | 
 | { | 
 | #ifdef SIG_SETMASK | 
 | 	int		op; | 
 | 	sigset_t	mask; | 
 |  | 
 | 	sigemptyset(&mask); | 
 | 	if (s) | 
 | 	{ | 
 | 		sigaddset(&mask, s); | 
 | 		op = SIG_UNBLOCK; | 
 | 	} | 
 | 	else op = SIG_SETMASK; | 
 | 	sigprocmask(op, &mask, NiL); | 
 | #else | 
 | #ifdef sigmask | 
 | 	sigsetmask(s ? (sigsetmask(0L) & ~sigmask(s)) : 0L); | 
 | #endif | 
 | #endif | 
 | } | 
 |  | 
 | static void | 
 | gotcha(int sig) | 
 | { | 
 | 	int	ret; | 
 |  | 
 | 	signal(sig, gotcha); | 
 | 	alarm(0); | 
 | 	state.signals++; | 
 | 	switch (sig) | 
 | 	{ | 
 | 	case SIGALRM: | 
 | 		ret = REG_EHUNG; | 
 | 		break; | 
 | 	case SIGBUS: | 
 | 		ret = REG_EBUS; | 
 | 		break; | 
 | 	default: | 
 | 		ret = REG_EFAULT; | 
 | 		break; | 
 | 	} | 
 | 	sigunblock(sig); | 
 | 	longjmp(state.gotcha, ret); | 
 | } | 
 |  | 
 | static char* | 
 | getline(FILE* fp) | 
 | { | 
 | 	static char	buf[32 * 1024]; | 
 |  | 
 | 	register char*	s = buf; | 
 | 	register char*	e = &buf[sizeof(buf)]; | 
 | 	register char*	b; | 
 |  | 
 | 	for (;;) | 
 | 	{ | 
 | 		if (!(b = fgets(s, e - s, fp))) | 
 | 			return 0; | 
 | 		state.lineno++; | 
 | 		s += strlen(s); | 
 | 		if (s == b || *--s != '\n' || s == b || *(s - 1) != '\\') | 
 | 		{ | 
 | 			*s = 0; | 
 | 			break; | 
 | 		} | 
 | 		s--; | 
 | 	} | 
 | 	return buf; | 
 | } | 
 |  | 
 | static unsigned long | 
 | note(unsigned long level, char* msg, unsigned long skip, unsigned long test) | 
 | { | 
 | 	if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY)) && !skip) | 
 | 	{ | 
 | 		printf("NOTE\t"); | 
 | 		if (msg) | 
 | 			printf("%s: ", msg); | 
 | 		printf("skipping lines %d", state.lineno); | 
 | 	} | 
 | 	return skip | level; | 
 | } | 
 |  | 
 | #define TABS(n)		&ts[7-((n)&7)] | 
 |  | 
 | static char		ts[] = "\t\t\t\t\t\t\t"; | 
 |  | 
 | static unsigned long | 
 | extract(int* tabs, char* spec, char* re, char* s, char* ans, char* msg, char* accept, regmatch_t* match, int nmatch, int nsub, unsigned long skip, unsigned long level, unsigned long test) | 
 | { | 
 | 	if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_OK|TEST_PASS|TEST_SUMMARY)) | 
 | 	{ | 
 | 		state.extracted = 1; | 
 | 		if (test & TEST_OK) | 
 | 		{ | 
 | 			state.passed++; | 
 | 			if ((test & TEST_VERIFY) && !(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) | 
 | 			{ | 
 | 				if (msg && strcmp(msg, "EXPECTED")) | 
 | 					printf("NOTE\t%s\n", msg); | 
 | 				return skip; | 
 | 			} | 
 | 			test &= ~(TEST_PASS|TEST_QUERY); | 
 | 		} | 
 | 		if (test & (TEST_QUERY|TEST_VERIFY)) | 
 | 		{ | 
 | 			if (test & TEST_BASELINE) | 
 | 				test &= ~(TEST_BASELINE|TEST_PASS); | 
 | 			else | 
 | 				test |= TEST_PASS; | 
 | 			skip |= level; | 
 | 		} | 
 | 		if (!(test & TEST_OK)) | 
 | 		{ | 
 | 			if (test & TEST_UNSPECIFIED) | 
 | 				state.unspecified++; | 
 | 			else | 
 | 				state.errors++; | 
 | 		} | 
 | 		if (test & (TEST_PASS|TEST_SUMMARY)) | 
 | 			return skip; | 
 | 		test &= ~TEST_DELIMIT; | 
 | 		printf("%s%s", spec, TABS(*tabs++)); | 
 | 		if ((test & (TEST_BASELINE|TEST_SAME)) == (TEST_BASELINE|TEST_SAME)) | 
 | 			printf("SAME"); | 
 | 		else | 
 | 			quote(re, -1, test); | 
 | 		printf("%s", TABS(*tabs++)); | 
 | 		quote(s, -1, test); | 
 | 		printf("%s", TABS(*tabs++)); | 
 | 		if (!(test & (TEST_ACTUAL|TEST_BASELINE)) || !accept && !match) | 
 | 			printf("%s", ans); | 
 | 		else if (accept) | 
 | 			printf("%s", accept); | 
 | 		else | 
 | 			matchprint(match, nmatch, nsub, NiL, test); | 
 | 		if (msg) | 
 | 			printf("%s%s", TABS(*tabs++), msg); | 
 | 		putchar('\n'); | 
 | 	} | 
 | 	else if (test & TEST_QUERY) | 
 | 		skip = note(level, msg, skip, test); | 
 | 	else if (test & TEST_VERIFY) | 
 | 		state.extracted = 1; | 
 | 	return skip; | 
 | } | 
 |  | 
 | static int | 
 | catchfree(regex_t* preg, int flags, int* tabs, char* spec, char* re, char* s, char* ans, char* msg, char* accept, regmatch_t* match, int nmatch, int nsub, unsigned long skip, unsigned long level, unsigned long test) | 
 | { | 
 | 	int	eret; | 
 |  | 
 | 	if (!(test & TEST_CATCH)) | 
 | 	{ | 
 | 		regfree(preg); | 
 | 		eret = 0; | 
 | 	} | 
 | 	else if (!(eret = setjmp(state.gotcha))) | 
 | 	{ | 
 | 		alarm(HUNG); | 
 | 		regfree(preg); | 
 | 		alarm(0); | 
 | 	} | 
 | 	else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) | 
 | 		extract(tabs, spec, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); | 
 | 	else | 
 | 	{ | 
 | 		report("failed", "regfree", re, NiL, -1, msg, flags, test); | 
 | 		error(preg, eret); | 
 | 	} | 
 | 	return eret; | 
 | } | 
 |  | 
 | static char* | 
 | expand(char* os, char* ot) | 
 | { | 
 | 	char*	s = os; | 
 | 	char*	t; | 
 | 	int	n = 0; | 
 | 	int	r; | 
 | 	long	m; | 
 |  | 
 | 	for (;;) | 
 | 	{ | 
 | 		switch (*s++) | 
 | 		{ | 
 | 		case 0: | 
 | 			break; | 
 | 		case '{': | 
 | 			n++; | 
 | 			continue; | 
 | 		case '}': | 
 | 			n--; | 
 | 			continue; | 
 | 		case 'R': | 
 | 			if (n == 1 && !memcmp(s, "E_DUP_MAX", 9)) | 
 | 			{ | 
 | 				s--; | 
 | 				for (t = ot; os < s; *t++ = *os++); | 
 | 				r = ((t - ot) >= 5 && t[-1] == '{' && t[-2] == '.' && t[-3] == '.' && t[-4] == '.') ? t[-5] : 0; | 
 | 				os = ot; | 
 | 				m = RE_DUP_MAX; | 
 | 				if (*(s += 10) == '+' || *s == '-') | 
 | 					m += strtol(s, &s, 10); | 
 | 				if (r) | 
 | 				{ | 
 | 					t -= 5; | 
 | 					while (m-- > 0) | 
 | 						*t++ = r; | 
 | 					while (*s && *s++ != '}'); | 
 | 				} | 
 | 				else | 
 | 					t += snprintf(t, 32, "%ld", m); | 
 | 				while (*t = *s++) | 
 | 					t++; | 
 | 				break; | 
 | 			} | 
 | 			continue; | 
 | 		default: | 
 | 			continue; | 
 | 		} | 
 | 		break; | 
 | 	} | 
 | 	return os; | 
 | } | 
 |  | 
 | int | 
 | main(int argc, char** argv) | 
 | { | 
 | 	int		flags; | 
 | 	int		cflags; | 
 | 	int		eflags; | 
 | 	int		nmatch; | 
 | 	int		nexec; | 
 | 	int		nstr; | 
 | 	int		cret; | 
 | 	int		eret; | 
 | 	int		nsub; | 
 | 	int		i; | 
 | 	int		j; | 
 | 	int		expected; | 
 | 	int		got; | 
 | 	int		locale; | 
 | 	int		subunitlen; | 
 | 	int		testno; | 
 | 	unsigned long	level; | 
 | 	unsigned long	skip; | 
 | 	char*		p; | 
 | 	char*		line; | 
 | 	char*		spec; | 
 | 	char*		re; | 
 | 	char*		s; | 
 | 	char*		ans; | 
 | 	char*		msg; | 
 | 	char*		fun; | 
 | 	char*		ppat; | 
 | 	char*		subunit; | 
 | 	char*		version; | 
 | 	char*		field[6]; | 
 | 	char*		delim[6]; | 
 | 	FILE*		fp; | 
 | 	int		tabs[6]; | 
 | 	char		unit[64]; | 
 | 	regmatch_t	match[100]; | 
 | 	regex_t		preg; | 
 |  | 
 | 	static char	pat[32 * 1024]; | 
 | 	static char	patbuf[32 * 1024]; | 
 | 	static char	strbuf[32 * 1024]; | 
 |  | 
 | 	int		nonosub = REG_NOSUB == 0; | 
 | 	int		nonexec = 0; | 
 |  | 
 | 	unsigned long	test = 0; | 
 |  | 
 | 	static char*	filter[] = { "-", 0 }; | 
 |  | 
 | 	state.NOMATCH.rm_so = state.NOMATCH.rm_eo = -2; | 
 | 	p = unit; | 
 | 	version = (char*)id + 10; | 
 | 	while (p < &unit[sizeof(unit)-1] && (*p = *version++) && !isspace(*p)) | 
 | 		p++; | 
 | 	*p = 0; | 
 | 	while ((p = *++argv) && *p == '-') | 
 | 		for (;;) | 
 | 		{ | 
 | 			switch (*++p) | 
 | 			{ | 
 | 			case 0: | 
 | 				break; | 
 | 			case 'c': | 
 | 				test |= TEST_CATCH; | 
 | 				continue; | 
 | 			case 'e': | 
 | 				test |= TEST_IGNORE_ERROR; | 
 | 				continue; | 
 | 			case 'h': | 
 | 			case '?': | 
 | 				help(0); | 
 | 				return 2; | 
 | 			case '-': | 
 | 				help(p[1] == 'h'); | 
 | 				return 2; | 
 | 			case 'n': | 
 | 				nonexec = 1; | 
 | 				continue; | 
 | 			case 'o': | 
 | 				test |= TEST_IGNORE_OVER; | 
 | 				continue; | 
 | 			case 'p': | 
 | 				test |= TEST_IGNORE_POSITION; | 
 | 				continue; | 
 | 			case 's': | 
 | #ifdef REG_DISCIPLINE | 
 | 				if (!(state.stack = stkalloc(stkstd, 0))) | 
 | 					fprintf(stderr, "%s: out of space [stack]", unit); | 
 | 				state.disc.disc.re_resizef = resizef; | 
 | 				state.disc.disc.re_resizehandle = (void*)stkstd; | 
 | #endif | 
 | 				continue; | 
 | 			case 'x': | 
 | 				nonosub = 1; | 
 | 				continue; | 
 | 			case 'v': | 
 | 				test |= TEST_VERBOSE; | 
 | 				continue; | 
 | 			case 'A': | 
 | 				test |= TEST_ACTUAL; | 
 | 				continue; | 
 | 			case 'B': | 
 | 				test |= TEST_BASELINE; | 
 | 				continue; | 
 | 			case 'F': | 
 | 				test |= TEST_FAIL; | 
 | 				continue; | 
 | 			case 'P': | 
 | 				test |= TEST_PASS; | 
 | 				continue; | 
 | 			case 'S': | 
 | 				test |= TEST_SUMMARY; | 
 | 				continue; | 
 | 			default: | 
 | 				fprintf(stderr, "%s: %c: invalid option\n", unit, *p); | 
 | 				return 2; | 
 | 			} | 
 | 			break; | 
 | 		} | 
 | 	if (!*argv) | 
 | 		argv = filter; | 
 | 	locale = 0; | 
 | 	while (state.file = *argv++) | 
 | 	{ | 
 | 		if (streq(state.file, "-") || streq(state.file, "/dev/stdin") || streq(state.file, "/dev/fd/0")) | 
 | 		{ | 
 | 			state.file = 0; | 
 | 			fp = stdin; | 
 | 		} | 
 | 		else if (!(fp = fopen(state.file, "r"))) | 
 | 		{ | 
 | 			fprintf(stderr, "%s: %s: cannot read\n", unit, state.file); | 
 | 			return 2; | 
 | 		} | 
 | 		testno = state.errors = state.ignored = state.lineno = state.passed = | 
 | 		state.signals = state.unspecified = state.warnings = 0; | 
 | 		skip = 0; | 
 | 		level = 1; | 
 | 		if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) | 
 | 		{ | 
 | 			printf("TEST\t%s ", unit); | 
 | 			if (s = state.file) | 
 | 			{ | 
 | 				subunit = p = 0; | 
 | 				for (;;) | 
 | 				{ | 
 | 					switch (*s++) | 
 | 					{ | 
 | 					case 0: | 
 | 						break; | 
 | 					case '/': | 
 | 						subunit = s; | 
 | 						continue; | 
 | 					case '.': | 
 | 						p = s - 1; | 
 | 						continue; | 
 | 					default: | 
 | 						continue; | 
 | 					} | 
 | 					break; | 
 | 				} | 
 | 				if (!subunit) | 
 | 					subunit = state.file; | 
 | 				if (p < subunit) | 
 | 					p = s - 1; | 
 | 				subunitlen = p - subunit; | 
 | 				printf("%-.*s ", subunitlen, subunit); | 
 | 			} | 
 | 			else | 
 | 				subunit = 0; | 
 | 			for (s = version; *s && (*s != ' ' || *(s + 1) != '$'); s++) | 
 | 				putchar(*s); | 
 | 			if (test & TEST_CATCH) | 
 | 				printf(", catch"); | 
 | 			if (test & TEST_IGNORE_ERROR) | 
 | 				printf(", ignore error code mismatches"); | 
 | 			if (test & TEST_IGNORE_POSITION) | 
 | 				printf(", ignore negative position mismatches"); | 
 | #ifdef REG_DISCIPLINE | 
 | 			if (state.stack) | 
 | 				printf(", stack"); | 
 | #endif | 
 | 			if (test & TEST_VERBOSE) | 
 | 				printf(", verbose"); | 
 | 			printf("\n"); | 
 | #ifdef REG_VERSIONID | 
 | 			if (regerror(REG_VERSIONID, NiL, pat, sizeof(pat)) > 0) | 
 | 				s = pat; | 
 | 			else | 
 | #endif | 
 | #ifdef REG_TEST_VERSION | 
 | 			s = REG_TEST_VERSION; | 
 | #else | 
 | 			s = "regex"; | 
 | #endif | 
 | 			printf("NOTE\t%s\n", s); | 
 | 			if (elementsof(unsupported) > 1) | 
 | 			{ | 
 | #if (REG_TEST_DEFAULT & (REG_AUGMENTED|REG_EXTENDED|REG_SHELL)) || !defined(REG_EXTENDED) | 
 | 				i = 0; | 
 | #else | 
 | 				i = REG_EXTENDED != 0; | 
 | #endif | 
 | 				for (got = 0; i < elementsof(unsupported) - 1; i++) | 
 | 				{ | 
 | 					if (!got) | 
 | 					{ | 
 | 						got = 1; | 
 | 						printf("NOTE\tunsupported: %s", unsupported[i]); | 
 | 					} | 
 | 					else | 
 | 						printf(",%s", unsupported[i]); | 
 | 				} | 
 | 				if (got) | 
 | 					printf("\n"); | 
 | 			} | 
 | 		} | 
 | #ifdef REG_DISCIPLINE | 
 | 		state.disc.disc.re_version = REG_VERSION; | 
 | 		state.disc.disc.re_compf = compf; | 
 | 		state.disc.disc.re_execf = execf; | 
 | 		if (!(state.disc.sp = sfstropen())) | 
 | 			bad("out of space [discipline string stream]\n", NiL, NiL, 0, 0); | 
 | 		preg.re_disc = &state.disc.disc; | 
 | #endif | 
 | 		if (test & TEST_CATCH) | 
 | 		{ | 
 | 			signal(SIGALRM, gotcha); | 
 | 			signal(SIGBUS, gotcha); | 
 | 			signal(SIGSEGV, gotcha); | 
 | 		} | 
 | 		while (p = getline(fp)) | 
 | 		{ | 
 |  | 
 | 		/* parse: */ | 
 |  | 
 | 			line = p; | 
 | 			if (*p == ':' && !isspace(*(p + 1))) | 
 | 			{ | 
 | 				while (*++p && *p != ':'); | 
 | 				if (!*p++) | 
 | 				{ | 
 | 					if (test & TEST_BASELINE) | 
 | 						printf("%s\n", line); | 
 | 					continue; | 
 | 				} | 
 | 			} | 
 | 			while (isspace(*p)) | 
 | 				p++; | 
 | 			if (*p == 0 || *p == '#' || *p == 'T') | 
 | 			{ | 
 | 				if (test & TEST_BASELINE) | 
 | 					printf("%s\n", line); | 
 | 				continue; | 
 | 			} | 
 | 			if (*p == ':' || *p == 'N') | 
 | 			{ | 
 | 				if (test & TEST_BASELINE) | 
 | 					printf("%s\n", line); | 
 | 				else if (!(test & (TEST_ACTUAL|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) | 
 | 				{ | 
 | 					while (*++p && !isspace(*p)); | 
 | 					while (isspace(*p)) | 
 | 						p++; | 
 | 					printf("NOTE	%s\n", p); | 
 | 				} | 
 | 				continue; | 
 | 			} | 
 | 			j = 0; | 
 | 			i = 0; | 
 | 			field[i++] = p; | 
 | 			for (;;) | 
 | 			{ | 
 | 				switch (*p++) | 
 | 				{ | 
 | 				case 0: | 
 | 					p--; | 
 | 					j = 0; | 
 | 					goto checkfield; | 
 | 				case '\t': | 
 | 					*(delim[i] = p - 1) = 0; | 
 | 					j = 1; | 
 | 				checkfield: | 
 | 					s = field[i - 1]; | 
 | 					if (streq(s, "NIL")) | 
 | 						field[i - 1] = 0; | 
 | 					else if (streq(s, "NULL")) | 
 | 						*s = 0; | 
 | 					while (*p == '\t') | 
 | 					{ | 
 | 						p++; | 
 | 						j++; | 
 | 					} | 
 | 					tabs[i - 1] = j; | 
 | 					if (!*p) | 
 | 						break; | 
 | 					if (i >= elementsof(field)) | 
 | 						bad("too many fields\n", NiL, NiL, 0, 0); | 
 | 					field[i++] = p; | 
 | 					/*FALLTHROUGH*/ | 
 | 				default: | 
 | 					continue; | 
 | 				} | 
 | 				break; | 
 | 			} | 
 | 			if (!(spec = field[0])) | 
 | 				bad("NIL spec\n", NiL, NiL, 0, 0); | 
 |  | 
 | 		/* interpret: */ | 
 |  | 
 | 			cflags = REG_TEST_DEFAULT; | 
 | 			eflags = REG_EXEC_DEFAULT; | 
 | 			test &= TEST_GLOBAL; | 
 | 			state.extracted = 0; | 
 | 			nmatch = 20; | 
 | 			nsub = -1; | 
 | 			for (p = spec; *p; p++) | 
 | 			{ | 
 | 				if (isdigit(*p)) | 
 | 				{ | 
 | 					nmatch = strtol(p, &p, 10); | 
 | 					if (nmatch >= elementsof(match)) | 
 | 						bad("nmatch must be < 100\n", NiL, NiL, 0, 0); | 
 | 					p--; | 
 | 					continue; | 
 | 				} | 
 | 				switch (*p) | 
 | 				{ | 
 | 				case 'A': | 
 | 					test |= TEST_ARE; | 
 | 					continue; | 
 | 				case 'B': | 
 | 					test |= TEST_BRE; | 
 | 					continue; | 
 | 				case 'C': | 
 | 					if (!(test & TEST_QUERY) && !(skip & level)) | 
 | 						bad("locale must be nested\n", NiL, NiL, 0, 0); | 
 | 					test &= ~TEST_QUERY; | 
 | 					if (locale) | 
 | 						bad("locale nesting not supported\n", NiL, NiL, 0, 0); | 
 | 					if (i != 2) | 
 | 						bad("locale field expected\n", NiL, NiL, 0, 0); | 
 | 					if (!(skip & level)) | 
 | 					{ | 
 | #if defined(LC_COLLATE) && defined(LC_CTYPE) | 
 | 						s = field[1]; | 
 | 						if (!s || streq(s, "POSIX")) | 
 | 							s = "C"; | 
 | 						if ((ans = setlocale(LC_COLLATE, s)) && streq(ans, "POSIX")) | 
 | 							ans = "C"; | 
 | 						if (!ans || !streq(ans, s) && streq(s, "C")) | 
 | 							ans = 0; | 
 | 						else if ((ans = setlocale(LC_CTYPE, s)) && streq(ans, "POSIX")) | 
 | 							ans = "C"; | 
 | 						if (!ans || !streq(ans, s) && streq(s, "C")) | 
 | 							skip = note(level, s, skip, test); | 
 | 						else | 
 | 						{ | 
 | 							if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) | 
 | 								printf("NOTE	\"%s\" locale\n", s); | 
 | 							locale = level; | 
 | 						} | 
 | #else | 
 | 						skip = note(level, skip, test, "locales not supported"); | 
 | #endif | 
 | 					} | 
 | 					cflags = NOTEST; | 
 | 					continue; | 
 | 				case 'E': | 
 | 					test |= TEST_ERE; | 
 | 					continue; | 
 | 				case 'K': | 
 | 					test |= TEST_KRE; | 
 | 					continue; | 
 | 				case 'L': | 
 | 					test |= TEST_LRE; | 
 | 					continue; | 
 | 				case 'S': | 
 | 					test |= TEST_SRE; | 
 | 					continue; | 
 |  | 
 | 				case 'a': | 
 | 					cflags |= REG_LEFT|REG_RIGHT; | 
 | 					continue; | 
 | 				case 'b': | 
 | 					eflags |= REG_NOTBOL; | 
 | 					continue; | 
 | 				case 'c': | 
 | 					cflags |= REG_COMMENT; | 
 | 					continue; | 
 | 				case 'd': | 
 | 					cflags |= REG_SHELL_DOT; | 
 | 					continue; | 
 | 				case 'e': | 
 | 					eflags |= REG_NOTEOL; | 
 | 					continue; | 
 | 				case 'f': | 
 | 					cflags |= REG_MULTIPLE; | 
 | 					continue; | 
 | 				case 'g': | 
 | 					cflags |= NOTEST; | 
 | 					continue; | 
 | 				case 'h': | 
 | 					cflags |= REG_MULTIREF; | 
 | 					continue; | 
 | 				case 'i': | 
 | 					cflags |= REG_ICASE; | 
 | 					continue; | 
 | 				case 'j': | 
 | 					cflags |= REG_SPAN; | 
 | 					continue; | 
 | 				case 'k': | 
 | 					cflags |= REG_ESCAPE; | 
 | 					continue; | 
 | 				case 'l': | 
 | 					cflags |= REG_LEFT; | 
 | 					continue; | 
 | 				case 'm': | 
 | 					cflags |= REG_MINIMAL; | 
 | 					continue; | 
 | 				case 'n': | 
 | 					cflags |= REG_NEWLINE; | 
 | 					continue; | 
 | 				case 'o': | 
 | 					cflags |= REG_SHELL_GROUP; | 
 | 					continue; | 
 | 				case 'p': | 
 | 					cflags |= REG_SHELL_PATH; | 
 | 					continue; | 
 | 				case 'q': | 
 | 					cflags |= REG_DELIMITED; | 
 | 					continue; | 
 | 				case 'r': | 
 | 					cflags |= REG_RIGHT; | 
 | 					continue; | 
 | 				case 's': | 
 | 					cflags |= REG_SHELL_ESCAPED; | 
 | 					continue; | 
 | 				case 't': | 
 | 					cflags |= REG_MUSTDELIM; | 
 | 					continue; | 
 | 				case 'u': | 
 | 					test |= TEST_UNSPECIFIED; | 
 | 					continue; | 
 | 				case 'v': | 
 | 					cflags |= REG_CLASS_ESCAPE; | 
 | 					continue; | 
 | 				case 'w': | 
 | 					cflags |= REG_NOSUB; | 
 | 					continue; | 
 | 				case 'x': | 
 | 					if (REG_LENIENT) | 
 | 						cflags |= REG_LENIENT; | 
 | 					else | 
 | 						test |= TEST_LENIENT; | 
 | 					continue; | 
 | 				case 'y': | 
 | 					eflags |= REG_LEFT; | 
 | 					continue; | 
 | 				case 'z': | 
 | 					cflags |= REG_NULL; | 
 | 					continue; | 
 |  | 
 | 				case '$': | 
 | 					test |= TEST_EXPAND; | 
 | 					continue; | 
 |  | 
 | 				case '/': | 
 | 					test |= TEST_SUB; | 
 | 					continue; | 
 |  | 
 | 				case '=': | 
 | 					test |= TEST_DECOMP; | 
 | 					continue; | 
 |  | 
 | 				case '?': | 
 | 					test |= TEST_VERIFY; | 
 | 					test &= ~(TEST_AND|TEST_OR); | 
 | 					state.verify = state.passed; | 
 | 					continue; | 
 | 				case '&': | 
 | 					test |= TEST_VERIFY|TEST_AND; | 
 | 					test &= ~TEST_OR; | 
 | 					continue; | 
 | 				case '|': | 
 | 					test |= TEST_VERIFY|TEST_OR; | 
 | 					test &= ~TEST_AND; | 
 | 					continue; | 
 | 				case ';': | 
 | 					test |= TEST_OR; | 
 | 					test &= ~TEST_AND; | 
 | 					continue; | 
 |  | 
 | 				case '{': | 
 | 					level <<= 1; | 
 | 					if (skip & (level >> 1)) | 
 | 					{ | 
 | 						skip |= level; | 
 | 						cflags = NOTEST; | 
 | 					} | 
 | 					else | 
 | 					{ | 
 | 						skip &= ~level; | 
 | 						test |= TEST_QUERY; | 
 | 					} | 
 | 					continue; | 
 | 				case '}': | 
 | 					if (level == 1) | 
 | 						bad("invalid {...} nesting\n", NiL, NiL, 0, 0); | 
 | 					if ((skip & level) && !(skip & (level>>1))) | 
 | 					{ | 
 | 						if (!(test & (TEST_BASELINE|TEST_SUMMARY))) | 
 | 						{ | 
 | 							if (test & (TEST_ACTUAL|TEST_FAIL)) | 
 | 								printf("}\n"); | 
 | 							else if (!(test & TEST_PASS)) | 
 | 								printf("-%d\n", state.lineno); | 
 | 						} | 
 | 					} | 
 | #if defined(LC_COLLATE) && defined(LC_CTYPE) | 
 | 					else if (locale & level) | 
 | 					{ | 
 | 						locale = 0; | 
 | 						if (!(skip & level)) | 
 | 						{ | 
 | 							s = "C"; | 
 | 							setlocale(LC_COLLATE, s); | 
 | 							setlocale(LC_CTYPE, s); | 
 | 							if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) | 
 | 								printf("NOTE	\"%s\" locale\n", s); | 
 | 							else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_PASS)) | 
 | 								printf("}\n"); | 
 | 						} | 
 | 						else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL)) | 
 | 							printf("}\n"); | 
 | 					} | 
 | #endif | 
 | 					level >>= 1; | 
 | 					cflags = NOTEST; | 
 | 					continue; | 
 |  | 
 | 				default: | 
 | 					bad("bad spec\n", spec, NiL, 0, test); | 
 | 					break; | 
 |  | 
 | 				} | 
 | 				break; | 
 | 			} | 
 | 			if ((cflags|eflags) == NOTEST || (skip & level) && (test & TEST_BASELINE)) | 
 | 			{ | 
 | 				if (test & TEST_BASELINE) | 
 | 				{ | 
 | 					while (i > 1) | 
 | 						*delim[--i] = '\t'; | 
 | 					printf("%s\n", line); | 
 | 				} | 
 | 				continue; | 
 | 			} | 
 | 			if (test & TEST_OR) | 
 | 			{ | 
 | 				if (!(test & TEST_VERIFY)) | 
 | 				{ | 
 | 					test &= ~TEST_OR; | 
 | 					if (state.passed == state.verify && i > 1) | 
 | 						printf("NOTE\t%s\n", field[1]); | 
 | 					continue; | 
 | 				} | 
 | 				else if (state.passed > state.verify) | 
 | 					continue; | 
 | 			} | 
 | 			else if (test & TEST_AND) | 
 | 			{ | 
 | 				if (state.passed == state.verify) | 
 | 					continue; | 
 | 				state.passed = state.verify; | 
 | 			} | 
 | 			if (i < ((test & TEST_DECOMP) ? 3 : 4)) | 
 | 				bad("too few fields\n", NiL, NiL, 0, test); | 
 | 			while (i < elementsof(field)) | 
 | 				field[i++] = 0; | 
 | 			if (re = field[1]) | 
 | 			{ | 
 | 				if (streq(re, "SAME")) | 
 | 				{ | 
 | 					re = ppat; | 
 | 					test |= TEST_SAME; | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | 					if (test & TEST_EXPAND) | 
 | 						escape(re); | 
 | 					re = expand(re, patbuf); | 
 | 					strcpy(ppat = pat, re); | 
 | 				} | 
 | 			} | 
 | 			else | 
 | 				ppat = 0; | 
 | 			nstr = -1; | 
 | 			if (s = field[2]) | 
 | 			{ | 
 | 				s = expand(s, strbuf); | 
 | 				if (test & TEST_EXPAND) | 
 | 				{ | 
 | 					nstr = escape(s); | 
 | #if _REG_nexec | 
 | 					if (nstr != strlen(s)) | 
 | 						nexec = nstr; | 
 | #endif | 
 | 				} | 
 | 			} | 
 | 			if (!(ans = field[(test & TEST_DECOMP) ? 2 : 3])) | 
 | 				bad("NIL answer\n", NiL, NiL, 0, test); | 
 | 			msg = field[4]; | 
 | 			fflush(stdout); | 
 | 			if (test & TEST_SUB) | 
 | #if _REG_subcomp | 
 | 				cflags |= REG_DELIMITED; | 
 | #else | 
 | 				continue; | 
 | #endif | 
 | #if !_REG_decomp | 
 | 			if (test & TEST_DECOMP) | 
 | 				continue; | 
 | #endif | 
 |  | 
 | 		compile: | 
 |  | 
 | 			if (state.extracted || (skip & level)) | 
 | 				continue; | 
 | #if !(REG_TEST_DEFAULT & (REG_AUGMENTED|REG_EXTENDED|REG_SHELL)) | 
 | #ifdef REG_EXTENDED | 
 | 			if (REG_EXTENDED != 0 && (test & TEST_BRE)) | 
 | #else | 
 | 			if (test & TEST_BRE) | 
 | #endif | 
 | 			{ | 
 | 				test &= ~TEST_BRE; | 
 | 				flags = cflags; | 
 | 				state.which = "BRE"; | 
 | 			} | 
 | 			else | 
 | #endif | 
 | #ifdef REG_EXTENDED | 
 | 			if (test & TEST_ERE) | 
 | 			{ | 
 | 				test &= ~TEST_ERE; | 
 | 				flags = cflags | REG_EXTENDED; | 
 | 				state.which = "ERE"; | 
 | 			} | 
 | 			else | 
 | #endif | 
 | #ifdef REG_AUGMENTED | 
 | 			if (test & TEST_ARE) | 
 | 			{ | 
 | 				test &= ~TEST_ARE; | 
 | 				flags = cflags | REG_AUGMENTED; | 
 | 				state.which = "ARE"; | 
 | 			} | 
 | 			else | 
 | #endif | 
 | #ifdef REG_LITERAL | 
 | 			if (test & TEST_LRE) | 
 | 			{ | 
 | 				test &= ~TEST_LRE; | 
 | 				flags = cflags | REG_LITERAL; | 
 | 				state.which = "LRE"; | 
 | 			} | 
 | 			else | 
 | #endif | 
 | #ifdef REG_SHELL | 
 | 			if (test & TEST_SRE) | 
 | 			{ | 
 | 				test &= ~TEST_SRE; | 
 | 				flags = cflags | REG_SHELL; | 
 | 				state.which = "SRE"; | 
 | 			} | 
 | 			else | 
 | #ifdef REG_AUGMENTED | 
 | 			if (test & TEST_KRE) | 
 | 			{ | 
 | 				test &= ~TEST_KRE; | 
 | 				flags = cflags | REG_SHELL | REG_AUGMENTED; | 
 | 				state.which = "KRE"; | 
 | 			} | 
 | 			else | 
 | #endif | 
 | #endif | 
 | 			{ | 
 | 				if (test & (TEST_BASELINE|TEST_PASS|TEST_VERIFY)) | 
 | 					extract(tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test|TEST_OK); | 
 | 				continue; | 
 | 			} | 
 | 			if ((test & (TEST_QUERY|TEST_VERBOSE|TEST_VERIFY)) == TEST_VERBOSE) | 
 | 			{ | 
 | 				printf("test %-3d %s ", state.lineno, state.which); | 
 | 				quote(re, -1, test|TEST_DELIMIT); | 
 | 				printf(" "); | 
 | 				quote(s, nstr, test|TEST_DELIMIT); | 
 | 				printf("\n"); | 
 | 			} | 
 |  | 
 | 		nosub: | 
 | 			fun = "regcomp"; | 
 | #if _REG_nexec | 
 | 			if (nstr >= 0 && nstr != strlen(s)) | 
 | 				nexec = nstr; | 
 |  | 
 | 			else | 
 | #endif | 
 | 				nexec = -1; | 
 | 			if (state.extracted || (skip & level)) | 
 | 				continue; | 
 | 			if (!(test & TEST_QUERY)) | 
 | 				testno++; | 
 | #ifdef REG_DISCIPLINE | 
 | 			if (state.stack) | 
 | 				stkset(stkstd, state.stack, 0); | 
 | 			flags |= REG_DISCIPLINE; | 
 | 			state.disc.ordinal = 0; | 
 | 			sfstrseek(state.disc.sp, 0, SEEK_SET); | 
 | #endif | 
 | 			if (!(test & TEST_CATCH)) | 
 | 				cret = regcomp(&preg, re, flags); | 
 | 			else if (!(cret = setjmp(state.gotcha))) | 
 | 			{ | 
 | 				alarm(HUNG); | 
 | 				cret = regcomp(&preg, re, flags); | 
 | 				alarm(0); | 
 | 			} | 
 | #if _REG_subcomp | 
 | 			if (!cret && (test & TEST_SUB)) | 
 | 			{ | 
 | 				fun = "regsubcomp"; | 
 | 				p = re + preg.re_npat; | 
 | 				if (!(test & TEST_CATCH)) | 
 | 					cret = regsubcomp(&preg, p, NiL, 0, 0); | 
 | 				else if (!(cret = setjmp(state.gotcha))) | 
 | 				{ | 
 | 					alarm(HUNG); | 
 | 					cret = regsubcomp(&preg, p, NiL, 0, 0); | 
 | 					alarm(0); | 
 | 				} | 
 | 				if (!cret && *(p += preg.re_npat) && !(preg.re_sub->re_flags & REG_SUB_LAST)) | 
 | 				{ | 
 | 					if (catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test)) | 
 | 						continue; | 
 | 					cret = REG_EFLAGS; | 
 | 				} | 
 | 			} | 
 | #endif | 
 | #if _REG_decomp | 
 | 			if (!cret && (test & TEST_DECOMP)) | 
 | 			{ | 
 | 				char	buf[128]; | 
 |  | 
 | 				if ((j = nmatch) > sizeof(buf)) | 
 | 					j = sizeof(buf); | 
 | 				fun = "regdecomp"; | 
 | 				p = re + preg.re_npat; | 
 | 				if (!(test & TEST_CATCH)) | 
 | 					i = regdecomp(&preg, -1, buf, j); | 
 | 				else if (!(cret = setjmp(state.gotcha))) | 
 | 				{ | 
 | 					alarm(HUNG); | 
 | 					i = regdecomp(&preg, -1, buf, j); | 
 | 					alarm(0); | 
 | 				} | 
 | 				if (!cret) | 
 | 				{ | 
 | 					catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); | 
 | 					if (i > j) | 
 | 					{ | 
 | 						if (i != (strlen(ans) + 1)) | 
 | 						{ | 
 | 							report("failed", fun, re, s, nstr, msg, flags, test); | 
 | 							printf(" %d byte buffer supplied, %d byte buffer required\n", j, i); | 
 | 						} | 
 | 					} | 
 | 					else if (strcmp(buf, ans)) | 
 | 					{ | 
 | 						report("failed", fun, re, s, nstr, msg, flags, test); | 
 | 						quote(ans, -1, test|TEST_DELIMIT); | 
 | 						printf(" expected, "); | 
 | 						quote(buf, -1, test|TEST_DELIMIT); | 
 | 						printf(" returned\n"); | 
 | 					} | 
 | 					continue; | 
 | 				} | 
 | 			} | 
 | #endif | 
 | 			if (!cret) | 
 | 			{ | 
 | 				if (!(flags & REG_NOSUB) && nsub < 0 && *ans == '(') | 
 | 				{ | 
 | 					for (p = ans; *p; p++) | 
 | 						if (*p == '(') | 
 | 							nsub++; | 
 | 						else if (*p == '{') | 
 | 							nsub--; | 
 | 					if (nsub >= 0) | 
 | 					{ | 
 | 						if (test & TEST_IGNORE_OVER) | 
 | 						{ | 
 | 							if (nmatch > nsub) | 
 | 								nmatch = nsub + 1; | 
 | 						} | 
 | 						else if (nsub != preg.re_nsub) | 
 | 						{ | 
 | 							if (nsub > preg.re_nsub) | 
 | 							{ | 
 | 								if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) | 
 | 									skip = extract(tabs, line, re, s, ans, msg, "OK", NiL, 0, 0, skip, level, test|TEST_DELIMIT); | 
 | 								else | 
 | 								{ | 
 | 									report("re_nsub incorrect", fun, re, NiL, -1, msg, flags, test); | 
 | 									printf("at least %d expected, %d returned\n", nsub, preg.re_nsub); | 
 | 									state.errors++; | 
 | 								} | 
 | 							} | 
 | 							else | 
 | 								nsub = preg.re_nsub; | 
 | 						} | 
 | 					} | 
 | 				} | 
 | 				if (!(test & (TEST_DECOMP|TEST_SUB)) && *ans && *ans != '(' && !streq(ans, "OK") && !streq(ans, "NOMATCH")) | 
 | 				{ | 
 | 					if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) | 
 | 						skip = extract(tabs, line, re, s, ans, msg, "OK", NiL, 0, 0, skip, level, test|TEST_DELIMIT); | 
 | 					else if (!(test & TEST_LENIENT)) | 
 | 					{ | 
 | 						report("failed", fun, re, NiL, -1, msg, flags, test); | 
 | 						printf("%s expected, OK returned\n", ans); | 
 | 					} | 
 | 					catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); | 
 | 					continue; | 
 | 				} | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				if (test & TEST_LENIENT) | 
 | 					/* we'll let it go this time */; | 
 | 				else if (!*ans || ans[0]=='(' || cret == REG_BADPAT && streq(ans, "NOMATCH")) | 
 | 				{ | 
 | 					got = 0; | 
 | 					for (i = 1; i < elementsof(codes); i++) | 
 | 						if (cret==codes[i].code) | 
 | 							got = i; | 
 | 					if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) | 
 | 						skip = extract(tabs, line, re, s, ans, msg, codes[got].name, NiL, 0, 0, skip, level, test|TEST_DELIMIT); | 
 | 					else | 
 | 					{ | 
 | 						report("failed", fun, re, NiL, -1, msg, flags, test); | 
 | 						printf("%s returned: ", codes[got].name); | 
 | 						error(&preg, cret); | 
 | 					} | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | 					expected = got = 0; | 
 | 					for (i = 1; i < elementsof(codes); i++) | 
 | 					{ | 
 | 						if (streq(ans, codes[i].name)) | 
 | 							expected = i; | 
 | 						if (cret==codes[i].code) | 
 | 							got = i; | 
 | 					} | 
 | 					if (!expected) | 
 | 					{ | 
 | 						if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) | 
 | 							skip = extract(tabs, line, re, s, ans, msg, codes[got].name, NiL, 0, 0, skip, level, test|TEST_DELIMIT); | 
 | 						else | 
 | 						{ | 
 | 							report("failed: invalid error code", NiL, re, NiL, -1, msg, flags, test); | 
 | 							printf("%s expected, %s returned\n", ans, codes[got].name); | 
 | 						} | 
 | 					} | 
 | 					else if (cret != codes[expected].code && cret != REG_BADPAT) | 
 | 					{ | 
 | 						if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) | 
 | 							skip = extract(tabs, line, re, s, ans, msg, codes[got].name, NiL, 0, 0, skip, level, test|TEST_DELIMIT); | 
 | 						else if (test & TEST_IGNORE_ERROR) | 
 | 							state.ignored++; | 
 | 						else | 
 | 						{ | 
 | 							report("should fail and did", fun, re, NiL, -1, msg, flags, test); | 
 | 							printf("%s expected, %s returned: ", ans, codes[got].name); | 
 | 							state.errors--; | 
 | 							state.warnings++; | 
 | 							error(&preg, cret); | 
 | 						} | 
 | 					} | 
 | 				} | 
 | 				goto compile; | 
 | 			} | 
 |  | 
 | #if _REG_nexec | 
 | 		execute: | 
 | 			if (nexec >= 0) | 
 | 				fun = "regnexec"; | 
 | 			else | 
 | #endif | 
 | 				fun = "regexec"; | 
 | 			 | 
 | 			for (i = 0; i < elementsof(match); i++) | 
 | 				match[i] = state.NOMATCH; | 
 |  | 
 | #if _REG_nexec | 
 | 			if (nexec >= 0) | 
 | 			{ | 
 | 				eret = regnexec(&preg, s, nexec, nmatch, match, eflags); | 
 | 				s[nexec] = 0; | 
 | 			} | 
 | 			else | 
 | #endif | 
 | 			{ | 
 | 				if (!(test & TEST_CATCH)) | 
 | 					eret = regexec(&preg, s, nmatch, match, eflags); | 
 | 				else if (!(eret = setjmp(state.gotcha))) | 
 | 				{ | 
 | 					alarm(HUNG); | 
 | 					eret = regexec(&preg, s, nmatch, match, eflags); | 
 | 					alarm(0); | 
 | 				} | 
 | 			} | 
 | #if _REG_subcomp | 
 | 			if ((test & TEST_SUB) && !eret) | 
 | 			{ | 
 | 				fun = "regsubexec"; | 
 | 				if (!(test & TEST_CATCH)) | 
 | 					eret = regsubexec(&preg, s, nmatch, match); | 
 | 				else if (!(eret = setjmp(state.gotcha))) | 
 | 				{ | 
 | 					alarm(HUNG); | 
 | 					eret = regsubexec(&preg, s, nmatch, match); | 
 | 					alarm(0); | 
 | 				} | 
 | 			} | 
 | #endif | 
 | 			if (flags & REG_NOSUB) | 
 | 			{ | 
 | 				if (eret) | 
 | 				{ | 
 | 					if (eret != REG_NOMATCH || !streq(ans, "NOMATCH")) | 
 | 					{ | 
 | 						if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) | 
 | 							skip = extract(tabs, line, re, s, ans, msg, "NOMATCH", NiL, 0, 0, skip, level, test|TEST_DELIMIT); | 
 | 						else | 
 | 						{ | 
 | 							report("REG_NOSUB failed", fun, re, s, nstr, msg, flags, test); | 
 | 							error(&preg, eret); | 
 | 						} | 
 | 					} | 
 | 				} | 
 | 				else if (streq(ans, "NOMATCH")) | 
 | 				{ | 
 | 					if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) | 
 | 						skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_DELIMIT); | 
 | 					else | 
 | 					{ | 
 | 						report("should fail and didn't", fun, re, s, nstr, msg, flags, test); | 
 | 						error(&preg, eret); | 
 | 					} | 
 | 				} | 
 | 			} | 
 | 			else if (eret) | 
 | 			{ | 
 | 				if (eret != REG_NOMATCH || !streq(ans, "NOMATCH")) | 
 | 				{ | 
 | 					if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) | 
 | 						skip = extract(tabs, line, re, s, ans, msg, "NOMATCH", NiL, 0, nsub, skip, level, test|TEST_DELIMIT); | 
 | 					else | 
 | 					{ | 
 | 						report("failed", fun, re, s, nstr, msg, flags, test); | 
 | 						if (eret != REG_NOMATCH) | 
 | 							error(&preg, eret); | 
 | 						else if (*ans) | 
 | 							printf("expected: %s\n", ans); | 
 | 						else | 
 | 							printf("\n"); | 
 | 					} | 
 | 				} | 
 | 			} | 
 | 			else if (streq(ans, "NOMATCH")) | 
 | 			{ | 
 | 				if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) | 
 | 					skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_DELIMIT); | 
 | 				else | 
 | 				{ | 
 | 					report("should fail and didn't", fun, re, s, nstr, msg, flags, test); | 
 | 					matchprint(match, nmatch, nsub, NiL, test); | 
 | 				} | 
 | 			} | 
 | #if _REG_subcomp | 
 | 			else if (test & TEST_SUB) | 
 | 			{ | 
 | 				p = preg.re_sub->re_buf; | 
 | 				if (strcmp(p, ans)) | 
 | 				{ | 
 | 					report("failed", fun, re, s, nstr, msg, flags, test); | 
 | 					quote(ans, -1, test|TEST_DELIMIT); | 
 | 					printf(" expected, "); | 
 | 					quote(p, -1, test|TEST_DELIMIT); | 
 | 					printf(" returned\n"); | 
 | 				} | 
 | 			} | 
 | #endif | 
 | 			else if (!*ans) | 
 | 			{ | 
 | 				if (match[0].rm_so != state.NOMATCH.rm_so) | 
 | 				{ | 
 | 					if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) | 
 | 						skip = extract(tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); | 
 | 					else | 
 | 					{ | 
 | 						report("failed: no match but match array assigned", NiL, re, s, nstr, msg, flags, test); | 
 | 						matchprint(match, nmatch, nsub, NiL, test); | 
 | 					} | 
 | 				} | 
 | 			} | 
 | 			else if (matchcheck(match, nmatch, nsub, ans, re, s, nstr, flags, test)) | 
 | 			{ | 
 | #if _REG_nexec | 
 | 				if (nexec < 0 && !nonexec) | 
 | 				{ | 
 | 					nexec = nstr >= 0 ? nstr : strlen(s); | 
 | 					s[nexec] = '\n'; | 
 | 					testno++; | 
 | 					goto execute; | 
 | 				} | 
 | #endif | 
 | 				if (!(test & (TEST_DECOMP|TEST_SUB|TEST_VERIFY)) && !nonosub) | 
 | 				{ | 
 | 					if (catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test)) | 
 | 						continue; | 
 | 					flags |= REG_NOSUB; | 
 | 					goto nosub; | 
 | 				} | 
 | 				if (test & (TEST_BASELINE|TEST_PASS|TEST_VERIFY)) | 
 | 					skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_OK); | 
 | 			} | 
 | 			else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) | 
 | 				skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_DELIMIT); | 
 | 			if (catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test)) | 
 | 				continue; | 
 | 			goto compile; | 
 | 		} | 
 | 		if (test & TEST_SUMMARY) | 
 | 			printf("tests=%-4d errors=%-4d warnings=%-2d ignored=%-2d unspecified=%-2d signals=%d\n", testno, state.errors, state.warnings, state.ignored, state.unspecified, state.signals); | 
 | 		else if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS))) | 
 | 		{ | 
 | 			printf("TEST\t%s", unit); | 
 | 			if (subunit) | 
 | 				printf(" %-.*s", subunitlen, subunit); | 
 | 			printf(", %d test%s", testno, testno == 1 ? "" : "s"); | 
 | 			if (state.ignored) | 
 | 				printf(", %d ignored mismatche%s", state.ignored, state.ignored == 1 ? "" : "s"); | 
 | 			if (state.warnings) | 
 | 				printf(", %d warning%s", state.warnings, state.warnings == 1 ? "" : "s"); | 
 | 			if (state.unspecified) | 
 | 				printf(", %d unspecified difference%s", state.unspecified, state.unspecified == 1 ? "" : "s"); | 
 | 			if (state.signals) | 
 | 				printf(", %d signal%s", state.signals, state.signals == 1 ? "" : "s"); | 
 | 			printf(", %d error%s\n", state.errors, state.errors == 1 ? "" : "s"); | 
 | 		} | 
 | 		if (fp != stdin) | 
 | 			fclose(fp); | 
 | 	} | 
 | 	return 0; | 
 | } |