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  * glob(3) test harness
24  * see help() for details
25  */
26 
27 static const char id[] = "\n@(#)$Id: testglob (AT&T Research) 2012-06-23 $\0\n";
28 
29 #if _PACKAGE_ast
30 
31 #include <ast.h>
32 
33 #define quniq(v,n)	struniq(v,n)
34 
35 #else
36 
37 #define fmtident(s)	((char*)(s)+10)
38 
39 static int
quniq(char ** argv,int n)40 quniq(char** argv, int n)
41 {
42 	register char**	ao;
43 	register char**	an;
44 	register char**	ae;
45 
46 	ao = an = argv;
47 	ae = ao + n;
48 	while (++an < ae) {
49 		while (streq(*ao, *an))
50 			if (++an >= ae)
51 				return ao - argv + 1;
52 		*++ao = *an;
53 	}
54 	return ao - argv + 1;
55 }
56 
57 #endif
58 
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <stdio.h>
62 #include <glob.h>
63 #include <ctype.h>
64 #include <setjmp.h>
65 #include <signal.h>
66 #include <unistd.h>
67 
68 #ifdef	__STDC__
69 #include <stdlib.h>
70 #include <locale.h>
71 #endif
72 
73 #ifndef NiL
74 #ifdef	__STDC__
75 #define NiL		0
76 #else
77 #define NiL		(char*)0
78 #endif
79 #endif
80 
81 #define H(x)		fprintf(stderr,x)
82 
83 static void
help(void)84 help(void)
85 {
86 H("NAME\n");
87 H("  testglob - glob(3) test harness\n");
88 H("\n");
89 H("SYNOPSIS\n");
90 H("  testglob [ options ] < testglob.dat\n");
91 H("\n");
92 H("DESCRIPTION\n");
93 H("  testglob reads glob(3) test specifications, one per line, from the\n");
94 H("  standard input and writes one output line for each failed test. A\n");
95 H("  summary line is written after all tests are done. Unsupported\n");
96 H("  features are noted before the first test, and tests requiring these\n");
97 H("  features are silently ignored. Each test is repeated with GLOB_LIST,\n");
98 H("  GLOB_STACK, and GLOB_LIST|GLOB_STACK set.\n");
99 H("\n");
100 H("OPTIONS\n");
101 H("  -c	catch signals and non-terminating calls\n");
102 H("  -e	ignore error code mismatches\n");
103 H("  -n	do not repeat tests with GLOB_LIST, GLOB_STACK, or GLOB_LIST|GLOB_STACK\n");
104 H("  -v	list each test line\n");
105 H("\n");
106 H("INPUT FORMAT\n");
107 H("  Input lines may be blank, a comment beginning with #, or a test\n");
108 H("  specification. A specification is five fields separated by one\n");
109 H("  or more tabs. NULL denotes the empty string and NIL denotes the\n");
110 H("  0 pointer.\n");
111 H("\n");
112 H("  Field 1: the glob(3) flags to apply, one character per GLOB_feature\n");
113 H("  flag. The test is skipped if GLOB_feature is not supported by the\n");
114 H("  implementation. If the first character is not [SK] then the\n");
115 H("  specification is a global control line. One or more of [SK] may be\n");
116 H("  specified; the test will be repeated for each mode.\n");
117 H("\n");
118 H("    K	GLOB_AUGMENTED		augmented (ksh) patterns\n");
119 H("    S	0			basic shell patterns\n");
120 H("\n");
121 H("    a	GLOB_APPEND		append to previous result\n");
122 H("    b	GLOB_BRACE		enable {...} expansion\n");
123 H("    c	GLOB_COMPLETE		shell file completion\n");
124 H("    e	GLOB_NOESCAPE		\\ not special\n");
125 H("    E	GLOB_ERR		abort on error\n");
126 H("    i	GLOB_ICASE		ignore case\n");
127 H("    m	GLOB_MARK		append / to directories\n");
128 H("    n	GLOB_NOCHECK		no match returns original pattern\n");
129 H("    r	GLOB_STARSTAR		enable /**/ expansion\n");
130 H("    s	GLOB_NOSORT		don't sort\n");
131 H("    u	standard unspecified behavior -- errors not counted\n");
132 H("\n");
133 H("  Field 1 control lines:\n");
134 H("\n");
135 H("    C	set LC_COLLATE and LC_CTYPE to the locale in field 2\n");
136 H("    I	set gl_fignore to the pattern in field 2\n");
137 H("    W	workspace file/dir; tab indentation denotes directory level\n");
138 H("\n");
139 H("    {				silent skip if failed until }\n");
140 H("    }				end of skip\n");
141 H("\n");
142 H("    : comment			comment copied to output\n");
143 H("\n");
144 H("  Field 2: a shell filename expansion pattern\n");
145 H("\n");
146 H("  Field 3: the expected glob() return value, OK for success, glob\n");
147 H("    return codes otherwise, with the GLOB_ prefix omitted\n");
148 H("\n");
149 H("  Field 4: optional space-separated matched file list\n");
150 H("\n");
151 H("  Field 5: optional comment appended to the report.\n");
152 H("\n");
153 H("CONTRIBUTORS\n");
154 H("  Glenn Fowler <gsf@research.att.com> (ksh strmatch, regex extensions)\n");
155 H("  David Korn <dgk@research.att.com> (ksh glob matcher)\n");
156 }
157 
158 #ifndef elementsof
159 #define elementsof(x)	(sizeof(x)/sizeof(x[0]))
160 #endif
161 
162 #ifndef streq
163 #define streq(a,b)	(*(a)==*(b)&&!strcmp(a,b))
164 #endif
165 
166 #define LOOPED		2
167 #define NOTEST		(~0)
168 
169 static const char* unsupported[] = {
170 #ifndef GLOB_AUGMENTED
171 	"AUGMENTED",
172 #endif
173 #ifndef GLOB_BRACE
174 	"BRACE",
175 #endif
176 #ifndef GLOB_COMPLETE
177 	"COMPLETE",
178 #endif
179 #ifndef GLOB_DISC
180 	"DISC",
181 #endif
182 #ifndef GLOB_ICASE
183 	"ICASE",
184 #endif
185 #ifndef GLOB_LIST
186 	"LIST",
187 #endif
188 #ifndef GLOB_STACK
189 	"STACK",
190 #endif
191 #ifndef GLOB_STARSTAR
192 	"STARSTAR",
193 #endif
194 	0
195 };
196 
197 #ifndef GLOB_BRACE
198 #define GLOB_BRACE	NOTEST
199 #endif
200 #ifndef GLOB_COMPLETE
201 #define GLOB_COMPLETE	NOTEST
202 #endif
203 #ifndef GLOB_DISC
204 #define GLOB_DISC	0
205 #endif
206 #ifndef GLOB_ICASE
207 #define GLOB_ICASE	NOTEST
208 #endif
209 #ifndef GLOB_LIST
210 #define GLOB_LIST	0
211 #endif
212 #ifndef GLOB_STACK
213 #define GLOB_STACK	0
214 #endif
215 #ifndef GLOB_STARSTAR
216 #define GLOB_STARSTAR	0
217 #endif
218 
219 #define GLOB_UNKNOWN	(-1)
220 
221 #ifndef GLOB_ABORTED
222 #define GLOB_ABORTED	(GLOB_UNKNOWN-1)
223 #endif
224 #ifndef GLOB_NOMATCH
225 #define GLOB_NOMATCH	(GLOB_UNKNOWN-2)
226 #endif
227 #ifndef GLOB_NOSPACE
228 #define GLOB_NOSPACE	(GLOB_UNKNOWN-3)
229 #endif
230 #ifndef GLOB_INTR
231 #define GLOB_INTR	(GLOB_UNKNOWN-4)
232 #endif
233 #ifndef GLOB_APPERR
234 #define GLOB_APPERR	(GLOB_UNKNOWN-5)
235 #endif
236 #ifndef GLOB_NOSYS
237 #define GLOB_NOSYS	(GLOB_UNKNOWN-6)
238 #endif
239 #ifndef GLOB_ELOOP
240 #define GLOB_ELOOP	(GLOB_UNKNOWN-7)
241 #endif
242 #ifndef GLOB_EBUS
243 #define GLOB_EBUS	(GLOB_UNKNOWN-8)
244 #endif
245 #ifndef GLOB_EFAULT
246 #define GLOB_EFAULT	(GLOB_UNKNOWN-9)
247 #endif
248 
249 static const struct { int code; char* name; char* desc;} codes[] = {
250 	GLOB_UNKNOWN,	"UNKNOWN",	"unknown",
251 	0,		"OK",		"ok",
252 	GLOB_ABORTED,	"ABORTED",	"glob aborted",
253 	GLOB_NOMATCH,	"NOMATCH",	"no match",
254 	GLOB_NOSPACE,	"NOSPACE",	"no space left",
255 	GLOB_INTR,	"INTR",		"an interrupt occurred",
256 	GLOB_APPERR,	"APPERR",	"aplication error",
257 	GLOB_NOSYS,	"NOSYS",	"not a system call",
258 	GLOB_ELOOP,	"ELOOP",	"recursin loop",
259 	GLOB_EBUS,	"EBUS",		"bus error",
260 	GLOB_EFAULT,	"EFAULT",	"memory fault",
261 };
262 
263 static struct {
264 	int		errors;
265 	struct {
266 	int		count;
267 	int		error;
268 	int		position;
269 	}		ignore;
270 	int		lineno;
271 	int		ret;
272 	int		signals;
273 	int		unspecified;
274 	int		warnings;
275 	char*		stack;
276 	char*		which;
277 	jmp_buf		gotcha;
278 } state;
279 
280 static void
quote(char * s)281 quote(char* s)
282 {
283 	unsigned char*	u = (unsigned char*)s;
284 	int		c;
285 
286 	if (!u)
287 		printf("NIL");
288 	else if (!*u)
289 		printf("NULL");
290 	else {
291 		printf("\"");
292 		for (;;) {
293 			switch (c = *u++) {
294 			case 0:
295 				break;;
296 			case '\\':
297 				printf("\\\\");
298 				continue;
299 			case '"':
300 				printf("\\\"");
301 				continue;
302 			case '\n':
303 				printf("\\n");
304 				continue;
305 			case '\r':
306 				printf("\\r");
307 				continue;
308 			case '\t':
309 				printf("\\t");
310 				continue;
311 			default:
312 				if (!iscntrl(c) && isprint(c))
313 					putchar(c);
314 				else
315 					printf("\\x%02x", c);
316 				continue;
317 			}
318 			break;
319 		}
320 		printf("\"");
321 	}
322 }
323 
324 static void
report(char * comment,char * pat,char * msg,int flags,int unspecified)325 report(char* comment, char* pat, char* msg, int flags, int unspecified)
326 {
327 	printf("%d: ", state.lineno);
328 #if GLOB_LIST
329 	if (flags & GLOB_LIST)
330 		printf("LIST: ");
331 #endif
332 #if GLOB_STACK
333 	if (flags & GLOB_STACK)
334 		printf("STACK: ");
335 #endif
336 	if (unspecified) {
337 		state.unspecified++;
338 		printf(" unspecified behavior");
339 	}
340 	else
341 		state.errors++;
342 	if (pat)
343 		quote(pat);
344 	printf(" %s %s", state.which, comment);
345 	if (msg && comment[strlen(comment)-1] != '\n')
346 		printf(": %s: ", msg);
347 }
348 
349 static int
note(int level,int skip,char * msg)350 note(int level, int skip, char* msg)
351 {
352 	if (!skip) {
353 		printf("NOTE\t");
354 		if (msg)
355 			printf("%s: ", msg);
356 		printf("skipping lines %d", state.lineno);
357 	}
358 	return skip | level;
359 }
360 
361 static void
bad(char * comment,char * pat)362 bad(char* comment, char* pat)
363 {
364 	printf("bad test case ");
365 	report(comment, pat, NiL, 0, 0);
366 	exit(1);
367 }
368 
369 static int
hex(int c)370 hex(int c)
371 {
372 	return isdigit(c) ? (c - '0') : (c - (isupper(c) ? 'A' : 'a') + 10);
373 }
374 
375 static void
escape(char * s)376 escape(char* s)
377 {
378 	char*	t;
379 
380 	for (t = s; *t = *s; s++, t++) {
381 		if (*s != '\\')
382 			continue;
383 		switch (*++s) {
384 
385 		case 0:
386 			*++t = 0;
387 			break;
388 		case '\\':
389 			*t = '\\';
390 			break;
391 		case 'n':
392 			*t = '\n';
393 			break;
394 		case 'r':
395 			*t = '\r';
396 			break;
397 		case 't':
398 			*t = '\t';
399 			break;
400 		case 'x':
401 			if (!isxdigit(s[1]) || !isxdigit(s[2]))
402 				bad("bad \\x\n", NiL);
403 			*t = hex(*++s) << 4;
404 			*t |= hex(*++s);
405 			break;
406 		default:
407 			s--;
408 			break;
409 		}
410 	}
411 }
412 
413 static void
sigunblock(int s)414 sigunblock(int s)
415 {
416 #ifdef SIG_SETMASK
417 	int		op;
418 	sigset_t	mask;
419 
420 	sigemptyset(&mask);
421 	if (s) {
422 		sigaddset(&mask, s);
423 		op = SIG_UNBLOCK;
424 	}
425 	else
426 		op = SIG_SETMASK;
427 	sigprocmask(op, &mask, NiL);
428 #else
429 #ifdef sigmask
430 	sigsetmask(s ? (sigsetmask(0L) & ~sigmask(s)) : 0L);
431 #endif
432 #endif
433 }
434 
435 static void
gotcha(int sig)436 gotcha(int sig)
437 {
438 	signal(sig, gotcha);
439 	alarm(0);
440 	state.signals++;
441 	switch (sig) {
442 
443 	case SIGALRM:
444 		state.ret = GLOB_ELOOP;
445 		break;
446 	case SIGBUS:
447 		state.ret = GLOB_EBUS;
448 		break;
449 	case SIGSEGV:
450 		state.ret = GLOB_EFAULT;
451 		break;
452 	}
453 	sigunblock(sig);
454 	longjmp(state.gotcha, 1);
455 }
456 
457 static int
qstrcmp(const void * a,const void * b)458 qstrcmp(const void* a, const void* b)
459 {
460 	return strcmp(*(char**)a, *(char**)b);
461 }
462 
463 static char*
getline(void)464 getline(void)
465 {
466 	static char	buf[32 * 1024];
467 
468 	register char*	s = buf;
469 	register char*	e = &buf[sizeof(buf)];
470 	register char*	b;
471 
472 	for (;;) {
473 		if (!(b = fgets(s, e - s, stdin)))
474 			return 0;
475 		state.lineno++;
476 		s += strlen(s) - 1;
477 		if (*s != '\n')
478 			break;
479 		if (s == b || *(s - 1) != '\\') {
480 			*s = 0;
481 			break;
482 		}
483 		s--;
484 	}
485 	return buf;
486 }
487 
488 int
main(int argc,char ** argv)489 main(int argc, char** argv)
490 {
491 	int		flags;
492 	int		query;
493 	int		unspecified;
494 	int		kre;
495 	int		sre;
496 	int		okre;
497 	int		osre;
498 	int		ret;
499 	int		i;
500 	int		m;
501 	int		n;
502 	int		expected;
503 	int		got;
504 	int		ok;
505 	int		nmodes;
506 	int		extra;
507 	char*		spec;
508 	char*		pat;
509 	char*		p;
510 	char*		bp;
511 	char*		k;
512 	char*		s;
513 	char*		bs;
514 	char*		ans;
515 	char*		err;
516 	char*		msg;
517 	char**		v;
518 	char*		field[5];
519 	char		unit[64];
520 	char		pathbuf[1024];
521 	char		linkbuf[1024];
522 	char*		buf;
523 	char*		path;
524 	char*		pathmax;
525 	char*		work[16];
526 	char*		av[256];
527 	glob_t		gl;
528 	struct stat	st;
529 #if GLOB_DISC
530 	char		fignore[128];
531 #endif
532 #if GLOB_LIST
533 	globlist_t*	gi;
534 #endif
535 
536 	int		catch = 0;
537 	int		cwd = 0;
538 	int		level = 1;
539 	int		locale = 0;
540 	int		skip = 0;
541 	int		testno = 0;
542 	int		verbose = 0;
543 	int		working = 0;
544 
545 	static int	modes[] = {
546 				0,
547 #if GLOB_LIST
548 				GLOB_LIST,
549 #endif
550 #if GLOB_STACK
551 				GLOB_STACK,
552 #endif
553 #if GLOB_LIST && GLOB_STACK
554 				GLOB_LIST|GLOB_STACK,
555 #endif
556 	};
557 
558 	nmodes = elementsof(modes);
559 	printf("TEST\t%s", s = fmtident(id));
560 	p = unit;
561 	while (p < &unit[sizeof(unit)-1] && (*p = *s++) && !isspace(*p))
562 		p++;
563 	*p = 0;
564 	while ((p = *++argv) && *p == '-')
565 		for (;;) {
566 			switch (*++p) {
567 
568 			case 0:
569 				break;
570 			case 'c':
571 				catch = 1;
572 				printf(", catch");
573 				continue;
574 			case 'e':
575 				state.ignore.error = 1;
576 				printf(", ignore error code mismatches");
577 				continue;
578 			case 'h':
579 			case '?':
580 			case '-':
581 				printf(", help\n\n");
582 				help();
583 				return 2;
584 			case 'n':
585 				nmodes = 1;
586 				continue;
587 			case 'v':
588 				verbose = 1;
589 				printf(", verbose");
590 				continue;
591 			default:
592 				printf(", invalid option %c", *p);
593 				continue;
594 			}
595 			break;
596 		}
597 	if (p)
598 		printf(", argument(s) ignored");
599 	printf("\n");
600 	if (elementsof(unsupported) > 1) {
601 		printf("NOTE\tunsupported:");
602 		got = ' ';
603 		for (i = 0; i < elementsof(unsupported) - 1; i++) {
604 			printf("%c%s", got, unsupported[i]);
605 			got = ',';
606 		}
607 		printf("\n");
608 	}
609 	if (catch) {
610 		signal(SIGALRM, gotcha);
611 		signal(SIGBUS, gotcha);
612 		signal(SIGSEGV, gotcha);
613 	}
614 	path = pathbuf + 1;
615 	pathmax = &path[elementsof(pathbuf)-1];
616 	path[0] = 0;
617 	work[cwd] = path;
618 	work[cwd + 1] = 0;
619 	work[cwd + 2] = 0;
620 	ok = 0;
621 	extra = 0;
622 	while (p = buf = getline()) {
623 
624 	/* parse: */
625 
626 		if (*p == 0 || *p == '#')
627 			continue;
628 		if (*p == ':') {
629 			while (*++p == ' ');
630 			printf("NOTE	%s\n", p);
631 			continue;
632 		}
633 		if (*p == 'W') {
634 			while (*++p == '\t');
635 			if (*p) {
636 				i = p - buf;
637 				if (i > (cwd + 1) || i >= elementsof(work))
638 					bad("invalid workspace depth\n", NiL);
639 				if (working) {
640 					working = 0;
641 					cwd = 1;
642 					if (chdir("../.."))
643 						bad("cannot chdir\n", "../..");
644 					else if (verbose)
645 						printf("test %-3d chdir ../..\n", state.lineno);
646 				}
647 				if (i > cwd) {
648 					if (path[0] && access(path, F_OK)) {
649 						if (verbose) {
650 							printf("test %-3d mkdir ", state.lineno);
651 							quote(path);
652 							printf("\n");
653 						}
654 						if (mkdir(path, 0755))
655 							bad("cannot create work directory\n", path);
656 					}
657 				}
658 				else {
659 					if (access(path, F_OK)) {
660 						if (k) {
661 							if (verbose) {
662 								printf("test %-3d  link ", state.lineno);
663 								quote(path);
664 								printf(" ");
665 								quote(k);
666 								printf("\n");
667 							}
668 							if (symlink(k, path))
669 								bad("cannot create work link\n", path);
670 							k = 0;
671 						}
672 						else if (!streq(work[cwd - 1], ".")) {
673 							if (verbose) {
674 								printf("test %-3d  file ", state.lineno);
675 								quote(path);
676 								printf("\n");
677 							}
678 							if (close(creat(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)))
679 								bad("cannot create work file\n", path);
680 						}
681 					}
682 					cwd = i - 1;
683 				}
684 				s = work[cwd];
685 				*(s - 1) = '/';
686 				k = 0;
687 				while (s < pathmax && *p) {
688 					if (*p == '\t') {
689 						k = linkbuf;
690 						while (k < &linkbuf[sizeof(linkbuf) - 1] && (*k = *++p))
691 							k++;
692 						*k = 0;
693 						k = linkbuf;
694 						break;
695 					}
696 					*s++ = *p++;
697 				}
698 				*s++ = 0;
699 				work[cwd = i] = s;
700 			}
701 			continue;
702 		}
703 		if (work[2]) {
704 			if (!streq(work[cwd-1], ".") && access(path, F_OK))
705 				if (k) {
706 					if (verbose) {
707 						printf("test %-3d  link ", state.lineno);
708 						quote(path);
709 						printf(" ");
710 						quote(k);
711 						printf("\n");
712 					}
713 					if (symlink(k, path))
714 						bad("cannot create work link\n", path);
715 				}
716 				else {
717 					if (verbose) {
718 						printf("test %-3d  file ", state.lineno);
719 						quote(path);
720 						printf("\n");
721 					}
722 					if (close(creat(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)))
723 						bad("cannot create work file\n", path);
724 				}
725 			*(work[2] - 1) = 0;
726 			if (!working)
727 				working = 1;
728 			else if (chdir("../.."))
729 				bad("cannot chdir\n", "../..");
730 			else if (verbose)
731 				printf("test %-3d chdir ..\n", state.lineno);
732 			if (chdir(path))
733 				bad("cannot chdir\n", path);
734 			else if (verbose) {
735 				printf("test %-3d chdir ", state.lineno);
736 				quote(path);
737 				printf("\n");
738 			}
739 			work[2] = 0;
740 		}
741 #if GLOB_DISC
742 		fignore[0] = 0;
743 #endif
744 		i = 0;
745 		field[i++] = p;
746 		for (;;) {
747 			switch (*p++) {
748 			case 0:
749 				p--;
750 				goto checkfield;
751 			case '\t':
752 				*(p - 1) = 0;
753 			checkfield:
754 				s = field[i - 1];
755 				if (streq(s, "NIL"))
756 					field[i - 1] = 0;
757 				else if (streq(s, "NULL"))
758 					*s = 0;
759 				while (*p == '\t')
760 					p++;
761 				if (!*p)
762 					break;
763 				if (i >= elementsof(field))
764 					bad("too many fields\n", NiL);
765 				field[i++] = p;
766 				/*FALLTHROUGH*/
767 			default:
768 				continue;
769 			}
770 			break;
771 		}
772 		if (!(spec = field[0]))
773 			bad("NIL spec\n", NiL);
774 
775 	/* interpret: */
776 
777 		flags = 0;
778 		query = unspecified = kre = sre = 0;
779 		for (p = spec; *p; p++) {
780 			switch (*p) {
781 			case 'C':
782 				if (!query && !(skip & level))
783 					bad("locale query expected\n", NiL);
784 				query = 0;
785 				if (locale)
786 					bad("locale nesting not supported\n", NiL);
787 				if (i != 2)
788 					bad("locale field expected\n", NiL);
789 				if (!(skip & level)) {
790 #if defined(LC_COLLATE) && defined(LC_CTYPE)
791 					s = field[1];
792 					if (!s || streq(s, "POSIX"))
793 						s = "C";
794 					if (!(ans = setlocale(LC_COLLATE, s)) || streq(ans, "C") || streq(ans, "POSIX") || !(ans = setlocale(LC_CTYPE, s)) || streq(ans, "C") || streq(ans, "POSIX"))
795 						skip = note(level, skip, s);
796 					else {
797 						printf("NOTE	\"%s\" locale\n", s);
798 						locale = level;
799 					}
800 #else
801 					skip = note(level, skip, "locales not supported");
802 #endif
803 				}
804 				flags |= NOTEST;
805 				continue;
806 			case 'E':
807 				flags |= GLOB_ERR;
808 				continue;
809 			case 'I':
810 #if GLOB_DISC
811 				if (field[1])
812 					strncpy(fignore, field[1], sizeof(fignore) - 1);
813 				else
814 					fignore[0] = 0;
815 #endif
816 				flags |= NOTEST;
817 				continue;
818 			case 'K':
819 				kre = 1;
820 				continue;
821 			case 'S':
822 				sre = 1;
823 				continue;
824 
825 			case 'a':
826 				flags |= GLOB_APPEND;
827 				continue;
828 			case 'b':
829 				flags |= GLOB_BRACE;
830 				continue;
831 			case 'c':
832 				flags |= GLOB_COMPLETE;
833 				continue;
834 			case 'e':
835 				flags |= GLOB_NOESCAPE;
836 				continue;
837 			case 'i':
838 				flags |= GLOB_ICASE;
839 				continue;
840 			case 'm':
841 				flags |= GLOB_MARK;
842 				continue;
843 			case 'n':
844 				flags |= GLOB_NOCHECK;
845 				continue;
846 			case 'r':
847 				flags |= GLOB_STARSTAR;
848 				continue;
849 			case 's':
850 				flags |= GLOB_NOSORT;
851 				continue;
852 			case 'u':
853 				unspecified = 1;
854 				continue;
855 
856 			case 'x':
857 #if GLOB_VERSION >= 20060717L
858 				extra = 15;
859 #endif
860 				continue;
861 
862 			case '{':
863 				level <<= 1;
864 				if (skip & (level >> 1)) {
865 					skip |= level;
866 					flags |= NOTEST;
867 				}
868 				else {
869 					skip &= ~level;
870 					query = 1;
871 				}
872 				continue;
873 			case '}':
874 				if (level == 1)
875 					bad("invalid {...} nesting\n", NiL);
876 				else {
877 					if ((skip & level) && !(skip & (level>>1)))
878 						printf("-%d\n", state.lineno);
879 #if defined(LC_COLLATE) && defined(LC_CTYPE)
880 					else if (locale & level) {
881 						locale = 0;
882 						if (!(skip & level)) {
883 							s = "C";
884 							setlocale(LC_COLLATE, s);
885 							setlocale(LC_CTYPE, s);
886 							printf("NOTE	\"%s\" locale\n", s);
887 						}
888 					}
889 #endif
890 					level >>= 1;
891 				}
892 				flags |= NOTEST;
893 				continue;
894 			default:
895 				bad("bad spec\n", spec);
896 				break;
897 
898 			}
899 			break;
900 		}
901 		if (flags == NOTEST || !sre && !kre)
902 			continue;
903 		if (i < 3)
904 			bad("too few fields\n", NiL);
905 		if (skip & level)
906 			continue;
907 		while (i < elementsof(field))
908 			field[i++] = 0;
909 		if (pat = field[1])
910 			escape(pat);
911 		if (ans = field[3])
912 			escape(ans);
913 		okre = kre;
914 		osre = sre;
915 		for (m = 0; m < nmodes; m++) {
916 			if (modes[m]) {
917 				if ((flags & modes[m]) == modes[m])
918 					continue;
919 				flags |= modes[m];
920 			}
921 			err = field[2];
922 			msg = field[4];
923 			kre = okre;
924 			sre = osre;
925 			fflush(stdout);
926 
927 	/* execute: */
928 
929 			if (sre) {
930 				state.which = "SRE";
931 				sre = 0;
932 #ifdef GLOB_AUGMENTED
933 				flags &= ~GLOB_AUGMENTED;
934 #endif
935 			}
936 #ifdef GLOB_AUGMENTED
937 			else if (kre) {
938 				state.which = "KRE";
939 				kre = 0;
940 				flags |= GLOB_AUGMENTED;
941 			}
942 #endif
943 			else
944 				continue;
945 			if (!m && !query && verbose) {
946 				printf("test %-3d ", state.lineno);
947 				quote(pat);
948 				printf("\n");
949 			}
950 			if (!ok || !(flags & GLOB_APPEND)) {
951 				if (ok) {
952 					ok = 0;
953 					globfree(&gl);
954 				}
955 				memset(&gl, 0, sizeof(gl));
956 			}
957 #if GLOB_DISC
958 			if (fignore[0]) {
959 				gl.gl_fignore = (const char*)fignore;
960 				flags |= GLOB_DISC;
961 			}
962 			else
963 				gl.gl_fignore = 0;
964 #if GLOB_VERSION >= 20060717L
965 			if (gl.gl_extra = extra)
966 				flags |= GLOB_DISC;
967 #endif
968 #endif
969 			if (!query)
970 				testno++;
971 			if (catch) {
972 				if (setjmp(state.gotcha))
973 					ret = state.ret;
974 				else {
975 					alarm(LOOPED);
976 					ret = glob(pat, flags, 0, &gl);
977 					alarm(0);
978 				}
979 			}
980 			else
981 				ret = glob(pat, flags, 0, &gl);
982 			if (ret == 0) {
983 				ok = 1;
984 				if (!gl.gl_pathc)
985 					ret = GLOB_NOMATCH;
986 			}
987 			expected = got = 0;
988 			for (i = 1; i < elementsof(codes); i++) {
989 				if (streq(err, codes[i].name))
990 					expected = i;
991 				if (ret == codes[i].code)
992 					got = i;
993 			}
994 			if (expected != got) {
995 				if (query)
996 					skip = note(level, skip, msg);
997 				else if (state.ignore.error)
998 					state.ignore.count++;
999 				else {
1000 					report("return failed: ", pat, msg, flags, unspecified);
1001 					printf("%s expected, %s returned", codes[expected].name, codes[got].name);
1002 					if (!ret)
1003 						printf(" with %d match%s", gl.gl_pathc, gl.gl_pathc == 1 ? "" : "es");
1004 					printf("\n");
1005 				}
1006 			}
1007 			else if (ret != GLOB_NOMATCH) {
1008 #if GLOB_LIST
1009 				if (flags & GLOB_LIST) {
1010 					n = 0;
1011 					for (gi = gl.gl_list; gi; gi = gi->gl_next) {
1012 						if (n >= (elementsof(av) - 1))
1013 							break;
1014 						av[n++] = gi->gl_path + extra;
1015 					}
1016 					av[n] = 0;
1017 					v = av;
1018 				}
1019 				else
1020 #endif
1021 				{
1022 					n = gl.gl_pathc;
1023 					v = gl.gl_pathv;
1024 				}
1025 				if (verbose) {
1026 					printf("    ");
1027 					for (i = 0; i < n; i++)
1028 						printf(" %s", v[i]);
1029 					printf("\n");
1030 				}
1031 				if (flags & (GLOB_LIST|GLOB_NOSORT)) {
1032 					qsort(v, n, sizeof(*v), qstrcmp);
1033 					if ((flags & GLOB_STARSTAR) && !(gl.gl_flags & GLOB_STARSTAR))
1034 						v[n = quniq(v, n)] = 0;
1035 				}
1036 				if (!ans)
1037 					ans = "";
1038 				bs = s = ans;
1039 				bp = p = "";
1040 				for (i = 0; i < n; i++) {
1041 					bp = p = v[i];
1042 					bs = s;
1043 					while (*p == *s && *s && *s != ' ') {
1044 						p++;
1045 						s++;
1046 					}
1047 					if (*p || *s && *s != ' ')
1048 						break;
1049 					while (*s == ' ')
1050 						s++;
1051 				}
1052 				if (*p || *s) {
1053 					if (!*p && i >= n)
1054 						bp = 0;
1055 					if (!*s)
1056 						bs = s = 0;
1057 					else {
1058 						while (*s && *s != ' ')
1059 							s++;
1060 						if (*s == ' ')
1061 							*s = 0;
1062 						else
1063 							s = 0;
1064 					}
1065 					report("match failed: ", pat, msg, flags, unspecified);
1066 					quote(bs);
1067 					printf(" expected, ");
1068 					quote(bp);
1069 					printf(" returned\n");
1070 					if (s)
1071 						*s = 0;
1072 				}
1073 			}
1074 			if (flags & GLOB_APPEND)
1075 				break;
1076 			flags &= ~modes[m];
1077 		}
1078 	}
1079 	printf("TEST\t%s, %d test%s", unit, testno, testno == 1 ? "" : "s");
1080 	if (state.ignore.count)
1081 		printf(", %d ignored mismatche%s", state.ignore.count, state.ignore.count == 1 ? "" : "s");
1082 	if (state.warnings)
1083 		printf(", %d warning%s", state.warnings, state.warnings == 1 ? "" : "s");
1084 	if (state.unspecified)
1085 		printf(", %d unspecified difference%s", state.unspecified, state.unspecified == 1 ? "" : "s");
1086 	if (state.signals)
1087 		printf(", %d signal%s", state.signals, state.signals == 1 ? "" : "s");
1088 	printf(", %d error%s\n", state.errors, state.errors == 1 ? "" : "s");
1089 	return 0;
1090 }
1091