116b8cffYuri Pankov#pragma prototyped noticed
216b8cffYuri Pankov
316b8cffYuri Pankov/*
416b8cffYuri Pankov * regex(3) test harness
516b8cffYuri Pankov *
616b8cffYuri Pankov * build:	cc -o testregex testregex.c
716b8cffYuri Pankov * help:	testregex --man
816b8cffYuri Pankov * note:	REG_* features are detected by #ifdef; if REG_* are enums
916b8cffYuri Pankov *		then supply #define REG_foo REG_foo for each enum REG_foo
1016b8cffYuri Pankov *
1116b8cffYuri Pankov *	Glenn Fowler <glenn.s.fowler@gmail.com>
1216b8cffYuri Pankov *	AT&T Research
1316b8cffYuri Pankov *
1416b8cffYuri Pankov * PLEASE: publish your tests so everyone can benefit
1516b8cffYuri Pankov *
1616b8cffYuri Pankov * The following license covers testregex.c and all associated test data.
1716b8cffYuri Pankov *
1816b8cffYuri Pankov * Permission is hereby granted, free of charge, to any person obtaining a
1916b8cffYuri Pankov * copy of THIS SOFTWARE FILE (the "Software"), to deal in the Software
2016b8cffYuri Pankov * without restriction, including without limitation the rights to use,
2116b8cffYuri Pankov * copy, modify, merge, publish, distribute, and/or sell copies of the
2216b8cffYuri Pankov * Software, and to permit persons to whom the Software is furnished to do
2316b8cffYuri Pankov * so, subject to the following disclaimer:
2416b8cffYuri Pankov *
2516b8cffYuri Pankov * THIS SOFTWARE IS PROVIDED BY AT&T ``AS IS'' AND ANY EXPRESS OR IMPLIED
2616b8cffYuri Pankov * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
2716b8cffYuri Pankov * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2816b8cffYuri Pankov * IN NO EVENT SHALL AT&T BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2916b8cffYuri Pankov * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3016b8cffYuri Pankov * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3116b8cffYuri Pankov * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3216b8cffYuri Pankov * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3316b8cffYuri Pankov * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3416b8cffYuri Pankov * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3516b8cffYuri Pankov */
3616b8cffYuri Pankov
3716b8cffYuri Pankovstatic const char id[] = "\n@(#)$Id: testregex (AT&T Research) 2010-06-10 $\0\n";
3816b8cffYuri Pankov
3916b8cffYuri Pankov#if _PACKAGE_ast
4016b8cffYuri Pankov#include <ast.h>
4116b8cffYuri Pankov#else
4216b8cffYuri Pankov#include <sys/types.h>
4316b8cffYuri Pankov#endif
4416b8cffYuri Pankov
4516b8cffYuri Pankov#include <stdio.h>
4616b8cffYuri Pankov#include <regex.h>
4716b8cffYuri Pankov#include <ctype.h>
4816b8cffYuri Pankov#include <setjmp.h>
4916b8cffYuri Pankov#include <signal.h>
5016b8cffYuri Pankov#include <string.h>
5116b8cffYuri Pankov#include <unistd.h>
5216b8cffYuri Pankov
5316b8cffYuri Pankov#ifdef	__STDC__
5416b8cffYuri Pankov#include <stdlib.h>
5516b8cffYuri Pankov#include <locale.h>
5616b8cffYuri Pankov#endif
5716b8cffYuri Pankov
5816b8cffYuri Pankov#ifndef RE_DUP_MAX
5916b8cffYuri Pankov#define RE_DUP_MAX	32767
6016b8cffYuri Pankov#endif
6116b8cffYuri Pankov
6216b8cffYuri Pankov#if !_PACKAGE_ast
6316b8cffYuri Pankov#undef	REG_DISCIPLINE
6416b8cffYuri Pankov#endif
6516b8cffYuri Pankov
6616b8cffYuri Pankov#ifndef REG_DELIMITED
6716b8cffYuri Pankov#undef	_REG_subcomp
6816b8cffYuri Pankov#endif
6916b8cffYuri Pankov
7016b8cffYuri Pankov#define TEST_ARE		0x00000001
7116b8cffYuri Pankov#define TEST_BRE		0x00000002
7216b8cffYuri Pankov#define TEST_ERE		0x00000004
7316b8cffYuri Pankov#define TEST_KRE		0x00000008
7416b8cffYuri Pankov#define TEST_LRE		0x00000010
7516b8cffYuri Pankov#define TEST_SRE		0x00000020
7616b8cffYuri Pankov
7716b8cffYuri Pankov#define TEST_EXPAND		0x00000100
7816b8cffYuri Pankov#define TEST_LENIENT		0x00000200
7916b8cffYuri Pankov
8016b8cffYuri Pankov#define TEST_QUERY		0x00000400
8116b8cffYuri Pankov#define TEST_SUB		0x00000800
8216b8cffYuri Pankov#define TEST_UNSPECIFIED	0x00001000
8316b8cffYuri Pankov#define TEST_VERIFY		0x00002000
8416b8cffYuri Pankov#define TEST_AND		0x00004000
8516b8cffYuri Pankov#define TEST_OR			0x00008000
8616b8cffYuri Pankov
8716b8cffYuri Pankov#define TEST_DELIMIT		0x00010000
8816b8cffYuri Pankov#define TEST_OK			0x00020000
8916b8cffYuri Pankov#define TEST_SAME		0x00040000
9016b8cffYuri Pankov
9116b8cffYuri Pankov#define TEST_ACTUAL		0x00100000
9216b8cffYuri Pankov#define TEST_BASELINE		0x00200000
9316b8cffYuri Pankov#define TEST_FAIL		0x00400000
9416b8cffYuri Pankov#define TEST_PASS		0x00800000
9516b8cffYuri Pankov#define TEST_SUMMARY		0x01000000
9616b8cffYuri Pankov
9716b8cffYuri Pankov#define TEST_IGNORE_ERROR	0x02000000
9816b8cffYuri Pankov#define TEST_IGNORE_OVER	0x04000000
9916b8cffYuri Pankov#define TEST_IGNORE_POSITION	0x08000000
10016b8cffYuri Pankov
10116b8cffYuri Pankov#define TEST_CATCH		0x10000000
10216b8cffYuri Pankov#define TEST_VERBOSE		0x20000000
10316b8cffYuri Pankov
10416b8cffYuri Pankov#define TEST_DECOMP		0x40000000
10516b8cffYuri Pankov
10616b8cffYuri Pankov#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)
10716b8cffYuri Pankov
10816b8cffYuri Pankov#ifdef REG_DISCIPLINE
10916b8cffYuri Pankov
11016b8cffYuri Pankov
11116b8cffYuri Pankov#include <stk.h>
11216b8cffYuri Pankov
11316b8cffYuri Pankovtypedef struct Disc_s
11416b8cffYuri Pankov{
11516b8cffYuri Pankov	regdisc_t	disc;
11616b8cffYuri Pankov	int		ordinal;
11716b8cffYuri Pankov	Sfio_t*		sp;
11816b8cffYuri Pankov} Disc_t;
11916b8cffYuri Pankov
12016b8cffYuri Pankovstatic void*
12116b8cffYuri Pankovcompf(const regex_t* re, const char* xstr, size_t xlen, regdisc_t* disc)
12216b8cffYuri Pankov{
12316b8cffYuri Pankov	Disc_t*		dp = (Disc_t*)disc;
12416b8cffYuri Pankov
12516b8cffYuri Pankov	return (void*)((char*)0 + ++dp->ordinal);
12616b8cffYuri Pankov}
12716b8cffYuri Pankov
12816b8cffYuri Pankovstatic int
12916b8cffYuri Pankovexecf(const regex_t* re, void* data, const char* xstr, size_t xlen, const char* sstr, size_t slen, char** snxt, regdisc_t* disc)
13016b8cffYuri Pankov{
13116b8cffYuri Pankov	Disc_t*		dp = (Disc_t*)disc;
13216b8cffYuri Pankov
13316b8cffYuri Pankov	sfprintf(dp->sp, "{%-.*s}(%lu:%d)", xlen, xstr, (char*)data - (char*)0, slen);
13416b8cffYuri Pankov	return atoi(xstr);
13516b8cffYuri Pankov}
13616b8cffYuri Pankov
13716b8cffYuri Pankovstatic void*
13816b8cffYuri Pankovresizef(void* handle, void* data, size_t size)
13916b8cffYuri Pankov{
14016b8cffYuri Pankov	if (!size)
14116b8cffYuri Pankov		return 0;
14216b8cffYuri Pankov	return stkalloc((Sfio_t*)handle, size);
14316b8cffYuri Pankov}
14416b8cffYuri Pankov
14516b8cffYuri Pankov#endif
14616b8cffYuri Pankov
14716b8cffYuri Pankov#ifndef NiL
14816b8cffYuri Pankov#ifdef	__STDC__
14916b8cffYuri Pankov#define NiL		0
15016b8cffYuri Pankov#else
15116b8cffYuri Pankov#define NiL		(char*)0
15216b8cffYuri Pankov#endif
15316b8cffYuri Pankov#endif
15416b8cffYuri Pankov
15516b8cffYuri Pankov#define H(x)		do{if(html)fprintf(stderr,x);}while(0)
15616b8cffYuri Pankov#define T(x)		fprintf(stderr,x)
15716b8cffYuri Pankov
15816b8cffYuri Pankovstatic void
15916b8cffYuri Pankovhelp(int html)
16016b8cffYuri Pankov{
16116b8cffYuri PankovH("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n");
16216b8cffYuri PankovH("<HTML>\n");
16316b8cffYuri PankovH("<HEAD>\n");
16416b8cffYuri PankovH("<TITLE>testregex man document</TITLE>\n");
16516b8cffYuri PankovH("</HEAD>\n");
16616b8cffYuri PankovH("<BODY bgcolor=white>\n");
16716b8cffYuri PankovH("<PRE>\n");
16816b8cffYuri PankovT("NAME\n");
16916b8cffYuri PankovT("  testregex - regex(3) test harness\n");
17016b8cffYuri PankovT("\n");
17116b8cffYuri PankovT("SYNOPSIS\n");
17216b8cffYuri PankovT("  testregex [ options ]\n");
17316b8cffYuri PankovT("\n");
17416b8cffYuri PankovT("DESCRIPTION\n");
17516b8cffYuri PankovT("  testregex reads regex(3) test specifications, one per line, from the\n");
17616b8cffYuri PankovT("  standard input and writes one output line for each failed test. A\n");
17716b8cffYuri PankovT("  summary line is written after all tests are done. Each successful\n");
17816b8cffYuri PankovT("  test is run again with REG_NOSUB. Unsupported features are noted\n");
17916b8cffYuri PankovT("  before the first test, and tests requiring these features are\n");
18016b8cffYuri PankovT("  silently ignored.\n");
18116b8cffYuri PankovT("\n");
18216b8cffYuri PankovT("OPTIONS\n");
18316b8cffYuri PankovT("  -c	catch signals and non-terminating calls\n");
18416b8cffYuri PankovT("  -e	ignore error return mismatches\n");
18516b8cffYuri PankovT("  -h	list help on standard error\n");
18616b8cffYuri PankovT("  -n	do not repeat successful tests with regnexec()\n");
18716b8cffYuri PankovT("  -o	ignore match[] overrun errors\n");
18816b8cffYuri PankovT("  -p	ignore negative position mismatches\n");
18916b8cffYuri PankovT("  -s	use stack instead of malloc\n");
19016b8cffYuri PankovT("  -x	do not repeat successful tests with REG_NOSUB\n");
19116b8cffYuri PankovT("  -v	list each test line\n");
19216b8cffYuri PankovT("  -A	list failed test lines with actual answers\n");
19316b8cffYuri PankovT("  -B	list all test lines with actual answers\n");
19416b8cffYuri PankovT("  -F	list failed test lines\n");
19516b8cffYuri PankovT("  -P	list passed test lines\n");
19616b8cffYuri PankovT("  -S	output one summary line\n");
19716b8cffYuri PankovT("\n");
19816b8cffYuri PankovT("INPUT FORMAT\n");
19916b8cffYuri PankovT("  Input lines may be blank, a comment beginning with #, or a test\n");
20016b8cffYuri PankovT("  specification. A specification is five fields separated by one\n");
20116b8cffYuri PankovT("  or more tabs. NULL denotes the empty string and NIL denotes the\n");
20216b8cffYuri PankovT("  0 pointer.\n");
20316b8cffYuri PankovT("\n");
20416b8cffYuri PankovT("  Field 1: the regex(3) flags to apply, one character per REG_feature\n");
20516b8cffYuri PankovT("  flag. The test is skipped if REG_feature is not supported by the\n");
20616b8cffYuri PankovT("  implementation. If the first character is not [BEASKLP] then the\n");
20716b8cffYuri PankovT("  specification is a global control line. One or more of [BEASKLP] may be\n");
20816b8cffYuri PankovT("  specified; the test will be repeated for each mode.\n");
20916b8cffYuri PankovT("\n");
21016b8cffYuri PankovT("    B 	basic			BRE	(grep, ed, sed)\n");
21116b8cffYuri PankovT("    E 	REG_EXTENDED		ERE	(egrep)\n");
21216b8cffYuri PankovT("    A	REG_AUGMENTED		ARE	(egrep with negation)\n");
21316b8cffYuri PankovT("    S	REG_SHELL		SRE	(sh glob)\n");
21416b8cffYuri PankovT("    K	REG_SHELL|REG_AUGMENTED	KRE	(ksh glob)\n");
21516b8cffYuri PankovT("    L	REG_LITERAL		LRE	(fgrep)\n");
21616b8cffYuri PankovT("\n");
21716b8cffYuri PankovT("    a	REG_LEFT|REG_RIGHT	implicit ^...$\n");
21816b8cffYuri PankovT("    b	REG_NOTBOL		lhs does not match ^\n");
21916b8cffYuri PankovT("    c	REG_COMMENT		ignore space and #...\\n\n");
22016b8cffYuri PankovT("    d	REG_SHELL_DOT		explicit leading . match\n");
22116b8cffYuri PankovT("    e	REG_NOTEOL		rhs does not match $\n");
22216b8cffYuri PankovT("    f	REG_MULTIPLE		multiple \\n separated patterns\n");
22316b8cffYuri PankovT("    g	FNM_LEADING_DIR		testfnmatch only -- match until /\n");
22416b8cffYuri PankovT("    h	REG_MULTIREF		multiple digit backref\n");
22516b8cffYuri PankovT("    i	REG_ICASE		ignore case\n");
22616b8cffYuri PankovT("    j	REG_SPAN		. matches \\n\n");
22716b8cffYuri PankovT("    k	REG_ESCAPE		\\ to ecape [...] delimiter\n");
22816b8cffYuri PankovT("    l	REG_LEFT		implicit ^...\n");
22916b8cffYuri PankovT("    m	REG_MINIMAL		minimal match\n");
23016b8cffYuri PankovT("    n	REG_NEWLINE		explicit \\n match\n");
23116b8cffYuri PankovT("    o	REG_ENCLOSED		(|&) magic inside [@|&](...)\n");
23216b8cffYuri PankovT("    p	REG_SHELL_PATH		explicit / match\n");
23316b8cffYuri PankovT("    q	REG_DELIMITED		delimited pattern\n");
23416b8cffYuri PankovT("    r	REG_RIGHT		implicit ...$\n");
23516b8cffYuri PankovT("    s	REG_SHELL_ESCAPED	\\ not special\n");
23616b8cffYuri PankovT("    t	REG_MUSTDELIM		all delimiters must be specified\n");
23716b8cffYuri PankovT("    u	standard unspecified behavior -- errors not counted\n");
23816b8cffYuri PankovT("    v	REG_CLASS_ESCAPE	\\ special inside [...]\n");
23916b8cffYuri PankovT("    w	REG_NOSUB		no subexpression match array\n");
24016b8cffYuri PankovT("    x	REG_LENIENT		let some errors slide\n");
24116b8cffYuri PankovT("    y	REG_LEFT		regexec() implicit ^...\n");
24216b8cffYuri PankovT("    z	REG_NULL		NULL subexpressions ok\n");
24316b8cffYuri PankovT("    $	                        expand C \\c escapes in fields 2 and 3\n");
24416b8cffYuri PankovT("    /	                        field 2 is a regsubcomp() expression\n");
24516b8cffYuri PankovT("    =	                        field 3 is a regdecomp() expression\n");
24616b8cffYuri PankovT("\n");
24716b8cffYuri PankovT("  Field 1 control lines:\n");
24816b8cffYuri PankovT("\n");
24916b8cffYuri PankovT("    C		set LC_COLLATE and LC_CTYPE to locale in field 2\n");
25016b8cffYuri PankovT("\n");
25116b8cffYuri PankovT("    ?test ...	output field 5 if passed and != EXPECTED, silent otherwise\n");
25216b8cffYuri PankovT("    &test ...	output field 5 if current and previous passed\n");
25316b8cffYuri PankovT("    |test ...	output field 5 if current passed and previous failed\n");
25416b8cffYuri PankovT("    ; ...	output field 2 if previous failed\n");
25516b8cffYuri PankovT("    {test ...	skip if failed until }\n");
25616b8cffYuri PankovT("    }		end of skip\n");
25716b8cffYuri PankovT("\n");
25816b8cffYuri PankovT("    : comment		comment copied as output NOTE\n");
25916b8cffYuri PankovT("    :comment:test	:comment: ignored\n");
26016b8cffYuri PankovT("    N[OTE] comment	comment copied as output NOTE\n");
26116b8cffYuri PankovT("    T[EST] comment	comment\n");
26216b8cffYuri PankovT("\n");
26316b8cffYuri PankovT("    number		use number for nmatch (20 by default)\n");
26416b8cffYuri PankovT("\n");
26516b8cffYuri PankovT("  Field 2: the regular expression pattern; SAME uses the pattern from\n");
26616b8cffYuri PankovT("    the previous specification. RE_DUP_MAX inside {...} expands to the\n");
26716b8cffYuri PankovT("    value from <limits.h>.\n");
26816b8cffYuri PankovT("\n");
26916b8cffYuri PankovT("  Field 3: the string to match. X...{RE_DUP_MAX} expands to RE_DUP_MAX\n");
27016b8cffYuri PankovT("    copies of X.\n");
27116b8cffYuri PankovT("\n");
27216b8cffYuri PankovT("  Field 4: the test outcome. This is either one of the posix error\n");
27316b8cffYuri PankovT("    codes (with REG_ omitted) or the match array, a list of (m,n)\n");
27416b8cffYuri PankovT("    entries with m and n being first and last+1 positions in the\n");
27516b8cffYuri PankovT("    field 3 string, or NULL if REG_NOSUB is in effect and success\n");
27616b8cffYuri PankovT("    is expected. BADPAT is acceptable in place of any regcomp(3)\n");
27716b8cffYuri PankovT("    error code. The match[] array is initialized to (-2,-2) before\n");
27816b8cffYuri PankovT("    each test. All array elements from 0 to nmatch-1 must be specified\n");
27916b8cffYuri PankovT("    in the outcome. Unspecified endpoints (offset -1) are denoted by ?.\n");
28016b8cffYuri PankovT("    Unset endpoints (offset -2) are denoted by X. {x}(o:n) denotes a\n");
28116b8cffYuri PankovT("    matched (?{...}) expression, where x is the text enclosed by {...},\n");
28216b8cffYuri PankovT("    o is the expression ordinal counting from 1, and n is the length of\n");
28316b8cffYuri PankovT("    the unmatched portion of the subject string. If x starts with a\n");
28416b8cffYuri PankovT("    number then that is the return value of re_execf(), otherwise 0 is\n");
28516b8cffYuri PankovT("    returned. RE_DUP_MAX[-+]N expands to the <limits.h> value -+N.\n");
28616b8cffYuri PankovT("\n");
28716b8cffYuri PankovT("  Field 5: optional comment appended to the report.\n");
28816b8cffYuri PankovT("\n");
28916b8cffYuri PankovT("CAVEAT\n");
29016b8cffYuri PankovT("    If a regex implementation misbehaves with memory then all bets are off.\n");
29116b8cffYuri PankovT("\n");
29216b8cffYuri PankovT("CONTRIBUTORS\n");
29316b8cffYuri PankovT("  Glenn Fowler    glenn.s.fowler@gmail.com        (ksh strmatch, regex extensions)\n");
29416b8cffYuri PankovT("  David Korn      dgkorn@gmail.com        (ksh glob matcher)\n");
29516b8cffYuri PankovT("  Doug McIlroy    mcilroy@dartmouth.edu       (ast regex/testre in C++)\n");
29616b8cffYuri PankovT("  Tom Lord        lord@regexps.com            (rx tests)\n");
29716b8cffYuri PankovT("  Henry Spencer   henry@zoo.toronto.edu       (original public regex)\n");
29816b8cffYuri PankovT("  Andrew Hume     andrew@research.att.com     (gre tests)\n");
29916b8cffYuri PankovT("  John Maddock    John_Maddock@compuserve.com (regex++ tests)\n");
30016b8cffYuri PankovT("  Philip Hazel    ph10@cam.ac.uk              (pcre tests)\n");
30116b8cffYuri PankovT("  Ville Laurikari vl@iki.fi                   (libtre tests)\n");
30216b8cffYuri PankovH("</PRE>\n");
30316b8cffYuri PankovH("</BODY>\n");
30416b8cffYuri PankovH("</HTML>\n");
30516b8cffYuri Pankov}
30616b8cffYuri Pankov
30716b8cffYuri Pankov#ifndef elementsof
30816b8cffYuri Pankov#define elementsof(x)	(sizeof(x)/sizeof(x[0]))
30916b8cffYuri Pankov#endif
31016b8cffYuri Pankov
31116b8cffYuri Pankov#ifndef streq
31216b8cffYuri Pankov#define streq(a,b)	(*(a)==*(b)&&!strcmp(a,b))
31316b8cffYuri Pankov#endif
31416b8cffYuri Pankov
31516b8cffYuri Pankov#define HUNG		2
31616b8cffYuri Pankov#define NOTEST		(~0)
31716b8cffYuri Pankov
31816b8cffYuri Pankov#ifndef REG_TEST_DEFAULT
31916b8cffYuri Pankov#define REG_TEST_DEFAULT	0
32016b8cffYuri Pankov#endif
32116b8cffYuri Pankov
32216b8cffYuri Pankov#ifndef REG_EXEC_DEFAULT
32316b8cffYuri Pankov#define REG_EXEC_DEFAULT	0
32416b8cffYuri Pankov#endif
32516b8cffYuri Pankov
32616b8cffYuri Pankovstatic const char* unsupported[] =
32716b8cffYuri Pankov{
32816b8cffYuri Pankov	"BASIC",
32916b8cffYuri Pankov#ifndef REG_EXTENDED
33016b8cffYuri Pankov	"EXTENDED",
33116b8cffYuri Pankov#endif
33216b8cffYuri Pankov#ifndef REG_AUGMENTED
33316b8cffYuri Pankov	"AUGMENTED",
33416b8cffYuri Pankov#endif
33516b8cffYuri Pankov#ifndef REG_SHELL
33616b8cffYuri Pankov	"SHELL",
33716b8cffYuri Pankov#endif
33816b8cffYuri Pankov
33916b8cffYuri Pankov#ifndef REG_CLASS_ESCAPE
34016b8cffYuri Pankov	"CLASS_ESCAPE",
34116b8cffYuri Pankov#endif
34216b8cffYuri Pankov#ifndef REG_COMMENT
34316b8cffYuri Pankov	"COMMENT",
34416b8cffYuri Pankov#endif
34516b8cffYuri Pankov#ifndef REG_DELIMITED
34616b8cffYuri Pankov	"DELIMITED",
34716b8cffYuri Pankov#endif
34816b8cffYuri Pankov#ifndef REG_DISCIPLINE
34916b8cffYuri Pankov	"DISCIPLINE",
35016b8cffYuri Pankov#endif
35116b8cffYuri Pankov#ifndef REG_ESCAPE
35216b8cffYuri Pankov	"ESCAPE",
35316b8cffYuri Pankov#endif
35416b8cffYuri Pankov#ifndef REG_ICASE
35516b8cffYuri Pankov	"ICASE",
35616b8cffYuri Pankov#endif
35716b8cffYuri Pankov#ifndef REG_LEFT
35816b8cffYuri Pankov	"LEFT",
35916b8cffYuri Pankov#endif
36016b8cffYuri Pankov#ifndef REG_LENIENT
36116b8cffYuri Pankov	"LENIENT",
36216b8cffYuri Pankov#endif
36316b8cffYuri Pankov#ifndef REG_LITERAL
36416b8cffYuri Pankov	"LITERAL",
36516b8cffYuri Pankov#endif
36616b8cffYuri Pankov#ifndef REG_MINIMAL
36716b8cffYuri Pankov	"MINIMAL",
36816b8cffYuri Pankov#endif
36916b8cffYuri Pankov#ifndef REG_MULTIPLE
37016b8cffYuri Pankov	"MULTIPLE",
37116b8cffYuri Pankov#endif
37216b8cffYuri Pankov#ifndef REG_MULTIREF
37316b8cffYuri Pankov	"MULTIREF",
37416b8cffYuri Pankov#endif
37516b8cffYuri Pankov#ifndef REG_MUSTDELIM
37616b8cffYuri Pankov	"MUSTDELIM",
37716b8cffYuri Pankov#endif
37816b8cffYuri Pankov#ifndef REG_NEWLINE
37916b8cffYuri Pankov	"NEWLINE",
38016b8cffYuri Pankov#endif
38116b8cffYuri Pankov#ifndef REG_NOTBOL
38216b8cffYuri Pankov	"NOTBOL",
38316b8cffYuri Pankov#endif
38416b8cffYuri Pankov#ifndef REG_NOTEOL
38516b8cffYuri Pankov	"NOTEOL",
38616b8cffYuri Pankov#endif
38716b8cffYuri Pankov#ifndef REG_NULL
38816b8cffYuri Pankov	"NULL",
38916b8cffYuri Pankov#endif
39016b8cffYuri Pankov#ifndef REG_RIGHT
39116b8cffYuri Pankov	"RIGHT",
39216b8cffYuri Pankov#endif
39316b8cffYuri Pankov#ifndef REG_SHELL_DOT
39416b8cffYuri Pankov	"SHELL_DOT",
39516b8cffYuri Pankov#endif
39616b8cffYuri Pankov#ifndef REG_SHELL_ESCAPED
39716b8cffYuri Pankov	"SHELL_ESCAPED",
39816b8cffYuri Pankov#endif
39916b8cffYuri Pankov#ifndef REG_SHELL_GROUP
40016b8cffYuri Pankov	"SHELL_GROUP",
40116b8cffYuri Pankov#endif
40216b8cffYuri Pankov#ifndef REG_SHELL_PATH
40316b8cffYuri Pankov	"SHELL_PATH",
40416b8cffYuri Pankov#endif
40516b8cffYuri Pankov#ifndef REG_SPAN
40616b8cffYuri Pankov	"SPAN",
40716b8cffYuri Pankov#endif
40816b8cffYuri Pankov#if REG_NOSUB & REG_TEST_DEFAULT
40916b8cffYuri Pankov	"SUBMATCH",
41016b8cffYuri Pankov#endif
41116b8cffYuri Pankov#if !_REG_nexec
41216b8cffYuri Pankov	"regnexec",
41316b8cffYuri Pankov#endif
41416b8cffYuri Pankov#if !_REG_subcomp
41516b8cffYuri Pankov	"regsubcomp",
41616b8cffYuri Pankov#endif
41716b8cffYuri Pankov#if !_REG_decomp
41816b8cffYuri Pankov	"redecomp",
41916b8cffYuri Pankov#endif
42016b8cffYuri Pankov	0
42116b8cffYuri Pankov};
42216b8cffYuri Pankov
42316b8cffYuri Pankov#ifndef REG_CLASS_ESCAPE
42416b8cffYuri Pankov#define REG_CLASS_ESCAPE	NOTEST
42516b8cffYuri Pankov#endif
42616b8cffYuri Pankov#ifndef REG_COMMENT
42716b8cffYuri Pankov#define REG_COMMENT	NOTEST
42816b8cffYuri Pankov#endif
42916b8cffYuri Pankov#ifndef REG_DELIMITED
43016b8cffYuri Pankov#define REG_DELIMITED	NOTEST
43116b8cffYuri Pankov#endif
43216b8cffYuri Pankov#ifndef REG_ESCAPE
43316b8cffYuri Pankov#define REG_ESCAPE	NOTEST
43416b8cffYuri Pankov#endif
43516b8cffYuri Pankov#ifndef REG_ICASE
43616b8cffYuri Pankov#define REG_ICASE	NOTEST
43716b8cffYuri Pankov#endif
43816b8cffYuri Pankov#ifndef REG_LEFT
43916b8cffYuri Pankov#define REG_LEFT	NOTEST
44016b8cffYuri Pankov#endif
44116b8cffYuri Pankov#ifndef REG_LENIENT
44216b8cffYuri Pankov#define REG_LENIENT	0
44316b8cffYuri Pankov#endif
44416b8cffYuri Pankov#ifndef REG_MINIMAL
44516b8cffYuri Pankov#define REG_MINIMAL	NOTEST
44616b8cffYuri Pankov#endif
44716b8cffYuri Pankov#ifndef REG_MULTIPLE
44816b8cffYuri Pankov#define REG_MULTIPLE	NOTEST
44916b8cffYuri Pankov#endif
45016b8cffYuri Pankov#ifndef REG_MULTIREF
45116b8cffYuri Pankov#define REG_MULTIREF	NOTEST
45216b8cffYuri Pankov#endif
45316b8cffYuri Pankov#ifndef REG_MUSTDELIM
45416b8cffYuri Pankov#define REG_MUSTDELIM	NOTEST
45516b8cffYuri Pankov#endif
45616b8cffYuri Pankov#ifndef REG_NEWLINE
45716b8cffYuri Pankov#define REG_NEWLINE	NOTEST
45816b8cffYuri Pankov#endif
45916b8cffYuri Pankov#ifndef REG_NOTBOL
46016b8cffYuri Pankov#define REG_NOTBOL	NOTEST
46116b8cffYuri Pankov#endif
46216b8cffYuri Pankov#ifndef REG_NOTEOL
46316b8cffYuri Pankov#define REG_NOTEOL	NOTEST
46416b8cffYuri Pankov#endif
46516b8cffYuri Pankov#ifndef REG_NULL
46616b8cffYuri Pankov#define REG_NULL	NOTEST
46716b8cffYuri Pankov#endif
46816b8cffYuri Pankov#ifndef REG_RIGHT
46916b8cffYuri Pankov#define REG_RIGHT	NOTEST
47016b8cffYuri Pankov#endif
47116b8cffYuri Pankov#ifndef REG_SHELL_DOT
47216b8cffYuri Pankov#define REG_SHELL_DOT	NOTEST
47316b8cffYuri Pankov#endif
47416b8cffYuri Pankov#ifndef REG_SHELL_ESCAPED
47516b8cffYuri Pankov#define REG_SHELL_ESCAPED	NOTEST
47616b8cffYuri Pankov#endif
47716b8cffYuri Pankov#ifndef REG_SHELL_GROUP
47816b8cffYuri Pankov#define REG_SHELL_GROUP	NOTEST
47916b8cffYuri Pankov#endif
48016b8cffYuri Pankov#ifndef REG_SHELL_PATH
48116b8cffYuri Pankov#define REG_SHELL_PATH	NOTEST
48216b8cffYuri Pankov#endif
48316b8cffYuri Pankov#ifndef REG_SPAN
48416b8cffYuri Pankov#define REG_SPAN	NOTEST
48516b8cffYuri Pankov#endif
48616b8cffYuri Pankov
48716b8cffYuri Pankov#define REG_UNKNOWN	(-1)
48816b8cffYuri Pankov
48916b8cffYuri Pankov#ifndef REG_ENEWLINE
49016b8cffYuri Pankov#define REG_ENEWLINE	(REG_UNKNOWN-1)
49116b8cffYuri Pankov#endif
49216b8cffYuri Pankov#ifndef REG_ENULL
49316b8cffYuri Pankov#ifndef REG_EMPTY
49416b8cffYuri Pankov#define REG_ENULL	(REG_UNKNOWN-2)
49516b8cffYuri Pankov#else
49616b8cffYuri Pankov#define REG_ENULL	REG_EMPTY
49716b8cffYuri Pankov#endif
49816b8cffYuri Pankov#endif
49916b8cffYuri Pankov#ifndef REG_ECOUNT
50016b8cffYuri Pankov#define REG_ECOUNT	(REG_UNKNOWN-3)
50116b8cffYuri Pankov#endif
50216b8cffYuri Pankov#ifndef REG_BADESC
50316b8cffYuri Pankov#define REG_BADESC	(REG_UNKNOWN-4)
50416b8cffYuri Pankov#endif
50516b8cffYuri Pankov#ifndef REG_EMEM
50616b8cffYuri Pankov#define REG_EMEM	(REG_UNKNOWN-5)
50716b8cffYuri Pankov#endif
50816b8cffYuri Pankov#ifndef REG_EHUNG
50916b8cffYuri Pankov#define REG_EHUNG	(REG_UNKNOWN-6)
51016b8cffYuri Pankov#endif
51116b8cffYuri Pankov#ifndef REG_EBUS
51216b8cffYuri Pankov#define REG_EBUS	(REG_UNKNOWN-7)
51316b8cffYuri Pankov#endif
51416b8cffYuri Pankov#ifndef REG_EFAULT
51516b8cffYuri Pankov#define REG_EFAULT	(REG_UNKNOWN-8)
51616b8cffYuri Pankov#endif
51716b8cffYuri Pankov#ifndef REG_EFLAGS
51816b8cffYuri Pankov#define REG_EFLAGS	(REG_UNKNOWN-9)
51916b8cffYuri Pankov#endif
52016b8cffYuri Pankov#ifndef REG_EDELIM
52116b8cffYuri Pankov#define REG_EDELIM	(REG_UNKNOWN-9)
52216b8cffYuri Pankov#endif
52316b8cffYuri Pankov
52416b8cffYuri Pankovstatic const struct { int code; char* name; } codes[] =
52516b8cffYuri Pankov{
52616b8cffYuri Pankov	REG_UNKNOWN,	"UNKNOWN",
52716b8cffYuri Pankov	REG_NOMATCH,	"NOMATCH",
52816b8cffYuri Pankov	REG_BADPAT,	"BADPAT",
52916b8cffYuri Pankov	REG_ECOLLATE,	"ECOLLATE",
53016b8cffYuri Pankov	REG_ECTYPE,	"ECTYPE",
53116b8cffYuri Pankov	REG_EESCAPE,	"EESCAPE",
53216b8cffYuri Pankov	REG_ESUBREG,	"ESUBREG",
53316b8cffYuri Pankov	REG_EBRACK,	"EBRACK",
53416b8cffYuri Pankov	REG_EPAREN,	"EPAREN",
53516b8cffYuri Pankov	REG_EBRACE,	"EBRACE",
53616b8cffYuri Pankov	REG_BADBR,	"BADBR",
53716b8cffYuri Pankov	REG_ERANGE,	"ERANGE",
53816b8cffYuri Pankov	REG_ESPACE,	"ESPACE",
53916b8cffYuri Pankov	REG_BADRPT,	"BADRPT",
54016b8cffYuri Pankov	REG_ENEWLINE,	"ENEWLINE",
54116b8cffYuri Pankov	REG_ENULL,	"ENULL",
54216b8cffYuri Pankov	REG_ECOUNT,	"ECOUNT",
54316b8cffYuri Pankov	REG_BADESC,	"BADESC",
54416b8cffYuri Pankov	REG_EMEM,	"EMEM",
54516b8cffYuri Pankov	REG_EHUNG,	"EHUNG",
54616b8cffYuri Pankov	REG_EBUS,	"EBUS",
54716b8cffYuri Pankov	REG_EFAULT,	"EFAULT",
54816b8cffYuri Pankov	REG_EFLAGS,	"EFLAGS",
54916b8cffYuri Pankov	REG_EDELIM,	"EDELIM",
55016b8cffYuri Pankov};
55116b8cffYuri Pankov
55216b8cffYuri Pankovstatic struct
55316b8cffYuri Pankov{
55416b8cffYuri Pankov	regmatch_t	NOMATCH;
55516b8cffYuri Pankov	int		errors;
55616b8cffYuri Pankov	int		extracted;
55716b8cffYuri Pankov	int		ignored;
55816b8cffYuri Pankov	int		lineno;
55916b8cffYuri Pankov	int		passed;
56016b8cffYuri Pankov	int		signals;
56116b8cffYuri Pankov	int		unspecified;
56216b8cffYuri Pankov	int		verify;
56316b8cffYuri Pankov	int		warnings;
56416b8cffYuri Pankov	char*		file;
56516b8cffYuri Pankov	char*		stack;
56616b8cffYuri Pankov	char*		which;
56716b8cffYuri Pankov	jmp_buf		gotcha;
56816b8cffYuri Pankov#ifdef REG_DISCIPLINE
56916b8cffYuri Pankov	Disc_t		disc;
57016b8cffYuri Pankov#endif
57116b8cffYuri Pankov} state;
57216b8cffYuri Pankov
57316b8cffYuri Pankovstatic void
57416b8cffYuri Pankovquote(char* s, int len, unsigned long test)
57516b8cffYuri Pankov{
57616b8cffYuri Pankov	unsigned char*	u = (unsigned char*)s;
57716b8cffYuri Pankov	unsigned char*	e;
57816b8cffYuri Pankov	int		c;
57916b8cffYuri Pankov#ifdef MB_CUR_MAX
58016b8cffYuri Pankov	int		w;
58116b8cffYuri Pankov#endif
58216b8cffYuri Pankov
58316b8cffYuri Pankov	if (!u)
58416b8cffYuri Pankov		printf("NIL");
58516b8cffYuri Pankov	else if (!*u && len <= 1)
58616b8cffYuri Pankov		printf("NULL");
58716b8cffYuri Pankov	else if (test & TEST_EXPAND)
58816b8cffYuri Pankov	{
58916b8cffYuri Pankov		if (len < 0)
59016b8cffYuri Pankov			len = strlen((char*)u);
59116b8cffYuri Pankov		e = u + len;
59216b8cffYuri Pankov		if (test & TEST_DELIMIT)
59316b8cffYuri Pankov			printf("\"");
59416b8cffYuri Pankov		while (u < e)
59516b8cffYuri Pankov			switch (c = *u++)
59616b8cffYuri Pankov			{
59716b8cffYuri Pankov			case '\\':
59816b8cffYuri Pankov				printf("\\\\");
59916b8cffYuri Pankov				break;
60016b8cffYuri Pankov			case '"':
60116b8cffYuri Pankov				if (test & TEST_DELIMIT)
60216b8cffYuri Pankov					printf("\\\"");
60316b8cffYuri Pankov				else
60416b8cffYuri Pankov					printf("\"");
60516b8cffYuri Pankov				break;
60616b8cffYuri Pankov			case '\a':
60716b8cffYuri Pankov				printf("\\a");
60816b8cffYuri Pankov				break;
60916b8cffYuri Pankov			case '\b':
61016b8cffYuri Pankov				printf("\\b");
61116b8cffYuri Pankov				break;
61216b8cffYuri Pankov			case 033:
61316b8cffYuri Pankov				printf("\\e");
61416b8cffYuri Pankov				break;
61516b8cffYuri Pankov			case '\f':
61616b8cffYuri Pankov				printf("\\f");
61716b8cffYuri Pankov				break;
61816b8cffYuri Pankov			case '\n':
61916b8cffYuri Pankov				printf("\\n");
62016b8cffYuri Pankov				break;
62116b8cffYuri Pankov			case '\r':
62216b8cffYuri Pankov				printf("\\r");
62316b8cffYuri Pankov				break;
62416b8cffYuri Pankov			case '\t':
62516b8cffYuri Pankov				printf("\\t");
62616b8cffYuri Pankov				break;
62716b8cffYuri Pankov			case '\v':
62816b8cffYuri Pankov				printf("\\v");
62916b8cffYuri Pankov				break;
63016b8cffYuri Pankov			default:
63116b8cffYuri Pankov#ifdef MB_CUR_MAX
63216b8cffYuri Pankov				s = (char*)u - 1;
63316b8cffYuri Pankov				if ((w = mblen(s, (char*)e - s)) > 1)
63416b8cffYuri Pankov				{
63516b8cffYuri Pankov					u += w - 1;
63616b8cffYuri Pankov					fwrite(s, 1, w, stdout);
63716b8cffYuri Pankov				}
63816b8cffYuri Pankov				else
63916b8cffYuri Pankov#endif
64016b8cffYuri Pankov				if (!iscntrl(c) && isprint(c))
64116b8cffYuri Pankov					putchar(c);
64216b8cffYuri Pankov				else
64316b8cffYuri Pankov					printf("\\x%02x", c);
64416b8cffYuri Pankov				break;
64516b8cffYuri Pankov			}
64616b8cffYuri Pankov		if (test & TEST_DELIMIT)
64716b8cffYuri Pankov			printf("\"");
64816b8cffYuri Pankov	}
64916b8cffYuri Pankov	else
65016b8cffYuri Pankov		printf("%s", s);
65116b8cffYuri Pankov}
65216b8cffYuri Pankov
65316b8cffYuri Pankovstatic void
65416b8cffYuri Pankovreport(char* comment, char* fun, char* re, char* s, int len, char* msg, int flags, unsigned long test)
65516b8cffYuri Pankov{
65616b8cffYuri Pankov	if (state.file)
65716b8cffYuri Pankov		printf("%s:", state.file);
65816b8cffYuri Pankov	printf("%d:", state.lineno);
65916b8cffYuri Pankov	if (re)
66016b8cffYuri Pankov	{
66116b8cffYuri Pankov		printf(" ");
66216b8cffYuri Pankov		quote(re, -1, test|TEST_DELIMIT);
66316b8cffYuri Pankov		if (s)
66416b8cffYuri Pankov		{
66516b8cffYuri Pankov			printf(" versus ");
66616b8cffYuri Pankov			quote(s, len, test|TEST_DELIMIT);
66716b8cffYuri Pankov		}
66816b8cffYuri Pankov	}
66916b8cffYuri Pankov	if (test & TEST_UNSPECIFIED)
67016b8cffYuri Pankov	{
67116b8cffYuri Pankov		state.unspecified++;
67216b8cffYuri Pankov		printf(" unspecified behavior");
67316b8cffYuri Pankov	}
67416b8cffYuri Pankov	else
67516b8cffYuri Pankov		state.errors++;
67616b8cffYuri Pankov	if (state.which)
67716b8cffYuri Pankov		printf(" %s", state.which);
67816b8cffYuri Pankov	if (flags & REG_NOSUB)
67916b8cffYuri Pankov		printf(" NOSUB");
68016b8cffYuri Pankov	if (fun)
68116b8cffYuri Pankov		printf(" %s", fun);
68216b8cffYuri Pankov	if (comment[strlen(comment)-1] == '\n')
68316b8cffYuri Pankov		printf(" %s", comment);
68416b8cffYuri Pankov	else
68516b8cffYuri Pankov	{
68616b8cffYuri Pankov		printf(" %s: ", comment);
68716b8cffYuri Pankov		if (msg)
68816b8cffYuri Pankov			printf("%s: ", msg);
68916b8cffYuri Pankov	}
69016b8cffYuri Pankov}
69116b8cffYuri Pankov
69216b8cffYuri Pankovstatic void
69316b8cffYuri Pankoverror(regex_t* preg, int code)
69416b8cffYuri Pankov{
69516b8cffYuri Pankov	char*	msg;
69616b8cffYuri Pankov	char	buf[256];
69716b8cffYuri Pankov
69816b8cffYuri Pankov	switch (code)
69916b8cffYuri Pankov	{
70016b8cffYuri Pankov	case REG_EBUS:
70116b8cffYuri Pankov		msg = "bus error";
70216b8cffYuri Pankov		break;
70316b8cffYuri Pankov	case REG_EFAULT:
70416b8cffYuri Pankov		msg = "memory fault";
70516b8cffYuri Pankov		break;
70616b8cffYuri Pankov	case REG_EHUNG:
70716b8cffYuri Pankov		msg = "did not terminate";
70816b8cffYuri Pankov		break;
70916b8cffYuri Pankov	default:
71016b8cffYuri Pankov		regerror(code, preg, msg = buf, sizeof buf);
71116b8cffYuri Pankov		break;
71216b8cffYuri Pankov	}
71316b8cffYuri Pankov	printf("%s\n", msg);
71416b8cffYuri Pankov}
71516b8cffYuri Pankov
71616b8cffYuri Pankovstatic void
71716b8cffYuri Pankovbad(char* comment, char* re, char* s, int len, unsigned long test)
71816b8cffYuri Pankov{
71916b8cffYuri Pankov	printf("bad test case ");
72016b8cffYuri Pankov	report(comment, NiL, re, s, len, NiL, 0, test);
72116b8cffYuri Pankov	exit(1);
72216b8cffYuri Pankov}
72316b8cffYuri Pankov
72416b8cffYuri Pankovstatic int
72516b8cffYuri Pankovescape(char* s)
72616b8cffYuri Pankov{
72716b8cffYuri Pankov	char*	b;
72816b8cffYuri Pankov	char*	t;
72916b8cffYuri Pankov	char*	q;
73016b8cffYuri Pankov	char*	e;
73116b8cffYuri Pankov	int	c;
73216b8cffYuri Pankov
73316b8cffYuri Pankov	for (b = t = s; *t = *s; s++, t++)
73416b8cffYuri Pankov		if (*s == '\\')
73516b8cffYuri Pankov			switch (*++s)
73616b8cffYuri Pankov			{
73716b8cffYuri Pankov			case '\\':
73816b8cffYuri Pankov				break;
73916b8cffYuri Pankov			case 'a':
74016b8cffYuri Pankov				*t = '\a';
74116b8cffYuri Pankov				break;
74216b8cffYuri Pankov			case 'b':
74316b8cffYuri Pankov				*t = '\b';
74416b8cffYuri Pankov				break;
74516b8cffYuri Pankov			case 'c':
74616b8cffYuri Pankov				if (*t = *++s)
74716b8cffYuri Pankov					*t &= 037;
74816b8cffYuri Pankov				else
74916b8cffYuri Pankov					s--;
75016b8cffYuri Pankov				break;
75116b8cffYuri Pankov			case 'e':
75216b8cffYuri Pankov			case 'E':
75316b8cffYuri Pankov				*t = 033;
75416b8cffYuri Pankov				break;
75516b8cffYuri Pankov			case 'f':
75616b8cffYuri Pankov				*t = '\f';
75716b8cffYuri Pankov				break;
75816b8cffYuri Pankov			case 'n':
75916b8cffYuri Pankov				*t = '\n';
76016b8cffYuri Pankov				break;
76116b8cffYuri Pankov			case 'r':
76216b8cffYuri Pankov				*t = '\r';
76316b8cffYuri Pankov				break;
76416b8cffYuri Pankov			case 's':
76516b8cffYuri Pankov				*t = ' ';
76616b8cffYuri Pankov				break;
76716b8cffYuri Pankov			case 't':
76816b8cffYuri Pankov				*t = '\t';
76916b8cffYuri Pankov				break;
77016b8cffYuri Pankov			case 'v':
77116b8cffYuri Pankov				*t = '\v';
77216b8cffYuri Pankov				break;
77316b8cffYuri Pankov			case 'u':
77416b8cffYuri Pankov			case 'x':
77516b8cffYuri Pankov				c = 0;
77616b8cffYuri Pankov				q = c == 'u' ? (s + 5) : (char*)0;
77716b8cffYuri Pankov				e = s + 1;
77816b8cffYuri Pankov				while (!e || !q || s < q)
77916b8cffYuri Pankov				{
78016b8cffYuri Pankov					switch (*++s)
78116b8cffYuri Pankov					{
78216b8cffYuri Pankov					case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
78316b8cffYuri Pankov						c = (c << 4) + *s - 'a' + 10;
78416b8cffYuri Pankov						continue;
78516b8cffYuri Pankov					case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
78616b8cffYuri Pankov						c = (c << 4) + *s - 'A' + 10;
78716b8cffYuri Pankov						continue;
78816b8cffYuri Pankov					case '0': case '1': case '2': case '3': case '4':
78916b8cffYuri Pankov					case '5': case '6': case '7': case '8': case '9':
79016b8cffYuri Pankov						c = (c << 4) + *s - '0';
79116b8cffYuri Pankov						continue;
79216b8cffYuri Pankov					case '{':
79316b8cffYuri Pankov					case '[':
79416b8cffYuri Pankov						if (s != e)
79516b8cffYuri Pankov						{
79616b8cffYuri Pankov							s--;
79716b8cffYuri Pankov							break;
79816b8cffYuri Pankov						}
79916b8cffYuri Pankov						e = 0;
80016b8cffYuri Pankov						continue;
80116b8cffYuri Pankov					case '}':
80216b8cffYuri Pankov					case ']':
80316b8cffYuri Pankov						if (e)
80416b8cffYuri Pankov							s--;
80516b8cffYuri Pankov						break;
80616b8cffYuri Pankov					default:
80716b8cffYuri Pankov						s--;
80816b8cffYuri Pankov						break;
80916b8cffYuri Pankov					}
81016b8cffYuri Pankov					break;
81116b8cffYuri Pankov				}
81216b8cffYuri Pankov				*t = c;
81316b8cffYuri Pankov				break;
81416b8cffYuri Pankov			case '0': case '1': case '2': case '3':
81516b8cffYuri Pankov			case '4': case '5': case '6': case '7':
81616b8cffYuri Pankov				c = *s - '0';
81716b8cffYuri Pankov				q = s + 2;
81816b8cffYuri Pankov				while (s < q)
81916b8cffYuri Pankov				{
82016b8cffYuri Pankov					switch (*++s)
82116b8cffYuri Pankov					{
82216b8cffYuri Pankov					case '0': case '1': case '2': case '3':
82316b8cffYuri Pankov					case '4': case '5': case '6': case '7':
82416b8cffYuri Pankov						c = (c << 3) + *s - '0';
82516b8cffYuri Pankov						break;
82616b8cffYuri Pankov					default:
82716b8cffYuri Pankov						q = --s;
82816b8cffYuri Pankov						break;
82916b8cffYuri Pankov					}
83016b8cffYuri Pankov				}
83116b8cffYuri Pankov				*t = c;
83216b8cffYuri Pankov				break;
83316b8cffYuri Pankov			default:
83416b8cffYuri Pankov				*(s + 1) = 0;
83516b8cffYuri Pankov				bad("invalid C \\ escape\n", s - 1, NiL, 0, 0);
83616b8cffYuri Pankov			}
83716b8cffYuri Pankov	return t - b;
83816b8cffYuri Pankov}
83916b8cffYuri Pankov
84016b8cffYuri Pankovstatic void
84116b8cffYuri Pankovmatchoffprint(int off)
84216b8cffYuri Pankov{
84316b8cffYuri Pankov	switch (off)
84416b8cffYuri Pankov	{
84516b8cffYuri Pankov	case -2:
84616b8cffYuri Pankov		printf("X");
84716b8cffYuri Pankov		break;
84816b8cffYuri Pankov	case -1:
84916b8cffYuri Pankov		printf("?");
85016b8cffYuri Pankov		break;
85116b8cffYuri Pankov	default:
85216b8cffYuri Pankov		printf("%d", off);
85316b8cffYuri Pankov		break;
85416b8cffYuri Pankov	}
85516b8cffYuri Pankov}
85616b8cffYuri Pankov
85716b8cffYuri Pankovstatic void
85816b8cffYuri Pankovmatchprint(regmatch_t* match, int nmatch, int nsub, char* ans, unsigned long test)
85916b8cffYuri Pankov{
86016b8cffYuri Pankov	int	i;
86116b8cffYuri Pankov
86216b8cffYuri Pankov	for (; nmatch > nsub + 1; nmatch--)
86316b8cffYuri Pankov		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))
86416b8cffYuri Pankov			break;
86516b8cffYuri Pankov	for (i = 0; i < nmatch; i++)
86616b8cffYuri Pankov	{
86716b8cffYuri Pankov		printf("(");
86816b8cffYuri Pankov		matchoffprint(match[i].rm_so);
86916b8cffYuri Pankov		printf(",");
87016b8cffYuri Pankov		matchoffprint(match[i].rm_eo);
87116b8cffYuri Pankov		printf(")");
87216b8cffYuri Pankov	}
87316b8cffYuri Pankov	if (!(test & (TEST_ACTUAL|TEST_BASELINE)))
87416b8cffYuri Pankov	{
87516b8cffYuri Pankov		if (ans)
87616b8cffYuri Pankov			printf(" expected: %s", ans);
87716b8cffYuri Pankov		printf("\n");
87816b8cffYuri Pankov	}
87916b8cffYuri Pankov}
88016b8cffYuri Pankov
88116b8cffYuri Pankovstatic int
88216b8cffYuri Pankovmatchcheck(regmatch_t* match, int nmatch, int nsub, char* ans, char* re, char* s, int len, int flags, unsigned long test)
88316b8cffYuri Pankov{
88416b8cffYuri Pankov	char*	p;
88516b8cffYuri Pankov	int	i;
88616b8cffYuri Pankov	int	m;
88716b8cffYuri Pankov	int	n;
88816b8cffYuri Pankov
88916b8cffYuri Pankov	if (streq(ans, "OK"))
89016b8cffYuri Pankov		return test & (TEST_BASELINE|TEST_PASS|TEST_VERIFY);
89116b8cffYuri Pankov	for (i = 0, p = ans; i < nmatch && *p; i++)
89216b8cffYuri Pankov	{
89316b8cffYuri Pankov		if (*p == '{')
89416b8cffYuri Pankov		{
89516b8cffYuri Pankov#ifdef REG_DISCIPLINE
89616b8cffYuri Pankov			char*	x;
89716b8cffYuri Pankov
89816b8cffYuri Pankov			if (!(x = sfstruse(state.disc.sp)))
89916b8cffYuri Pankov				bad("out of space [discipline string]\n", NiL, NiL, 0, 0);
90016b8cffYuri Pankov			if (strcmp(p, x))
90116b8cffYuri Pankov			{
90216b8cffYuri Pankov				if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY))
90316b8cffYuri Pankov					return 0;
90416b8cffYuri Pankov				report("callout failed", NiL, re, s, len, NiL, flags, test);
90516b8cffYuri Pankov				quote(p, -1, test);
90616b8cffYuri Pankov				printf(" expected, ");
90716b8cffYuri Pankov				quote(x, -1, test);
90816b8cffYuri Pankov				printf(" returned\n");
90916b8cffYuri Pankov			}
91016b8cffYuri Pankov#endif
91116b8cffYuri Pankov			break;
91216b8cffYuri Pankov		}
91316b8cffYuri Pankov		if (*p++ != '(')
91416b8cffYuri Pankov			bad("improper answer\n", re, s, -1, test);
91516b8cffYuri Pankov		if (*p == '?')
91616b8cffYuri Pankov		{
91716b8cffYuri Pankov			m = -1;
91816b8cffYuri Pankov			p++;
91916b8cffYuri Pankov		}
92016b8cffYuri Pankov		else if (*p == 'R' && !memcmp(p, "RE_DUP_MAX", 10))
92116b8cffYuri Pankov		{
92216b8cffYuri Pankov			m = RE_DUP_MAX;
92316b8cffYuri Pankov			p += 10;
92416b8cffYuri Pankov			if (*p == '+' || *p == '-')
92516b8cffYuri Pankov				m += strtol(p, &p, 10);
92616b8cffYuri Pankov		}
92716b8cffYuri Pankov		else
92816b8cffYuri Pankov			m = strtol(p, &p, 10);
92916b8cffYuri Pankov		if (*p++ != ',')
93016b8cffYuri Pankov			bad("improper answer\n", re, s, -1, test);
93116b8cffYuri Pankov		if (*p == '?')
93216b8cffYuri Pankov		{
93316b8cffYuri Pankov			n = -1;
93416b8cffYuri Pankov			p++;
93516b8cffYuri Pankov		}
93616b8cffYuri Pankov		else if (*p == 'R' && !memcmp(p, "RE_DUP_MAX", 10))
93716b8cffYuri Pankov		{
93816b8cffYuri Pankov			n = RE_DUP_MAX;
93916b8cffYuri Pankov			p += 10;
94016b8cffYuri Pankov			if (*p == '+' || *p == '-')
94116b8cffYuri Pankov				n += strtol(p, &p, 10);
94216b8cffYuri Pankov		}
94316b8cffYuri Pankov		else
94416b8cffYuri Pankov			n = strtol(p, &p, 10);
94516b8cffYuri Pankov		if (*p++ != ')')
94616b8cffYuri Pankov			bad("improper answer\n", re, s, -1, test);
94716b8cffYuri Pankov		if (m!=match[i].rm_so || n!=match[i].rm_eo)
94816b8cffYuri Pankov		{
94916b8cffYuri Pankov			if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)))
95016b8cffYuri Pankov			{
95116b8cffYuri Pankov				report("failed: match was", NiL, re, s, len, NiL, flags, test);
95216b8cffYuri Pankov				matchprint(match, nmatch, nsub, ans, test);
95316b8cffYuri Pankov			}
95416b8cffYuri Pankov			return 0;
95516b8cffYuri Pankov		}
95616b8cffYuri Pankov	}
95716b8cffYuri Pankov	for (; i < nmatch; i++)
95816b8cffYuri Pankov	{
95916b8cffYuri Pankov		if (match[i].rm_so!=-1 || match[i].rm_eo!=-1)
96016b8cffYuri Pankov		{
96116b8cffYuri Pankov			if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_VERIFY)))
96216b8cffYuri Pankov			{
96316b8cffYuri Pankov				if ((test & TEST_IGNORE_POSITION) && (match[i].rm_so<0 || match[i].rm_eo<0))
96416b8cffYuri Pankov				{
96516b8cffYuri Pankov					state.ignored++;
96616b8cffYuri Pankov					return 0;
96716b8cffYuri Pankov				}
96816b8cffYuri Pankov				if (!(test & TEST_SUMMARY))
96916b8cffYuri Pankov				{
97016b8cffYuri Pankov					report("failed: match was", NiL, re, s, len, NiL, flags, test);
97116b8cffYuri Pankov					matchprint(match, nmatch, nsub, ans, test);
97216b8cffYuri Pankov				}
97316b8cffYuri Pankov			}
97416b8cffYuri Pankov			return 0;
97516b8cffYuri Pankov		}
97616b8cffYuri Pankov	}
97716b8cffYuri Pankov	if (!(test & TEST_IGNORE_OVER) && match[nmatch].rm_so != state.NOMATCH.rm_so)
97816b8cffYuri Pankov	{
97916b8cffYuri Pankov		if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)))
98016b8cffYuri Pankov		{
98116b8cffYuri Pankov			report("failed: overran match array", NiL, re, s, len, NiL, flags, test);
98216b8cffYuri Pankov			matchprint(match, nmatch + 1, nsub, NiL, test);
98316b8cffYuri Pankov		}
98416b8cffYuri Pankov		return 0;
98516b8cffYuri Pankov	}
98616b8cffYuri Pankov	return 1;
98716b8cffYuri Pankov}
98816b8cffYuri Pankov
98916b8cffYuri Pankovstatic void
99016b8cffYuri Pankovsigunblock(int s)
99116b8cffYuri Pankov{
99216b8cffYuri Pankov#ifdef SIG_SETMASK
99316b8cffYuri Pankov	int		op;
99416b8cffYuri Pankov	sigset_t	mask;
99516b8cffYuri Pankov
99616b8cffYuri Pankov	sigemptyset(&mask);
99716b8cffYuri Pankov	if (s)
99816b8cffYuri Pankov	{
99916b8cffYuri Pankov		sigaddset(&mask, s);
100016b8cffYuri Pankov		op = SIG_UNBLOCK;
100116b8cffYuri Pankov	}
100216b8cffYuri Pankov	else op = SIG_SETMASK;
100316b8cffYuri Pankov	sigprocmask(op, &mask, NiL);
100416b8cffYuri Pankov#else
100516b8cffYuri Pankov#ifdef sigmask
100616b8cffYuri Pankov	sigsetmask(s ? (sigsetmask(0L) & ~sigmask(s)) : 0L);
100716b8cffYuri Pankov#endif
100816b8cffYuri Pankov#endif
100916b8cffYuri Pankov}
101016b8cffYuri Pankov
101116b8cffYuri Pankovstatic void
101216b8cffYuri Pankovgotcha(int sig)
101316b8cffYuri Pankov{
101416b8cffYuri Pankov	int	ret;
101516b8cffYuri Pankov
101616b8cffYuri Pankov	signal(sig, gotcha);
101716b8cffYuri Pankov	alarm(0);
101816b8cffYuri Pankov	state.signals++;
101916b8cffYuri Pankov	switch (sig)
102016b8cffYuri Pankov	{
102116b8cffYuri Pankov	case SIGALRM:
102216b8cffYuri Pankov		ret = REG_EHUNG;
102316b8cffYuri Pankov		break;
102416b8cffYuri Pankov	case SIGBUS:
102516b8cffYuri Pankov		ret = REG_EBUS;
102616b8cffYuri Pankov		break;
102716b8cffYuri Pankov	default:
102816b8cffYuri Pankov		ret = REG_EFAULT;
102916b8cffYuri Pankov		break;
103016b8cffYuri Pankov	}
103116b8cffYuri Pankov	sigunblock(sig);
103216b8cffYuri Pankov	longjmp(state.gotcha, ret);
103316b8cffYuri Pankov}
103416b8cffYuri Pankov
103516b8cffYuri Pankovstatic char*
103616b8cffYuri Pankovget_line(FILE* fp)
103716b8cffYuri Pankov{
103816b8cffYuri Pankov	static char	buf[32 * 1024];
103916b8cffYuri Pankov
104016b8cffYuri Pankov	register char*	s = buf;
104116b8cffYuri Pankov	register char*	e = &buf[sizeof(buf)];
104216b8cffYuri Pankov	register char*	b;
104316b8cffYuri Pankov
104416b8cffYuri Pankov	for (;;)
104516b8cffYuri Pankov	{
104616b8cffYuri Pankov		if (!(b = fgets(s, e - s, fp)))
104716b8cffYuri Pankov			return 0;
104816b8cffYuri Pankov		state.lineno++;
104916b8cffYuri Pankov		s += strlen(s);
105016b8cffYuri Pankov		if (s == b || *--s != '\n' || s == b || *(s - 1) != '\\')
105116b8cffYuri Pankov		{
105216b8cffYuri Pankov			*s = 0;
105316b8cffYuri Pankov			break;
105416b8cffYuri Pankov		}
105516b8cffYuri Pankov		s--;
105616b8cffYuri Pankov	}
105716b8cffYuri Pankov	return buf;
105816b8cffYuri Pankov}
105916b8cffYuri Pankov
106016b8cffYuri Pankovstatic unsigned long
106116b8cffYuri Pankovnote(unsigned long level, char* msg, unsigned long skip, unsigned long test)
106216b8cffYuri Pankov{
106316b8cffYuri Pankov	if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY)) && !skip)
106416b8cffYuri Pankov	{
106516b8cffYuri Pankov		printf("NOTE\t");
106616b8cffYuri Pankov		if (msg)
106716b8cffYuri Pankov			printf("%s: ", msg);
106816b8cffYuri Pankov		printf("skipping lines %d", state.lineno);
106916b8cffYuri Pankov	}
107016b8cffYuri Pankov	return skip | level;
107116b8cffYuri Pankov}
107216b8cffYuri Pankov
107316b8cffYuri Pankov#define TABS(n)		&ts[7-((n)&7)]
107416b8cffYuri Pankov
107516b8cffYuri Pankovstatic char		ts[] = "\t\t\t\t\t\t\t";
107616b8cffYuri Pankov
107716b8cffYuri Pankovstatic unsigned long
107816b8cffYuri Pankovextract(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)
107916b8cffYuri Pankov{
108016b8cffYuri Pankov	if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_OK|TEST_PASS|TEST_SUMMARY))
1081