xref: /netbsd-src/tests/lib/libc/regex/t_regex_att.c (revision e78273b60de8c5f53f60507342f14f15f220e0b4)
1*e78273b6Schristos /*	$NetBSD: t_regex_att.c,v 1.4 2021/02/23 16:00:37 christos Exp $	*/
2e7d17825Sjmmv 
3e7d17825Sjmmv /*-
4e7d17825Sjmmv  * Copyright (c) 2011 The NetBSD Foundation, Inc.
5e7d17825Sjmmv  * All rights reserved.
6e7d17825Sjmmv  *
7e7d17825Sjmmv  * This code is derived from software contributed to The NetBSD Foundation
8e7d17825Sjmmv  * by Christos Zoulas.
9e7d17825Sjmmv  *
10e7d17825Sjmmv  * Redistribution and use in source and binary forms, with or without
11e7d17825Sjmmv  * modification, are permitted provided that the following conditions
12e7d17825Sjmmv  * are met:
13e7d17825Sjmmv  * 1. Redistributions of source code must retain the above copyright
14e7d17825Sjmmv  *    notice, this list of conditions and the following disclaimer.
15e7d17825Sjmmv  * 2. Redistributions in binary form must reproduce the above copyright
16e7d17825Sjmmv  *    notice, this list of conditions and the following disclaimer in the
17e7d17825Sjmmv  *    documentation and/or other materials provided with the distribution.
18e7d17825Sjmmv  * 3. All advertising materials mentioning features or use of this software
19e7d17825Sjmmv  *    must display the following acknowledgement:
20e7d17825Sjmmv  *        This product includes software developed by the NetBSD
21e7d17825Sjmmv  *        Foundation, Inc. and its contributors.
22e7d17825Sjmmv  * 4. Neither the name of The NetBSD Foundation nor the names of its
23e7d17825Sjmmv  *    contributors may be used to endorse or promote products derived
24e7d17825Sjmmv  *    from this software without specific prior written permission.
25e7d17825Sjmmv  *
26e7d17825Sjmmv  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27e7d17825Sjmmv  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28e7d17825Sjmmv  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29e7d17825Sjmmv  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30e7d17825Sjmmv  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31e7d17825Sjmmv  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32e7d17825Sjmmv  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33e7d17825Sjmmv  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34e7d17825Sjmmv  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35e7d17825Sjmmv  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36e7d17825Sjmmv  * POSSIBILITY OF SUCH DAMAGE.
37e7d17825Sjmmv  */
38e7d17825Sjmmv 
39e7d17825Sjmmv #include <sys/cdefs.h>
40*e78273b6Schristos __RCSID("$NetBSD: t_regex_att.c,v 1.4 2021/02/23 16:00:37 christos Exp $");
41e7d17825Sjmmv 
42e7d17825Sjmmv #include <sys/param.h>
43e7d17825Sjmmv 
44e7d17825Sjmmv #include <atf-c.h>
4532eb699fSchristos #include <ctype.h>
4632eb699fSchristos #include <regex.h>
4732eb699fSchristos #include <stdio.h>
4832eb699fSchristos #include <stdlib.h>
4932eb699fSchristos #include <string.h>
5032eb699fSchristos #include <util.h>
5132eb699fSchristos #include <vis.h>
52e7d17825Sjmmv 
53e7d17825Sjmmv static const char sep[] = "\r\n\t";
54e7d17825Sjmmv static const char delim[3] = "\\\\\0";
55e7d17825Sjmmv 
56e7d17825Sjmmv 
57e7d17825Sjmmv static void
fail(const char * pattern,const char * input,size_t lineno)58e7d17825Sjmmv fail(const char *pattern, const char *input, size_t lineno) {
59e7d17825Sjmmv 	fprintf(stderr,
60e7d17825Sjmmv 	    "skipping failed test at line %zu (pattern=%s, input=%s)\n",
61e7d17825Sjmmv 	    lineno, pattern, input);
62e7d17825Sjmmv }
63e7d17825Sjmmv 
64e7d17825Sjmmv static int
bug(const char * pattern,const char * input,size_t lineno)65e7d17825Sjmmv bug(const char *pattern, const char *input, size_t lineno) {
66e7d17825Sjmmv 	static const struct {
67e7d17825Sjmmv 		const char *p;
68e7d17825Sjmmv 		const char *i;
69e7d17825Sjmmv 	} b[] = {
70e7d17825Sjmmv #if defined(REGEX_SPENCER)
71e7d17825Sjmmv 		/*
72e7d17825Sjmmv 		 * The default libc implementation by Henry Spencer
73e7d17825Sjmmv 		 */
74e7d17825Sjmmv 		{ "a[-]?c", "ac" },			// basic.dat
75e7d17825Sjmmv 		{ "(a*)*", "a" },			// categorization.dat
76e7d17825Sjmmv 		{ "(aba|a*b)*", "ababa" },		// categorization.dat
77e7d17825Sjmmv 		{ "\\(a\\(b\\)*\\)*\\2", "abab" },	// categorization.dat
78e7d17825Sjmmv 		{ "(a*)*", "aaaaaa" },			// nullsubexpression.dat
79e7d17825Sjmmv 		{ "(a*)*", "aaaaaax" },			// nullsubexpression.dat
80e7d17825Sjmmv 		{ "(a*)+", "a" },			// nullsubexpression.dat
81e7d17825Sjmmv 		{ "(a*)+", "aaaaaa" },			// nullsubexpression.dat
82e7d17825Sjmmv 		{ "(a*)+", "aaaaaax" },			// nullsubexpression.dat
83e7d17825Sjmmv 		{ "([a]*)*", "a" },			// nullsubexpression.dat
84e7d17825Sjmmv 		{ "([a]*)*", "aaaaaa" },		// nullsubexpression.dat
85e7d17825Sjmmv 		{ "([a]*)*", "aaaaaax" },		// nullsubexpression.dat
86e7d17825Sjmmv 		{ "([a]*)+", "a" },			// nullsubexpression.dat
87e7d17825Sjmmv 		{ "([a]*)+", "aaaaaa" },		// nullsubexpression.dat
88e7d17825Sjmmv 		{ "([a]*)+", "aaaaaax" },		// nullsubexpression.dat
89e7d17825Sjmmv 		{ "([^b]*)*", "a" },			// nullsubexpression.dat
90e7d17825Sjmmv 		{ "([^b]*)*", "aaaaaa" },		// nullsubexpression.dat
91e7d17825Sjmmv 		{ "([^b]*)*", "aaaaaab" },		// nullsubexpression.dat
92e7d17825Sjmmv 		{ "([ab]*)*", "a" },			// nullsubexpression.dat
93e7d17825Sjmmv 		{ "([ab]*)*", "aaaaaa" },		// nullsubexpression.dat
94e7d17825Sjmmv 		{ "([ab]*)*", "ababab" },		// nullsubexpression.dat
95e7d17825Sjmmv 		{ "([ab]*)*", "bababa" },		// nullsubexpression.dat
96e7d17825Sjmmv 		{ "([ab]*)*", "b" },			// nullsubexpression.dat
97e7d17825Sjmmv 		{ "([ab]*)*", "bbbbbb" },		// nullsubexpression.dat
98e7d17825Sjmmv 		{ "([ab]*)*", "aaaabcde" },		// nullsubexpression.dat
99e7d17825Sjmmv 		{ "([^a]*)*", "b" },			// nullsubexpression.dat
100e7d17825Sjmmv 		{ "([^a]*)*", "bbbbbb" },		// nullsubexpression.dat
101e7d17825Sjmmv 		{ "([^ab]*)*", "ccccxx" },		// nullsubexpression.dat
102e7d17825Sjmmv 		{ "\\(a*\\)*\\(x\\)", "ax" },		// nullsubexpression.dat
103e7d17825Sjmmv 		{ "\\(a*\\)*\\(x\\)", "axa" },		// nullsubexpression.dat
104e7d17825Sjmmv 		{ "\\(a*\\)*\\(x\\)\\(\\1\\)", "x" },	// nullsubexpression.dat
105e7d17825Sjmmv /* crash! */	{ "\\(a*\\)*\\(x\\)\\(\\1\\)", "ax" },	// nullsubexpression.dat
106e7d17825Sjmmv /* crash! */	{ "\\(a*\\)*\\(x\\)\\(\\1\\)\\(x\\)", "axxa" },	// ""
107e7d17825Sjmmv 		{ "(a*)*(x)",  "ax" },			// nullsubexpression.dat
108e7d17825Sjmmv 		{ "(a*)*(x)",  "axa" },			// nullsubexpression.dat
109e7d17825Sjmmv 		{ "(a*)+(x)",  "ax" },			// nullsubexpression.dat
110e7d17825Sjmmv 		{ "(a*)+(x)",  "axa" },			// nullsubexpression.dat
111e7d17825Sjmmv 		{ "((a|ab)(c|bcd))(d*)", "abcd" },	// forcedassoc.dat
112e7d17825Sjmmv 		{ "((a|ab)(bcd|c))(d*)", "abcd" },	// forcedassoc.dat
113e7d17825Sjmmv 		{ "((ab|a)(c|bcd))(d*)", "abcd" },	// forcedassoc.dat
114e7d17825Sjmmv 		{ "((ab|a)(bcd|c))(d*)", "abcd" },	// forcedassoc.dat
115e7d17825Sjmmv 		{ "((a*)(b|abc))(c*)", "abc" },		// forcedassoc.dat
116e7d17825Sjmmv 		{ "((a*)(abc|b))(c*)", "abc" },		// forcedassoc.dat
117e7d17825Sjmmv 		{ "((..)|(.)){2}", "aaa" },		// repetition.dat
118e7d17825Sjmmv 		{ "((..)|(.)){3}", "aaa" },		// repetition.dat
119e7d17825Sjmmv 		{ "((..)|(.)){3}", "aaaa" },		// repetition.dat
120e7d17825Sjmmv 		{ "((..)|(.)){3}", "aaaaa" },		// repetition.dat
121e7d17825Sjmmv 		{ "X(.?){0,}Y", "X1234567Y" },		// repetition.dat
122e7d17825Sjmmv 		{ "X(.?){1,}Y", "X1234567Y" },		// repetition.dat
123e7d17825Sjmmv 		{ "X(.?){2,}Y", "X1234567Y" },		// repetition.dat
124e7d17825Sjmmv 		{ "X(.?){3,}Y", "X1234567Y" },		// repetition.dat
125e7d17825Sjmmv 		{ "X(.?){4,}Y", "X1234567Y" },		// repetition.dat
126e7d17825Sjmmv 		{ "X(.?){5,}Y", "X1234567Y" },		// repetition.dat
127e7d17825Sjmmv 		{ "X(.?){6,}Y", "X1234567Y" },		// repetition.dat
128e7d17825Sjmmv 		{ "X(.?){7,}Y", "X1234567Y" },		// repetition.dat
129e7d17825Sjmmv 		{ "X(.?){0,8}Y", "X1234567Y" },		// repetition.dat
130e7d17825Sjmmv 		{ "X(.?){1,8}Y", "X1234567Y" },		// repetition.dat
131e7d17825Sjmmv 		{ "X(.?){2,8}Y", "X1234567Y" },		// repetition.dat
132e7d17825Sjmmv 		{ "X(.?){3,8}Y", "X1234567Y" },		// repetition.dat
133e7d17825Sjmmv 		{ "X(.?){4,8}Y", "X1234567Y" },		// repetition.dat
134e7d17825Sjmmv 		{ "X(.?){5,8}Y", "X1234567Y" },		// repetition.dat
135e7d17825Sjmmv 		{ "X(.?){6,8}Y", "X1234567Y" },		// repetition.dat
136e7d17825Sjmmv 		{ "X(.?){7,8}Y", "X1234567Y" },		// repetition.dat
137e7d17825Sjmmv 		{ "(a|ab|c|bcd){0,}(d*)", "ababcd" },	// repetition.dat
138e7d17825Sjmmv 		{ "(a|ab|c|bcd){1,}(d*)", "ababcd" },	// repetition.dat
139e7d17825Sjmmv 		{ "(a|ab|c|bcd){2,}(d*)", "ababcd" },	// repetition.dat
140e7d17825Sjmmv 		{ "(a|ab|c|bcd){3,}(d*)", "ababcd" },	// repetition.dat
141e7d17825Sjmmv 		{ "(a|ab|c|bcd){1,10}(d*)", "ababcd" },	// repetition.dat
142e7d17825Sjmmv 		{ "(a|ab|c|bcd){2,10}(d*)", "ababcd" },	// repetition.dat
143e7d17825Sjmmv 		{ "(a|ab|c|bcd){3,10}(d*)", "ababcd" },	// repetition.dat
144e7d17825Sjmmv 		{ "(a|ab|c|bcd)*(d*)", "ababcd" },	// repetition.dat
145e7d17825Sjmmv 		{ "(a|ab|c|bcd)+(d*)", "ababcd" },	// repetition.dat
146e7d17825Sjmmv 		{ "(ab|a|c|bcd){0,}(d*)", "ababcd" },	// repetition.dat
147e7d17825Sjmmv 		{ "(ab|a|c|bcd){1,}(d*)", "ababcd" },	// repetition.dat
148e7d17825Sjmmv 		{ "(ab|a|c|bcd){2,}(d*)", "ababcd" },	// repetition.dat
149e7d17825Sjmmv 		{ "(ab|a|c|bcd){3,}(d*)", "ababcd" },	// repetition.dat
150e7d17825Sjmmv 		{ "(ab|a|c|bcd){1,10}(d*)", "ababcd" },	// repetition.dat
151e7d17825Sjmmv 		{ "(ab|a|c|bcd){2,10}(d*)", "ababcd" },	// repetition.dat
152e7d17825Sjmmv 		{ "(ab|a|c|bcd){3,10}(d*)", "ababcd" },	// repetition.dat
153e7d17825Sjmmv 		{ "(ab|a|c|bcd)*(d*)", "ababcd" },	// repetition.dat
154e7d17825Sjmmv 		{ "(ab|a|c|bcd)+(d*)", "ababcd" },	// repetition.dat
155e7d17825Sjmmv #elif defined(REGEX_TRE)
156e7d17825Sjmmv 		{ "a[-]?c", "ac" },			// basic.dat
157e7d17825Sjmmv 		{ "a\\(b\\)*\\1", "a" },		// categorization.dat
158e7d17825Sjmmv 		{ "a\\(b\\)*\\1", "abab" },		// categorization.dat
159e7d17825Sjmmv 		{ "\\(a\\(b\\)*\\)*\\2", "abab" },	// categorization.dat
160e7d17825Sjmmv 		{ "\\(a*\\)*\\(x\\)\\(\\1\\)", "ax" },	// categorization.dat
161e7d17825Sjmmv 		{ "\\(a*\\)*\\(x\\)\\(\\1\\)\\(x\\)", "axxa" },	// ""
162e7d17825Sjmmv 		{ "((..)|(.))*", "aa" },		// repetition.dat
163e7d17825Sjmmv 		{ "((..)|(.))*", "aaa" },		// repetition.dat
164e7d17825Sjmmv 		{ "((..)|(.))*", "aaaaa" },		// repetition.dat
165e7d17825Sjmmv 		{ "X(.?){7,}Y", "X1234567Y" },		// repetition.dat
166e7d17825Sjmmv #else
167e7d17825Sjmmv 		{ "", "" }
168e7d17825Sjmmv #endif
169e7d17825Sjmmv 	};
170e7d17825Sjmmv 
171e7d17825Sjmmv 	for (size_t i = 0; i < __arraycount(b); i++) {
172e7d17825Sjmmv 		if (strcmp(pattern, b[i].p) == 0 &&
173e7d17825Sjmmv 		    strcmp(input, b[i].i) == 0) {
174e7d17825Sjmmv 			fail(pattern, input, lineno);
175e7d17825Sjmmv 			return 1;
176e7d17825Sjmmv 		}
177e7d17825Sjmmv 	}
178e7d17825Sjmmv 	return 0;
179e7d17825Sjmmv }
180e7d17825Sjmmv 
181e7d17825Sjmmv #ifdef REGEX_SPENCER
182e7d17825Sjmmv #define HAVE_BRACES	1
183e7d17825Sjmmv #define HAVE_MINIMAL	0
184e7d17825Sjmmv #endif
185e7d17825Sjmmv #ifndef HAVE_BRACES
186e7d17825Sjmmv #define HAVE_BRACES	1
187e7d17825Sjmmv #endif
188e7d17825Sjmmv #ifndef HAVE_MINIMAL
189e7d17825Sjmmv #define HAVE_MINIMAL	1
190e7d17825Sjmmv #endif
191e7d17825Sjmmv 
192e7d17825Sjmmv static int
optional(const char * s)193e7d17825Sjmmv optional(const char *s)
194e7d17825Sjmmv {
195e7d17825Sjmmv 	static const struct{
196e7d17825Sjmmv 		const char *n;
197e7d17825Sjmmv 		int v;
198e7d17825Sjmmv 	} nv[]= {
199e7d17825Sjmmv 		{ "[[<element>]] not supported", HAVE_BRACES },
200e7d17825Sjmmv 		{ "no *? +? mimimal match ops", HAVE_MINIMAL },
201e7d17825Sjmmv 	};
202e7d17825Sjmmv 
203e7d17825Sjmmv 	for (size_t i = 0; i < __arraycount(nv); i++)
204e7d17825Sjmmv 		if (strcmp(nv[i].n, s) == 0) {
205e7d17825Sjmmv 			if (nv[i].v)
206e7d17825Sjmmv 				return 0;
207e7d17825Sjmmv 			fprintf(stderr, "skipping unsupported [%s] tests\n", s);
208e7d17825Sjmmv 			return 1;
209e7d17825Sjmmv 		}
210e7d17825Sjmmv 
211e7d17825Sjmmv 	ATF_REQUIRE_MSG(0, "Unknown feature: %s", s);
212e7d17825Sjmmv 	return 0;
213e7d17825Sjmmv }
214e7d17825Sjmmv 
215e7d17825Sjmmv static int
unsupported(const char * s)216e7d17825Sjmmv unsupported(const char *s)
217e7d17825Sjmmv {
218e7d17825Sjmmv 	static const char *we[] = {
219e7d17825Sjmmv #if defined(REGEX_SPENCER)
220e7d17825Sjmmv 		"ASSOCIATIVITY=left",		// have right associativity
221e7d17825Sjmmv 		"SUBEXPRESSION=precedence",	// have grouping subexpression
222e7d17825Sjmmv 		"REPEAT_LONGEST=last",		// have first repeat longest
223e7d17825Sjmmv 		"BUG=alternation-order",	// don't have it
224e7d17825Sjmmv 		"BUG=first-match",		// don't have it
225e7d17825Sjmmv 		"BUG=nomatch-match",		// don't have it
226e7d17825Sjmmv 		"BUG=repeat-any",		// don't have it
227e7d17825Sjmmv 		"BUG=range-null",		// don't have it
228e7d17825Sjmmv 		"BUG=repeat-null-unknown",	// don't have it
229e7d17825Sjmmv 		"BUG=repeat-null",		// don't have it
230e7d17825Sjmmv 		"BUG=repeat-artifact",		// don't have it
231e7d17825Sjmmv 		"BUG=subexpression-first",	// don't have it
232e7d17825Sjmmv #elif defined(REGEX_TRE)
233e7d17825Sjmmv 		"ASSOCIATIVITY=right",		// have left associativity
234e7d17825Sjmmv 		"SUBEXPRESSION=grouping",	// have precedence subexpression
235e7d17825Sjmmv 		"REPEAT_LONGEST=first",		// have last repeat longest
236e7d17825Sjmmv 		"LENGTH=first",			// have last length
237e7d17825Sjmmv 		"BUG=alternation-order",	// don't have it
238e7d17825Sjmmv 		"BUG=first-match",		// don't have it
239e7d17825Sjmmv 		"BUG=range-null",		// don't have it
240e7d17825Sjmmv 		"BUG=repeat-null",		// don't have it
241e7d17825Sjmmv 		"BUG=repeat-artifact",		// don't have it
242e7d17825Sjmmv 		"BUG=subexpression-first",	// don't have it
243e7d17825Sjmmv 		"BUG=repeat-short",		// don't have it
244e7d17825Sjmmv #endif
245e7d17825Sjmmv 	};
246e7d17825Sjmmv 
247e7d17825Sjmmv 	if (s == NULL)
248e7d17825Sjmmv 		return 0;
249e7d17825Sjmmv 
250e7d17825Sjmmv 	while (*s == '#' || isspace((unsigned char)*s))
251e7d17825Sjmmv 		s++;
252e7d17825Sjmmv 
253e7d17825Sjmmv 	for (size_t i = 0; i < __arraycount(we); i++)
254e7d17825Sjmmv 		if (strcmp(we[i], s) == 0)
255e7d17825Sjmmv 			return 1;
256e7d17825Sjmmv 	return 0;
257e7d17825Sjmmv }
258e7d17825Sjmmv 
259e7d17825Sjmmv static void
geterror(const char * s,int * comp,int * exec)260e7d17825Sjmmv geterror(const char *s, int *comp, int *exec)
261e7d17825Sjmmv {
262e7d17825Sjmmv 	static const struct {
263e7d17825Sjmmv 		const char *n;
264e7d17825Sjmmv 		int v;
265e7d17825Sjmmv 		int ce;
266e7d17825Sjmmv 	} nv[] = {
267e7d17825Sjmmv #define COMP 1
268e7d17825Sjmmv #define EXEC 2
269e7d17825Sjmmv 		{ "OK", 0, COMP|EXEC },
270e7d17825Sjmmv #define _DO(a, b)	{ # a, REG_ ## a, b },
271e7d17825Sjmmv 		_DO(NOMATCH, EXEC)
272e7d17825Sjmmv 		_DO(BADPAT, COMP)
273e7d17825Sjmmv 		_DO(ECOLLATE, COMP)
274e7d17825Sjmmv 		_DO(ECTYPE, COMP)
275e7d17825Sjmmv 		_DO(EESCAPE, COMP)
276e7d17825Sjmmv 		_DO(ESUBREG, COMP)
277e7d17825Sjmmv 		_DO(EBRACK, COMP)
278e7d17825Sjmmv 		_DO(EPAREN, COMP)
279e7d17825Sjmmv 		_DO(EBRACE, COMP)
280e7d17825Sjmmv 		_DO(BADBR, COMP)
281e7d17825Sjmmv 		_DO(ERANGE, COMP)
282e7d17825Sjmmv 		_DO(ESPACE, EXEC)
283e7d17825Sjmmv 		_DO(BADRPT, COMP)
284e7d17825Sjmmv 		_DO(EMPTY, COMP)
285e7d17825Sjmmv 		_DO(ASSERT, COMP)
286e7d17825Sjmmv 		_DO(INVARG, COMP)
287*e78273b6Schristos #ifdef REG_ENOSYS
288e7d17825Sjmmv 		_DO(ENOSYS, COMP)
289*e78273b6Schristos #endif
290*e78273b6Schristos #ifdef REG_ILLSEQ
291*e78273b6Schristos 		_DO(ILLSEQ, COMP)
292*e78273b6Schristos #endif
293e7d17825Sjmmv #undef _DO
294e7d17825Sjmmv 	};
295e7d17825Sjmmv 	*comp = 0;
296e7d17825Sjmmv 	*exec = 0;
297e7d17825Sjmmv 	for (size_t i = 0; i < __arraycount(nv); i++)
298e7d17825Sjmmv 		if (strcmp(s, nv[i].n) == 0) {
299e7d17825Sjmmv 			if (nv[i].ce & COMP)
300e7d17825Sjmmv 				*comp = nv[i].v;
301e7d17825Sjmmv 			if (nv[i].ce & EXEC)
302e7d17825Sjmmv 				*exec = nv[i].v;
303e7d17825Sjmmv 			return;
304e7d17825Sjmmv 		}
305e7d17825Sjmmv 	ATF_REQUIRE_MSG(0, "Unknown error %s", s);
306e7d17825Sjmmv 	return;
307e7d17825Sjmmv }
308e7d17825Sjmmv 
309e7d17825Sjmmv static int
getflags(char * s)310e7d17825Sjmmv getflags(char *s)
311e7d17825Sjmmv {
312e7d17825Sjmmv 	int flags = 0;
313e7d17825Sjmmv 
314e7d17825Sjmmv 	for (;; s++)
315e7d17825Sjmmv 		switch (*s) {
316e7d17825Sjmmv 		case '0': case '1': case '2': case '3': case '4':
317e7d17825Sjmmv 		case '5': case '6': case '7': case '8': case '9':
318e7d17825Sjmmv 			*s = '\0';
319e7d17825Sjmmv 			break;
320e7d17825Sjmmv 		case '\0':
321e7d17825Sjmmv 			return flags;
322e7d17825Sjmmv 		case 'B':
323e7d17825Sjmmv 		case 'E':
324e7d17825Sjmmv 		case 'F':
325e7d17825Sjmmv 		case 'L':
326e7d17825Sjmmv 			break;
327e7d17825Sjmmv 		case 'i':
328e7d17825Sjmmv 			flags |= REG_ICASE;
329e7d17825Sjmmv 			*s = '\0';
330e7d17825Sjmmv 			break;
331e7d17825Sjmmv 		case '$':
332e7d17825Sjmmv 			*s = '\0';
333e7d17825Sjmmv 			break;
334e7d17825Sjmmv 		case 'n':
335e7d17825Sjmmv 			*s = '\0';
336e7d17825Sjmmv 			break;
337e7d17825Sjmmv 		default:
338e7d17825Sjmmv 			ATF_REQUIRE_MSG(0, "Unknown char %c", *s);
339e7d17825Sjmmv 			break;
340e7d17825Sjmmv 		}
341e7d17825Sjmmv }
342e7d17825Sjmmv 
343e7d17825Sjmmv static size_t
getmatches(const char * s)344e7d17825Sjmmv getmatches(const char *s)
345e7d17825Sjmmv {
346e7d17825Sjmmv 	size_t i;
347e7d17825Sjmmv 	char *q;
348e7d17825Sjmmv 	for (i = 0; (q = strchr(s, '(')) != NULL; i++, s = q + 1)
349e7d17825Sjmmv 		continue;
350e7d17825Sjmmv 	ATF_REQUIRE_MSG(i != 0, "No parentheses found");
351e7d17825Sjmmv 	return i;
352e7d17825Sjmmv }
353e7d17825Sjmmv 
354e7d17825Sjmmv static void
checkcomment(const char * s,size_t lineno)355e7d17825Sjmmv checkcomment(const char *s, size_t lineno)
356e7d17825Sjmmv {
357e7d17825Sjmmv 	if (s && strstr(s, "BUG") != NULL)
358e7d17825Sjmmv 		fprintf(stderr, "Expected %s at line %zu\n", s, lineno);
359e7d17825Sjmmv }
360e7d17825Sjmmv 
361e7d17825Sjmmv static void
checkmatches(const char * matches,size_t nm,const regmatch_t * pm,size_t lineno)362e7d17825Sjmmv checkmatches(const char *matches, size_t nm, const regmatch_t *pm,
363e7d17825Sjmmv     size_t lineno)
364e7d17825Sjmmv {
365e7d17825Sjmmv 	if (nm == 0)
366e7d17825Sjmmv 		return;
367e7d17825Sjmmv 
368e7d17825Sjmmv 	char *res;
369e7d17825Sjmmv 	size_t len = strlen(matches) + 1, off = 0;
370e7d17825Sjmmv 
371e7d17825Sjmmv 	ATF_REQUIRE((res = strdup(matches)) != NULL);
372e7d17825Sjmmv 	for (size_t i = 0; i < nm; i++) {
373e7d17825Sjmmv 		int l;
374e7d17825Sjmmv 		if (pm[i].rm_so == -1 && pm[i].rm_eo == -1)
375e7d17825Sjmmv 			l = snprintf(res + off, len - off, "(?,?)");
376e7d17825Sjmmv 		else
377e7d17825Sjmmv 			l = snprintf(res + off, len - off, "(%lld,%lld)",
378e7d17825Sjmmv 			    (long long)pm[i].rm_so, (long long)pm[i].rm_eo);
379e7d17825Sjmmv 		ATF_REQUIRE_MSG((size_t) l < len - off, "String too long %s"
380e7d17825Sjmmv 		    " cur=%d, max=%zu", res, l, len - off);
381e7d17825Sjmmv 		off += l;
382e7d17825Sjmmv 	}
3836a94a076Schristos 	ATF_CHECK_STREQ_MSG(res, matches, " at line %zu", lineno);
384e7d17825Sjmmv 	free(res);
385e7d17825Sjmmv }
386e7d17825Sjmmv 
387e7d17825Sjmmv static void
att_test(const struct atf_tc * tc,const char * data_name)388e7d17825Sjmmv att_test(const struct atf_tc *tc, const char *data_name)
389e7d17825Sjmmv {
390e7d17825Sjmmv 	regex_t re;
391e7d17825Sjmmv 	char *line, *lastpattern = NULL, data_path[MAXPATHLEN];
392e7d17825Sjmmv 	size_t len, lineno = 0;
393e7d17825Sjmmv 	int skipping = 0;
394e7d17825Sjmmv 	FILE *input_file;
395e7d17825Sjmmv 
396e7d17825Sjmmv 	snprintf(data_path, sizeof(data_path), "%s/data/%s.dat",
397e7d17825Sjmmv 	    atf_tc_get_config_var(tc, "srcdir"), data_name);
398e7d17825Sjmmv 
399e7d17825Sjmmv 	input_file = fopen(data_path, "r");
400e7d17825Sjmmv 	if (input_file == NULL)
401e7d17825Sjmmv 		atf_tc_fail("Failed to open input file %s", data_path);
402e7d17825Sjmmv 
403e7d17825Sjmmv 	for (; (line = fparseln(input_file, &len, &lineno, delim, 0))
404e7d17825Sjmmv 	    != NULL; free(line)) {
405e7d17825Sjmmv 		char *name, *pattern, *input, *matches, *comment;
406e7d17825Sjmmv 		regmatch_t *pm;
407e7d17825Sjmmv 		size_t nm;
408e7d17825Sjmmv #ifdef DEBUG
409e7d17825Sjmmv 		fprintf(stderr, "[%s]\n", line);
410e7d17825Sjmmv #endif
411e7d17825Sjmmv 		if ((name = strtok(line, sep)) == NULL)
412e7d17825Sjmmv 			continue;
413e7d17825Sjmmv 
414e7d17825Sjmmv 		/*
415e7d17825Sjmmv 		 * We check these early so that we skip the lines quickly
416e7d17825Sjmmv 		 * in order to do more strict testing on the other arguments
417e7d17825Sjmmv 		 * The same characters are also tested in the switch below
418e7d17825Sjmmv 		 */
419e7d17825Sjmmv 		if (*name == '}') {
420e7d17825Sjmmv 			skipping = 0;
421e7d17825Sjmmv 			continue;
422e7d17825Sjmmv 		}
423e7d17825Sjmmv 		if (skipping)
424e7d17825Sjmmv 			continue;
425e7d17825Sjmmv 		if (*name == ';' || *name == '#' || strcmp(name, "NOTE") == 0)
426e7d17825Sjmmv 			continue;
427e7d17825Sjmmv 		if (*name == ':') {
428e7d17825Sjmmv 			/* Skip ":HA#???:" prefix */
429e7d17825Sjmmv 			while (*++name && *name != ':')
430e7d17825Sjmmv 				continue;
431e7d17825Sjmmv 			if (*name)
432e7d17825Sjmmv 				name++;
433e7d17825Sjmmv 		}
434e7d17825Sjmmv 
435e7d17825Sjmmv 		ATF_REQUIRE_MSG((pattern = strtok(NULL, sep)) != NULL,
436e7d17825Sjmmv 			"Missing pattern at line %zu", lineno);
437e7d17825Sjmmv 		ATF_REQUIRE_MSG((input = strtok(NULL, sep)) != NULL,
438e7d17825Sjmmv 			"Missing input at line %zu", lineno);
439e7d17825Sjmmv 
440e7d17825Sjmmv 		if (strchr(name, '$')) {
441e7d17825Sjmmv 			ATF_REQUIRE(strunvis(pattern, pattern) != -1);
442e7d17825Sjmmv 			ATF_REQUIRE(strunvis(input, input) != -1);
443e7d17825Sjmmv 		}
444e7d17825Sjmmv 
445e7d17825Sjmmv 
446e7d17825Sjmmv 		if (strcmp(input, "NULL") == 0)
447e7d17825Sjmmv 			*input = '\0';
448e7d17825Sjmmv 
449e7d17825Sjmmv 		if (strcmp(pattern, "SAME") == 0) {
450e7d17825Sjmmv 			ATF_REQUIRE(lastpattern != NULL);
451e7d17825Sjmmv 			pattern = lastpattern;
452e7d17825Sjmmv 		} else {
453e7d17825Sjmmv 			free(lastpattern);
454e7d17825Sjmmv 			ATF_REQUIRE((lastpattern = strdup(pattern)) != NULL);
455e7d17825Sjmmv 		}
456e7d17825Sjmmv 
457e7d17825Sjmmv 		ATF_REQUIRE_MSG((matches = strtok(NULL, sep)) != NULL,
458e7d17825Sjmmv 		    "Missing matches at line %zu", lineno);
459e7d17825Sjmmv 
460e7d17825Sjmmv 		comment = strtok(NULL, sep);
461e7d17825Sjmmv 		switch (*name) {
462e7d17825Sjmmv 		case '{':	/* Begin optional implementation */
463e7d17825Sjmmv 			if (optional(comment)) {
464e7d17825Sjmmv 				skipping++;
465e7d17825Sjmmv 				continue;
466e7d17825Sjmmv 			}
467e7d17825Sjmmv 			name++;	/* We have it, so ignore */
468e7d17825Sjmmv 			break;
469e7d17825Sjmmv 		case '}':	/* End optional implementation */
470e7d17825Sjmmv 			skipping = 0;
471e7d17825Sjmmv 			continue;
472e7d17825Sjmmv 		case '?':	/* Optional */
473e7d17825Sjmmv 		case '|':	/* Alternative */
474e7d17825Sjmmv 			if (unsupported(comment))
475e7d17825Sjmmv 				continue;
476e7d17825Sjmmv 			name++;	/* We have it, so ignore */
477e7d17825Sjmmv 			break;
478e7d17825Sjmmv 		case '#':	/* Comment */
479e7d17825Sjmmv 		case ';':	/* Skip */
480e7d17825Sjmmv 			continue;
481e7d17825Sjmmv 		default:
482e7d17825Sjmmv 			break;
483e7d17825Sjmmv 		}
484e7d17825Sjmmv 
485e7d17825Sjmmv 		/* XXX: Our bug */
486e7d17825Sjmmv 		if (bug(pattern, input, lineno))
487e7d17825Sjmmv 			continue;
488e7d17825Sjmmv 
489e7d17825Sjmmv 		int comp, exec;
490e7d17825Sjmmv 		if (*matches != '(') {
491e7d17825Sjmmv 			geterror(matches, &comp, &exec);
492e7d17825Sjmmv 			pm = NULL;
493e7d17825Sjmmv 			nm = 0;
494e7d17825Sjmmv 		} else {
495e7d17825Sjmmv 			comp = exec = 0;
496e7d17825Sjmmv 			nm = getmatches(matches);
497e7d17825Sjmmv 			ATF_REQUIRE((pm = calloc(nm, sizeof(*pm))) != NULL);
498e7d17825Sjmmv 		}
499e7d17825Sjmmv 
500e7d17825Sjmmv 
501e7d17825Sjmmv 
502e7d17825Sjmmv 		int iflags = getflags(name);
503e7d17825Sjmmv 		for (; *name; name++) {
504e7d17825Sjmmv 			int flags;
505e7d17825Sjmmv 			switch (*name) {
506e7d17825Sjmmv 			case 'B':
507e7d17825Sjmmv 				flags = REG_BASIC;
508e7d17825Sjmmv 				break;
509e7d17825Sjmmv 			case 'E':
510e7d17825Sjmmv 				flags = REG_EXTENDED;
511e7d17825Sjmmv 				break;
512e7d17825Sjmmv 			case 'L':
513e7d17825Sjmmv 				flags = REG_NOSPEC;
514e7d17825Sjmmv 				break;
515e7d17825Sjmmv 			default:
516e7d17825Sjmmv 				ATF_REQUIRE_MSG(0, "Bad name %c", *name);
517e7d17825Sjmmv 				continue;
518e7d17825Sjmmv 			}
519e7d17825Sjmmv 			int c = regcomp(&re, pattern, flags | iflags);
520e7d17825Sjmmv 			ATF_REQUIRE_MSG(c == comp,
521e7d17825Sjmmv 			    "regcomp returned %d for pattern %s at line %zu",
522e7d17825Sjmmv 			    c, pattern, lineno);
523e7d17825Sjmmv 			if (c)
524e7d17825Sjmmv 				continue;
525e7d17825Sjmmv 			int e = regexec(&re, input, nm, pm, 0);
526e7d17825Sjmmv 			ATF_REQUIRE_MSG(e == exec, "Expected error %d,"
527e7d17825Sjmmv 			    " got %d at line %zu", exec, e, lineno);
528e7d17825Sjmmv 			checkmatches(matches, nm, pm, lineno);
529e7d17825Sjmmv 			checkcomment(comment, lineno);
530e7d17825Sjmmv 			regfree(&re);
531e7d17825Sjmmv 		}
532e7d17825Sjmmv 		free(pm);
533e7d17825Sjmmv 	}
534e7d17825Sjmmv 
535e7d17825Sjmmv 	fclose(input_file);
536e7d17825Sjmmv }
537e7d17825Sjmmv 
538e7d17825Sjmmv ATF_TC(basic);
ATF_TC_HEAD(basic,tc)539e7d17825Sjmmv ATF_TC_HEAD(basic, tc)
540e7d17825Sjmmv {
541e7d17825Sjmmv 	atf_tc_set_md_var(tc, "descr", "Tests basic functionality");
542e7d17825Sjmmv }
ATF_TC_BODY(basic,tc)543e7d17825Sjmmv ATF_TC_BODY(basic, tc)
544e7d17825Sjmmv {
545e7d17825Sjmmv 	att_test(tc, "basic");
546e7d17825Sjmmv }
547e7d17825Sjmmv 
548e7d17825Sjmmv ATF_TC(categorization);
ATF_TC_HEAD(categorization,tc)549e7d17825Sjmmv ATF_TC_HEAD(categorization, tc)
550e7d17825Sjmmv {
551e7d17825Sjmmv 	atf_tc_set_md_var(tc, "descr", "Tests implementation categorization");
552e7d17825Sjmmv }
ATF_TC_BODY(categorization,tc)553e7d17825Sjmmv ATF_TC_BODY(categorization, tc)
554e7d17825Sjmmv {
555e7d17825Sjmmv 	att_test(tc, "categorization");
556e7d17825Sjmmv }
557e7d17825Sjmmv 
558e7d17825Sjmmv ATF_TC(nullsubexpr);
ATF_TC_HEAD(nullsubexpr,tc)559e7d17825Sjmmv ATF_TC_HEAD(nullsubexpr, tc)
560e7d17825Sjmmv {
561e7d17825Sjmmv 	atf_tc_set_md_var(tc, "descr", "Tests (...)*");
562e7d17825Sjmmv }
ATF_TC_BODY(nullsubexpr,tc)563e7d17825Sjmmv ATF_TC_BODY(nullsubexpr, tc)
564e7d17825Sjmmv {
565e7d17825Sjmmv 	att_test(tc, "nullsubexpr");
566e7d17825Sjmmv }
567e7d17825Sjmmv 
568e7d17825Sjmmv ATF_TC(leftassoc);
ATF_TC_HEAD(leftassoc,tc)569e7d17825Sjmmv ATF_TC_HEAD(leftassoc, tc)
570e7d17825Sjmmv {
571e7d17825Sjmmv 	atf_tc_set_md_var(tc, "descr", "Tests left-associative "
572e7d17825Sjmmv 	    "implementations");
573e7d17825Sjmmv }
ATF_TC_BODY(leftassoc,tc)574e7d17825Sjmmv ATF_TC_BODY(leftassoc, tc)
575e7d17825Sjmmv {
576e7d17825Sjmmv #if SKIP_LEFTASSOC
577e7d17825Sjmmv 	/* jmmv: I converted the original shell-based tests to C and they
578e7d17825Sjmmv 	 * disabled this test in a very unconventional way without giving
579e7d17825Sjmmv 	 * any explation.  Mark as broken here, but I don't know why. */
580e7d17825Sjmmv 	atf_tc_expect_fail("Reason for breakage unknown");
581e7d17825Sjmmv #endif
582e7d17825Sjmmv 	att_test(tc, "leftassoc");
583e7d17825Sjmmv }
584e7d17825Sjmmv 
585e7d17825Sjmmv ATF_TC(rightassoc);
ATF_TC_HEAD(rightassoc,tc)586e7d17825Sjmmv ATF_TC_HEAD(rightassoc, tc)
587e7d17825Sjmmv {
588e7d17825Sjmmv 	atf_tc_set_md_var(tc, "descr", "Tests right-associative "
589e7d17825Sjmmv 	    "implementations");
590e7d17825Sjmmv }
ATF_TC_BODY(rightassoc,tc)591e7d17825Sjmmv ATF_TC_BODY(rightassoc, tc)
592e7d17825Sjmmv {
593e7d17825Sjmmv #if SKIP_RIGHTASSOC
594e7d17825Sjmmv 	/* jmmv: I converted the original shell-based tests to C and they
595e7d17825Sjmmv 	 * disabled this test in a very unconventional way without giving
596e7d17825Sjmmv 	 * any explation.  Mark as broken here, but I don't know why. */
597e7d17825Sjmmv 	atf_tc_expect_fail("Reason for breakage unknown");
598e7d17825Sjmmv #endif
599e7d17825Sjmmv 	att_test(tc, "rightassoc");
600e7d17825Sjmmv }
601e7d17825Sjmmv 
602e7d17825Sjmmv ATF_TC(forcedassoc);
ATF_TC_HEAD(forcedassoc,tc)603e7d17825Sjmmv ATF_TC_HEAD(forcedassoc, tc)
604e7d17825Sjmmv {
605e7d17825Sjmmv 	atf_tc_set_md_var(tc, "descr", "Tests subexpression grouping to "
606e7d17825Sjmmv 	    "force association");
607e7d17825Sjmmv }
ATF_TC_BODY(forcedassoc,tc)608e7d17825Sjmmv ATF_TC_BODY(forcedassoc, tc)
609e7d17825Sjmmv {
610e7d17825Sjmmv 	att_test(tc, "forcedassoc");
611e7d17825Sjmmv }
612e7d17825Sjmmv 
613e7d17825Sjmmv ATF_TC(repetition);
ATF_TC_HEAD(repetition,tc)614e7d17825Sjmmv ATF_TC_HEAD(repetition, tc)
615e7d17825Sjmmv {
616e7d17825Sjmmv 	atf_tc_set_md_var(tc, "descr", "Tests implicit vs. explicit "
617e7d17825Sjmmv 	    "repetition");
618e7d17825Sjmmv }
ATF_TC_BODY(repetition,tc)619e7d17825Sjmmv ATF_TC_BODY(repetition, tc)
620e7d17825Sjmmv {
621e7d17825Sjmmv 	att_test(tc, "repetition");
622e7d17825Sjmmv }
623e7d17825Sjmmv 
ATF_TP_ADD_TCS(tp)624e7d17825Sjmmv ATF_TP_ADD_TCS(tp)
625e7d17825Sjmmv {
626e7d17825Sjmmv 
627e7d17825Sjmmv 	ATF_TP_ADD_TC(tp, basic);
628e7d17825Sjmmv 	ATF_TP_ADD_TC(tp, categorization);
629e7d17825Sjmmv 	ATF_TP_ADD_TC(tp, nullsubexpr);
630e7d17825Sjmmv 	ATF_TP_ADD_TC(tp, leftassoc);
631e7d17825Sjmmv 	ATF_TP_ADD_TC(tp, rightassoc);
632e7d17825Sjmmv 	ATF_TP_ADD_TC(tp, forcedassoc);
633e7d17825Sjmmv 	ATF_TP_ADD_TC(tp, repetition);
634e7d17825Sjmmv 	return atf_no_error();
635e7d17825Sjmmv }
636