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