lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame^] | 1 | /* |
| 2 | * regex(3) test harness |
| 3 | * |
| 4 | * build: cc -o testregex testregex.c |
| 5 | * help: testregex --man |
| 6 | * note: REG_* features are detected by #ifdef; if REG_* are enums |
| 7 | * then supply #define REG_foo REG_foo for each enum REG_foo |
| 8 | * |
| 9 | * Glenn Fowler <gsf@research.att.com> |
| 10 | * AT&T Labs Research |
| 11 | * |
| 12 | * PLEASE: publish your tests so everyone can benefit |
| 13 | * |
| 14 | * Permission is hereby granted, free of charge, to any person obtaining a |
| 15 | * copy of THIS SOFTWARE FILE (the "Software"), to deal in the Software |
| 16 | * without restriction, including without limitation the rights to use, |
| 17 | * copy, modify, merge, publish, distribute, and/or sell copies of the |
| 18 | * Software, and to permit persons to whom the Software is furnished to do |
| 19 | * so, subject to the following disclaimer: |
| 20 | * |
| 21 | * THIS SOFTWARE IS PROVIDED BY AT&T ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| 22 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| 23 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| 24 | * IN NO EVENT SHALL AT&T BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 31 | */ |
| 32 | |
| 33 | static const char id[] = "\n@(#)$Id: testregex (AT&T Research) 2005-05-20 $\0\n"; |
| 34 | |
| 35 | #if _PACKAGE_ast |
| 36 | #include <ast.h> |
| 37 | #else |
| 38 | #include <sys/types.h> |
| 39 | #endif |
| 40 | |
| 41 | #include <stdio.h> |
| 42 | #include <regex.h> |
| 43 | #include <ctype.h> |
| 44 | #include <setjmp.h> |
| 45 | #include <signal.h> |
| 46 | #include <string.h> |
| 47 | #include <unistd.h> |
| 48 | |
| 49 | #ifdef __STDC__ |
| 50 | #include <stdlib.h> |
| 51 | #include <locale.h> |
| 52 | #endif |
| 53 | |
| 54 | #if !_PACKAGE_ast |
| 55 | #undef REG_DISCIPLINE |
| 56 | #endif |
| 57 | |
| 58 | #ifndef REG_DELIMITED |
| 59 | #undef _REG_subcomp |
| 60 | #endif |
| 61 | |
| 62 | #define TEST_ARE 0x00000001 |
| 63 | #define TEST_BRE 0x00000002 |
| 64 | #define TEST_ERE 0x00000004 |
| 65 | #define TEST_KRE 0x00000008 |
| 66 | #define TEST_LRE 0x00000010 |
| 67 | #define TEST_SRE 0x00000020 |
| 68 | |
| 69 | #define TEST_EXPAND 0x00000040 |
| 70 | #define TEST_LENIENT 0x00000080 |
| 71 | |
| 72 | #define TEST_QUERY 0x00000100 |
| 73 | #define TEST_SUB 0x00000200 |
| 74 | #define TEST_UNSPECIFIED 0x00000400 |
| 75 | #define TEST_VERIFY 0x00000800 |
| 76 | #define TEST_AND 0x00001000 |
| 77 | #define TEST_OR 0x00002000 |
| 78 | |
| 79 | #define TEST_DELIMIT 0x00010000 |
| 80 | #define TEST_OK 0x00020000 |
| 81 | #define TEST_SAME 0x00040000 |
| 82 | |
| 83 | #define TEST_ACTUAL 0x00100000 |
| 84 | #define TEST_BASELINE 0x00200000 |
| 85 | #define TEST_FAIL 0x00400000 |
| 86 | #define TEST_PASS 0x00800000 |
| 87 | #define TEST_SUMMARY 0x01000000 |
| 88 | |
| 89 | #define TEST_IGNORE_ERROR 0x02000000 |
| 90 | #define TEST_IGNORE_OVER 0x04000000 |
| 91 | #define TEST_IGNORE_POSITION 0x08000000 |
| 92 | |
| 93 | #define TEST_CATCH 0x10000000 |
| 94 | #define TEST_VERBOSE 0x20000000 |
| 95 | |
| 96 | #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) |
| 97 | |
| 98 | #ifdef REG_DISCIPLINE |
| 99 | |
| 100 | |
| 101 | #include <stk.h> |
| 102 | |
| 103 | typedef struct Disc_s |
| 104 | { |
| 105 | regdisc_t disc; |
| 106 | int ordinal; |
| 107 | Sfio_t* sp; |
| 108 | } Disc_t; |
| 109 | |
| 110 | static void* |
| 111 | compf(const regex_t* re, const char* xstr, size_t xlen, regdisc_t* disc) |
| 112 | { |
| 113 | Disc_t* dp = (Disc_t*)disc; |
| 114 | |
| 115 | return (void*)++dp->ordinal; |
| 116 | } |
| 117 | |
| 118 | static int |
| 119 | execf(const regex_t* re, void* data, const char* xstr, size_t xlen, const char* sstr, size_t slen, char** snxt, regdisc_t* disc) |
| 120 | { |
| 121 | Disc_t* dp = (Disc_t*)disc; |
| 122 | |
| 123 | sfprintf(dp->sp, "{%-.*s}(%d:%d)", xlen, xstr, (int)data, slen); |
| 124 | return atoi(xstr); |
| 125 | } |
| 126 | |
| 127 | static void* |
| 128 | resizef(void* handle, void* data, size_t size) |
| 129 | { |
| 130 | if (!size) |
| 131 | return 0; |
| 132 | return stkalloc((Sfio_t*)handle, size); |
| 133 | } |
| 134 | |
| 135 | #endif |
| 136 | |
| 137 | #ifndef NiL |
| 138 | #ifdef __STDC__ |
| 139 | #define NiL 0 |
| 140 | #else |
| 141 | #define NiL (char*)0 |
| 142 | #endif |
| 143 | #endif |
| 144 | |
| 145 | #define H(x) do{if(html)fprintf(stderr,x);}while(0) |
| 146 | #define T(x) fprintf(stderr,x) |
| 147 | |
| 148 | static void |
| 149 | help(int html) |
| 150 | { |
| 151 | H("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n"); |
| 152 | H("<HTML>\n"); |
| 153 | H("<HEAD>\n"); |
| 154 | H("<TITLE>testregex man document</TITLE>\n"); |
| 155 | H("</HEAD>\n"); |
| 156 | H("<BODY bgcolor=white>\n"); |
| 157 | H("<PRE>\n"); |
| 158 | T("NAME\n"); |
| 159 | T(" testregex - regex(3) test harness\n"); |
| 160 | T("\n"); |
| 161 | T("SYNOPSIS\n"); |
| 162 | T(" testregex [ options ]\n"); |
| 163 | T("\n"); |
| 164 | T("DESCRIPTION\n"); |
| 165 | T(" testregex reads regex(3) test specifications, one per line, from the\n"); |
| 166 | T(" standard input and writes one output line for each failed test. A\n"); |
| 167 | T(" summary line is written after all tests are done. Each successful\n"); |
| 168 | T(" test is run again with REG_NOSUB. Unsupported features are noted\n"); |
| 169 | T(" before the first test, and tests requiring these features are\n"); |
| 170 | T(" silently ignored.\n"); |
| 171 | T("\n"); |
| 172 | T("OPTIONS\n"); |
| 173 | T(" -c catch signals and non-terminating calls\n"); |
| 174 | T(" -e ignore error return mismatches\n"); |
| 175 | T(" -h list help on standard error\n"); |
| 176 | T(" -n do not repeat successful tests with regnexec()\n"); |
| 177 | T(" -o ignore match[] overrun errors\n"); |
| 178 | T(" -p ignore negative position mismatches\n"); |
| 179 | T(" -s use stack instead of malloc\n"); |
| 180 | T(" -x do not repeat successful tests with REG_NOSUB\n"); |
| 181 | T(" -v list each test line\n"); |
| 182 | T(" -A list failed test lines with actual answers\n"); |
| 183 | T(" -B list all test lines with actual answers\n"); |
| 184 | T(" -F list failed test lines\n"); |
| 185 | T(" -P list passed test lines\n"); |
| 186 | T(" -S output one summary line\n"); |
| 187 | T("\n"); |
| 188 | T("INPUT FORMAT\n"); |
| 189 | T(" Input lines may be blank, a comment beginning with #, or a test\n"); |
| 190 | T(" specification. A specification is five fields separated by one\n"); |
| 191 | T(" or more tabs. NULL denotes the empty string and NIL denotes the\n"); |
| 192 | T(" 0 pointer.\n"); |
| 193 | T("\n"); |
| 194 | T(" Field 1: the regex(3) flags to apply, one character per REG_feature\n"); |
| 195 | T(" flag. The test is skipped if REG_feature is not supported by the\n"); |
| 196 | T(" implementation. If the first character is not [BEASKL] then the\n"); |
| 197 | T(" specification is a global control line. One or more of [BEASKL] may be\n"); |
| 198 | T(" specified; the test will be repeated for each mode.\n"); |
| 199 | T("\n"); |
| 200 | T(" B basic BRE (grep, ed, sed)\n"); |
| 201 | T(" E REG_EXTENDED ERE (egrep)\n"); |
| 202 | T(" A REG_AUGMENTED ARE (egrep with negation)\n"); |
| 203 | T(" S REG_SHELL SRE (sh glob)\n"); |
| 204 | T(" K REG_SHELL|REG_AUGMENTED KRE (ksh glob)\n"); |
| 205 | T(" L REG_LITERAL LRE (fgrep)\n"); |
| 206 | T("\n"); |
| 207 | T(" a REG_LEFT|REG_RIGHT implicit ^...$\n"); |
| 208 | T(" b REG_NOTBOL lhs does not match ^\n"); |
| 209 | T(" c REG_COMMENT ignore space and #...\\n\n"); |
| 210 | T(" d REG_SHELL_DOT explicit leading . match\n"); |
| 211 | T(" e REG_NOTEOL rhs does not match $\n"); |
| 212 | T(" f REG_MULTIPLE multiple \\n separated patterns\n"); |
| 213 | T(" g FNM_LEADING_DIR testfnmatch only -- match until /\n"); |
| 214 | T(" h REG_MULTIREF multiple digit backref\n"); |
| 215 | T(" i REG_ICASE ignore case\n"); |
| 216 | T(" j REG_SPAN . matches \\n\n"); |
| 217 | T(" k REG_ESCAPE \\ to ecape [...] delimiter\n"); |
| 218 | T(" l REG_LEFT implicit ^...\n"); |
| 219 | T(" m REG_MINIMAL minimal match\n"); |
| 220 | T(" n REG_NEWLINE explicit \\n match\n"); |
| 221 | T(" o REG_ENCLOSED (|&) magic inside [@|&](...)\n"); |
| 222 | T(" p REG_SHELL_PATH explicit / match\n"); |
| 223 | T(" q REG_DELIMITED delimited pattern\n"); |
| 224 | T(" r REG_RIGHT implicit ...$\n"); |
| 225 | T(" s REG_SHELL_ESCAPED \\ not special\n"); |
| 226 | T(" t REG_MUSTDELIM all delimiters must be specified\n"); |
| 227 | T(" u standard unspecified behavior -- errors not counted\n"); |
| 228 | T(" w REG_NOSUB no subexpression match array\n"); |
| 229 | T(" x REG_LENIENT let some errors slide\n"); |
| 230 | T(" y REG_LEFT regexec() implicit ^...\n"); |
| 231 | T(" z REG_NULL NULL subexpressions ok\n"); |
| 232 | T(" $ expand C \\c escapes in fields 2 and 3\n"); |
| 233 | T(" / field 2 is a regsubcomp() expression\n"); |
| 234 | T("\n"); |
| 235 | T(" Field 1 control lines:\n"); |
| 236 | T("\n"); |
| 237 | T(" C set LC_COLLATE and LC_CTYPE to locale in field 2\n"); |
| 238 | T("\n"); |
| 239 | T(" ?test ... output field 5 if passed and != EXPECTED, silent otherwise\n"); |
| 240 | T(" &test ... output field 5 if current and previous passed\n"); |
| 241 | T(" |test ... output field 5 if current passed and previous failed\n"); |
| 242 | T(" ; ... output field 2 if previous failed\n"); |
| 243 | T(" {test ... skip if failed until }\n"); |
| 244 | T(" } end of skip\n"); |
| 245 | T("\n"); |
| 246 | T(" : comment comment copied as output NOTE\n"); |
| 247 | T(" :comment:test :comment: ignored\n"); |
| 248 | T(" N[OTE] comment comment copied as output NOTE\n"); |
| 249 | T(" T[EST] comment comment\n"); |
| 250 | T("\n"); |
| 251 | T(" number use number for nmatch (20 by default)\n"); |
| 252 | T("\n"); |
| 253 | T(" Field 2: the regular expression pattern; SAME uses the pattern from\n"); |
| 254 | T(" the previous specification.\n"); |
| 255 | T("\n"); |
| 256 | T(" Field 3: the string to match.\n"); |
| 257 | T("\n"); |
| 258 | T(" Field 4: the test outcome. This is either one of the posix error\n"); |
| 259 | T(" codes (with REG_ omitted) or the match array, a list of (m,n)\n"); |
| 260 | T(" entries with m and n being first and last+1 positions in the\n"); |
| 261 | T(" field 3 string, or NULL if REG_NOSUB is in effect and success\n"); |
| 262 | T(" is expected. BADPAT is acceptable in place of any regcomp(3)\n"); |
| 263 | T(" error code. The match[] array is initialized to (-2,-2) before\n"); |
| 264 | T(" each test. All array elements from 0 to nmatch-1 must be specified\n"); |
| 265 | T(" in the outcome. Unspecified endpoints (offset -1) are denoted by ?.\n"); |
| 266 | T(" Unset endpoints (offset -2) are denoted by X. {x}(o:n) denotes a\n"); |
| 267 | T(" matched (?{...}) expression, where x is the text enclosed by {...},\n"); |
| 268 | T(" o is the expression ordinal counting from 1, and n is the length of\n"); |
| 269 | T(" the unmatched portion of the subject string. If x starts with a\n"); |
| 270 | T(" number then that is the return value of re_execf(), otherwise 0 is\n"); |
| 271 | T(" returned.\n"); |
| 272 | T("\n"); |
| 273 | T(" Field 5: optional comment appended to the report.\n"); |
| 274 | T("\n"); |
| 275 | T("CAVEAT\n"); |
| 276 | T(" If a regex implementation misbehaves with memory then all bets are off.\n"); |
| 277 | T("\n"); |
| 278 | T("CONTRIBUTORS\n"); |
| 279 | T(" Glenn Fowler gsf@research.att.com (ksh strmatch, regex extensions)\n"); |
| 280 | T(" David Korn dgk@research.att.com (ksh glob matcher)\n"); |
| 281 | T(" Doug McIlroy mcilroy@dartmouth.edu (ast regex/testre in C++)\n"); |
| 282 | T(" Tom Lord lord@regexps.com (rx tests)\n"); |
| 283 | T(" Henry Spencer henry@zoo.toronto.edu (original public regex)\n"); |
| 284 | T(" Andrew Hume andrew@research.att.com (gre tests)\n"); |
| 285 | T(" John Maddock John_Maddock@compuserve.com (regex++ tests)\n"); |
| 286 | T(" Philip Hazel ph10@cam.ac.uk (pcre tests)\n"); |
| 287 | T(" Ville Laurikari vl@iki.fi (libtre tests)\n"); |
| 288 | H("</PRE>\n"); |
| 289 | H("</BODY>\n"); |
| 290 | H("</HTML>\n"); |
| 291 | } |
| 292 | |
| 293 | #ifndef elementsof |
| 294 | #define elementsof(x) (sizeof(x)/sizeof(x[0])) |
| 295 | #endif |
| 296 | |
| 297 | #ifndef streq |
| 298 | #define streq(a,b) (*(a)==*(b)&&!strcmp(a,b)) |
| 299 | #endif |
| 300 | |
| 301 | #define HUNG 5 |
| 302 | #define NOTEST (~0) |
| 303 | |
| 304 | #ifndef REG_TEST_DEFAULT |
| 305 | #define REG_TEST_DEFAULT 0 |
| 306 | #endif |
| 307 | |
| 308 | #ifndef REG_EXEC_DEFAULT |
| 309 | #define REG_EXEC_DEFAULT 0 |
| 310 | #endif |
| 311 | |
| 312 | static const char* unsupported[] = |
| 313 | { |
| 314 | "BASIC", |
| 315 | #ifndef REG_EXTENDED |
| 316 | "EXTENDED", |
| 317 | #endif |
| 318 | #ifndef REG_AUGMENTED |
| 319 | "AUGMENTED", |
| 320 | #endif |
| 321 | #ifndef REG_SHELL |
| 322 | "SHELL", |
| 323 | #endif |
| 324 | |
| 325 | #ifndef REG_COMMENT |
| 326 | "COMMENT", |
| 327 | #endif |
| 328 | #ifndef REG_DELIMITED |
| 329 | "DELIMITED", |
| 330 | #endif |
| 331 | #ifndef REG_DISCIPLINE |
| 332 | "DISCIPLINE", |
| 333 | #endif |
| 334 | #ifndef REG_ESCAPE |
| 335 | "ESCAPE", |
| 336 | #endif |
| 337 | #ifndef REG_ICASE |
| 338 | "ICASE", |
| 339 | #endif |
| 340 | #ifndef REG_LEFT |
| 341 | "LEFT", |
| 342 | #endif |
| 343 | #ifndef REG_LENIENT |
| 344 | "LENIENT", |
| 345 | #endif |
| 346 | #ifndef REG_LITERAL |
| 347 | "LITERAL", |
| 348 | #endif |
| 349 | #ifndef REG_MINIMAL |
| 350 | "MINIMAL", |
| 351 | #endif |
| 352 | #ifndef REG_MULTIPLE |
| 353 | "MULTIPLE", |
| 354 | #endif |
| 355 | #ifndef REG_MULTIREF |
| 356 | "MULTIREF", |
| 357 | #endif |
| 358 | #ifndef REG_MUSTDELIM |
| 359 | "MUSTDELIM", |
| 360 | #endif |
| 361 | #ifndef REG_NEWLINE |
| 362 | "NEWLINE", |
| 363 | #endif |
| 364 | #ifndef REG_NOTBOL |
| 365 | "NOTBOL", |
| 366 | #endif |
| 367 | #ifndef REG_NOTEOL |
| 368 | "NOTEOL", |
| 369 | #endif |
| 370 | #ifndef REG_NULL |
| 371 | "NULL", |
| 372 | #endif |
| 373 | #ifndef REG_RIGHT |
| 374 | "RIGHT", |
| 375 | #endif |
| 376 | #ifndef REG_SHELL_DOT |
| 377 | "SHELL_DOT", |
| 378 | #endif |
| 379 | #ifndef REG_SHELL_ESCAPED |
| 380 | "SHELL_ESCAPED", |
| 381 | #endif |
| 382 | #ifndef REG_SHELL_GROUP |
| 383 | "SHELL_GROUP", |
| 384 | #endif |
| 385 | #ifndef REG_SHELL_PATH |
| 386 | "SHELL_PATH", |
| 387 | #endif |
| 388 | #ifndef REG_SPAN |
| 389 | "SPAN", |
| 390 | #endif |
| 391 | #if REG_NOSUB & REG_TEST_DEFAULT |
| 392 | "SUBMATCH", |
| 393 | #endif |
| 394 | #if !_REG_nexec |
| 395 | "regnexec", |
| 396 | #endif |
| 397 | #if !_REG_subcomp |
| 398 | "regsubcomp", |
| 399 | #endif |
| 400 | 0 |
| 401 | }; |
| 402 | |
| 403 | #ifndef REG_COMMENT |
| 404 | #define REG_COMMENT NOTEST |
| 405 | #endif |
| 406 | #ifndef REG_DELIMITED |
| 407 | #define REG_DELIMITED NOTEST |
| 408 | #endif |
| 409 | #ifndef REG_ESCAPE |
| 410 | #define REG_ESCAPE NOTEST |
| 411 | #endif |
| 412 | #ifndef REG_ICASE |
| 413 | #define REG_ICASE NOTEST |
| 414 | #endif |
| 415 | #ifndef REG_LEFT |
| 416 | #define REG_LEFT NOTEST |
| 417 | #endif |
| 418 | #ifndef REG_LENIENT |
| 419 | #define REG_LENIENT 0 |
| 420 | #endif |
| 421 | #ifndef REG_MINIMAL |
| 422 | #define REG_MINIMAL NOTEST |
| 423 | #endif |
| 424 | #ifndef REG_MULTIPLE |
| 425 | #define REG_MULTIPLE NOTEST |
| 426 | #endif |
| 427 | #ifndef REG_MULTIREF |
| 428 | #define REG_MULTIREF NOTEST |
| 429 | #endif |
| 430 | #ifndef REG_MUSTDELIM |
| 431 | #define REG_MUSTDELIM NOTEST |
| 432 | #endif |
| 433 | #ifndef REG_NEWLINE |
| 434 | #define REG_NEWLINE NOTEST |
| 435 | #endif |
| 436 | #ifndef REG_NOTBOL |
| 437 | #define REG_NOTBOL NOTEST |
| 438 | #endif |
| 439 | #ifndef REG_NOTEOL |
| 440 | #define REG_NOTEOL NOTEST |
| 441 | #endif |
| 442 | #ifndef REG_NULL |
| 443 | #define REG_NULL NOTEST |
| 444 | #endif |
| 445 | #ifndef REG_RIGHT |
| 446 | #define REG_RIGHT NOTEST |
| 447 | #endif |
| 448 | #ifndef REG_SHELL_DOT |
| 449 | #define REG_SHELL_DOT NOTEST |
| 450 | #endif |
| 451 | #ifndef REG_SHELL_ESCAPED |
| 452 | #define REG_SHELL_ESCAPED NOTEST |
| 453 | #endif |
| 454 | #ifndef REG_SHELL_GROUP |
| 455 | #define REG_SHELL_GROUP NOTEST |
| 456 | #endif |
| 457 | #ifndef REG_SHELL_PATH |
| 458 | #define REG_SHELL_PATH NOTEST |
| 459 | #endif |
| 460 | #ifndef REG_SPAN |
| 461 | #define REG_SPAN NOTEST |
| 462 | #endif |
| 463 | |
| 464 | #define REG_UNKNOWN (-1) |
| 465 | |
| 466 | #ifndef REG_ENEWLINE |
| 467 | #define REG_ENEWLINE (REG_UNKNOWN-1) |
| 468 | #endif |
| 469 | #ifndef REG_ENULL |
| 470 | #ifndef REG_EMPTY |
| 471 | #define REG_ENULL (REG_UNKNOWN-2) |
| 472 | #else |
| 473 | #define REG_ENULL REG_EMPTY |
| 474 | #endif |
| 475 | #endif |
| 476 | #ifndef REG_ECOUNT |
| 477 | #define REG_ECOUNT (REG_UNKNOWN-3) |
| 478 | #endif |
| 479 | #ifndef REG_BADESC |
| 480 | #define REG_BADESC (REG_UNKNOWN-4) |
| 481 | #endif |
| 482 | #ifndef REG_EMEM |
| 483 | #define REG_EMEM (REG_UNKNOWN-5) |
| 484 | #endif |
| 485 | #ifndef REG_EHUNG |
| 486 | #define REG_EHUNG (REG_UNKNOWN-6) |
| 487 | #endif |
| 488 | #ifndef REG_EBUS |
| 489 | #define REG_EBUS (REG_UNKNOWN-7) |
| 490 | #endif |
| 491 | #ifndef REG_EFAULT |
| 492 | #define REG_EFAULT (REG_UNKNOWN-8) |
| 493 | #endif |
| 494 | #ifndef REG_EFLAGS |
| 495 | #define REG_EFLAGS (REG_UNKNOWN-9) |
| 496 | #endif |
| 497 | #ifndef REG_EDELIM |
| 498 | #define REG_EDELIM (REG_UNKNOWN-9) |
| 499 | #endif |
| 500 | |
| 501 | static const struct { int code; char* name; } codes[] = |
| 502 | { |
| 503 | {REG_UNKNOWN, "UNKNOWN"}, |
| 504 | {REG_NOMATCH, "NOMATCH"}, |
| 505 | {REG_BADPAT, "BADPAT"}, |
| 506 | {REG_ECOLLATE, "ECOLLATE"}, |
| 507 | {REG_ECTYPE, "ECTYPE"}, |
| 508 | {REG_EESCAPE, "EESCAPE"}, |
| 509 | {REG_ESUBREG, "ESUBREG"}, |
| 510 | {REG_EBRACK, "EBRACK"}, |
| 511 | {REG_EPAREN, "EPAREN"}, |
| 512 | {REG_EBRACE, "EBRACE"}, |
| 513 | {REG_BADBR, "BADBR"}, |
| 514 | {REG_ERANGE, "ERANGE"}, |
| 515 | {REG_ESPACE, "ESPACE"}, |
| 516 | {REG_BADRPT, "BADRPT"}, |
| 517 | {REG_ENEWLINE, "ENEWLINE"}, |
| 518 | {REG_ENULL, "ENULL"}, |
| 519 | {REG_ECOUNT, "ECOUNT"}, |
| 520 | {REG_BADESC, "BADESC"}, |
| 521 | {REG_EMEM, "EMEM"}, |
| 522 | {REG_EHUNG, "EHUNG"}, |
| 523 | {REG_EBUS, "EBUS"}, |
| 524 | {REG_EFAULT, "EFAULT"}, |
| 525 | {REG_EFLAGS, "EFLAGS"}, |
| 526 | {REG_EDELIM, "EDELIM"}, |
| 527 | }; |
| 528 | |
| 529 | static struct |
| 530 | { |
| 531 | regmatch_t NOMATCH; |
| 532 | int errors; |
| 533 | int extracted; |
| 534 | int ignored; |
| 535 | int lineno; |
| 536 | int passed; |
| 537 | int signals; |
| 538 | int unspecified; |
| 539 | int verify; |
| 540 | int warnings; |
| 541 | char* file; |
| 542 | char* stack; |
| 543 | char* which; |
| 544 | jmp_buf gotcha; |
| 545 | #ifdef REG_DISCIPLINE |
| 546 | Disc_t disc; |
| 547 | #endif |
| 548 | } state; |
| 549 | |
| 550 | static void |
| 551 | quote(char* s, int len, unsigned long test) |
| 552 | { |
| 553 | unsigned char* u = (unsigned char*)s; |
| 554 | unsigned char* e; |
| 555 | int c; |
| 556 | |
| 557 | if (!u) |
| 558 | printf("NIL"); |
| 559 | else if (!*u && len <= 1) |
| 560 | printf("NULL"); |
| 561 | else if (test & TEST_EXPAND) |
| 562 | { |
| 563 | if (len < 0) |
| 564 | len = strlen((char*)u); |
| 565 | e = u + len; |
| 566 | if (test & TEST_DELIMIT) |
| 567 | printf("\""); |
| 568 | while (u < e) |
| 569 | switch (c = *u++) |
| 570 | { |
| 571 | case '\\': |
| 572 | printf("\\\\"); |
| 573 | break; |
| 574 | case '"': |
| 575 | if (test & TEST_DELIMIT) |
| 576 | printf("\\\""); |
| 577 | else |
| 578 | printf("\""); |
| 579 | break; |
| 580 | case '\a': |
| 581 | printf("\\a"); |
| 582 | break; |
| 583 | case '\b': |
| 584 | printf("\\b"); |
| 585 | break; |
| 586 | case 033: |
| 587 | printf("\\e"); |
| 588 | break; |
| 589 | case '\f': |
| 590 | printf("\\f"); |
| 591 | break; |
| 592 | case '\n': |
| 593 | printf("\\n"); |
| 594 | break; |
| 595 | case '\r': |
| 596 | printf("\\r"); |
| 597 | break; |
| 598 | case '\t': |
| 599 | printf("\\t"); |
| 600 | break; |
| 601 | case '\v': |
| 602 | printf("\\v"); |
| 603 | break; |
| 604 | default: |
| 605 | if (!iscntrl(c) && isprint(c)) |
| 606 | putchar(c); |
| 607 | else |
| 608 | printf("\\x%02x", c); |
| 609 | break; |
| 610 | } |
| 611 | if (test & TEST_DELIMIT) |
| 612 | printf("\""); |
| 613 | } |
| 614 | else |
| 615 | printf("%s", s); |
| 616 | } |
| 617 | |
| 618 | static void |
| 619 | report(char* comment, char* fun, char* re, char* s, int len, char* msg, int flags, unsigned long test) |
| 620 | { |
| 621 | if (state.file) |
| 622 | printf("%s:", state.file); |
| 623 | printf("%d:", state.lineno); |
| 624 | if (re) |
| 625 | { |
| 626 | printf(" "); |
| 627 | quote(re, -1, test|TEST_DELIMIT); |
| 628 | if (s) |
| 629 | { |
| 630 | printf(" versus "); |
| 631 | quote(s, len, test|TEST_DELIMIT); |
| 632 | } |
| 633 | } |
| 634 | if (test & TEST_UNSPECIFIED) |
| 635 | { |
| 636 | state.unspecified++; |
| 637 | printf(" unspecified behavior"); |
| 638 | } |
| 639 | else |
| 640 | state.errors++; |
| 641 | if (state.which) |
| 642 | printf(" %s", state.which); |
| 643 | if (flags & REG_NOSUB) |
| 644 | printf(" NOSUB"); |
| 645 | if (fun) |
| 646 | printf(" %s", fun); |
| 647 | if (comment[strlen(comment)-1] == '\n') |
| 648 | printf(" %s", comment); |
| 649 | else |
| 650 | { |
| 651 | printf(" %s: ", comment); |
| 652 | if (msg) |
| 653 | printf("%s: ", msg); |
| 654 | } |
| 655 | } |
| 656 | |
| 657 | static void |
| 658 | error(regex_t* preg, int code) |
| 659 | { |
| 660 | char* msg; |
| 661 | char buf[256]; |
| 662 | |
| 663 | switch (code) |
| 664 | { |
| 665 | case REG_EBUS: |
| 666 | msg = "bus error"; |
| 667 | break; |
| 668 | case REG_EFAULT: |
| 669 | msg = "memory fault"; |
| 670 | break; |
| 671 | case REG_EHUNG: |
| 672 | msg = "did not terminate"; |
| 673 | break; |
| 674 | default: |
| 675 | regerror(code, preg, msg = buf, sizeof buf); |
| 676 | break; |
| 677 | } |
| 678 | printf("%s\n", msg); |
| 679 | } |
| 680 | |
| 681 | static void |
| 682 | bad(char* comment, char* re, char* s, int len, unsigned long test) |
| 683 | { |
| 684 | printf("bad test case "); |
| 685 | report(comment, NiL, re, s, len, NiL, 0, test); |
| 686 | exit(1); |
| 687 | } |
| 688 | |
| 689 | static int |
| 690 | escape(char* s) |
| 691 | { |
| 692 | char* b; |
| 693 | char* t; |
| 694 | char* q; |
| 695 | char* e; |
| 696 | int c; |
| 697 | |
| 698 | for (b = t = s; (*t = *s); s++, t++) |
| 699 | if (*s == '\\') |
| 700 | switch (*++s) |
| 701 | { |
| 702 | case '\\': |
| 703 | break; |
| 704 | case 'a': |
| 705 | *t = '\a'; |
| 706 | break; |
| 707 | case 'b': |
| 708 | *t = '\b'; |
| 709 | break; |
| 710 | case 'c': |
| 711 | if ((*t = *++s)) |
| 712 | *t &= 037; |
| 713 | else |
| 714 | s--; |
| 715 | break; |
| 716 | case 'e': |
| 717 | case 'E': |
| 718 | *t = 033; |
| 719 | break; |
| 720 | case 'f': |
| 721 | *t = '\f'; |
| 722 | break; |
| 723 | case 'n': |
| 724 | *t = '\n'; |
| 725 | break; |
| 726 | case 'r': |
| 727 | *t = '\r'; |
| 728 | break; |
| 729 | case 's': |
| 730 | *t = ' '; |
| 731 | break; |
| 732 | case 't': |
| 733 | *t = '\t'; |
| 734 | break; |
| 735 | case 'v': |
| 736 | *t = '\v'; |
| 737 | break; |
| 738 | case 'u': |
| 739 | case 'x': |
| 740 | c = 0; |
| 741 | q = c == 'u' ? (s + 5) : (char*)0; |
| 742 | e = s + 1; |
| 743 | while (!e || !q || s < q) |
| 744 | { |
| 745 | switch (*++s) |
| 746 | { |
| 747 | case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': |
| 748 | c = (c << 4) + *s - 'a' + 10; |
| 749 | continue; |
| 750 | case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': |
| 751 | c = (c << 4) + *s - 'A' + 10; |
| 752 | continue; |
| 753 | case '0': case '1': case '2': case '3': case '4': |
| 754 | case '5': case '6': case '7': case '8': case '9': |
| 755 | c = (c << 4) + *s - '0'; |
| 756 | continue; |
| 757 | case '{': |
| 758 | case '[': |
| 759 | if (s != e) |
| 760 | { |
| 761 | s--; |
| 762 | break; |
| 763 | } |
| 764 | e = 0; |
| 765 | continue; |
| 766 | case '}': |
| 767 | case ']': |
| 768 | if (e) |
| 769 | s--; |
| 770 | break; |
| 771 | default: |
| 772 | s--; |
| 773 | break; |
| 774 | } |
| 775 | break; |
| 776 | } |
| 777 | *t = c; |
| 778 | break; |
| 779 | case '0': case '1': case '2': case '3': |
| 780 | case '4': case '5': case '6': case '7': |
| 781 | c = *s - '0'; |
| 782 | q = s + 2; |
| 783 | while (s < q) |
| 784 | { |
| 785 | switch (*++s) |
| 786 | { |
| 787 | case '0': case '1': case '2': case '3': |
| 788 | case '4': case '5': case '6': case '7': |
| 789 | c = (c << 3) + *s - '0'; |
| 790 | break; |
| 791 | default: |
| 792 | q = --s; |
| 793 | break; |
| 794 | } |
| 795 | } |
| 796 | *t = c; |
| 797 | break; |
| 798 | default: |
| 799 | *(s + 1) = 0; |
| 800 | bad("invalid C \\ escape\n", s - 1, NiL, 0, 0); |
| 801 | } |
| 802 | return t - b; |
| 803 | } |
| 804 | |
| 805 | static void |
| 806 | matchoffprint(int off) |
| 807 | { |
| 808 | switch (off) |
| 809 | { |
| 810 | case -2: |
| 811 | printf("X"); |
| 812 | break; |
| 813 | case -1: |
| 814 | printf("?"); |
| 815 | break; |
| 816 | default: |
| 817 | printf("%d", off); |
| 818 | break; |
| 819 | } |
| 820 | } |
| 821 | |
| 822 | static void |
| 823 | matchprint(regmatch_t* match, int nmatch, int nsub, char* ans, unsigned long test) |
| 824 | { |
| 825 | int i; |
| 826 | |
| 827 | for (; nmatch > nsub + 1; nmatch--) |
| 828 | 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))) |
| 829 | break; |
| 830 | for (i = 0; i < nmatch; i++) |
| 831 | { |
| 832 | printf("("); |
| 833 | matchoffprint(match[i].rm_so); |
| 834 | printf(","); |
| 835 | matchoffprint(match[i].rm_eo); |
| 836 | printf(")"); |
| 837 | } |
| 838 | if (!(test & (TEST_ACTUAL|TEST_BASELINE))) |
| 839 | { |
| 840 | if (ans) |
| 841 | printf(" expected: %s", ans); |
| 842 | printf("\n"); |
| 843 | } |
| 844 | } |
| 845 | |
| 846 | static int |
| 847 | matchcheck(regmatch_t* match, int nmatch, int nsub, char* ans, char* re, char* s, int len, int flags, unsigned long test) |
| 848 | { |
| 849 | char* p; |
| 850 | int i; |
| 851 | int m; |
| 852 | int n; |
| 853 | |
| 854 | if (streq(ans, "OK")) |
| 855 | return test & (TEST_BASELINE|TEST_PASS|TEST_VERIFY); |
| 856 | for (i = 0, p = ans; i < nmatch && *p; i++) |
| 857 | { |
| 858 | if (*p == '{') |
| 859 | { |
| 860 | #ifdef REG_DISCIPLINE |
| 861 | char* x; |
| 862 | |
| 863 | x = sfstruse(state.disc.sp); |
| 864 | if (strcmp(p, x)) |
| 865 | { |
| 866 | if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) |
| 867 | return 0; |
| 868 | report("callout failed", NiL, re, s, len, NiL, flags, test); |
| 869 | quote(p, -1, test); |
| 870 | printf(" expected, "); |
| 871 | quote(x, -1, test); |
| 872 | printf(" returned\n"); |
| 873 | } |
| 874 | #endif |
| 875 | break; |
| 876 | } |
| 877 | if (*p++ != '(') |
| 878 | bad("improper answer\n", re, s, -1, test); |
| 879 | if (*p == '?') |
| 880 | { |
| 881 | m = -1; |
| 882 | p++; |
| 883 | } |
| 884 | else |
| 885 | m = strtol(p, &p, 10); |
| 886 | if (*p++ != ',') |
| 887 | bad("improper answer\n", re, s, -1, test); |
| 888 | if (*p == '?') |
| 889 | { |
| 890 | n = -1; |
| 891 | p++; |
| 892 | } |
| 893 | else |
| 894 | n = strtol(p, &p, 10); |
| 895 | if (*p++ != ')') |
| 896 | bad("improper answer\n", re, s, -1, test); |
| 897 | if (m!=match[i].rm_so || n!=match[i].rm_eo) |
| 898 | { |
| 899 | if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY))) |
| 900 | { |
| 901 | report("failed: match was", NiL, re, s, len, NiL, flags, test); |
| 902 | matchprint(match, nmatch, nsub, ans, test); |
| 903 | } |
| 904 | return 0; |
| 905 | } |
| 906 | } |
| 907 | for (; i < nmatch; i++) |
| 908 | { |
| 909 | if (match[i].rm_so!=-1 || match[i].rm_eo!=-1) |
| 910 | { |
| 911 | if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_VERIFY))) |
| 912 | { |
| 913 | if ((test & TEST_IGNORE_POSITION) && (match[i].rm_so<0 || match[i].rm_eo<0)) |
| 914 | { |
| 915 | state.ignored++; |
| 916 | return 0; |
| 917 | } |
| 918 | if (!(test & TEST_SUMMARY)) |
| 919 | { |
| 920 | report("failed: match was", NiL, re, s, len, NiL, flags, test); |
| 921 | matchprint(match, nmatch, nsub, ans, test); |
| 922 | } |
| 923 | } |
| 924 | return 0; |
| 925 | } |
| 926 | } |
| 927 | if (!(test & TEST_IGNORE_OVER) && match[nmatch].rm_so != state.NOMATCH.rm_so) |
| 928 | { |
| 929 | if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY))) |
| 930 | { |
| 931 | report("failed: overran match array", NiL, re, s, len, NiL, flags, test); |
| 932 | matchprint(match, nmatch + 1, nsub, NiL, test); |
| 933 | } |
| 934 | return 0; |
| 935 | } |
| 936 | return 1; |
| 937 | } |
| 938 | |
| 939 | static void |
| 940 | sigunblock(int s) |
| 941 | { |
| 942 | #ifdef SIG_SETMASK |
| 943 | int op; |
| 944 | sigset_t mask; |
| 945 | |
| 946 | sigemptyset(&mask); |
| 947 | if (s) |
| 948 | { |
| 949 | sigaddset(&mask, s); |
| 950 | op = SIG_UNBLOCK; |
| 951 | } |
| 952 | else op = SIG_SETMASK; |
| 953 | sigprocmask(op, &mask, NiL); |
| 954 | #else |
| 955 | #ifdef sigmask |
| 956 | sigsetmask(s ? (sigsetmask(0L) & ~sigmask(s)) : 0L); |
| 957 | #endif |
| 958 | #endif |
| 959 | } |
| 960 | |
| 961 | static void |
| 962 | gotcha(int sig) |
| 963 | { |
| 964 | int ret; |
| 965 | |
| 966 | signal(sig, gotcha); |
| 967 | alarm(0); |
| 968 | state.signals++; |
| 969 | switch (sig) |
| 970 | { |
| 971 | case SIGALRM: |
| 972 | ret = REG_EHUNG; |
| 973 | break; |
| 974 | case SIGBUS: |
| 975 | ret = REG_EBUS; |
| 976 | break; |
| 977 | default: |
| 978 | ret = REG_EFAULT; |
| 979 | break; |
| 980 | } |
| 981 | sigunblock(sig); |
| 982 | longjmp(state.gotcha, ret); |
| 983 | } |
| 984 | |
| 985 | static char* |
| 986 | my_getline(FILE* fp) |
| 987 | { |
| 988 | static char buf[32 * 1024]; |
| 989 | |
| 990 | register char* s = buf; |
| 991 | register char* e = &buf[sizeof(buf)]; |
| 992 | register char* b; |
| 993 | |
| 994 | for (;;) |
| 995 | { |
| 996 | if (!(b = fgets(s, e - s, fp))) |
| 997 | return 0; |
| 998 | state.lineno++; |
| 999 | s += strlen(s); |
| 1000 | if (s == b || *--s != '\n' || s == b || *(s - 1) != '\\') |
| 1001 | { |
| 1002 | *s = 0; |
| 1003 | break; |
| 1004 | } |
| 1005 | s--; |
| 1006 | } |
| 1007 | return buf; |
| 1008 | } |
| 1009 | |
| 1010 | static unsigned long |
| 1011 | note(unsigned long level, char* msg, unsigned long skip, unsigned long test) |
| 1012 | { |
| 1013 | if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY)) && !skip) |
| 1014 | { |
| 1015 | printf("NOTE\t"); |
| 1016 | if (msg) |
| 1017 | printf("%s: ", msg); |
| 1018 | printf("skipping lines %d", state.lineno); |
| 1019 | } |
| 1020 | return skip | level; |
| 1021 | } |
| 1022 | |
| 1023 | #define TABS(n) &ts[7-((n)&7)] |
| 1024 | |
| 1025 | static char ts[] = "\t\t\t\t\t\t\t"; |
| 1026 | |
| 1027 | static unsigned long |
| 1028 | 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) |
| 1029 | { |
| 1030 | if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_OK|TEST_PASS|TEST_SUMMARY)) |
| 1031 | { |
| 1032 | state.extracted = 1; |
| 1033 | if (test & TEST_OK) |
| 1034 | { |
| 1035 | state.passed++; |
| 1036 | if ((test & TEST_VERIFY) && !(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) |
| 1037 | { |
| 1038 | if (msg && strcmp(msg, "EXPECTED")) |
| 1039 | printf("NOTE\t%s\n", msg); |
| 1040 | return skip; |
| 1041 | } |
| 1042 | test &= ~(TEST_PASS|TEST_QUERY); |
| 1043 | } |
| 1044 | if (test & (TEST_QUERY|TEST_VERIFY)) |
| 1045 | { |
| 1046 | if (test & TEST_BASELINE) |
| 1047 | test &= ~(TEST_BASELINE|TEST_PASS); |
| 1048 | else |
| 1049 | test |= TEST_PASS; |
| 1050 | skip |= level; |
| 1051 | } |
| 1052 | if (!(test & TEST_OK)) |
| 1053 | { |
| 1054 | if (test & TEST_UNSPECIFIED) |
| 1055 | state.unspecified++; |
| 1056 | else |
| 1057 | state.errors++; |
| 1058 | } |
| 1059 | if (test & (TEST_PASS|TEST_SUMMARY)) |
| 1060 | return skip; |
| 1061 | test &= ~TEST_DELIMIT; |
| 1062 | printf("%s%s", spec, TABS(*tabs++)); |
| 1063 | if ((test & (TEST_BASELINE|TEST_SAME)) == (TEST_BASELINE|TEST_SAME)) |
| 1064 | printf("SAME"); |
| 1065 | else |
| 1066 | quote(re, -1, test); |
| 1067 | printf("%s", TABS(*tabs++)); |
| 1068 | quote(s, -1, test); |
| 1069 | printf("%s", TABS(*tabs++)); |
| 1070 | if (!(test & (TEST_ACTUAL|TEST_BASELINE)) || (!accept && !match)) |
| 1071 | printf("%s", ans); |
| 1072 | else if (accept) |
| 1073 | printf("%s", accept); |
| 1074 | else |
| 1075 | matchprint(match, nmatch, nsub, NiL, test); |
| 1076 | if (msg) |
| 1077 | printf("%s%s", TABS(*tabs++), msg); |
| 1078 | putchar('\n'); |
| 1079 | } |
| 1080 | else if (test & TEST_QUERY) |
| 1081 | skip = note(level, msg, skip, test); |
| 1082 | else if (test & TEST_VERIFY) |
| 1083 | state.extracted = 1; |
| 1084 | return skip; |
| 1085 | } |
| 1086 | |
| 1087 | static int |
| 1088 | 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) |
| 1089 | { |
| 1090 | int eret; |
| 1091 | |
| 1092 | if (!(test & TEST_CATCH)) |
| 1093 | { |
| 1094 | regfree(preg); |
| 1095 | eret = 0; |
| 1096 | } |
| 1097 | else if (!(eret = setjmp(state.gotcha))) |
| 1098 | { |
| 1099 | alarm(HUNG); |
| 1100 | regfree(preg); |
| 1101 | alarm(0); |
| 1102 | } |
| 1103 | else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) |
| 1104 | extract(tabs, spec, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); |
| 1105 | else |
| 1106 | { |
| 1107 | report("failed", "regfree", re, NiL, -1, msg, flags, test); |
| 1108 | error(preg, eret); |
| 1109 | } |
| 1110 | return eret; |
| 1111 | } |
| 1112 | |
| 1113 | int |
| 1114 | old_main(int unused_param_argc, char** argv) |
| 1115 | { |
| 1116 | int flags; |
| 1117 | int cflags; |
| 1118 | int eflags; |
| 1119 | int nmatch; |
| 1120 | int nexec; |
| 1121 | int nstr; |
| 1122 | int cret; |
| 1123 | int eret; |
| 1124 | int nsub; |
| 1125 | int i; |
| 1126 | int j; |
| 1127 | int expected; |
| 1128 | int got; |
| 1129 | int locale; |
| 1130 | int subunitlen = 0; |
| 1131 | int testno; |
| 1132 | unsigned long level; |
| 1133 | unsigned long skip; |
| 1134 | char* p; |
| 1135 | char* line; |
| 1136 | char* spec; |
| 1137 | char* re; |
| 1138 | char* s; |
| 1139 | char* ans; |
| 1140 | char* msg; |
| 1141 | char* fun; |
| 1142 | char* ppat = NULL; |
| 1143 | char* subunit = NULL; |
| 1144 | char* version; |
| 1145 | char* field[6]; |
| 1146 | char* delim[6]; |
| 1147 | FILE* fp; |
| 1148 | int tabs[6]; |
| 1149 | char unit[64]; |
| 1150 | regmatch_t match[100]; |
| 1151 | regex_t preg; |
| 1152 | |
| 1153 | static char pat[32 * 1024]; |
| 1154 | |
| 1155 | int nonosub = REG_NOSUB == 0; |
| 1156 | int nonexec = 0; |
| 1157 | |
| 1158 | unsigned long test = 0; |
| 1159 | |
| 1160 | static char* filter[] = { "-", 0 }; |
| 1161 | |
| 1162 | state.NOMATCH.rm_so = state.NOMATCH.rm_eo = -2; |
| 1163 | p = unit; |
| 1164 | version = (char*)id + 10; |
| 1165 | while (p < &unit[sizeof(unit)-1] && (*p = *version++) && !isspace(*p)) |
| 1166 | p++; |
| 1167 | *p = 0; |
| 1168 | while ((p = *++argv) && *p == '-') |
| 1169 | for (;;) |
| 1170 | { |
| 1171 | switch (*++p) |
| 1172 | { |
| 1173 | case 0: |
| 1174 | break; |
| 1175 | case 'c': |
| 1176 | test |= TEST_CATCH; |
| 1177 | continue; |
| 1178 | case 'e': |
| 1179 | test |= TEST_IGNORE_ERROR; |
| 1180 | continue; |
| 1181 | case 'h': |
| 1182 | case '?': |
| 1183 | help(0); |
| 1184 | return 2; |
| 1185 | case '-': |
| 1186 | help(p[1] == 'h'); |
| 1187 | return 2; |
| 1188 | case 'n': |
| 1189 | nonexec = 1; |
| 1190 | continue; |
| 1191 | case 'o': |
| 1192 | test |= TEST_IGNORE_OVER; |
| 1193 | continue; |
| 1194 | case 'p': |
| 1195 | test |= TEST_IGNORE_POSITION; |
| 1196 | continue; |
| 1197 | case 's': |
| 1198 | #ifdef REG_DISCIPLINE |
| 1199 | if (!(state.stack = stkalloc(stkstd, 0))) |
| 1200 | fprintf(stderr, "%s: out of space [stack]", unit); |
| 1201 | state.disc.disc.re_resizef = resizef; |
| 1202 | state.disc.disc.re_resizehandle = (void*)stkstd; |
| 1203 | #endif |
| 1204 | continue; |
| 1205 | case 'x': |
| 1206 | nonosub = 1; |
| 1207 | continue; |
| 1208 | case 'v': |
| 1209 | test |= TEST_VERBOSE; |
| 1210 | continue; |
| 1211 | case 'A': |
| 1212 | test |= TEST_ACTUAL; |
| 1213 | continue; |
| 1214 | case 'B': |
| 1215 | test |= TEST_BASELINE; |
| 1216 | continue; |
| 1217 | case 'F': |
| 1218 | test |= TEST_FAIL; |
| 1219 | continue; |
| 1220 | case 'P': |
| 1221 | test |= TEST_PASS; |
| 1222 | continue; |
| 1223 | case 'S': |
| 1224 | test |= TEST_SUMMARY; |
| 1225 | continue; |
| 1226 | default: |
| 1227 | fprintf(stderr, "%s: %c: invalid option\n", unit, *p); |
| 1228 | return 2; |
| 1229 | } |
| 1230 | break; |
| 1231 | } |
| 1232 | if (!*argv) |
| 1233 | argv = filter; |
| 1234 | locale = 0; |
| 1235 | while ((state.file = *argv++)) |
| 1236 | { |
| 1237 | if (streq(state.file, "-") || streq(state.file, "/dev/stdin") || streq(state.file, "/dev/fd/0")) |
| 1238 | { |
| 1239 | state.file = 0; |
| 1240 | fp = stdin; |
| 1241 | } |
| 1242 | else if (!(fp = fopen(state.file, "r"))) |
| 1243 | { |
| 1244 | fprintf(stderr, "%s: %s: cannot read\n", unit, state.file); |
| 1245 | return 2; |
| 1246 | } |
| 1247 | testno = state.errors = state.ignored = state.lineno = state.passed = |
| 1248 | state.signals = state.unspecified = state.warnings = 0; |
| 1249 | skip = 0; |
| 1250 | level = 1; |
| 1251 | if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) |
| 1252 | { |
| 1253 | printf("TEST\t%s ", unit); |
| 1254 | if ((s = state.file)) |
| 1255 | { |
| 1256 | subunit = p = 0; |
| 1257 | for (;;) |
| 1258 | { |
| 1259 | switch (*s++) |
| 1260 | { |
| 1261 | case 0: |
| 1262 | break; |
| 1263 | case '/': |
| 1264 | subunit = s; |
| 1265 | continue; |
| 1266 | case '.': |
| 1267 | p = s - 1; |
| 1268 | continue; |
| 1269 | default: |
| 1270 | continue; |
| 1271 | } |
| 1272 | break; |
| 1273 | } |
| 1274 | if (!subunit) |
| 1275 | subunit = state.file; |
| 1276 | if (p < subunit) |
| 1277 | p = s - 1; |
| 1278 | subunitlen = p - subunit; |
| 1279 | printf("%-.*s ", subunitlen, subunit); |
| 1280 | } |
| 1281 | else |
| 1282 | subunit = 0; |
| 1283 | for (s = version; *s && (*s != ' ' || *(s + 1) != '$'); s++) |
| 1284 | putchar(*s); |
| 1285 | if (test & TEST_CATCH) |
| 1286 | printf(", catch"); |
| 1287 | if (test & TEST_IGNORE_ERROR) |
| 1288 | printf(", ignore error code mismatches"); |
| 1289 | if (test & TEST_IGNORE_POSITION) |
| 1290 | printf(", ignore negative position mismatches"); |
| 1291 | #ifdef REG_DISCIPLINE |
| 1292 | if (state.stack) |
| 1293 | printf(", stack"); |
| 1294 | #endif |
| 1295 | if (test & TEST_VERBOSE) |
| 1296 | printf(", verbose"); |
| 1297 | printf("\n"); |
| 1298 | #ifdef REG_VERSIONID |
| 1299 | if (regerror(REG_VERSIONID, NiL, pat, sizeof(pat)) > 0) |
| 1300 | s = pat; |
| 1301 | else |
| 1302 | #endif |
| 1303 | #ifdef REG_TEST_VERSION |
| 1304 | s = REG_TEST_VERSION; |
| 1305 | #else |
| 1306 | s = "regex"; |
| 1307 | #endif |
| 1308 | printf("NOTE\t%s\n", s); |
| 1309 | if (elementsof(unsupported) > 1) |
| 1310 | { |
| 1311 | #if (REG_TEST_DEFAULT & (REG_AUGMENTED|REG_EXTENDED|REG_SHELL)) || !defined(REG_EXTENDED) |
| 1312 | i = 0; |
| 1313 | #else |
| 1314 | i = REG_EXTENDED != 0; |
| 1315 | #endif |
| 1316 | for (got = 0; i < elementsof(unsupported) - 1; i++) |
| 1317 | { |
| 1318 | if (!got) |
| 1319 | { |
| 1320 | got = 1; |
| 1321 | printf("NOTE\tunsupported: %s", unsupported[i]); |
| 1322 | } |
| 1323 | else |
| 1324 | printf(",%s", unsupported[i]); |
| 1325 | } |
| 1326 | if (got) |
| 1327 | printf("\n"); |
| 1328 | } |
| 1329 | } |
| 1330 | #ifdef REG_DISCIPLINE |
| 1331 | state.disc.disc.re_version = REG_VERSION; |
| 1332 | state.disc.disc.re_compf = compf; |
| 1333 | state.disc.disc.re_execf = execf; |
| 1334 | if (!(state.disc.sp = sfstropen())) |
| 1335 | bad("out of space [discipline string stream]\n", NiL, NiL, 0, 0); |
| 1336 | preg.re_disc = &state.disc.disc; |
| 1337 | #endif |
| 1338 | if (test & TEST_CATCH) |
| 1339 | { |
| 1340 | signal(SIGALRM, gotcha); |
| 1341 | signal(SIGBUS, gotcha); |
| 1342 | signal(SIGSEGV, gotcha); |
| 1343 | } |
| 1344 | while ((p = my_getline(fp))) |
| 1345 | { |
| 1346 | |
| 1347 | /* parse: */ |
| 1348 | |
| 1349 | line = p; |
| 1350 | if (*p == ':' && !isspace(*(p + 1))) |
| 1351 | { |
| 1352 | while (*++p && *p != ':'); |
| 1353 | if (!*p++) |
| 1354 | { |
| 1355 | if (test & TEST_BASELINE) |
| 1356 | printf("%s\n", line); |
| 1357 | continue; |
| 1358 | } |
| 1359 | } |
| 1360 | while (isspace(*p)) |
| 1361 | p++; |
| 1362 | if (*p == 0 || *p == '#' || *p == 'T') |
| 1363 | { |
| 1364 | if (test & TEST_BASELINE) |
| 1365 | printf("%s\n", line); |
| 1366 | continue; |
| 1367 | } |
| 1368 | if (*p == ':' || *p == 'N') |
| 1369 | { |
| 1370 | if (test & TEST_BASELINE) |
| 1371 | printf("%s\n", line); |
| 1372 | else if (!(test & (TEST_ACTUAL|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) |
| 1373 | { |
| 1374 | while (*++p && !isspace(*p)); |
| 1375 | while (isspace(*p)) |
| 1376 | p++; |
| 1377 | printf("NOTE %s\n", p); |
| 1378 | } |
| 1379 | continue; |
| 1380 | } |
| 1381 | j = 0; |
| 1382 | i = 0; |
| 1383 | field[i++] = p; |
| 1384 | for (;;) |
| 1385 | { |
| 1386 | switch (*p++) |
| 1387 | { |
| 1388 | case 0: |
| 1389 | p--; |
| 1390 | j = 0; |
| 1391 | goto checkfield; |
| 1392 | case '\t': |
| 1393 | *(delim[i] = p - 1) = 0; |
| 1394 | j = 1; |
| 1395 | checkfield: |
| 1396 | s = field[i - 1]; |
| 1397 | if (streq(s, "NIL")) |
| 1398 | field[i - 1] = 0; |
| 1399 | else if (streq(s, "NULL")) |
| 1400 | *s = 0; |
| 1401 | while (*p == '\t') |
| 1402 | { |
| 1403 | p++; |
| 1404 | j++; |
| 1405 | } |
| 1406 | tabs[i - 1] = j; |
| 1407 | if (!*p) |
| 1408 | break; |
| 1409 | if (i >= elementsof(field)) |
| 1410 | bad("too many fields\n", NiL, NiL, 0, 0); |
| 1411 | field[i++] = p; |
| 1412 | /*FALLTHROUGH*/ |
| 1413 | default: |
| 1414 | continue; |
| 1415 | } |
| 1416 | break; |
| 1417 | } |
| 1418 | if (!(spec = field[0])) |
| 1419 | bad("NIL spec\n", NiL, NiL, 0, 0); |
| 1420 | |
| 1421 | /* interpret: */ |
| 1422 | |
| 1423 | cflags = REG_TEST_DEFAULT; |
| 1424 | eflags = REG_EXEC_DEFAULT; |
| 1425 | test &= TEST_GLOBAL; |
| 1426 | state.extracted = 0; |
| 1427 | nmatch = 20; |
| 1428 | nsub = -1; |
| 1429 | for (p = spec; *p; p++) |
| 1430 | { |
| 1431 | if (isdigit(*p)) |
| 1432 | { |
| 1433 | nmatch = strtol(p, &p, 10); |
| 1434 | if (nmatch >= elementsof(match)) |
| 1435 | bad("nmatch must be < 100\n", NiL, NiL, 0, 0); |
| 1436 | p--; |
| 1437 | continue; |
| 1438 | } |
| 1439 | switch (*p) |
| 1440 | { |
| 1441 | case 'A': |
| 1442 | test |= TEST_ARE; |
| 1443 | continue; |
| 1444 | case 'B': |
| 1445 | test |= TEST_BRE; |
| 1446 | continue; |
| 1447 | case 'C': |
| 1448 | if (!(test & TEST_QUERY) && !(skip & level)) |
| 1449 | bad("locale must be nested\n", NiL, NiL, 0, 0); |
| 1450 | test &= ~TEST_QUERY; |
| 1451 | if (locale) |
| 1452 | bad("locale nesting not supported\n", NiL, NiL, 0, 0); |
| 1453 | if (i != 2) |
| 1454 | bad("locale field expected\n", NiL, NiL, 0, 0); |
| 1455 | if (!(skip & level)) |
| 1456 | { |
| 1457 | #if defined(LC_COLLATE) && defined(LC_CTYPE) |
| 1458 | s = field[1]; |
| 1459 | if (!s || streq(s, "POSIX")) |
| 1460 | s = "C"; |
| 1461 | if (!(ans = setlocale(LC_COLLATE, s)) || streq(ans, "C") || streq(ans, "POSIX") || !(ans = setlocale(LC_CTYPE, s)) || streq(ans, "C") || streq(ans, "POSIX")) |
| 1462 | skip = note(level, s, skip, test); |
| 1463 | else |
| 1464 | { |
| 1465 | if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) |
| 1466 | printf("NOTE \"%s\" locale\n", s); |
| 1467 | locale = level; |
| 1468 | } |
| 1469 | #else |
| 1470 | skip = note(level, skip, test, "locales not supported"); |
| 1471 | #endif |
| 1472 | } |
| 1473 | cflags = NOTEST; |
| 1474 | continue; |
| 1475 | case 'E': |
| 1476 | test |= TEST_ERE; |
| 1477 | continue; |
| 1478 | case 'K': |
| 1479 | test |= TEST_KRE; |
| 1480 | continue; |
| 1481 | case 'L': |
| 1482 | test |= TEST_LRE; |
| 1483 | continue; |
| 1484 | case 'S': |
| 1485 | test |= TEST_SRE; |
| 1486 | continue; |
| 1487 | |
| 1488 | case 'a': |
| 1489 | cflags |= REG_LEFT|REG_RIGHT; |
| 1490 | continue; |
| 1491 | case 'b': |
| 1492 | eflags |= REG_NOTBOL; |
| 1493 | continue; |
| 1494 | case 'c': |
| 1495 | cflags |= REG_COMMENT; |
| 1496 | continue; |
| 1497 | case 'd': |
| 1498 | cflags |= REG_SHELL_DOT; |
| 1499 | continue; |
| 1500 | case 'e': |
| 1501 | eflags |= REG_NOTEOL; |
| 1502 | continue; |
| 1503 | case 'f': |
| 1504 | cflags |= REG_MULTIPLE; |
| 1505 | continue; |
| 1506 | case 'g': |
| 1507 | cflags |= NOTEST; |
| 1508 | continue; |
| 1509 | case 'h': |
| 1510 | cflags |= REG_MULTIREF; |
| 1511 | continue; |
| 1512 | case 'i': |
| 1513 | cflags |= REG_ICASE; |
| 1514 | continue; |
| 1515 | case 'j': |
| 1516 | cflags |= REG_SPAN; |
| 1517 | continue; |
| 1518 | case 'k': |
| 1519 | cflags |= REG_ESCAPE; |
| 1520 | continue; |
| 1521 | case 'l': |
| 1522 | cflags |= REG_LEFT; |
| 1523 | continue; |
| 1524 | case 'm': |
| 1525 | cflags |= REG_MINIMAL; |
| 1526 | continue; |
| 1527 | case 'n': |
| 1528 | cflags |= REG_NEWLINE; |
| 1529 | continue; |
| 1530 | case 'o': |
| 1531 | cflags |= REG_SHELL_GROUP; |
| 1532 | continue; |
| 1533 | case 'p': |
| 1534 | cflags |= REG_SHELL_PATH; |
| 1535 | continue; |
| 1536 | case 'q': |
| 1537 | cflags |= REG_DELIMITED; |
| 1538 | continue; |
| 1539 | case 'r': |
| 1540 | cflags |= REG_RIGHT; |
| 1541 | continue; |
| 1542 | case 's': |
| 1543 | cflags |= REG_SHELL_ESCAPED; |
| 1544 | continue; |
| 1545 | case 't': |
| 1546 | cflags |= REG_MUSTDELIM; |
| 1547 | continue; |
| 1548 | case 'u': |
| 1549 | test |= TEST_UNSPECIFIED; |
| 1550 | continue; |
| 1551 | case 'w': |
| 1552 | cflags |= REG_NOSUB; |
| 1553 | continue; |
| 1554 | case 'x': |
| 1555 | if (REG_LENIENT) |
| 1556 | cflags |= REG_LENIENT; |
| 1557 | else |
| 1558 | test |= TEST_LENIENT; |
| 1559 | continue; |
| 1560 | case 'y': |
| 1561 | eflags |= REG_LEFT; |
| 1562 | continue; |
| 1563 | case 'z': |
| 1564 | cflags |= REG_NULL; |
| 1565 | continue; |
| 1566 | |
| 1567 | case '$': |
| 1568 | test |= TEST_EXPAND; |
| 1569 | continue; |
| 1570 | |
| 1571 | case '/': |
| 1572 | test |= TEST_SUB; |
| 1573 | continue; |
| 1574 | |
| 1575 | case '?': |
| 1576 | test |= TEST_VERIFY; |
| 1577 | test &= ~(TEST_AND|TEST_OR); |
| 1578 | state.verify = state.passed; |
| 1579 | continue; |
| 1580 | case '&': |
| 1581 | test |= TEST_VERIFY|TEST_AND; |
| 1582 | test &= ~TEST_OR; |
| 1583 | continue; |
| 1584 | case '|': |
| 1585 | test |= TEST_VERIFY|TEST_OR; |
| 1586 | test &= ~TEST_AND; |
| 1587 | continue; |
| 1588 | case ';': |
| 1589 | test |= TEST_OR; |
| 1590 | test &= ~TEST_AND; |
| 1591 | continue; |
| 1592 | |
| 1593 | case '{': |
| 1594 | level <<= 1; |
| 1595 | if (skip & (level >> 1)) |
| 1596 | { |
| 1597 | skip |= level; |
| 1598 | cflags = NOTEST; |
| 1599 | } |
| 1600 | else |
| 1601 | { |
| 1602 | skip &= ~level; |
| 1603 | test |= TEST_QUERY; |
| 1604 | } |
| 1605 | continue; |
| 1606 | case '}': |
| 1607 | if (level == 1) |
| 1608 | bad("invalid {...} nesting\n", NiL, NiL, 0, 0); |
| 1609 | if ((skip & level) && !(skip & (level>>1))) |
| 1610 | { |
| 1611 | if (!(test & (TEST_BASELINE|TEST_SUMMARY))) |
| 1612 | { |
| 1613 | if (test & (TEST_ACTUAL|TEST_FAIL)) |
| 1614 | printf("}\n"); |
| 1615 | else if (!(test & TEST_PASS)) |
| 1616 | printf("-%d\n", state.lineno); |
| 1617 | } |
| 1618 | } |
| 1619 | #if defined(LC_COLLATE) && defined(LC_CTYPE) |
| 1620 | else if (locale & level) |
| 1621 | { |
| 1622 | locale = 0; |
| 1623 | if (!(skip & level)) |
| 1624 | { |
| 1625 | s = "C"; |
| 1626 | setlocale(LC_COLLATE, s); |
| 1627 | setlocale(LC_CTYPE, s); |
| 1628 | if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) |
| 1629 | printf("NOTE \"%s\" locale\n", s); |
| 1630 | else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_PASS)) |
| 1631 | printf("}\n"); |
| 1632 | } |
| 1633 | else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL)) |
| 1634 | printf("}\n"); |
| 1635 | } |
| 1636 | #endif |
| 1637 | level >>= 1; |
| 1638 | cflags = NOTEST; |
| 1639 | continue; |
| 1640 | |
| 1641 | default: |
| 1642 | bad("bad spec\n", spec, NiL, 0, test); |
| 1643 | break; |
| 1644 | |
| 1645 | } |
| 1646 | break; |
| 1647 | } |
| 1648 | if ((cflags|eflags) == NOTEST || ((skip & level) && (test & TEST_BASELINE))) |
| 1649 | { |
| 1650 | if (test & TEST_BASELINE) |
| 1651 | { |
| 1652 | while (i > 1) |
| 1653 | *delim[--i] = '\t'; |
| 1654 | printf("%s\n", line); |
| 1655 | } |
| 1656 | continue; |
| 1657 | } |
| 1658 | if (test & TEST_OR) |
| 1659 | { |
| 1660 | if (!(test & TEST_VERIFY)) |
| 1661 | { |
| 1662 | test &= ~TEST_OR; |
| 1663 | if (state.passed == state.verify && i > 1) |
| 1664 | printf("NOTE\t%s\n", field[1]); |
| 1665 | continue; |
| 1666 | } |
| 1667 | else if (state.passed > state.verify) |
| 1668 | continue; |
| 1669 | } |
| 1670 | else if (test & TEST_AND) |
| 1671 | { |
| 1672 | if (state.passed == state.verify) |
| 1673 | continue; |
| 1674 | state.passed = state.verify; |
| 1675 | } |
| 1676 | if (i < 4) |
| 1677 | bad("too few fields\n", NiL, NiL, 0, test); |
| 1678 | while (i < elementsof(field)) |
| 1679 | field[i++] = 0; |
| 1680 | if ((re = field[1])) |
| 1681 | { |
| 1682 | if (streq(re, "SAME")) |
| 1683 | { |
| 1684 | re = ppat; |
| 1685 | test |= TEST_SAME; |
| 1686 | } |
| 1687 | else |
| 1688 | { |
| 1689 | if (test & TEST_EXPAND) |
| 1690 | escape(re); |
| 1691 | strcpy(ppat = pat, re); |
| 1692 | } |
| 1693 | } |
| 1694 | else |
| 1695 | ppat = 0; |
| 1696 | nstr = -1; |
| 1697 | if ((s = field[2]) && (test & TEST_EXPAND)) |
| 1698 | { |
| 1699 | nstr = escape(s); |
| 1700 | #if _REG_nexec |
| 1701 | if (nstr != strlen(s)) |
| 1702 | nexec = nstr; |
| 1703 | #endif |
| 1704 | } |
| 1705 | if (!(ans = field[3])) |
| 1706 | bad("NIL answer\n", NiL, NiL, 0, test); |
| 1707 | msg = field[4]; |
| 1708 | fflush(stdout); |
| 1709 | if (test & TEST_SUB) |
| 1710 | #if _REG_subcomp |
| 1711 | cflags |= REG_DELIMITED; |
| 1712 | #else |
| 1713 | continue; |
| 1714 | #endif |
| 1715 | |
| 1716 | compile: |
| 1717 | |
| 1718 | if (state.extracted || (skip & level)) |
| 1719 | continue; |
| 1720 | #if !(REG_TEST_DEFAULT & (REG_AUGMENTED|REG_EXTENDED|REG_SHELL)) |
| 1721 | #ifdef REG_EXTENDED |
| 1722 | if (REG_EXTENDED != 0 && (test & TEST_BRE)) |
| 1723 | #else |
| 1724 | if (test & TEST_BRE) |
| 1725 | #endif |
| 1726 | { |
| 1727 | test &= ~TEST_BRE; |
| 1728 | flags = cflags; |
| 1729 | state.which = "BRE"; |
| 1730 | } |
| 1731 | else |
| 1732 | #endif |
| 1733 | #ifdef REG_EXTENDED |
| 1734 | if (test & TEST_ERE) |
| 1735 | { |
| 1736 | test &= ~TEST_ERE; |
| 1737 | flags = cflags | REG_EXTENDED; |
| 1738 | state.which = "ERE"; |
| 1739 | } |
| 1740 | else |
| 1741 | #endif |
| 1742 | #ifdef REG_AUGMENTED |
| 1743 | if (test & TEST_ARE) |
| 1744 | { |
| 1745 | test &= ~TEST_ARE; |
| 1746 | flags = cflags | REG_AUGMENTED; |
| 1747 | state.which = "ARE"; |
| 1748 | } |
| 1749 | else |
| 1750 | #endif |
| 1751 | #ifdef REG_LITERAL |
| 1752 | if (test & TEST_LRE) |
| 1753 | { |
| 1754 | test &= ~TEST_LRE; |
| 1755 | flags = cflags | REG_LITERAL; |
| 1756 | state.which = "LRE"; |
| 1757 | } |
| 1758 | else |
| 1759 | #endif |
| 1760 | #ifdef REG_SHELL |
| 1761 | if (test & TEST_SRE) |
| 1762 | { |
| 1763 | test &= ~TEST_SRE; |
| 1764 | flags = cflags | REG_SHELL; |
| 1765 | state.which = "SRE"; |
| 1766 | } |
| 1767 | else |
| 1768 | #ifdef REG_AUGMENTED |
| 1769 | if (test & TEST_KRE) |
| 1770 | { |
| 1771 | test &= ~TEST_KRE; |
| 1772 | flags = cflags | REG_SHELL | REG_AUGMENTED; |
| 1773 | state.which = "KRE"; |
| 1774 | } |
| 1775 | else |
| 1776 | #endif |
| 1777 | #endif |
| 1778 | { |
| 1779 | if (test & (TEST_BASELINE|TEST_PASS|TEST_VERIFY)) |
| 1780 | extract(tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test|TEST_OK); |
| 1781 | continue; |
| 1782 | } |
| 1783 | if ((test & (TEST_QUERY|TEST_VERBOSE|TEST_VERIFY)) == TEST_VERBOSE) |
| 1784 | { |
| 1785 | printf("test %-3d %s ", state.lineno, state.which); |
| 1786 | quote(re, -1, test|TEST_DELIMIT); |
| 1787 | printf(" "); |
| 1788 | quote(s, nstr, test|TEST_DELIMIT); |
| 1789 | printf("\n"); |
| 1790 | } |
| 1791 | |
| 1792 | nosub: |
| 1793 | fun = "regcomp"; |
| 1794 | #if _REG_nexec |
| 1795 | if (nstr >= 0 && nstr != strlen(s)) |
| 1796 | nexec = nstr; |
| 1797 | |
| 1798 | else |
| 1799 | #endif |
| 1800 | nexec = -1; |
| 1801 | if (state.extracted || (skip & level)) |
| 1802 | continue; |
| 1803 | if (!(test & TEST_QUERY)) |
| 1804 | testno++; |
| 1805 | #ifdef REG_DISCIPLINE |
| 1806 | if (state.stack) |
| 1807 | stkset(stkstd, state.stack, 0); |
| 1808 | flags |= REG_DISCIPLINE; |
| 1809 | state.disc.ordinal = 0; |
| 1810 | sfstrseek(state.disc.sp, 0, SEEK_SET); |
| 1811 | #endif |
| 1812 | if (!(test & TEST_CATCH)) |
| 1813 | cret = regcomp(&preg, re, flags); |
| 1814 | else if (!(cret = setjmp(state.gotcha))) |
| 1815 | { |
| 1816 | alarm(HUNG); |
| 1817 | cret = regcomp(&preg, re, flags); |
| 1818 | alarm(0); |
| 1819 | } |
| 1820 | #if _REG_subcomp |
| 1821 | if (!cret && (test & TEST_SUB)) |
| 1822 | { |
| 1823 | fun = "regsubcomp"; |
| 1824 | p = re + preg.re_npat; |
| 1825 | if (!(test & TEST_CATCH)) |
| 1826 | cret = regsubcomp(&preg, p, NiL, 0, 0); |
| 1827 | else if (!(cret = setjmp(state.gotcha))) |
| 1828 | { |
| 1829 | alarm(HUNG); |
| 1830 | cret = regsubcomp(&preg, p, NiL, 0, 0); |
| 1831 | alarm(0); |
| 1832 | } |
| 1833 | if (!cret && *(p += preg.re_npat) && !(preg.re_sub->re_flags & REG_SUB_LAST)) |
| 1834 | { |
| 1835 | if (catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test)) |
| 1836 | continue; |
| 1837 | cret = REG_EFLAGS; |
| 1838 | } |
| 1839 | } |
| 1840 | #endif |
| 1841 | if (!cret) |
| 1842 | { |
| 1843 | if (!(flags & REG_NOSUB) && nsub < 0 && *ans == '(') |
| 1844 | { |
| 1845 | for (p = ans; *p; p++) |
| 1846 | if (*p == '(') |
| 1847 | nsub++; |
| 1848 | else if (*p == '{') |
| 1849 | nsub--; |
| 1850 | if (nsub >= 0) |
| 1851 | { |
| 1852 | if (test & TEST_IGNORE_OVER) |
| 1853 | { |
| 1854 | if (nmatch > nsub) |
| 1855 | nmatch = nsub + 1; |
| 1856 | } |
| 1857 | else if (nsub != preg.re_nsub) |
| 1858 | { |
| 1859 | if (nsub > preg.re_nsub) |
| 1860 | { |
| 1861 | if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) |
| 1862 | skip = extract(tabs, line, re, s, ans, msg, "OK", NiL, 0, 0, skip, level, test|TEST_DELIMIT); |
| 1863 | else |
| 1864 | { |
| 1865 | report("re_nsub incorrect", fun, re, NiL, -1, msg, flags, test); |
| 1866 | printf("at least %d expected, %zd returned\n", nsub, preg.re_nsub); |
| 1867 | state.errors++; |
| 1868 | } |
| 1869 | } |
| 1870 | else |
| 1871 | nsub = preg.re_nsub; |
| 1872 | } |
| 1873 | } |
| 1874 | } |
| 1875 | if (!(test & TEST_SUB) && *ans && *ans != '(' && !streq(ans, "OK") && !streq(ans, "NOMATCH")) |
| 1876 | { |
| 1877 | if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) |
| 1878 | skip = extract(tabs, line, re, s, ans, msg, "OK", NiL, 0, 0, skip, level, test|TEST_DELIMIT); |
| 1879 | else if (!(test & TEST_LENIENT)) |
| 1880 | { |
| 1881 | report("failed", fun, re, NiL, -1, msg, flags, test); |
| 1882 | printf("%s expected, OK returned\n", ans); |
| 1883 | } |
| 1884 | catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); |
| 1885 | continue; |
| 1886 | } |
| 1887 | } |
| 1888 | else |
| 1889 | { |
| 1890 | if (test & TEST_LENIENT) |
| 1891 | /* we'll let it go this time */; |
| 1892 | else if (!*ans || ans[0]=='(' || (cret == REG_BADPAT && streq(ans, "NOMATCH"))) |
| 1893 | { |
| 1894 | got = 0; |
| 1895 | for (i = 1; i < elementsof(codes); i++) |
| 1896 | if (cret==codes[i].code) |
| 1897 | got = i; |
| 1898 | if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) |
| 1899 | skip = extract(tabs, line, re, s, ans, msg, codes[got].name, NiL, 0, 0, skip, level, test|TEST_DELIMIT); |
| 1900 | else |
| 1901 | { |
| 1902 | report("failed", fun, re, NiL, -1, msg, flags, test); |
| 1903 | printf("%s returned: ", codes[got].name); |
| 1904 | error(&preg, cret); |
| 1905 | } |
| 1906 | } |
| 1907 | else |
| 1908 | { |
| 1909 | expected = got = 0; |
| 1910 | for (i = 1; i < elementsof(codes); i++) |
| 1911 | { |
| 1912 | if (streq(ans, codes[i].name)) |
| 1913 | expected = i; |
| 1914 | if (cret==codes[i].code) |
| 1915 | got = i; |
| 1916 | } |
| 1917 | if (!expected) |
| 1918 | { |
| 1919 | if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) |
| 1920 | skip = extract(tabs, line, re, s, ans, msg, codes[got].name, NiL, 0, 0, skip, level, test|TEST_DELIMIT); |
| 1921 | else |
| 1922 | { |
| 1923 | report("failed: invalid error code", NiL, re, NiL, -1, msg, flags, test); |
| 1924 | printf("%s expected, %s returned\n", ans, codes[got].name); |
| 1925 | } |
| 1926 | } |
| 1927 | else if (cret != codes[expected].code && cret != REG_BADPAT) |
| 1928 | { |
| 1929 | if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) |
| 1930 | skip = extract(tabs, line, re, s, ans, msg, codes[got].name, NiL, 0, 0, skip, level, test|TEST_DELIMIT); |
| 1931 | else if (test & TEST_IGNORE_ERROR) |
| 1932 | state.ignored++; |
| 1933 | else |
| 1934 | { |
| 1935 | report("should fail and did", fun, re, NiL, -1, msg, flags, test); |
| 1936 | printf("%s expected, %s returned: ", ans, codes[got].name); |
| 1937 | state.errors--; |
| 1938 | state.warnings++; |
| 1939 | error(&preg, cret); |
| 1940 | } |
| 1941 | } |
| 1942 | } |
| 1943 | goto compile; |
| 1944 | } |
| 1945 | |
| 1946 | #if _REG_nexec |
| 1947 | execute: |
| 1948 | if (nexec >= 0) |
| 1949 | fun = "regnexec"; |
| 1950 | else |
| 1951 | #endif |
| 1952 | fun = "regexec"; |
| 1953 | |
| 1954 | for (i = 0; i < elementsof(match); i++) |
| 1955 | match[i] = state.NOMATCH; |
| 1956 | |
| 1957 | #if _REG_nexec |
| 1958 | if (nexec >= 0) |
| 1959 | { |
| 1960 | eret = regnexec(&preg, s, nexec, nmatch, match, eflags); |
| 1961 | s[nexec] = 0; |
| 1962 | } |
| 1963 | else |
| 1964 | #endif |
| 1965 | { |
| 1966 | if (!(test & TEST_CATCH)) |
| 1967 | eret = regexec(&preg, s, nmatch, match, eflags); |
| 1968 | else if (!(eret = setjmp(state.gotcha))) |
| 1969 | { |
| 1970 | alarm(HUNG); |
| 1971 | eret = regexec(&preg, s, nmatch, match, eflags); |
| 1972 | alarm(0); |
| 1973 | } |
| 1974 | } |
| 1975 | #if _REG_subcomp |
| 1976 | if ((test & TEST_SUB) && !eret) |
| 1977 | { |
| 1978 | fun = "regsubexec"; |
| 1979 | if (!(test & TEST_CATCH)) |
| 1980 | eret = regsubexec(&preg, s, nmatch, match); |
| 1981 | else if (!(eret = setjmp(state.gotcha))) |
| 1982 | { |
| 1983 | alarm(HUNG); |
| 1984 | eret = regsubexec(&preg, s, nmatch, match); |
| 1985 | alarm(0); |
| 1986 | } |
| 1987 | } |
| 1988 | #endif |
| 1989 | if (flags & REG_NOSUB) |
| 1990 | { |
| 1991 | if (eret) |
| 1992 | { |
| 1993 | if (eret != REG_NOMATCH || !streq(ans, "NOMATCH")) |
| 1994 | { |
| 1995 | if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) |
| 1996 | skip = extract(tabs, line, re, s, ans, msg, "NOMATCH", NiL, 0, 0, skip, level, test|TEST_DELIMIT); |
| 1997 | else |
| 1998 | { |
| 1999 | report("REG_NOSUB failed", fun, re, s, nstr, msg, flags, test); |
| 2000 | error(&preg, eret); |
| 2001 | } |
| 2002 | } |
| 2003 | } |
| 2004 | else if (streq(ans, "NOMATCH")) |
| 2005 | { |
| 2006 | if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) |
| 2007 | skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_DELIMIT); |
| 2008 | else |
| 2009 | { |
| 2010 | report("should fail and didn't", fun, re, s, nstr, msg, flags, test); |
| 2011 | error(&preg, eret); |
| 2012 | } |
| 2013 | } |
| 2014 | } |
| 2015 | else if (eret) |
| 2016 | { |
| 2017 | if (eret != REG_NOMATCH || !streq(ans, "NOMATCH")) |
| 2018 | { |
| 2019 | if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) |
| 2020 | skip = extract(tabs, line, re, s, ans, msg, "NOMATCH", NiL, 0, nsub, skip, level, test|TEST_DELIMIT); |
| 2021 | else |
| 2022 | { |
| 2023 | report("failed", fun, re, s, nstr, msg, flags, test); |
| 2024 | if (eret != REG_NOMATCH) |
| 2025 | error(&preg, eret); |
| 2026 | else if (*ans) |
| 2027 | printf("expected: %s\n", ans); |
| 2028 | else |
| 2029 | printf("\n"); |
| 2030 | } |
| 2031 | } |
| 2032 | } |
| 2033 | else if (streq(ans, "NOMATCH")) |
| 2034 | { |
| 2035 | if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) |
| 2036 | skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_DELIMIT); |
| 2037 | else |
| 2038 | { |
| 2039 | report("should fail and didn't", fun, re, s, nstr, msg, flags, test); |
| 2040 | matchprint(match, nmatch, nsub, NiL, test); |
| 2041 | } |
| 2042 | } |
| 2043 | #if _REG_subcomp |
| 2044 | else if (test & TEST_SUB) |
| 2045 | { |
| 2046 | p = preg.re_sub->re_buf; |
| 2047 | if (strcmp(p, ans)) |
| 2048 | { |
| 2049 | report("failed", fun, re, s, nstr, msg, flags, test); |
| 2050 | quote(ans, -1, test|TEST_DELIMIT); |
| 2051 | printf(" expected, "); |
| 2052 | quote(p, -1, test|TEST_DELIMIT); |
| 2053 | printf(" returned\n"); |
| 2054 | } |
| 2055 | } |
| 2056 | #endif |
| 2057 | else if (!*ans) |
| 2058 | { |
| 2059 | if (match[0].rm_so != state.NOMATCH.rm_so) |
| 2060 | { |
| 2061 | if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) |
| 2062 | skip = extract(tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); |
| 2063 | else |
| 2064 | { |
| 2065 | report("failed: no match but match array assigned", NiL, re, s, nstr, msg, flags, test); |
| 2066 | matchprint(match, nmatch, nsub, NiL, test); |
| 2067 | } |
| 2068 | } |
| 2069 | } |
| 2070 | else if (matchcheck(match, nmatch, nsub, ans, re, s, nstr, flags, test)) |
| 2071 | { |
| 2072 | #if _REG_nexec |
| 2073 | if (nexec < 0 && !nonexec) |
| 2074 | { |
| 2075 | nexec = nstr >= 0 ? nstr : strlen(s); |
| 2076 | s[nexec] = '\n'; |
| 2077 | testno++; |
| 2078 | goto execute; |
| 2079 | } |
| 2080 | #endif |
| 2081 | if (!(test & (TEST_SUB|TEST_VERIFY)) && !nonosub) |
| 2082 | { |
| 2083 | if (catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test)) |
| 2084 | continue; |
| 2085 | flags |= REG_NOSUB; |
| 2086 | goto nosub; |
| 2087 | } |
| 2088 | if (test & (TEST_BASELINE|TEST_PASS|TEST_VERIFY)) |
| 2089 | skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_OK); |
| 2090 | } |
| 2091 | else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) |
| 2092 | skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_DELIMIT); |
| 2093 | if (catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test)) |
| 2094 | continue; |
| 2095 | goto compile; |
| 2096 | } |
| 2097 | if (test & TEST_SUMMARY) |
| 2098 | 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); |
| 2099 | else if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS))) |
| 2100 | { |
| 2101 | printf("TEST\t%s", unit); |
| 2102 | if (subunit) |
| 2103 | printf(" %-.*s", subunitlen, subunit); |
| 2104 | printf(", %d test%s", testno, testno == 1 ? "" : "s"); |
| 2105 | if (state.ignored) |
| 2106 | printf(", %d ignored mismatch%s", state.ignored, state.ignored == 1 ? "" : "es"); |
| 2107 | if (state.warnings) |
| 2108 | printf(", %d warning%s", state.warnings, state.warnings == 1 ? "" : "s"); |
| 2109 | if (state.unspecified) |
| 2110 | printf(", %d unspecified difference%s", state.unspecified, state.unspecified == 1 ? "" : "s"); |
| 2111 | if (state.signals) |
| 2112 | printf(", %d signal%s", state.signals, state.signals == 1 ? "" : "s"); |
| 2113 | printf(", %d error%s\n", state.errors, state.errors == 1 ? "" : "s"); |
| 2114 | } |
| 2115 | if (fp != stdin) |
| 2116 | fclose(fp); |
| 2117 | } |
| 2118 | return 0; |
| 2119 | } |
| 2120 | |
| 2121 | int main(int argc, char **argv) |
| 2122 | { |
| 2123 | static char *param[][4] = { |
| 2124 | { NULL, "basic.dat" , NULL }, |
| 2125 | { NULL, "categorize.dat" , NULL }, |
| 2126 | { NULL, "forcedassoc.dat" , NULL }, |
| 2127 | { NULL, "-c", "interpretation.dat", NULL }, |
| 2128 | { NULL, "leftassoc.dat" , NULL }, |
| 2129 | { NULL, "-c", "nullsubexpr.dat" , NULL }, |
| 2130 | { NULL, "repetition.dat" , NULL }, |
| 2131 | { NULL, "rightassoc.dat" , NULL }, |
| 2132 | }; |
| 2133 | int r, i; |
| 2134 | |
| 2135 | if (argv[1]) |
| 2136 | return old_main(argc, argv); |
| 2137 | |
| 2138 | r = 0; |
| 2139 | for (i = 0; i < sizeof(param) / sizeof(param[0]); i++) { |
| 2140 | param[i][0] = argv[0]; |
| 2141 | printf("Testing %s\n", param[i][1][0] != '-' ? param[i][1] : param[i][2]); |
| 2142 | r |= old_main(3 /* not used */, param[i]); |
| 2143 | } |
| 2144 | return r; |
| 2145 | } |