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