Add AT&T Research regex(3) regression testsuite
authorJohn Marino <draco@marino.st>
Mon, 3 Aug 2015 21:35:20 +0000 (23:35 +0200)
committerJohn Marino <draco@marino.st>
Mon, 3 Aug 2015 21:48:56 +0000 (23:48 +0200)
Before we replace our ancient regex, we need to baseline it.
This is a well-known and maintained regex testsuite from AT&T Research.
The following commands from tools/regression/lib/libc-regex can be used:

  Make full-test-run   (this runs all tests consecutively)

  make test-basic
  make test-categorize
  make test-nullsubexpr
  make test-leftassoc
  make test-rightassoc
  make test-forcedassoc
  make test-repetition

To change the locale, set LOCALE (e.g. make LOCAL=en_US.UTF-8 test-basic)
These are the baseline results:

  basic       : TEST testregex, 539 tests,  0 errors
  categorize  : TEST testregex,  20 tests,  0 errors
  nullsubexpr : TEST testregex,  84 tests, 31 errors
  leftassoc   : TEST testregex,  12 tests, 12 errors
  rightassoc  : TEST testregex,  24 tests,  0 errors
  forcedassoc : TEST testregex,  48 tests,  8 errors
  repetition  : TEST testregex, 129 tests, 37 errors

UNSUPPORTED:
  AUGMENTED,SHELL,CLASS_ESCAPE,COMMENT,DELIMITED,DISCIPLINE,ESCAPE,LEFT,
  LENIENT,LITERAL,MINIMAL,MULTIPLE,MULTIREF,MUSTDELIM,NULL,RIGHT,
  SHELL_DOT,SHELL_ESCAPED,SHELL_GROUP,SHELL_PATH,SPAN,regnexec,
  regsubcomp,redecomp

tools/regression/lib/libc-regex/Makefile [new file with mode: 0644]
tools/regression/lib/libc-regex/README [new file with mode: 0644]
tools/regression/lib/libc-regex/datafiles/basic.dat [new file with mode: 0644]
tools/regression/lib/libc-regex/datafiles/categorize.dat [new file with mode: 0644]
tools/regression/lib/libc-regex/datafiles/forcedassoc.dat [new file with mode: 0644]
tools/regression/lib/libc-regex/datafiles/leftassoc.dat [new file with mode: 0644]
tools/regression/lib/libc-regex/datafiles/nullsubexpr.dat [new file with mode: 0644]
tools/regression/lib/libc-regex/datafiles/repetition.dat [new file with mode: 0644]
tools/regression/lib/libc-regex/datafiles/rightassoc.dat [new file with mode: 0644]
tools/regression/lib/libc-regex/testregex.c [new file with mode: 0644]

diff --git a/tools/regression/lib/libc-regex/Makefile b/tools/regression/lib/libc-regex/Makefile
new file mode 100644 (file)
index 0000000..7b74ee6
--- /dev/null
@@ -0,0 +1,18 @@
+PROG=          testregex
+NOMAN=         yes
+
+LOCALE?=       C
+TYPES=         basic categorize nullsubexpr leftassoc rightassoc \
+               forcedassoc repetition
+
+.for type in ${TYPES}
+test-${type}: testregex datafiles/${type}.dat
+       @echo
+       @echo Testing ${type} ...
+       @env LANG=${LOCALE} \
+               ${.CURDIR}/testregex < ${.CURDIR}/datafiles/${type}.dat
+.endfor
+
+full-test-run: ${TYPES:C/^/test-/}
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/lib/libc-regex/README b/tools/regression/lib/libc-regex/README
new file mode 100644 (file)
index 0000000..3e70295
--- /dev/null
@@ -0,0 +1,149 @@
+NAME
+  testregex - regex(3) test harness
+
+SYNOPSIS
+  testregex [ options ]
+
+DESCRIPTION
+  testregex reads regex(3) test specifications, one per line, from the
+  standard input and writes one output line for each failed test. A
+  summary line is written after all tests are done. Each successful
+  test is run again with REG_NOSUB. Unsupported features are noted
+  before the first test, and tests requiring these features are
+  silently ignored.
+
+OPTIONS
+  -c   catch signals and non-terminating calls
+  -e   ignore error return mismatches
+  -h   list help on standard error
+  -n   do not repeat successful tests with regnexec()
+  -o   ignore match[] overrun errors
+  -p   ignore negative position mismatches
+  -s   use stack instead of malloc
+  -x   do not repeat successful tests with REG_NOSUB
+  -v   list each test line
+  -A   list failed test lines with actual answers
+  -B   list all test lines with actual answers
+  -F   list failed test lines
+  -P   list passed test lines
+  -S   output one summary line
+
+INPUT FORMAT
+  Input lines may be blank, a comment beginning with #, or a test
+  specification. A specification is five fields separated by one
+  or more tabs. NULL denotes the empty string and NIL denotes the
+  0 pointer.
+
+  Field 1: the regex(3) flags to apply, one character per REG_feature
+  flag. The test is skipped if REG_feature is not supported by the
+  implementation. If the first character is not [BEASKLP] then the
+  specification is a global control line. One or more of [BEASKLP] may be
+  specified; the test will be repeated for each mode.
+
+    B  basic                   BRE     (grep, ed, sed)
+    E  REG_EXTENDED            ERE     (egrep)
+    A  REG_AUGMENTED           ARE     (egrep with negation)
+    S  REG_SHELL               SRE     (sh glob)
+    K  REG_SHELL|REG_AUGMENTED KRE     (ksh glob)
+    L  REG_LITERAL             LRE     (fgrep)
+
+    a  REG_LEFT|REG_RIGHT      implicit ^...$
+    b  REG_NOTBOL              lhs does not match ^
+    c  REG_COMMENT             ignore space and #...\n
+    d  REG_SHELL_DOT           explicit leading . match
+    e  REG_NOTEOL              rhs does not match $
+    f  REG_MULTIPLE            multiple \n separated patterns
+    g  FNM_LEADING_DIR         testfnmatch only -- match until /
+    h  REG_MULTIREF            multiple digit backref
+    i  REG_ICASE               ignore case
+    j  REG_SPAN                . matches \n
+    k  REG_ESCAPE              \ to ecape [...] delimiter
+    l  REG_LEFT                implicit ^...
+    m  REG_MINIMAL             minimal match
+    n  REG_NEWLINE             explicit \n match
+    o  REG_ENCLOSED            (|&) magic inside [@|&](...)
+    p  REG_SHELL_PATH          explicit / match
+    q  REG_DELIMITED           delimited pattern
+    r  REG_RIGHT               implicit ...$
+    s  REG_SHELL_ESCAPED       \ not special
+    t  REG_MUSTDELIM           all delimiters must be specified
+    u  standard unspecified behavior -- errors not counted
+    v  REG_CLASS_ESCAPE        \ special inside [...]
+    w  REG_NOSUB               no subexpression match array
+    x  REG_LENIENT             let some errors slide
+    y  REG_LEFT                regexec() implicit ^...
+    z  REG_NULL                NULL subexpressions ok
+    $                          expand C \c escapes in fields 2 and 3
+    /                          field 2 is a regsubcomp() expression
+    =                          field 3 is a regdecomp() expression
+
+  Field 1 control lines:
+
+    C          set LC_COLLATE and LC_CTYPE to locale in field 2
+
+    ?test ...  output field 5 if passed and != EXPECTED, silent otherwise
+    &test ...  output field 5 if current and previous passed
+    |test ...  output field 5 if current passed and previous failed
+    ; ...      output field 2 if previous failed
+    {test ...  skip if failed until }
+    }          end of skip
+
+    : comment          comment copied as output NOTE
+    :comment:test      :comment: ignored
+    N[OTE] comment     comment copied as output NOTE
+    T[EST] comment     comment
+
+    number             use number for nmatch (20 by default)
+
+  Field 2: the regular expression pattern; SAME uses the pattern from
+    the previous specification.
+
+  Field 3: the string to match.
+
+  Field 4: the test outcome. This is either one of the posix error
+    codes (with REG_ omitted) or the match array, a list of (m,n)
+    entries with m and n being first and last+1 positions in the
+    field 3 string, or NULL if REG_NOSUB is in effect and success
+    is expected. BADPAT is acceptable in place of any regcomp(3)
+    error code. The match[] array is initialized to (-2,-2) before
+    each test. All array elements from 0 to nmatch-1 must be specified
+    in the outcome. Unspecified endpoints (offset -1) are denoted by ?.
+    Unset endpoints (offset -2) are denoted by X. {x}(o:n) denotes a
+    matched (?{...}) expression, where x is the text enclosed by {...},
+    o is the expression ordinal counting from 1, and n is the length of
+    the unmatched portion of the subject string. If x starts with a
+    number then that is the return value of re_execf(), otherwise 0 is
+    returned.
+
+  Field 5: optional comment appended to the report.
+
+CAVEAT
+    If a regex implementation misbehaves with memory then all bets are off.
+
+CONTRIBUTORS
+  Glenn Fowler    glenn.s.fowler@gmail.com    (ksh strmatch, regex extensions)
+  David Korn      dgkorn@gmail.com            (ksh glob matcher)
+  Doug McIlroy    mcilroy@dartmouth.edu       (ast regex/testre in C++)
+  Tom Lord        lord@regexps.com            (rx tests)
+  Henry Spencer   henry@zoo.toronto.edu       (original public regex)
+  Andrew Hume     andrew@research.att.com     (gre tests)
+  John Maddock    John_Maddock@compuserve.com (regex++ tests)
+  Philip Hazel    ph10@cam.ac.uk              (pcre tests)
+  Ville Laurikari vl@iki.fi                   (libtre tests)
+  
+WEB SITE
+  http://www2.research.att.com/~astopen/testregex/
+  AT&T Research regex(3) regression tests
+
+  Glenn Fowler <glenn.s.fowler@gmail.com>
+  AT&T Research - Florham Park NJ
+
+  testregex.c 2004-05-31 is the latest source for the AT&T Research regression
+  test harness for the X/Open regex pattern match interface.
+  The source and test data posted here are license free.
+
+  testregex can:
+     - verify stability for a particular implementation in the face of source
+       code and/or compilation environment changes
+     - verify standard compliance for all implementations
+     - provide a basis for discussions on what compliance means
diff --git a/tools/regression/lib/libc-regex/datafiles/basic.dat b/tools/regression/lib/libc-regex/datafiles/basic.dat
new file mode 100644 (file)
index 0000000..5c50f37
--- /dev/null
@@ -0,0 +1,216 @@
+NOTE   all standard compliant implementations should pass these : 2002-05-31
+
+BE     abracadabra$    abracadabracadabra      (7,18)
+BE     a...b           abababbb                (2,7)
+BE     XXXXXX          ..XXXXXX                (2,8)
+E      \)              ()      (1,2)
+BE     a]              a]a     (0,2)
+B      }               }       (0,1)
+E      \}              }       (0,1)
+BE     \]              ]       (0,1)
+B      ]               ]       (0,1)
+E      ]               ]       (0,1)
+B      {               {       (0,1)
+B      }               }       (0,1)
+BE     ^a              ax      (0,1)
+BE     \^a             a^a     (1,3)
+BE     a\^             a^      (0,2)
+BE     a$              aa      (1,2)
+BE     a\$             a$      (0,2)
+BE     ^$              NULL    (0,0)
+E      $^              NULL    (0,0)
+E      a($)            aa      (1,2)(2,2)
+E      a*(^a)          aa      (0,1)(0,1)
+E      (..)*(...)*             a       (0,0)
+E      (..)*(...)*             abcd    (0,4)(2,4)
+E      (ab|a)(bc|c)            abc     (0,3)(0,2)(2,3)
+E      (ab)c|abc               abc     (0,3)(0,2)
+E      a{0}b           ab                      (1,2)
+E      (a*)(b?)(b+)b{3}        aaabbbbbbb      (0,10)(0,3)(3,4)(4,7)
+E      (a*)(b{0,1})(b{1,})b{3} aaabbbbbbb      (0,10)(0,3)(3,4)(4,7)
+E      a{9876543210}   NULL    BADBR
+E      ((a|a)|a)                       a       (0,1)(0,1)(0,1)
+E      (a*)(a|aa)                      aaaa    (0,4)(0,3)(3,4)
+E      a*(a.|aa)                       aaaa    (0,4)(2,4)
+E      a(b)|c(d)|a(e)f                 aef     (0,3)(?,?)(?,?)(1,2)
+E      (a|b)?.*                        b       (0,1)(0,1)
+E      (a|b)c|a(b|c)                   ac      (0,2)(0,1)
+E      (a|b)c|a(b|c)                   ab      (0,2)(?,?)(1,2)
+E      (a|b)*c|(a|ab)*c                abc     (0,3)(1,2)
+E      (a|b)*c|(a|ab)*c                xc      (1,2)
+E      (.a|.b).*|.*(.a|.b)             xa      (0,2)(0,2)
+E      a?(ab|ba)ab                     abab    (0,4)(0,2)
+E      a?(ac{0}b|ba)ab                 abab    (0,4)(0,2)
+E      ab|abab                         abbabab (0,2)
+E      aba|bab|bba                     baaabbbaba      (5,8)
+E      aba|bab                         baaabbbaba      (6,9)
+E      (aa|aaa)*|(a|aaaaa)             aa      (0,2)(0,2)
+E      (a.|.a.)*|(a|.a...)             aa      (0,2)(0,2)
+E      ab|a                            xabc    (1,3)
+E      ab|a                            xxabc   (2,4)
+Ei     (Ab|cD)*                        aBcD    (0,4)(2,4)
+BE     [^-]                    --a             (2,3)
+BE     [a-]*                   --a             (0,3)
+BE     [a-m-]*                 --amoma--       (0,4)
+E      :::1:::0:|:::1:1:0:     :::0:::1:::1:::0:       (8,17)
+E      :::1:::0:|:::1:1:1:     :::0:::1:::1:::0:       (8,17)
+{E     [[:upper:]]             A               (0,1)   [[<element>]] not supported
+E      [[:lower:]]+            `az{            (1,3)
+E      [[:upper:]]+            @AZ[            (1,3)
+BE     [[-]]                   [[-]]           (2,4)
+BE     [[.NIL.]]       NULL    ECOLLATE
+BE     [[=aleph=]]     NULL    ECOLLATE
+}
+BE$    \n              \n      (0,1)
+BEn$   \n              \n      (0,1)
+BE$    [^a]            \n      (0,1)
+BE$    \na             \na     (0,2)
+E      (a)(b)(c)       abc     (0,3)(0,1)(1,2)(2,3)
+BE     xxx             xxx     (0,3)
+E1     (^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\* */?)0*[6-7]))([^0-9]|$)      feb 6,  (0,6)
+E1     (^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\* */?)0*[6-7]))([^0-9]|$)      2/7     (0,3)
+E1     (^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\* */?)0*[6-7]))([^0-9]|$)      feb 1,Feb 6     (5,11)
+E3     ((((((((((((((((((((((((((((((x))))))))))))))))))))))))))))))   x       (0,1)(0,1)(0,1)
+E3     ((((((((((((((((((((((((((((((x))))))))))))))))))))))))))))))*  xx      (0,2)(1,2)(1,2)
+E      a?(ab|ba)*      ababababababababababababababababababababababababababababababababababababababababa       (0,81)(79,81)
+E      abaa|abbaa|abbbaa|abbbbaa       ababbabbbabbbabbbbabbbbaa       (18,25)
+E      abaa|abbaa|abbbaa|abbbbaa       ababbabbbabbbabbbbabaa  (18,22)
+E      aaac|aabc|abac|abbc|baac|babc|bbac|bbbc baaabbbabac     (7,11)
+BE$    .*                      \x01\xff        (0,2)
+E      aaaa|bbbb|cccc|ddddd|eeeeee|fffffff|gggg|hhhh|iiiii|jjjjj|kkkkk|llll            XaaaXbbbXcccXdddXeeeXfffXgggXhhhXiiiXjjjXkkkXlllXcbaXaaaa       (53,57)
+L      aaaa\nbbbb\ncccc\nddddd\neeeeee\nfffffff\ngggg\nhhhh\niiiii\njjjjj\nkkkkk\nllll         XaaaXbbbXcccXdddXeeeXfffXgggXhhhXiiiXjjjXkkkXlllXcbaXaaaa       NOMATCH
+E      a*a*a*a*a*b             aaaaaaaaab      (0,10)
+BE     ^                       NULL            (0,0)
+BE     $                       NULL            (0,0)
+BE     ^$                      NULL            (0,0)
+BE     ^a$                     a               (0,1)
+BE     abc                     abc             (0,3)
+BE     abc                     xabcy           (1,4)
+BE     abc                     ababc           (2,5)
+BE     ab*c                    abc             (0,3)
+BE     ab*bc                   abc             (0,3)
+BE     ab*bc                   abbc            (0,4)
+BE     ab*bc                   abbbbc          (0,6)
+E      ab+bc                   abbc            (0,4)
+E      ab+bc                   abbbbc          (0,6)
+E      ab?bc                   abbc            (0,4)
+E      ab?bc                   abc             (0,3)
+E      ab?c                    abc             (0,3)
+BE     ^abc$                   abc             (0,3)
+BE     ^abc                    abcc            (0,3)
+BE     abc$                    aabc            (1,4)
+BE     ^                       abc             (0,0)
+BE     $                       abc             (3,3)
+BE     a.c                     abc             (0,3)
+BE     a.c                     axc             (0,3)
+BE     a.*c                    axyzc           (0,5)
+BE     a[bc]d                  abd             (0,3)
+BE     a[b-d]e                 ace             (0,3)
+BE     a[b-d]                  aac             (1,3)
+BE     a[-b]                   a-              (0,2)
+BE     a[b-]                   a-              (0,2)
+BE     a]                      a]              (0,2)
+BE     a[]]b                   a]b             (0,3)
+BE     a[^bc]d                 aed             (0,3)
+BE     a[^-b]c                 adc             (0,3)
+BE     a[^]b]c                 adc             (0,3)
+E      ab|cd                   abc             (0,2)
+E      ab|cd                   abcd            (0,2)
+E      a\(b                    a(b             (0,3)
+E      a\(*b                   ab              (0,2)
+E      a\(*b                   a((b            (0,4)
+E      ((a))                   abc             (0,1)(0,1)(0,1)
+E      (a)b(c)                 abc             (0,3)(0,1)(2,3)
+E      a+b+c                   aabbabc         (4,7)
+E      a*                      aaa             (0,3)
+E      (a*)*                   -               (0,0)(0,0)
+E      (a*)+                   -               (0,0)(0,0)
+E      (a*|b)*                 -               (0,0)(0,0)
+E      (a+|b)*                 ab              (0,2)(1,2)
+E      (a+|b)+                 ab              (0,2)(1,2)
+E      (a+|b)?                 ab              (0,1)(0,1)
+BE     [^ab]*                  cde             (0,3)
+E      (^)*                    -               (0,0)(0,0)
+BE     a*                      NULL            (0,0)
+E      ([abc])*d               abbbcd          (0,6)(4,5)
+E      ([abc])*bcd             abcd            (0,4)(0,1)
+E      a|b|c|d|e               e               (0,1)
+E      (a|b|c|d|e)f            ef              (0,2)(0,1)
+E      ((a*|b))*               -               (0,0)(0,0)(0,0)
+BE     abcd*efg                abcdefg         (0,7)
+BE     ab*                     xabyabbbz       (1,3)
+BE     ab*                     xayabbbz        (1,2)
+E      (ab|cd)e                abcde           (2,5)(2,4)
+BE     [abhgefdc]ij            hij             (0,3)
+E      (a|b)c*d                abcd            (1,4)(1,2)
+E      (ab|ab*)bc              abc             (0,3)(0,1)
+E      a([bc]*)c*              abc             (0,3)(1,3)
+E      a([bc]*)(c*d)           abcd            (0,4)(1,3)(3,4)
+E      a([bc]+)(c*d)           abcd            (0,4)(1,3)(3,4)
+E      a([bc]*)(c+d)           abcd            (0,4)(1,2)(2,4)
+E      a[bcd]*dcdcde           adcdcde         (0,7)
+E      (ab|a)b*c               abc             (0,3)(0,2)
+E      ((a)(b)c)(d)            abcd            (0,4)(0,3)(0,1)(1,2)(3,4)
+BE     [A-Za-z_][A-Za-z0-9_]*  alpha           (0,5)
+E      ^a(bc+|b[eh])g|.h$      abh             (1,3)
+E      (bc+d$|ef*g.|h?i(j|k))  effgz           (0,5)(0,5)
+E      (bc+d$|ef*g.|h?i(j|k))  ij              (0,2)(0,2)(1,2)
+E      (bc+d$|ef*g.|h?i(j|k))  reffgz          (1,6)(1,6)
+E      (((((((((a)))))))))     a               (0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)
+BE     multiple words          multiple words yeah     (0,14)
+E      (.*)c(.*)               abcde           (0,5)(0,2)(3,5)
+BE     abcd                    abcd            (0,4)
+E      a(bc)d                  abcd            (0,4)(1,3)
+E      a[\ 1-\ 3]?c                a\ 2c             (0,3)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Muammar Qaddafi (0,15)(?,?)(10,12)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Mo'ammar Gadhafi        (0,16)(?,?)(11,13)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Muammar Kaddafi (0,15)(?,?)(10,12)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Muammar Qadhafi (0,15)(?,?)(10,12)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Muammar Gadafi  (0,14)(?,?)(10,11)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Mu'ammar Qadafi (0,15)(?,?)(11,12)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Moamar Gaddafi  (0,14)(?,?)(9,11)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Mu'ammar Qadhdhafi      (0,18)(?,?)(13,15)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Muammar Khaddafi        (0,16)(?,?)(11,13)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Muammar Ghaddafy        (0,16)(?,?)(11,13)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Muammar Ghadafi (0,15)(?,?)(11,12)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Muammar Ghaddafi        (0,16)(?,?)(11,13)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Muamar Kaddafi  (0,14)(?,?)(9,11)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Muammar Quathafi        (0,16)(?,?)(11,13)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Muammar Gheddafi        (0,16)(?,?)(11,13)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Moammar Khadafy (0,15)(?,?)(11,12)
+E      M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]       Moammar Qudhafi (0,15)(?,?)(10,12)
+E      a+(b|c)*d+              aabcdd                  (0,6)(3,4)
+E      ^.+$                    vivi                    (0,4)
+E      ^(.+)$                  vivi                    (0,4)(0,4)
+E      ^([^!.]+).att.com!(.+)$ gryphon.att.com!eby     (0,19)(0,7)(16,19)
+E      ^([^!]+!)?([^!]+)$      bas                     (0,3)(?,?)(0,3)
+E      ^([^!]+!)?([^!]+)$      bar!bas                 (0,7)(0,4)(4,7)
+E      ^([^!]+!)?([^!]+)$      foo!bas                 (0,7)(0,4)(4,7)
+E      ^.+!([^!]+!)([^!]+)$    foo!bar!bas             (0,11)(4,8)(8,11)
+E      ((foo)|(bar))!bas       bar!bas                 (0,7)(0,3)(?,?)(0,3)
+E      ((foo)|(bar))!bas       foo!bar!bas             (4,11)(4,7)(?,?)(4,7)
+E      ((foo)|(bar))!bas       foo!bas                 (0,7)(0,3)(0,3)
+E      ((foo)|bar)!bas         bar!bas                 (0,7)(0,3)
+E      ((foo)|bar)!bas         foo!bar!bas             (4,11)(4,7)
+E      ((foo)|bar)!bas         foo!bas                 (0,7)(0,3)(0,3)
+E      (foo|(bar))!bas         bar!bas                 (0,7)(0,3)(0,3)
+E      (foo|(bar))!bas         foo!bar!bas             (4,11)(4,7)(4,7)
+E      (foo|(bar))!bas         foo!bas                 (0,7)(0,3)
+E      (foo|bar)!bas           bar!bas                 (0,7)(0,3)
+E      (foo|bar)!bas           foo!bar!bas             (4,11)(4,7)
+E      (foo|bar)!bas           foo!bas                 (0,7)(0,3)
+E      ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ foo!bar!bas     (0,11)(0,11)(?,?)(?,?)(4,8)(8,11)
+E      ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ bas             (0,3)(?,?)(0,3)
+E      ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ bar!bas         (0,7)(0,4)(4,7)
+E      ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ foo!bar!bas     (0,11)(?,?)(?,?)(4,8)(8,11)
+E      ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ foo!bas         (0,7)(0,4)(4,7)
+E      ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ bas             (0,3)(0,3)(?,?)(0,3)
+E      ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ bar!bas         (0,7)(0,7)(0,4)(4,7)
+E      ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ foo!bar!bas     (0,11)(0,11)(?,?)(?,?)(4,8)(8,11)
+E      ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ foo!bas         (0,7)(0,7)(0,4)(4,7)
+E      .*(/XXX).*                      /XXX                    (0,4)(0,4)
+E      .*(\\XXX).*                     \XXX                    (0,4)(0,4)
+E      \\XXX                           \XXX                    (0,4)
+E      .*(/000).*                      /000                    (0,4)(0,4)
+E      .*(\\000).*                     \000                    (0,4)(0,4)
+E      \\000                           \000                    (0,4)
diff --git a/tools/regression/lib/libc-regex/datafiles/categorize.dat b/tools/regression/lib/libc-regex/datafiles/categorize.dat
new file mode 100644 (file)
index 0000000..d348512
--- /dev/null
@@ -0,0 +1,62 @@
+NOTE   regex implementation categorization 2004-05-31
+
+?E     aa*                     xaxaax  (1,2)                                   POSITION=leftmost
+;                                                                              POSITION=bug
+
+?E     (a*)(ab)*(b*)           abc     (0,2)(0,1)(?,?)(1,2)                    ASSOCIATIVITY=right
+|E     (a*)(ab)*(b*)           abc     (0,2)(0,0)(0,2)(2,2)                    ASSOCIATIVITY=left
+;                                                                              ASSOCIATIVITY=bug
+
+?E     ((a*)(ab)*)((b*)(a*))   aba     (0,3)(0,2)(0,0)(0,2)(2,3)(2,2)(2,3)     SUBEXPRESSION=precedence
+|E     ((a*)(ab)*)((b*)(a*))   aba     (0,3)(0,1)(0,1)(?,?)(1,3)(1,2)(2,3)     SUBEXPRESSION=grouping
+;                                                                              SUBEXPRESSION=bug
+
+?E     (...?.?)*               xxxxxx  (0,6)(4,6)                              REPEAT_LONGEST=first
+|E     (...?.?)*               xxxxxx  (0,6)(2,6)                              REPEAT_LONGEST=last
+|E     (...?.?)*               xxxxxx  OK                                      REPEAT_LONGEST=unknown
+;                                                                              REPEAT_LONGEST=bug
+
+?E     (a|ab)(bc|c)            abcabc  (0,3)(0,2)(2,3)                         EXPECTED
+|E     (a|ab)(bc|c)            abcabc  (0,3)(0,1)(1,3)                         BUG=alternation-order
+;                                                                              BUG=alternation-order-UNKNOWN
+
+?E     (aba|a*b)(aba|a*b)      ababa   (0,5)(0,2)(2,5)                         EXPECTED
+|E     (aba|a*b)(aba|a*b)      ababa   (0,4)(0,3)(3,4)                         BUG=first-match
+;                                                                              BUG=unknown-match
+
+?B     a\(b\)*\1               a       NOMATCH                                 EXPECTED
+|B     a\(b\)*\1               a       (0,1)                                   BUG=nomatch-match
+|B     a\(b\)*\1               abab    (0,2)(1,2)                              # BUG=repeat-any
+;                                                                              BUG=nomatch-match-UNKNOWN
+
+?E     (a*){2}                 xxxxx   (0,0)(0,0)                              EXPECTED
+|E     (a*){2}                 xxxxx   (5,5)(5,5)                              BUG=range-null
+;                                                                              BUG=range-null-UNKNOWN
+
+?B     a\(b\)*\1               abab    NOMATCH                                 EXPECTED
+|B     a\(b\)*\1               abab    (0,1)                                   # BUG=nomatch-match
+|B     a\(b\)*\1               abab    (0,2)(1,2)                              BUG=repeat-any
+;                                                                              BUG=repeat-any-UNKNOWN
+
+?E     (a*)*                   a       (0,1)(0,1)                              EXPECTED
+|E     (a*)*                   ax      (0,1)(0,1)                              BUG=repeat-null-unknown
+|E     (a*)*                   a       (0,1)(1,1)                              BUG=repeat-null
+;                                                                              BUG=repeat-null-UNKNOWN
+
+?E     (aba|a*b)*              ababa   (0,5)(2,5)                              EXPECTED
+|E     (aba|a*b)*              ababa   (0,5)(3,4)                              BUG=repeat-short
+|E     (aba|a*b)*              ababa   (0,4)(3,4)                              # LENGTH=first
+;                                                                              BUG=repeat-short-UNKNOWN
+
+?E     (a(b)?)+                aba     (0,3)(2,3)                              EXPECTED
+|E     (a(b)?)+                aba     (0,3)(2,3)(1,2)                         BUG=repeat-artifact
+;                                                                              BUG=repeat-artifact-UNKNOWN
+
+?B     \(a\(b\)*\)*\2          abab    NOMATCH                                 EXPECTED
+|B     \(a\(b\)*\)*\2          abab    (0,4)(2,3)(1,2)                         BUG=repeat-artifact-nomatch
+;                                                                              BUG=repeat-artifact-nomatch-UNKNOWN
+
+?E     (a?)((ab)?)(b?)a?(ab)?b?        abab    (0,4)(0,1)(1,1)(?,?)(1,2)(2,4)  BUG=subexpression-first
+|E     .*(.*)                          ab      (0,2)(2,2)                      EXPECTED
+|E     .*(.*)                          ab      (0,2)(0,2)                      BUG=subexpression-first
+;                                                                              BUG=subexpression-first-UNKNOWN
diff --git a/tools/regression/lib/libc-regex/datafiles/forcedassoc.dat b/tools/regression/lib/libc-regex/datafiles/forcedassoc.dat
new file mode 100644 (file)
index 0000000..39f3111
--- /dev/null
@@ -0,0 +1,30 @@
+NOTE   left-assoc:pass-all right-assoc:pass-all : 2002-04-29
+
+E      (a|ab)(c|bcd)           abcd    (0,4)(0,1)(1,4)
+E      (a|ab)(bcd|c)           abcd    (0,4)(0,1)(1,4)
+E      (ab|a)(c|bcd)           abcd    (0,4)(0,1)(1,4)
+E      (ab|a)(bcd|c)           abcd    (0,4)(0,1)(1,4)
+E      ((a|ab)(c|bcd))(d*)     abcd    (0,4)(0,4)(0,1)(1,4)(4,4)
+E      ((a|ab)(bcd|c))(d*)     abcd    (0,4)(0,4)(0,1)(1,4)(4,4)
+E      ((ab|a)(c|bcd))(d*)     abcd    (0,4)(0,4)(0,1)(1,4)(4,4)
+E      ((ab|a)(bcd|c))(d*)     abcd    (0,4)(0,4)(0,1)(1,4)(4,4)
+E      (a|ab)((c|bcd)(d*))     abcd    (0,4)(0,2)(2,4)(2,3)(3,4)
+E      (a|ab)((bcd|c)(d*))     abcd    (0,4)(0,2)(2,4)(2,3)(3,4)
+E      (ab|a)((c|bcd)(d*))     abcd    (0,4)(0,2)(2,4)(2,3)(3,4)
+E      (ab|a)((bcd|c)(d*))     abcd    (0,4)(0,2)(2,4)(2,3)(3,4)
+E      (a*)(b|abc)             abc     (0,3)(0,0)(0,3)
+E      (a*)(abc|b)             abc     (0,3)(0,0)(0,3)
+E      ((a*)(b|abc))(c*)       abc     (0,3)(0,3)(0,0)(0,3)(3,3)
+E      ((a*)(abc|b))(c*)       abc     (0,3)(0,3)(0,0)(0,3)(3,3)
+E      (a*)((b|abc)(c*))       abc     (0,3)(0,1)(1,3)(1,2)(2,3)
+E      (a*)((abc|b)(c*))       abc     (0,3)(0,1)(1,3)(1,2)(2,3)
+E      (a*)(b|abc)             abc     (0,3)(0,0)(0,3)
+E      (a*)(abc|b)             abc     (0,3)(0,0)(0,3)
+E      ((a*)(b|abc))(c*)       abc     (0,3)(0,3)(0,0)(0,3)(3,3)
+E      ((a*)(abc|b))(c*)       abc     (0,3)(0,3)(0,0)(0,3)(3,3)
+E      (a*)((b|abc)(c*))       abc     (0,3)(0,1)(1,3)(1,2)(2,3)
+E      (a*)((abc|b)(c*))       abc     (0,3)(0,1)(1,3)(1,2)(2,3)
+E      (a|ab)                  ab      (0,2)(0,2)
+E      (ab|a)                  ab      (0,2)(0,2)
+E      (a|ab)(b*)              ab      (0,2)(0,2)(2,2)
+E      (ab|a)(b*)              ab      (0,2)(0,2)(2,2)
diff --git a/tools/regression/lib/libc-regex/datafiles/leftassoc.dat b/tools/regression/lib/libc-regex/datafiles/leftassoc.dat
new file mode 100644 (file)
index 0000000..9c068c6
--- /dev/null
@@ -0,0 +1,16 @@
+NOTE   left-assoc:pass-all right-assoc:pass-none : 2002-04-29
+
+E      (a|ab)(c|bcd)(d*)       abcd    (0,4)(0,1)(1,4)(4,4)
+E      (a|ab)(bcd|c)(d*)       abcd    (0,4)(0,1)(1,4)(4,4)
+E      (ab|a)(c|bcd)(d*)       abcd    (0,4)(0,1)(1,4)(4,4)
+E      (ab|a)(bcd|c)(d*)       abcd    (0,4)(0,1)(1,4)(4,4)
+
+E      (a*)(b|abc)(c*)         abc     (0,3)(0,0)(0,3)(3,3)
+E      (a*)(abc|b)(c*)         abc     (0,3)(0,0)(0,3)(3,3)
+E      (a*)(b|abc)(c*)         abc     (0,3)(0,0)(0,3)(3,3)
+E      (a*)(abc|b)(c*)         abc     (0,3)(0,0)(0,3)(3,3)
+
+E      (a|ab)(c|bcd)(d|.*)     abcd    (0,4)(0,1)(1,4)(4,4)
+E      (a|ab)(bcd|c)(d|.*)     abcd    (0,4)(0,1)(1,4)(4,4)
+E      (ab|a)(c|bcd)(d|.*)     abcd    (0,4)(0,1)(1,4)(4,4)
+E      (ab|a)(bcd|c)(d|.*)     abcd    (0,4)(0,1)(1,4)(4,4)
diff --git a/tools/regression/lib/libc-regex/datafiles/nullsubexpr.dat b/tools/regression/lib/libc-regex/datafiles/nullsubexpr.dat
new file mode 100644 (file)
index 0000000..c73d8f0
--- /dev/null
@@ -0,0 +1,73 @@
+NOTE   null subexpression matches : 2002-06-06
+
+E      (a*)*           a               (0,1)(0,1)
+E      SAME            x               (0,0)(0,0)
+E      SAME            aaaaaa          (0,6)(0,6)
+E      SAME            aaaaaax         (0,6)(0,6)
+E      (a*)+           a               (0,1)(0,1)
+E      SAME            x               (0,0)(0,0)
+E      SAME            aaaaaa          (0,6)(0,6)
+E      SAME            aaaaaax         (0,6)(0,6)
+E      (a+)*           a               (0,1)(0,1)
+E      SAME            x               (0,0)
+E      SAME            aaaaaa          (0,6)(0,6)
+E      SAME            aaaaaax         (0,6)(0,6)
+E      (a+)+           a               (0,1)(0,1)
+E      SAME            x               NOMATCH
+E      SAME            aaaaaa          (0,6)(0,6)
+E      SAME            aaaaaax         (0,6)(0,6)
+
+E      ([a]*)*         a               (0,1)(0,1)
+E      SAME            x               (0,0)(0,0)
+E      SAME            aaaaaa          (0,6)(0,6)
+E      SAME            aaaaaax         (0,6)(0,6)
+E      ([a]*)+         a               (0,1)(0,1)
+E      SAME            x               (0,0)(0,0)
+E      SAME            aaaaaa          (0,6)(0,6)
+E      SAME            aaaaaax         (0,6)(0,6)
+E      ([^b]*)*        a               (0,1)(0,1)
+E      SAME            b               (0,0)(0,0)
+E      SAME            aaaaaa          (0,6)(0,6)
+E      SAME            aaaaaab         (0,6)(0,6)
+E      ([ab]*)*        a               (0,1)(0,1)
+E      SAME            aaaaaa          (0,6)(0,6)
+E      SAME            ababab          (0,6)(0,6)
+E      SAME            bababa          (0,6)(0,6)
+E      SAME            b               (0,1)(0,1)
+E      SAME            bbbbbb          (0,6)(0,6)
+E      SAME            aaaabcde        (0,5)(0,5)
+E      ([^a]*)*        b               (0,1)(0,1)
+E      SAME            bbbbbb          (0,6)(0,6)
+E      SAME            aaaaaa          (0,0)(0,0)
+E      ([^ab]*)*       ccccxx          (0,6)(0,6)
+E      SAME            ababab          (0,0)(0,0)
+
+E      ((z)+|a)*       zabcde          (0,2)(1,2)
+
+{E     a+?             aaaaaa          (0,1)   no *? +? mimimal match ops
+E      (a)             aaa             (0,1)(0,1)
+E      (a*?)           aaa             (0,0)(0,0)
+E      (a)*?           aaa             (0,0)
+E      (a*?)*?         aaa             (0,0)
+}
+
+B      \(a*\)*\(x\)            x       (0,1)(0,0)(0,1)
+B      \(a*\)*\(x\)            ax      (0,2)(0,1)(1,2)
+B      \(a*\)*\(x\)            axa     (0,2)(0,1)(1,2)
+B      \(a*\)*\(x\)\(\1\)      x       (0,1)(0,0)(0,1)(1,1)
+B      \(a*\)*\(x\)\(\1\)      ax      (0,2)(1,1)(1,2)(2,2)
+B      \(a*\)*\(x\)\(\1\)      axa     (0,3)(0,1)(1,2)(2,3)
+B      \(a*\)*\(x\)\(\1\)\(x\) axax    (0,4)(0,1)(1,2)(2,3)(3,4)
+B      \(a*\)*\(x\)\(\1\)\(x\) axxa    (0,3)(1,1)(1,2)(2,2)(2,3)
+
+E      (a*)*(x)                x       (0,1)(0,0)(0,1)
+E      (a*)*(x)                ax      (0,2)(0,1)(1,2)
+E      (a*)*(x)                axa     (0,2)(0,1)(1,2)
+
+E      (a*)+(x)                x       (0,1)(0,0)(0,1)
+E      (a*)+(x)                ax      (0,2)(0,1)(1,2)
+E      (a*)+(x)                axa     (0,2)(0,1)(1,2)
+
+E      (a*){2}(x)              x       (0,1)(0,0)(0,1)
+E      (a*){2}(x)              ax      (0,2)(1,1)(1,2)
+E      (a*){2}(x)              axa     (0,2)(1,1)(1,2)
diff --git a/tools/regression/lib/libc-regex/datafiles/repetition.dat b/tools/regression/lib/libc-regex/datafiles/repetition.dat
new file mode 100644 (file)
index 0000000..92944b5
--- /dev/null
@@ -0,0 +1,140 @@
+NOTE   implicit vs. explicit repetitions : 2009-02-02
+
+# Glenn Fowler <glenn.s.fowler@gmail.com>
+# conforming matches (column 4) must match one of the following BREs
+#      NOMATCH
+#      (0,.)\((\(.\),\(.\))(?,?)(\2,\3)\)*
+#      (0,.)\((\(.\),\(.\))(\2,\3)(?,?)\)*
+# i.e., each 3-tuple has two identical elements and one (?,?)
+
+E      ((..)|(.))                              NULL            NOMATCH
+E      ((..)|(.))((..)|(.))                    NULL            NOMATCH
+E      ((..)|(.))((..)|(.))((..)|(.))          NULL            NOMATCH
+
+E      ((..)|(.)){1}                           NULL            NOMATCH
+E      ((..)|(.)){2}                           NULL            NOMATCH
+E      ((..)|(.)){3}                           NULL            NOMATCH
+
+E      ((..)|(.))*                             NULL            (0,0)
+
+E      ((..)|(.))                              a               (0,1)(0,1)(?,?)(0,1)
+E      ((..)|(.))((..)|(.))                    a               NOMATCH
+E      ((..)|(.))((..)|(.))((..)|(.))          a               NOMATCH
+
+E      ((..)|(.)){1}                           a               (0,1)(0,1)(?,?)(0,1)
+E      ((..)|(.)){2}                           a               NOMATCH
+E      ((..)|(.)){3}                           a               NOMATCH
+
+E      ((..)|(.))*                             a               (0,1)(0,1)(?,?)(0,1)
+
+E      ((..)|(.))                              aa              (0,2)(0,2)(0,2)(?,?)
+E      ((..)|(.))((..)|(.))                    aa              (0,2)(0,1)(?,?)(0,1)(1,2)(?,?)(1,2)
+E      ((..)|(.))((..)|(.))((..)|(.))          aa              NOMATCH
+
+E      ((..)|(.)){1}                           aa              (0,2)(0,2)(0,2)(?,?)
+E      ((..)|(.)){2}                           aa              (0,2)(1,2)(?,?)(1,2)
+E      ((..)|(.)){3}                           aa              NOMATCH
+
+E      ((..)|(.))*                             aa              (0,2)(0,2)(0,2)(?,?)
+
+E      ((..)|(.))                              aaa             (0,2)(0,2)(0,2)(?,?)
+E      ((..)|(.))((..)|(.))                    aaa             (0,3)(0,2)(0,2)(?,?)(2,3)(?,?)(2,3)
+E      ((..)|(.))((..)|(.))((..)|(.))          aaa             (0,3)(0,1)(?,?)(0,1)(1,2)(?,?)(1,2)(2,3)(?,?)(2,3)
+
+E      ((..)|(.)){1}                           aaa             (0,2)(0,2)(0,2)(?,?)
+E      ((..)|(.)){2}                           aaa             (0,3)(2,3)(?,?)(2,3)
+E      ((..)|(.)){3}                           aaa             (0,3)(2,3)(?,?)(2,3)
+
+E      ((..)|(.))*                             aaa             (0,3)(2,3)(?,?)(2,3)
+
+E      ((..)|(.))                              aaaa            (0,2)(0,2)(0,2)(?,?)
+E      ((..)|(.))((..)|(.))                    aaaa            (0,4)(0,2)(0,2)(?,?)(2,4)(2,4)(?,?)
+E      ((..)|(.))((..)|(.))((..)|(.))          aaaa            (0,4)(0,2)(0,2)(?,?)(2,3)(?,?)(2,3)(3,4)(?,?)(3,4)
+
+E      ((..)|(.)){1}                           aaaa            (0,2)(0,2)(0,2)(?,?)
+E      ((..)|(.)){2}                           aaaa            (0,4)(2,4)(2,4)(?,?)
+E      ((..)|(.)){3}                           aaaa            (0,4)(3,4)(?,?)(3,4)
+
+E      ((..)|(.))*                             aaaa            (0,4)(2,4)(2,4)(?,?)
+
+E      ((..)|(.))                              aaaaa           (0,2)(0,2)(0,2)(?,?)
+E      ((..)|(.))((..)|(.))                    aaaaa           (0,4)(0,2)(0,2)(?,?)(2,4)(2,4)(?,?)
+E      ((..)|(.))((..)|(.))((..)|(.))          aaaaa           (0,5)(0,2)(0,2)(?,?)(2,4)(2,4)(?,?)(4,5)(?,?)(4,5)
+
+E      ((..)|(.)){1}                           aaaaa           (0,2)(0,2)(0,2)(?,?)
+E      ((..)|(.)){2}                           aaaaa           (0,4)(2,4)(2,4)(?,?)
+E      ((..)|(.)){3}                           aaaaa           (0,5)(4,5)(?,?)(4,5)
+
+E      ((..)|(.))*                             aaaaa           (0,5)(4,5)(?,?)(4,5)
+
+E      ((..)|(.))                              aaaaaa          (0,2)(0,2)(0,2)(?,?)
+E      ((..)|(.))((..)|(.))                    aaaaaa          (0,4)(0,2)(0,2)(?,?)(2,4)(2,4)(?,?)
+E      ((..)|(.))((..)|(.))((..)|(.))          aaaaaa          (0,6)(0,2)(0,2)(?,?)(2,4)(2,4)(?,?)(4,6)(4,6)(?,?)
+
+E      ((..)|(.)){1}                           aaaaaa          (0,2)(0,2)(0,2)(?,?)
+E      ((..)|(.)){2}                           aaaaaa          (0,4)(2,4)(2,4)(?,?)
+E      ((..)|(.)){3}                           aaaaaa          (0,6)(4,6)(4,6)(?,?)
+
+E      ((..)|(.))*                             aaaaaa          (0,6)(4,6)(4,6)(?,?)
+
+NOTE   additional repetition tests graciously provided by Chris Kuklewicz www.haskell.org 2009-02-02
+
+# These test a bug in OS X / FreeBSD / NetBSD, and libtree. 
+# Linux/GLIBC gets the {8,} and {8,8} wrong.
+
+:HA#100:E      X(.?){0,}Y      X1234567Y       (0,9)(7,8)
+:HA#101:E      X(.?){1,}Y      X1234567Y       (0,9)(7,8)
+:HA#102:E      X(.?){2,}Y      X1234567Y       (0,9)(7,8)
+:HA#103:E      X(.?){3,}Y      X1234567Y       (0,9)(7,8)
+:HA#104:E      X(.?){4,}Y      X1234567Y       (0,9)(7,8)
+:HA#105:E      X(.?){5,}Y      X1234567Y       (0,9)(7,8)
+:HA#106:E      X(.?){6,}Y      X1234567Y       (0,9)(7,8)
+:HA#107:E      X(.?){7,}Y      X1234567Y       (0,9)(7,8)
+:HA#108:E      X(.?){8,}Y      X1234567Y       (0,9)(8,8)
+:HA#110:E      X(.?){0,8}Y     X1234567Y       (0,9)(7,8)
+:HA#111:E      X(.?){1,8}Y     X1234567Y       (0,9)(7,8)
+:HA#112:E      X(.?){2,8}Y     X1234567Y       (0,9)(7,8)
+:HA#113:E      X(.?){3,8}Y     X1234567Y       (0,9)(7,8)
+:HA#114:E      X(.?){4,8}Y     X1234567Y       (0,9)(7,8)
+:HA#115:E      X(.?){5,8}Y     X1234567Y       (0,9)(7,8)
+:HA#116:E      X(.?){6,8}Y     X1234567Y       (0,9)(7,8)
+:HA#117:E      X(.?){7,8}Y     X1234567Y       (0,9)(7,8)
+:HA#118:E      X(.?){8,8}Y     X1234567Y       (0,9)(8,8)
+
+# These test a fixed bug in my regex-tdfa that did not keep the expanded
+# form properly grouped, so right association did the wrong thing with
+# these ambiguous patterns (crafted just to test my code when I became
+# suspicious of my implementation).  The first subexpression should use
+# "ab" then "a" then "bcd".
+
+# OS X / FreeBSD / NetBSD badly fail many of these, with impossible
+# results like (0,6)(4,5)(6,6).
+
+:HA#260:E      (a|ab|c|bcd){0,}(d*)    ababcd  (0,6)(3,6)(6,6)
+:HA#261:E      (a|ab|c|bcd){1,}(d*)    ababcd  (0,6)(3,6)(6,6)
+:HA#262:E      (a|ab|c|bcd){2,}(d*)    ababcd  (0,6)(3,6)(6,6)
+:HA#263:E      (a|ab|c|bcd){3,}(d*)    ababcd  (0,6)(3,6)(6,6)
+:HA#264:E      (a|ab|c|bcd){4,}(d*)    ababcd  NOMATCH
+:HA#265:E      (a|ab|c|bcd){0,10}(d*)  ababcd  (0,6)(3,6)(6,6)
+:HA#266:E      (a|ab|c|bcd){1,10}(d*)  ababcd  (0,6)(3,6)(6,6)
+:HA#267:E      (a|ab|c|bcd){2,10}(d*)  ababcd  (0,6)(3,6)(6,6)
+:HA#268:E      (a|ab|c|bcd){3,10}(d*)  ababcd  (0,6)(3,6)(6,6)
+:HA#269:E      (a|ab|c|bcd){4,10}(d*)  ababcd  NOMATCH
+:HA#270:E      (a|ab|c|bcd)*(d*)       ababcd  (0,6)(3,6)(6,6)
+:HA#271:E      (a|ab|c|bcd)+(d*)       ababcd  (0,6)(3,6)(6,6)
+
+# The above worked on Linux/GLIBC but the following often fail.
+# They also trip up OS X / FreeBSD / NetBSD:
+
+:HA#280:E      (ab|a|c|bcd){0,}(d*)    ababcd  (0,6)(3,6)(6,6)
+:HA#281:E      (ab|a|c|bcd){1,}(d*)    ababcd  (0,6)(3,6)(6,6)
+:HA#282:E      (ab|a|c|bcd){2,}(d*)    ababcd  (0,6)(3,6)(6,6)
+:HA#283:E      (ab|a|c|bcd){3,}(d*)    ababcd  (0,6)(3,6)(6,6)
+:HA#284:E      (ab|a|c|bcd){4,}(d*)    ababcd  NOMATCH
+:HA#285:E      (ab|a|c|bcd){0,10}(d*)  ababcd  (0,6)(3,6)(6,6)
+:HA#286:E      (ab|a|c|bcd){1,10}(d*)  ababcd  (0,6)(3,6)(6,6)
+:HA#287:E      (ab|a|c|bcd){2,10}(d*)  ababcd  (0,6)(3,6)(6,6)
+:HA#288:E      (ab|a|c|bcd){3,10}(d*)  ababcd  (0,6)(3,6)(6,6)
+:HA#289:E      (ab|a|c|bcd){4,10}(d*)  ababcd  NOMATCH
+:HA#290:E      (ab|a|c|bcd)*(d*)       ababcd  (0,6)(3,6)(6,6)
+:HA#291:E      (ab|a|c|bcd)+(d*)       ababcd  (0,6)(3,6)(6,6)
diff --git a/tools/regression/lib/libc-regex/datafiles/rightassoc.dat b/tools/regression/lib/libc-regex/datafiles/rightassoc.dat
new file mode 100644 (file)
index 0000000..ed7f28e
--- /dev/null
@@ -0,0 +1,16 @@
+NOTE   left-assoc:pass-none right-assoc:pass-all : 2002-04-29
+
+E      (a|ab)(c|bcd)(d*)       abcd    (0,4)(0,2)(2,3)(3,4)
+E      (a|ab)(bcd|c)(d*)       abcd    (0,4)(0,2)(2,3)(3,4)
+E      (ab|a)(c|bcd)(d*)       abcd    (0,4)(0,2)(2,3)(3,4)
+E      (ab|a)(bcd|c)(d*)       abcd    (0,4)(0,2)(2,3)(3,4)
+
+E      (a*)(b|abc)(c*)         abc     (0,3)(0,1)(1,2)(2,3)
+E      (a*)(abc|b)(c*)         abc     (0,3)(0,1)(1,2)(2,3)
+E      (a*)(b|abc)(c*)         abc     (0,3)(0,1)(1,2)(2,3)
+E      (a*)(abc|b)(c*)         abc     (0,3)(0,1)(1,2)(2,3)
+
+E      (a|ab)(c|bcd)(d|.*)     abcd    (0,4)(0,2)(2,3)(3,4)
+E      (a|ab)(bcd|c)(d|.*)     abcd    (0,4)(0,2)(2,3)(3,4)
+E      (ab|a)(c|bcd)(d|.*)     abcd    (0,4)(0,2)(2,3)(3,4)
+E      (ab|a)(bcd|c)(d|.*)     abcd    (0,4)(0,2)(2,3)(3,4)
diff --git a/tools/regression/lib/libc-regex/testregex.c b/tools/regression/lib/libc-regex/testregex.c
new file mode 100644 (file)
index 0000000..c893bba
--- /dev/null
@@ -0,0 +1,2286 @@
+#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 <glenn.s.fowler@gmail.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    glenn.s.fowler@gmail.com        (ksh strmatch, regex extensions)\n");
+T("  David Korn      dgkorn@gmail.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;
+}