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