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