| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119 | /* * 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 Labs Research * * PLEASE: publish your tests so everyone can benefit * * 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) 2005-05-20 $\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#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		0x00000040#define TEST_LENIENT		0x00000080#define TEST_QUERY		0x00000100#define TEST_SUB		0x00000200#define TEST_UNSPECIFIED	0x00000400#define TEST_VERIFY		0x00000800#define TEST_AND		0x00001000#define TEST_OR			0x00002000#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_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*)++dp->ordinal;}static intexecf(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}(%d:%d)", xlen, xstr, (int)data, 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 voidhelp(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 [BEASKL] then the\n");T("  specification is a global control line. One or more of [BEASKL] 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("    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("\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.\n");T("\n");T("  Field 3: the string to match.\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.\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#endifstatic const char* unsupported[] ={	"BASIC",#ifndef REG_EXTENDED	"EXTENDED",#endif#ifndef REG_AUGMENTED	"AUGMENTED",#endif#ifndef REG_SHELL	"SHELL",#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	0};#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)#endifstatic 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 voidquote(char* s, int len, unsigned long test){	unsigned char*	u = (unsigned char*)s;	unsigned char*	e;	int		c;	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:				if (!iscntrl(c) && isprint(c))					putchar(c);				else					printf("\\x%02x", c);				break;			}		if (test & TEST_DELIMIT)			printf("\"");	}	else		printf("%s", s);}static voidreport(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 voiderror(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 voidbad(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 intescape(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 voidmatchoffprint(int off){	switch (off)	{	case -2:		printf("X");		break;	case -1:		printf("?");		break;	default:		printf("%d", off);		break;	}}static voidmatchprint(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 intmatchcheck(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;			x = sfstruse(state.disc.sp);			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			m = strtol(p, &p, 10);		if (*p++ != ',')			bad("improper answer\n", re, s, -1, test);		if (*p == '?')		{			n = -1;			p++;		}		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 voidsigunblock(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 voidgotcha(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*my_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 longnote(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 longextract(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 intcatchfree(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;}intmain(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 = 0;	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 = NULL;	char*		subunit = NULL;	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];	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 = my_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, "C") || streq(ans, "POSIX") || !(ans = setlocale(LC_CTYPE, s)) || streq(ans, "C") || streq(ans, "POSIX"))							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 '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_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 < 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);					strcpy(ppat = pat, re);				}			}			else				ppat = 0;			nstr = -1;			if ((s = field[2]) && (test & TEST_EXPAND))			{				nstr = escape(s);#if _REG_nexec				if (nstr != strlen(s))					nexec = nstr;#endif			}			if (!(ans = field[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		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 (!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, %zd returned\n", nsub, preg.re_nsub);									state.errors++;								}							}							else								nsub = preg.re_nsub;						}					}				}				if (!(test & 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_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;}
 |