1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1995-2012 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *               Glenn Fowler <glenn.s.fowler@gmail.com>                *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 
22 /*
23  * strmatch(3) test harness
24  * see testmatch --help for a description of the input format
25  */
26 
27 #if OLD
28 #define LEGACY		"old"
29 #else
30 #define LEGACY		""
31 #endif
32 
33 static const char id[] = "\n@(#)$Id: test" LEGACY "match (AT&T Research) 2012-06-25 $\0\n";
34 
35 #if _PACKAGE_ast
36 #include <ast.h>
37 #else
38 #define fmtident(s)	((char*)(s)+10)
39 #endif
40 
41 #include <stdio.h>
42 #include <ctype.h>
43 #include <setjmp.h>
44 #include <signal.h>
45 #include <unistd.h>
46 
47 #ifdef	__STDC__
48 #include <stdlib.h>
49 #include <locale.h>
50 #endif
51 
52 #ifndef NiL
53 #ifdef	__STDC__
54 #define NiL		0
55 #else
56 #define NiL		(char*)0
57 #endif
58 #endif
59 
60 #define H(x)		fprintf(stderr,x)
61 
62 static void
help(void)63 help(void)
64 {
65 H("NAME\n");
66 H("  testmatch - strgrpmatch(3) test harness\n");
67 H("\n");
68 H("SYNOPSIS\n");
69 H("  testmatch [ options ] testmatch.dat\n");
70 H("\n");
71 H("DESCRIPTION\n");
72 H("  testmatch reads strgrpmatch(3) test specifications, one per line, from\n");
73 H("  the standard input and writes one output line for each failed test. A\n");
74 H("  summary line is written after all tests are done. Each successful\n");
75 H("  test is run again with no subexpression pointer array. Unsupported\n");
76 H("  features are noted before the first test, and tests requiring these\n");
77 H("  features are silently ignored.\n");
78 H("\n");
79 #if OLD
80 H("  This version tests the legacy ad-hoc implementation with roots in the\n");
81 H("  Bourne sh implementation.\n");
82 #else
83 H("  This version tests the AST <regex.h> implementation.\n");
84 #endif
85 H("\n");
86 H("OPTIONS\n");
87 H("  -c	catch signals and non-terminating calls\n");
88 H("  -h	list help\n");
89 H("  -v	list each test line\n");
90 H("\n");
91 H("INPUT FORMAT\n");
92 H("  Input lines may be blank, a comment beginning with #, or a test\n");
93 H("  specification. A specification is five fields separated by one\n");
94 H("  or more tabs. NULL denotes the empty string and NIL denotes the\n");
95 H("  0 pointer.\n");
96 H("\n");
97 H("  Field 1: the strgrpmatch(3) flags to apply, one character per\n");
98 H("  STR_feature flag. The test is skipped if STR_feature is not supported\n");
99 H("  by the implementation. If the first character is not [SK] then the\n");
100 H("  specification is a global control line. Note that no distinction\n");
101 H("  is made between SRE and KRE tests. STR_MAXIMAL is set by default.\n");
102 H("  Specifications containing testre(1T) flags are silently ignored.\n");
103 H("\n");
104 H("    S	0			SRE	(sh glob)\n");
105 H("    K	0			KRE	(ksh glob)\n");
106 H("\n");
107 H("    a	STR_LEFT|STR_RIGHT	implicit ^...$\n");
108 H("    i	STR_ICASE		ignore case\n");
109 H("    l	STR_LEFT		implicit ^...\n");
110 H("    m	~STR_MAXIMAL		minimal match (default is STR_MAXIMAL)\n");
111 H("    r	STR_RIGHT		implicit ...$\n");
112 H("    u	standard unspecified behavior -- errors not counted\n");
113 H("    $	                        expand C \\c escapes in fields 2 and 3\n");
114 H("\n");
115 H("  Field 1 control lines:\n");
116 H("\n");
117 H("    C	set LC_COLLATE and LC_CTYPE to locale in field 2\n");
118 H("\n");
119 H("    {				silent skip if failed until }\n");
120 H("    }				end of skip\n");
121 H("\n");
122 H("    : comment			comment copied to output\n");
123 H("\n");
124 H("    number			use number for nmatch (20 by default)\n");
125 H("\n");
126 H("  Field 2: the strgrpmatch expression pattern; SAME uses the pattern\n");
127 H("    from the previous specification.\n");
128 H("\n");
129 H("  Field 3: the string to match.\n");
130 H("\n");
131 H("  Field 4: the test outcome. This is either OK, NOMATCH, BADPAT, or\n");
132 H("    the match array, a list of (m,n) entries with m and n being first\n");
133 H("    and last+1 positions in the field 3 string, or NULL if subexpression\n");
134 H("    array is specified and success is expected. The match[]\n");
135 H("    subexpression pointer array is initialized to (-2,-2).\n");
136 H("    All array elements not equal to (-2,-2) must be specified\n");
137 H("    in the outcome. Unspecified endpoints are denoted by ?.\n");
138 H("\n");
139 H("  Field 5: optional comment appended to the report.\n");
140 H("\n");
141 H("CONTRIBUTORS\n");
142 H("  Glenn Fowler <gsf@research.att.com> (ksh strgrpmatch)\n");
143 H("  David Korn <dgk@research.att.com> (ksh glob matcher)\n");
144 }
145 
146 #ifndef elementsof
147 #define elementsof(x)	(sizeof(x)/sizeof(x[0]))
148 #endif
149 
150 #ifndef streq
151 #define streq(a,b)	(*(a)==*(b)&&!strcmp(a,b))
152 #endif
153 
154 #define LOOPED		2
155 #define NOTEST		(~0)
156 
157 #if !defined(STR_ICASE) && !defined(STR_LEFT)  && !defined(STR_MAXIMAL) && !defined(STR_RIGHT)
158 #define NOT_SUPPORTED	1
159 #endif
160 
161 static const char* unsupported[] =
162 {
163 	0,
164 #if NOT_SUPPORTED
165 	"strmatch",
166 	"strgrpmatch",
167 #endif
168 #ifndef STR_ICASE
169 	"ICASE",
170 #endif
171 #ifndef STR_LEFT
172 	"LEFT",
173 #endif
174 #ifndef STR_MAXIMAL
175 	"MAXIMAL",
176 #endif
177 #ifndef STR_RIGHT
178 	"RIGHT",
179 #endif
180 };
181 
182 #ifndef STR_ICASE
183 #define STR_ICASE	NOTEST
184 #endif
185 #ifndef STR_LEFT
186 #define STR_LEFT	NOTEST
187 #endif
188 #ifndef STR_MAXIMAL
189 #define STR_MAXIMAL	NOTEST
190 #endif
191 #ifndef STR_RIGHT
192 #define STR_RIGHT	NOTEST
193 #endif
194 
195 #if STR_ICASE==NOTEST && STR_LEFT==NOTEST  && STR_MAXIMAL==NOTEST && STR_RIGHT==NOTEST
196 #define NOT_SUPPORTED	1
197 #endif
198 
199 static struct
200 {
201 	int		errors;
202 	struct
203 	{
204 	int		count;
205 	int		error;
206 	int		position;
207 	}		ignore;
208 	int		lineno;
209 	int		ret;
210 	int		signals;
211 	int		unspecified;
212 	int		warnings;
213 	char*		file;
214 	char*		which;
215 	jmp_buf		gotcha;
216 } state;
217 
218 static void
quote(char * s,int expand)219 quote(char* s, int expand)
220 {
221 	unsigned char*	u = (unsigned char*)s;
222 	int		c;
223 
224 	if (!u)
225 		printf("NIL");
226 	else if (!*u)
227 		printf("NULL");
228 	else if (expand)
229 	{
230 		printf("\"");
231 		for (;;)
232 		{
233 			switch (c = *u++)
234 			{
235 			case 0:
236 				break;;
237 			case '\\':
238 				printf("\\\\");
239 				continue;
240 			case '"':
241 				printf("\\\"");
242 				continue;
243 			case '\a':
244 				printf("\\a");
245 				continue;
246 			case '\b':
247 				printf("\\b");
248 				continue;
249 			case '\f':
250 				printf("\\f");
251 				continue;
252 			case '\n':
253 				printf("\\n");
254 				continue;
255 			case '\r':
256 				printf("\\r");
257 				continue;
258 			case '\t':
259 				printf("\\t");
260 				continue;
261 			case '\v':
262 				printf("\\v");
263 				continue;
264 			default:
265 				if (!iscntrl(c) && isprint(c))
266 					putchar(c);
267 				else
268 					printf("\\x%02x", c);
269 				continue;
270 			}
271 			break;
272 		}
273 		printf("\"");
274 	}
275 	else
276 		printf("%s", s);
277 }
278 
279 static void
report(char * comment,char * fun,char * re,char * s,char * msg,int flags,int unspecified,int expand)280 report(char* comment, char* fun, char* re, char* s, char* msg, int flags, int unspecified, int expand)
281 {
282 	if (state.file)
283 		printf("%s:", state.file);
284 	printf("%d:", state.lineno);
285 	if (re)
286 	{
287 		printf(" ");
288 		quote(re, expand);
289 		if (s)
290 		{
291 			printf(" versus ");
292 			quote(s, expand);
293 		}
294 	}
295 	if (unspecified)
296 	{
297 		state.unspecified++;
298 		printf(" unspecified behavior");
299 	}
300 	else
301 		state.errors++;
302 	printf(" %s", state.which);
303 	if (fun)
304 		printf(" %s", fun);
305 	if (comment[strlen(comment)-1] == '\n')
306 		printf(" %s", comment);
307 	else
308 	{
309 		printf(" %s: ", comment);
310 		if (msg)
311 			printf("%s: ", msg);
312 	}
313 }
314 
315 static int
note(int level,int skip,char * msg)316 note(int level, int skip, char* msg)
317 {
318 	if (!skip)
319 	{
320 		printf("NOTE\t");
321 		if (msg)
322 			printf("%s: ", msg);
323 		printf("skipping lines %d", state.lineno);
324 	}
325 	return skip | level;
326 }
327 
328 static void
bad(char * comment,char * re,char * s,int expand)329 bad(char* comment, char* re, char* s, int expand)
330 {
331 	printf("bad test case ");
332 	report(comment, NiL, re, s, NiL, 0, 0, expand);
333 	exit(1);
334 }
335 
336 static void
escape(char * s)337 escape(char* s)
338 {
339 	char*	e;
340 	char*	t;
341 	char*	q;
342 	int	c;
343 
344 	for (t = s; *t = *s; s++, t++)
345 		if (*s == '\\')
346 			switch (*++s)
347 			{
348 			case '\\':
349 				break;
350 			case 'a':
351 				*t = '\a';
352 				break;
353 			case 'b':
354 				*t = '\b';
355 				break;
356 			case 'c':
357 				if (*t = *s)
358 					s++;
359 				*t &= 037;
360 				break;
361 			case 'e':
362 			case 'E':
363 				*t = 033;
364 				break;
365 			case 'f':
366 				*t = '\f';
367 				break;
368 			case 'n':
369 				*t = '\n';
370 				break;
371 			case 'r':
372 				*t = '\r';
373 				break;
374 			case 's':
375 				*t = ' ';
376 				break;
377 			case 't':
378 				*t = '\t';
379 				break;
380 			case 'v':
381 				*t = '\v';
382 				break;
383 			case 'u':
384 			case 'x':
385 				q = *s == 'u' ? (s + 5) : (char*)0;
386 				c = 0;
387 				e = s + 1;
388 				while (!e || !q || s < q)
389 				{
390 					switch (*++s)
391 					{
392 					case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
393 						c = (c << 4) + *s - 'a' + 10;
394 						continue;
395 					case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
396 						c = (c << 4) + *s - 'A' + 10;
397 						continue;
398 					case '0': case '1': case '2': case '3': case '4':
399 					case '5': case '6': case '7': case '8': case '9':
400 						c = (c << 4) + *s - '0';
401 						continue;
402 					case '{':
403 					case '[':
404 						if (s != e)
405 						{
406 							s--;
407 							break;
408 						}
409 						e = 0;
410 						if (q && *(s + 1) == 'U' && *(s + 2) == '+')
411 							s += 2;
412 						continue;
413 					case '}':
414 					case ']':
415 						if (e)
416 							s--;
417 						break;
418 					default:
419 						s--;
420 						break;
421 					}
422 					break;
423 				}
424 				*t = c;
425 				break;
426 			case '0': case '1': case '2': case '3':
427 			case '4': case '5': case '6': case '7':
428 				c = *s - '0';
429 				q = s + 2;
430 				while (s < q)
431 					switch (*++s)
432 					{
433 					case '0': case '1': case '2': case '3':
434 					case '4': case '5': case '6': case '7':
435 						c = (c << 3) + *s - '0';
436 						break;
437 					default:
438 						q = --s;
439 						break;
440 					}
441 				*t = c;
442 				break;
443 			default:
444 				bad("invalid C \\ escape\n", NiL, NiL, 0);
445 			}
446 }
447 
448 static void
matchprint(ssize_t * match,int nmatch,char * ans)449 matchprint(ssize_t* match, int nmatch, char* ans)
450 {
451 	int	i;
452 
453 	for (; nmatch > 0; nmatch -= 2)
454 		if (match[nmatch-2] != -2 && (!state.ignore.position || match[nmatch-2] >= 0 && match[nmatch-2] >= 0))
455 			break;
456 	for (i = 0; i < nmatch; i += 2)
457 	{
458 		printf("(");
459 		if (match[i] == -1)
460 			printf("?");
461 		else
462 			printf("%zd", match[i]);
463 		printf(",");
464 		if (match[i+1] == -1)
465 			printf("?");
466 		else
467 			printf("%zd", match[i+1]);
468 		printf(")");
469 	}
470 	if (ans)
471 		printf(" expected: %s", ans);
472 	printf("\n");
473 }
474 
475 static int
matchcheck(int nmatch,ssize_t * match,char * ans,char * re,char * s,int flags,int query,int unspecified,int expand)476 matchcheck(int nmatch, ssize_t* match, char* ans, char* re, char* s, int flags, int query, int unspecified, int expand)
477 {
478 	char*	p;
479 	int	i;
480 	ssize_t	m;
481 	ssize_t	n;
482 
483 	for (i = 0, p = ans; i < nmatch && *p; i += 2)
484 	{
485 		if (*p++ != '(')
486 			bad("improper answer\n", re, s, expand);
487 		if (*p == '?')
488 		{
489 			m = -1;
490 			p++;
491 		}
492 		else
493 			m = strtol(p, &p, 10);
494 		if (*p++ != ',')
495 			bad("improper answer\n", re, s, expand);
496 		if (*p == '?')
497 		{
498 			n = -1;
499 			p++;
500 		}
501 		else
502 			n = strtol(p, &p, 10);
503 		if (*p++ != ')')
504 			bad("improper answer\n", re, s, expand);
505 		if (m!=match[i] || n!=match[i+1])
506 		{
507 			if (!query)
508 			{
509 				report("failed: match was", NiL, re, s, NiL, flags, unspecified, expand);
510 				matchprint(match, nmatch, ans);
511 			}
512 			return 0;
513 		}
514 	}
515 	for (; i < nmatch; i += 2)
516 	{
517 		if (match[i]!=-2 || match[i+1]!=-2)
518 		{
519 			if (!query)
520 			{
521 				if (state.ignore.position && (match[i]<0 || match[i+1]<0))
522 				{
523 					state.ignore.count++;
524 					return 0;
525 				}
526 				report("failed: match was", NiL, re, s, NiL, flags, unspecified, expand);
527 				matchprint(match, nmatch, ans);
528 			}
529 			return 0;
530 		}
531 	}
532 	if (match[nmatch] != -2)
533 	{
534 		report("failed: overran match array", NiL, re, s, NiL, flags, unspecified, expand);
535 		matchprint(match, nmatch + 1, NiL);
536 	}
537 	return 1;
538 }
539 
540 static void
sigunblock(int s)541 sigunblock(int s)
542 {
543 #ifdef SIG_SETMASK
544 	int		op;
545 	sigset_t	mask;
546 
547 	sigemptyset(&mask);
548 	if (s)
549 	{
550 		sigaddset(&mask, s);
551 		op = SIG_UNBLOCK;
552 	}
553 	else op = SIG_SETMASK;
554 	sigprocmask(op, &mask, NiL);
555 #else
556 #ifdef sigmask
557 	sigsetmask(s ? (sigsetmask(0L) & ~sigmask(s)) : 0L);
558 #endif
559 #endif
560 }
561 
562 static void
gotcha(int sig)563 gotcha(int sig)
564 {
565 	signal(sig, gotcha);
566 	alarm(0);
567 	state.signals++;
568 	state.ret = 0;
569 	sigunblock(sig);
570 	longjmp(state.gotcha, 1);
571 }
572 
573 static char*
getline(FILE * fp)574 getline(FILE* fp)
575 {
576 	static char	buf[32 * 1024];
577 
578 	register char*	s = buf;
579 	register char*	e = &buf[sizeof(buf)];
580 	register char*	b;
581 
582 	for (;;)
583 	{
584 		if (!(b = fgets(s, e - s, fp)))
585 			return 0;
586 		state.lineno++;
587 		s += strlen(s) - 1;
588 		if (*s != '\n')
589 			break;
590 		if (s == b || *(s - 1) != '\\')
591 		{
592 			*s = 0;
593 			break;
594 		}
595 		s--;
596 	}
597 	return buf;
598 }
599 
600 int
main(int argc,char ** argv)601 main(int argc, char** argv)
602 {
603 	int		cflags;
604 	int		eflags;
605 	int		query;
606 	int		expand;
607 	int		unspecified;
608 	int		kre;
609 	int		sre;
610 	int		nmatch;
611 	int		rmatch;
612 	int		eret;
613 	int		i;
614 	int		sub;
615 	int		subunitlen;
616 	int		level = 1;
617 	int		locale = 0;
618 	int		skip = 0;
619 	int		testno = 0;
620 	FILE*		fp;
621 	char*		p;
622 	char*		spec;
623 	char*		re;
624 	char*		s;
625 	char*		ans;
626 	char*		msg;
627 	char*		fun;
628 	char*		subunit;
629 	char*		version;
630 	char*		field[6];
631 	char		unit[64];
632 	ssize_t		match[200];
633 
634 	int		catch = 0;
635 	int		verbose = 0;
636 
637 	static char*	filter[] = { "-", 0 };
638 
639 	version = fmtident(id);
640 	p = unit;
641 	while (p < &unit[sizeof(unit)-1] && (*p = *version++) && !isspace(*p))
642 		p++;
643 	*p = 0;
644 	while ((p = *++argv) && *p == '-')
645 		for (;;)
646 		{
647 			switch (*++p)
648 			{
649 			case 0:
650 				break;
651 			case 'c':
652 				catch = 1;
653 				continue;
654 			case 'h':
655 			case '?':
656 			case '-':
657 				help();
658 				return 2;
659 			case 'p':
660 				state.ignore.position = 1;
661 				continue;
662 			case 'v':
663 				verbose = 1;
664 				continue;
665 			default:
666 				fprintf(stderr, "%s: -%c: invalid option", unit, *p);
667 				continue;
668 			}
669 			break;
670 		}
671 	if (catch)
672 	{
673 		signal(SIGALRM, gotcha);
674 		signal(SIGBUS, gotcha);
675 		signal(SIGSEGV, gotcha);
676 	}
677 	if (!*argv)
678 		argv = filter;
679 	while (state.file = *argv++)
680 	{
681 		if (streq(state.file, "-") || streq(state.file, "/dev/stdin") || streq(state.file, "/dev/fd/0"))
682 		{
683 			state.file = 0;
684 			fp = stdin;
685 		}
686 		else if (!(fp = fopen(state.file, "r")))
687 		{
688 			fprintf(stderr, "%s: %s: cannot read\n", unit, state.file);
689 			return 2;
690 		}
691 		printf("TEST\t%s ", unit);
692 		if (s = state.file)
693 		{
694 			subunit = p = 0;
695 			for (;;)
696 			{
697 				switch (*s++)
698 				{
699 				case 0:
700 					break;
701 				case '/':
702 					subunit = s;
703 					continue;
704 				case '.':
705 					p = s - 1;
706 					continue;
707 				default:
708 					continue;
709 				}
710 				break;
711 			}
712 			if (!subunit)
713 				subunit = state.file;
714 			if (p < subunit)
715 				p = s - 1;
716 			subunitlen = p - subunit;
717 			if (subunitlen == strlen(unit) && !memcmp(subunit, unit, subunitlen))
718 				subunit = 0;
719 			else
720 				printf("%-.*s ", subunitlen, subunit);
721 		}
722 		else
723 			subunit = 0;
724 		printf("%s", version);
725 		if (catch)
726 			printf(", catch");
727 		if (verbose)
728 			printf(", verbose");
729 		for (i = 1; i < elementsof(unsupported); i++)
730 			printf(", no%s", unsupported[i]);
731 		printf("\n");
732 		level = 1;
733 		locale = skip = testno = 0;
734 		state.ignore.count = state.lineno = state.signals = state.unspecified = state.warnings = 0;
735 		while (p = getline(fp))
736 		{
737 
738 		/* parse: */
739 
740 			if (*p == 0 || *p == '#')
741 				continue;
742 			if (*p == ':')
743 			{
744 				while (*++p == ' ');
745 				printf("NOTE	%s\n", p);
746 				continue;
747 			}
748 			i = 0;
749 			field[i++] = p;
750 			for (;;)
751 			{
752 				switch (*p++)
753 				{
754 				case 0:
755 					p--;
756 					goto checkfield;
757 				case '\t':
758 					*(p - 1) = 0;
759 				checkfield:
760 					s = field[i - 1];
761 					if (streq(s, "NIL"))
762 						field[i - 1] = 0;
763 					else if (streq(s, "NULL"))
764 						*s = 0;
765 					while (*p == '\t')
766 						p++;
767 					if (!*p)
768 						break;
769 					if (i >= elementsof(field))
770 						bad("too many fields\n", NiL, NiL, 0);
771 					field[i++] = p;
772 					/*FALLTHROUGH*/
773 				default:
774 					continue;
775 				}
776 				break;
777 			}
778 			if (!(spec = field[0]))
779 				bad("NIL spec\n", NiL, NiL, 0);
780 
781 		/* interpret: */
782 
783 			cflags = 0;
784 #if STR_MAXIMAL != NOTEST
785 			eflags = STR_MAXIMAL;
786 #else
787 			eflags = 0;
788 #endif
789 			expand = query = unspecified = kre = sre = 0;
790 			nmatch = 20;
791 			for (p = spec; *p; p++)
792 			{
793 				if (isdigit(*p))
794 				{
795 					if ((nmatch = 2 * strtol(p, &p, 10)) >= elementsof(match))
796 						bad("nmatch too large\n", spec, NiL, 0);
797 					p--;
798 					continue;
799 				}
800 				switch (*p)
801 				{
802 				case 'A':
803 					continue;
804 				case 'B':
805 					continue;
806 				case 'C':
807 					if (!query && !(skip & level))
808 						bad("locale query expected\n", NiL, NiL, 0);
809 					query = 0;
810 #if OLD
811 					if (!(skip & level))
812 						skip = note(level, skip, "locales not supported by old strmatch()");
813 #else
814 					if (locale)
815 						bad("locale nesting not supported\n", NiL, NiL, 0);
816 					if (i != 2)
817 						bad("locale field expected\n", NiL, NiL, 0);
818 					if (!(skip & level))
819 					{
820 #if defined(LC_COLLATE) && defined(LC_CTYPE)
821 						s = field[1];
822 						if (!s || streq(s, "POSIX"))
823 							s = "C";
824 						if (!(ans = setlocale(LC_COLLATE, s)) || streq(ans, "C") || streq(ans, "POSIX") || !(ans = setlocale(LC_CTYPE, s)) || streq(ans, "C") || streq(ans, "POSIX"))
825 							skip = note(level, skip, s);
826 						else
827 						{
828 							printf("NOTE	\"%s\" locale\n", s);
829 							locale = level;
830 						}
831 #else
832 						skip = note(level, skip, "locales not supported");
833 #endif
834 					}
835 #endif
836 					cflags = NOTEST;
837 					continue;
838 				case 'E':
839 					continue;
840 				case 'K':
841 					kre = 1;
842 					continue;
843 				case 'L':
844 					continue;
845 				case 'S':
846 					sre = 1;
847 					continue;
848 
849 				case 'a':
850 					eflags |= STR_LEFT|STR_RIGHT;
851 					continue;
852 				case 'b':
853 					cflags = NOTEST;
854 					continue;
855 				case 'c':
856 					cflags = NOTEST;
857 					continue;
858 				case 'd':
859 					cflags = NOTEST;
860 					continue;
861 				case 'e':
862 					cflags = NOTEST;
863 					continue;
864 				case 'f':
865 					continue;
866 				case 'g':
867 					cflags = NOTEST;
868 					continue;
869 				case 'h':
870 					cflags = NOTEST;
871 					continue;
872 				case 'i':
873 					eflags |= STR_ICASE;
874 					continue;
875 				case 'j':
876 					cflags = NOTEST;
877 					continue;
878 				case 'k':
879 					cflags = NOTEST;
880 					continue;
881 				case 'l':
882 					eflags |= STR_LEFT;
883 					continue;
884 				case 'm':
885 #if STR_MAXIMAL != NOTEST
886 					eflags &= ~STR_MAXIMAL;
887 #else
888 					eflags = NOTEST;
889 #endif
890 					continue;
891 				case 'n':
892 					cflags = NOTEST;
893 					continue;
894 				case 'o':
895 					cflags = NOTEST;
896 					continue;
897 				case 'p':
898 					cflags = NOTEST;
899 					continue;
900 				case 'r':
901 					eflags |= STR_RIGHT;
902 					continue;
903 				case 's':
904 					cflags = NOTEST;
905 					continue;
906 				case 'u':
907 					unspecified = 1;
908 					continue;
909 				case 'v':
910 					cflags = NOTEST;
911 					continue;
912 				case 'x':
913 					cflags = NOTEST;
914 					continue;
915 				case 'y':
916 					eflags = NOTEST;
917 					continue;
918 				case 'z':
919 					cflags = NOTEST;
920 					continue;
921 
922 				case '$':
923 					expand = 1;
924 					continue;
925 				case '/':
926 					cflags = NOTEST;
927 					continue;
928 
929 				case '{':
930 					level <<= 1;
931 					if (skip & (level >> 1))
932 					{
933 						skip |= level;
934 						cflags = NOTEST;
935 					}
936 					else
937 					{
938 						skip &= ~level;
939 						query = 1;
940 					}
941 					continue;
942 				case '}':
943 					if (level == 1)
944 						bad("invalid {...} nesting\n", NiL, NiL, 0);
945 					else
946 					{
947 						if ((skip & level) && !(skip & (level>>1)))
948 							printf("-%d\n", state.lineno);
949 #if defined(LC_COLLATE) && defined(LC_CTYPE)
950 						else if (locale & level)
951 						{
952 							locale = 0;
953 							if (!(skip & level))
954 							{
955 								s = "C";
956 								setlocale(LC_COLLATE, s);
957 								setlocale(LC_CTYPE, s);
958 								printf("NOTE	\"%s\" locale\n", s);
959 							}
960 						}
961 #endif
962 						skip &= ~level;
963 						level >>= 1;
964 					}
965 					cflags = NOTEST;
966 					continue;
967 
968 				default:
969 					bad("bad spec\n", spec, NiL, 0);
970 					break;
971 
972 				}
973 				break;
974 			}
975 			if ((cflags|eflags) == NOTEST || !sre && !kre)
976 				continue;
977 			if (i < 4)
978 				bad("too few fields\n", NiL, NiL, 0);
979 			while (i < elementsof(field))
980 				field[i++] = 0;
981 			if ((re = field[1]) && expand)
982 				escape(re);
983 			if ((s = field[2]) && expand)
984 				escape(s);
985 			if (!(ans = field[3]))
986 				bad("NIL answer\n", NiL, NiL, 0);
987 			msg = field[4];
988 			fflush(stdout);
989 
990 		compile:
991 
992 			if (skip)
993 				continue;
994 #if NOT_SUPPORTED == 0
995 			if (sre)
996 			{
997 				state.which = "SRE";
998 				sre = 0;
999 			}
1000 			else if (kre)
1001 			{
1002 				state.which = "KRE";
1003 				kre = 0;
1004 			}
1005 			else
1006 				continue;
1007 			if (!query && verbose)
1008 				printf("test %-3d %s \"%s\" \"%s\"\n", state.lineno, state.which, re, s);
1009 			sub = 1;
1010 
1011 		nosub:
1012 
1013 			if (skip)
1014 				continue;
1015 			if (!query)
1016 				testno++;
1017 			for (i = 0; i < elementsof(match); i++)
1018 				match[i] = -2;
1019 			if (catch)
1020 			{
1021 				if (setjmp(state.gotcha))
1022 					eret = state.ret;
1023 				else
1024 				{
1025 					alarm(LOOPED);
1026 					if (sub)
1027 					{
1028 						fun = "strgrpmatch";
1029 						eret = (rmatch = strgrpmatch(s, re, match, nmatch / 2, eflags)) == 0;
1030 						if (verbose)
1031 							printf("[%s]", fun);
1032 					}
1033 					else
1034 					{
1035 						fun = "strmatch";
1036 						eret = (rmatch = strmatch(s, re)) == 0;
1037 					}
1038 					alarm(0);
1039 				}
1040 			}
1041 			else if (sub)
1042 			{
1043 				fun = "strgrpmatch";
1044 				eret = (rmatch = strgrpmatch(s, re, match, nmatch / 2, eflags)) == 0;
1045 				if (verbose)
1046 					printf("[%s]", fun);
1047 			}
1048 			else
1049 			{
1050 				fun = "strmatch";
1051 				eret = (rmatch = strmatch(s, re)) == 0;
1052 			}
1053 #if OLD
1054 			if (eret && streq(s, re))
1055 			{
1056 				note(level, skip, "old strmatch() does not fall back to literal match on error");
1057 				printf("-EOF\n");
1058 				goto skip;
1059 			}
1060 #endif
1061 			if (!sub)
1062 			{
1063 				if (eret)
1064 				{
1065 					if (!streq(ans, "NOMATCH") && *ans != 'E')
1066 					{
1067 						if (query)
1068 							skip = note(level, skip, msg);
1069 						else
1070 							report("failed", fun, re, s, msg, nmatch, unspecified, expand);
1071 							if (eret == 1)
1072 								printf("OK expected, NOMATCH returned");
1073 							else
1074 								printf("OK expected, error %d returned", eret);
1075 							printf("\n");
1076 					}
1077 				}
1078 				else if (streq(ans, "NOMATCH") || *ans == 'E')
1079 				{
1080 					if (query)
1081 						skip = note(level, skip, msg);
1082 					else
1083 					{
1084 						report("failed", fun, re, s, msg, nmatch, unspecified, expand);
1085 						printf("%s expected, OK returned", ans);
1086 						printf("\n");
1087 					}
1088 				}
1089 			}
1090 			else if (eret)
1091 			{
1092 				if (!streq(ans, "NOMATCH") && *ans != 'E')
1093 				{
1094 					if (query)
1095 						skip = note(level, skip, msg);
1096 					else
1097 					{
1098 						report("failed", fun, re, s, msg, nmatch, unspecified, expand);
1099 						if (eret == 1)
1100 							printf("OK expected, NOMATCH returned");
1101 						else
1102 							printf("OK expected, error %d returned", eret);
1103 						printf("\n");
1104 					}
1105 				}
1106 			}
1107 			else if (streq(ans, "NOMATCH") || *ans == 'E')
1108 			{
1109 				if (query)
1110 					skip = note(level, skip, msg);
1111 				else
1112 				{
1113 					report("should fail and didn't", fun, re, s, msg, nmatch, unspecified, expand);
1114 					matchprint(match, nmatch, NiL);
1115 				}
1116 			}
1117 			else if (!*ans)
1118 			{
1119 				if (match[0] != -2)
1120 				{
1121 					if (query)
1122 						skip = note(level, skip, msg);
1123 					else
1124 					{
1125 						report("failed: no match but match array assigned", NiL, re, s, msg, nmatch, unspecified, expand);
1126 						matchprint(match, nmatch, NiL);
1127 					}
1128 				}
1129 			}
1130 			else if (!matchcheck(2 * rmatch, match, ans, re, s, nmatch, query, unspecified, expand))
1131 			{
1132 				if (eflags ^ (STR_LEFT|STR_RIGHT))
1133 					continue;
1134 				sub = 0;
1135 				goto nosub;
1136 			}
1137 			else if (query)
1138 				skip = note(level, skip, msg);
1139 			goto compile;
1140 #endif
1141 		}
1142 #if OLD
1143 
1144 	skip:
1145 
1146 #endif
1147 		printf("TEST\t%s", unit);
1148 		if (subunit)
1149 			printf(" %-.*s", subunitlen, subunit);
1150 		printf(", %d test%s", testno, testno == 1 ? "" : "s");
1151 		if (state.ignore.count)
1152 			printf(", %d ignored mismatche%s", state.ignore.count, state.ignore.count == 1 ? "" : "s");
1153 		if (state.warnings)
1154 			printf(", %d warning%s", state.warnings, state.warnings == 1 ? "" : "s");
1155 		if (state.unspecified)
1156 			printf(", %d unspecified difference%s", state.unspecified, state.unspecified == 1 ? "" : "s");
1157 		if (state.signals)
1158 			printf(", %d signal%s", state.signals, state.signals == 1 ? "" : "s");
1159 		printf(", %d error%s\n", state.errors, state.errors == 1 ? "" : "s");
1160 		if (fp != stdin)
1161 			fclose(fp);
1162 	}
1163 	return 0;
1164 }
1165