|  | #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; | 
|  | } |