xref: /netbsd-src/tests/lib/libc/regex/main.c (revision 8621598a2680b3b4666324df3054a2ad264d069c)
1*8621598aSchristos /*	$NetBSD: main.c,v 1.4 2021/02/23 17:13:44 christos Exp $	*/
217f9a364Spgoyette 
317f9a364Spgoyette /*-
417f9a364Spgoyette  * Copyright (c) 1993 The NetBSD Foundation, Inc.
517f9a364Spgoyette  * All rights reserved.
617f9a364Spgoyette  *
717f9a364Spgoyette  * Redistribution and use in source and binary forms, with or without
817f9a364Spgoyette  * modification, are permitted provided that the following conditions
917f9a364Spgoyette  * are met:
1017f9a364Spgoyette  * 1. Redistributions of source code must retain the above copyright
1117f9a364Spgoyette  *    notice, this list of conditions and the following disclaimer.
1217f9a364Spgoyette  * 2. Redistributions in binary form must reproduce the above copyright
1317f9a364Spgoyette  *    notice, this list of conditions and the following disclaimer in the
1417f9a364Spgoyette  *    documentation and/or other materials provided with the distribution.
1517f9a364Spgoyette  *
1617f9a364Spgoyette  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1717f9a364Spgoyette  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1817f9a364Spgoyette  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1917f9a364Spgoyette  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2017f9a364Spgoyette  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2117f9a364Spgoyette  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2217f9a364Spgoyette  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2317f9a364Spgoyette  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2417f9a364Spgoyette  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2517f9a364Spgoyette  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2617f9a364Spgoyette  * POSSIBILITY OF SUCH DAMAGE.
2717f9a364Spgoyette  */
2817f9a364Spgoyette 
2917f9a364Spgoyette #include <assert.h>
3017f9a364Spgoyette #include <regex.h>
3117f9a364Spgoyette #include <stdio.h>
3217f9a364Spgoyette #include <stdlib.h>
3317f9a364Spgoyette #include <string.h>
3417f9a364Spgoyette #include <unistd.h>
3517f9a364Spgoyette 
3617f9a364Spgoyette #include <sys/types.h>
3717f9a364Spgoyette 
3817f9a364Spgoyette #include "test_regex.h"
3917f9a364Spgoyette 
4017f9a364Spgoyette char *progname;
4117f9a364Spgoyette int debug = 0;
4217f9a364Spgoyette int line = 0;
4317f9a364Spgoyette int status = 0;
4417f9a364Spgoyette 
4517f9a364Spgoyette int copts = REG_EXTENDED;
4617f9a364Spgoyette int eopts = 0;
4717f9a364Spgoyette regoff_t startoff = 0;
4817f9a364Spgoyette regoff_t endoff = 0;
4917f9a364Spgoyette 
5017f9a364Spgoyette static char empty = '\0';
5117f9a364Spgoyette 
5217f9a364Spgoyette static char *eprint(int);
5317f9a364Spgoyette static int efind(char *);
5417f9a364Spgoyette 
553d2aab13Schristos #ifndef REG_ATOI
563d2aab13Schristos #define REG_ATOI 0
573d2aab13Schristos #define REG_ITOA 0
583d2aab13Schristos #define REG_PEND 0
593d2aab13Schristos #define REG_TRACE 0
603d2aab13Schristos #define REG_BACKR 0
613d2aab13Schristos #define REG_NOSPEC 0
623d2aab13Schristos #define REG_LARGE 0
633d2aab13Schristos #endif
643d2aab13Schristos 
6517f9a364Spgoyette /*
6617f9a364Spgoyette  * main - do the simple case, hand off to regress() for regression
6717f9a364Spgoyette  */
6817f9a364Spgoyette int
main(int argc,char * argv[])6917f9a364Spgoyette main(int argc, char *argv[])
7017f9a364Spgoyette {
7117f9a364Spgoyette 	regex_t re;
7217f9a364Spgoyette #	define	NS	10
7317f9a364Spgoyette 	regmatch_t subs[NS];
7417f9a364Spgoyette 	char erbuf[100];
7517f9a364Spgoyette 	int err;
7617f9a364Spgoyette 	size_t len;
7717f9a364Spgoyette 	int c;
7817f9a364Spgoyette 	int errflg = 0;
7917f9a364Spgoyette 	int i;
8017f9a364Spgoyette 	extern int optind;
8117f9a364Spgoyette 	extern char *optarg;
8217f9a364Spgoyette 
8317f9a364Spgoyette 	progname = argv[0];
8417f9a364Spgoyette 
853d2aab13Schristos 	while ((c = getopt(argc, argv, "c:E:e:S:x")) != -1)
8617f9a364Spgoyette 		switch (c) {
8717f9a364Spgoyette 		case 'c':	/* compile options */
8817f9a364Spgoyette 			copts = options('c', optarg);
8917f9a364Spgoyette 			break;
9017f9a364Spgoyette 		case 'e':	/* execute options */
9117f9a364Spgoyette 			eopts = options('e', optarg);
9217f9a364Spgoyette 			break;
9317f9a364Spgoyette 		case 'E':	/* end offset */
9417f9a364Spgoyette 			endoff = (regoff_t)atoi(optarg);
9517f9a364Spgoyette 			break;
963d2aab13Schristos 		case 'S':	/* start offset */
973d2aab13Schristos 			startoff = (regoff_t)atoi(optarg);
983d2aab13Schristos 			break;
9917f9a364Spgoyette 		case 'x':	/* Debugging. */
10017f9a364Spgoyette 			debug++;
10117f9a364Spgoyette 			break;
10217f9a364Spgoyette 		case '?':
10317f9a364Spgoyette 		default:
10417f9a364Spgoyette 			errflg++;
10517f9a364Spgoyette 			break;
10617f9a364Spgoyette 		}
10717f9a364Spgoyette 	if (errflg) {
10817f9a364Spgoyette 		fprintf(stderr, "usage: %s ", progname);
10917f9a364Spgoyette 		fprintf(stderr, "[-c copt][-C][-d] [re]\n");
11017f9a364Spgoyette 		exit(2);
11117f9a364Spgoyette 	}
11217f9a364Spgoyette 
11317f9a364Spgoyette 	if (optind >= argc) {
11417f9a364Spgoyette 		regress(stdin);
11517f9a364Spgoyette 		exit(status);
11617f9a364Spgoyette 	}
11717f9a364Spgoyette 
11817f9a364Spgoyette 	err = regcomp(&re, argv[optind++], copts);
11917f9a364Spgoyette 	if (err) {
12017f9a364Spgoyette 		len = regerror(err, &re, erbuf, sizeof(erbuf));
12117f9a364Spgoyette 		fprintf(stderr, "error %s, %zd/%zd `%s'\n",
12217f9a364Spgoyette 			eprint(err), len, (size_t)sizeof(erbuf), erbuf);
12317f9a364Spgoyette 		exit(status);
12417f9a364Spgoyette 	}
12517f9a364Spgoyette 	regprint(&re, stdout);
12617f9a364Spgoyette 
12717f9a364Spgoyette 	if (optind >= argc) {
12817f9a364Spgoyette 		regfree(&re);
12917f9a364Spgoyette 		exit(status);
13017f9a364Spgoyette 	}
13117f9a364Spgoyette 
13217f9a364Spgoyette 	if (eopts&REG_STARTEND) {
13317f9a364Spgoyette 		subs[0].rm_so = startoff;
13417f9a364Spgoyette 		subs[0].rm_eo = strlen(argv[optind]) - endoff;
13517f9a364Spgoyette 	}
13617f9a364Spgoyette 	err = regexec(&re, argv[optind], (size_t)NS, subs, eopts);
13717f9a364Spgoyette 	if (err) {
13817f9a364Spgoyette 		len = regerror(err, &re, erbuf, sizeof(erbuf));
13917f9a364Spgoyette 		fprintf(stderr, "error %s, %zd/%zd `%s'\n",
14017f9a364Spgoyette 			eprint(err), len, (size_t)sizeof(erbuf), erbuf);
14117f9a364Spgoyette 		exit(status);
14217f9a364Spgoyette 	}
14317f9a364Spgoyette 	if (!(copts&REG_NOSUB)) {
14417f9a364Spgoyette 		len = (int)(subs[0].rm_eo - subs[0].rm_so);
14517f9a364Spgoyette 		if (subs[0].rm_so != -1) {
14617f9a364Spgoyette 			if (len != 0)
14717f9a364Spgoyette 				printf("match `%.*s'\n", (int)len,
14817f9a364Spgoyette 					argv[optind] + subs[0].rm_so);
14917f9a364Spgoyette 			else
15017f9a364Spgoyette 				printf("match `'@%.1s\n",
15117f9a364Spgoyette 					argv[optind] + subs[0].rm_so);
15217f9a364Spgoyette 		}
15317f9a364Spgoyette 		for (i = 1; i < NS; i++)
15417f9a364Spgoyette 			if (subs[i].rm_so != -1)
15517f9a364Spgoyette 				printf("(%d) `%.*s'\n", i,
15617f9a364Spgoyette 					(int)(subs[i].rm_eo - subs[i].rm_so),
15717f9a364Spgoyette 					argv[optind] + subs[i].rm_so);
15817f9a364Spgoyette 	}
15917f9a364Spgoyette 	exit(status);
16017f9a364Spgoyette }
16117f9a364Spgoyette 
16217f9a364Spgoyette /*
16317f9a364Spgoyette  * regress - main loop of regression test
16417f9a364Spgoyette  */
16517f9a364Spgoyette void
regress(FILE * in)16617f9a364Spgoyette regress(FILE *in)
16717f9a364Spgoyette {
16817f9a364Spgoyette 	char inbuf[1000];
16917f9a364Spgoyette #	define	MAXF	10
17017f9a364Spgoyette 	char *f[MAXF];
17117f9a364Spgoyette 	int nf;
17217f9a364Spgoyette 	int i;
17317f9a364Spgoyette 	char erbuf[100];
17417f9a364Spgoyette 	size_t ne;
17517f9a364Spgoyette 	const char *badpat = "invalid regular expression";
17617f9a364Spgoyette #	define	SHORT	10
17717f9a364Spgoyette 	const char *bpname = "REG_BADPAT";
17817f9a364Spgoyette 	regex_t re;
17917f9a364Spgoyette 
18017f9a364Spgoyette 	while (fgets(inbuf, sizeof(inbuf), in) != NULL) {
18117f9a364Spgoyette 		line++;
18217f9a364Spgoyette 		if (inbuf[0] == '#' || inbuf[0] == '\n')
18317f9a364Spgoyette 			continue;			/* NOTE CONTINUE */
18417f9a364Spgoyette 		inbuf[strlen(inbuf)-1] = '\0';	/* get rid of stupid \n */
18517f9a364Spgoyette 		if (debug)
18617f9a364Spgoyette 			fprintf(stdout, "%d:\n", line);
18717f9a364Spgoyette 		nf = split(inbuf, f, MAXF, "\t\t");
18817f9a364Spgoyette 		if (nf < 3) {
18917f9a364Spgoyette 			fprintf(stderr, "bad input, line %d\n", line);
19017f9a364Spgoyette 			exit(1);
19117f9a364Spgoyette 		}
19217f9a364Spgoyette 		for (i = 0; i < nf; i++)
19317f9a364Spgoyette 			if (strcmp(f[i], "\"\"") == 0)
19417f9a364Spgoyette 				f[i] = &empty;
19517f9a364Spgoyette 		if (nf <= 3)
19617f9a364Spgoyette 			f[3] = NULL;
19717f9a364Spgoyette 		if (nf <= 4)
19817f9a364Spgoyette 			f[4] = NULL;
19917f9a364Spgoyette 		try(f[0], f[1], f[2], f[3], f[4], options('c', f[1]));
20017f9a364Spgoyette 		if (opt('&', f[1]))	/* try with either type of RE */
20117f9a364Spgoyette 			try(f[0], f[1], f[2], f[3], f[4],
20217f9a364Spgoyette 					options('c', f[1]) &~ REG_EXTENDED);
20317f9a364Spgoyette 	}
20417f9a364Spgoyette 
2052b8aaed8Splunky 	ne = regerror(REG_BADPAT, NULL, erbuf, sizeof(erbuf));
20617f9a364Spgoyette 	if (strcmp(erbuf, badpat) != 0 || ne != strlen(badpat)+1) {
20717f9a364Spgoyette 		fprintf(stderr, "end: regerror() test gave `%s' not `%s'\n",
20817f9a364Spgoyette 							erbuf, badpat);
20917f9a364Spgoyette 		status = 1;
21017f9a364Spgoyette 	}
2112b8aaed8Splunky 	ne = regerror(REG_BADPAT, NULL, erbuf, (size_t)SHORT);
21217f9a364Spgoyette 	if (strncmp(erbuf, badpat, SHORT-1) != 0 || erbuf[SHORT-1] != '\0' ||
21317f9a364Spgoyette 						ne != strlen(badpat)+1) {
21417f9a364Spgoyette 		fprintf(stderr, "end: regerror() short test gave `%s' not `%.*s'\n",
21517f9a364Spgoyette 						erbuf, SHORT-1, badpat);
21617f9a364Spgoyette 		status = 1;
21717f9a364Spgoyette 	}
2182b8aaed8Splunky 	ne = regerror(REG_ITOA|REG_BADPAT, NULL, erbuf, sizeof(erbuf));
21917f9a364Spgoyette 	if (strcmp(erbuf, bpname) != 0 || ne != strlen(bpname)+1) {
22017f9a364Spgoyette 		fprintf(stderr, "end: regerror() ITOA test gave `%s' not `%s'\n",
22117f9a364Spgoyette 						erbuf, bpname);
22217f9a364Spgoyette 		status = 1;
22317f9a364Spgoyette 	}
2243d2aab13Schristos #if REG_ATOI
22517f9a364Spgoyette 	re.re_endp = bpname;
2263d2aab13Schristos #endif
22717f9a364Spgoyette 	ne = regerror(REG_ATOI, &re, erbuf, sizeof(erbuf));
22817f9a364Spgoyette 	if (atoi(erbuf) != (int)REG_BADPAT) {
22917f9a364Spgoyette 		fprintf(stderr, "end: regerror() ATOI test gave `%s' not `%ld'\n",
23017f9a364Spgoyette 						erbuf, (long)REG_BADPAT);
23117f9a364Spgoyette 		status = 1;
23217f9a364Spgoyette 	} else if (ne != strlen(erbuf)+1) {
23317f9a364Spgoyette 		fprintf(stderr, "end: regerror() ATOI test len(`%s') = %ld\n",
23417f9a364Spgoyette 						erbuf, (long)REG_BADPAT);
23517f9a364Spgoyette 		status = 1;
23617f9a364Spgoyette 	}
23717f9a364Spgoyette }
23817f9a364Spgoyette 
23917f9a364Spgoyette /*
24017f9a364Spgoyette  - try - try it, and report on problems
24117f9a364Spgoyette  == void try(char *f0, char *f1, char *f2, char *f3, char *f4, int opts);
24217f9a364Spgoyette  */
24317f9a364Spgoyette void
try(char * f0,char * f1,char * f2,char * f3,char * f4,int opts)24417f9a364Spgoyette try(char *f0, char *f1, char *f2, char *f3, char *f4, int opts)
24517f9a364Spgoyette {
24617f9a364Spgoyette 	regex_t re;
24717f9a364Spgoyette #	define	NSUBS	10
24817f9a364Spgoyette 	regmatch_t subs[NSUBS];
24917f9a364Spgoyette #	define	NSHOULD	15
25017f9a364Spgoyette 	char *should[NSHOULD];
25117f9a364Spgoyette 	int nshould;
25217f9a364Spgoyette 	char erbuf[100];
25317f9a364Spgoyette 	int err;
25417f9a364Spgoyette 	int len;
25517f9a364Spgoyette 	const char *type = (opts & REG_EXTENDED) ? "ERE" : "BRE";
25617f9a364Spgoyette 	int i;
25717f9a364Spgoyette 	char *grump;
25817f9a364Spgoyette 	char f0copy[1000];
25917f9a364Spgoyette 	char f2copy[1000];
26017f9a364Spgoyette 
26117f9a364Spgoyette 	strcpy(f0copy, f0);
2623d2aab13Schristos #if REG_ATOI
26317f9a364Spgoyette 	re.re_endp = (opts&REG_PEND) ? f0copy + strlen(f0copy) : NULL;
2643d2aab13Schristos #endif
26517f9a364Spgoyette 	fixstr(f0copy);
26617f9a364Spgoyette 	err = regcomp(&re, f0copy, opts);
26717f9a364Spgoyette 	if (err != 0 && (!opt('C', f1) || err != efind(f2))) {
26817f9a364Spgoyette 		/* unexpected error or wrong error */
26917f9a364Spgoyette 		len = regerror(err, &re, erbuf, sizeof(erbuf));
27017f9a364Spgoyette 		fprintf(stderr, "%d: %s error %s, %d/%d `%s'\n",
27117f9a364Spgoyette 					line, type, eprint(err), len,
27217f9a364Spgoyette 					(int)sizeof(erbuf), erbuf);
27317f9a364Spgoyette 		status = 1;
27417f9a364Spgoyette 	} else if (err == 0 && opt('C', f1)) {
27517f9a364Spgoyette 		/* unexpected success */
27617f9a364Spgoyette 		fprintf(stderr, "%d: %s should have given REG_%s\n",
27717f9a364Spgoyette 						line, type, f2);
27817f9a364Spgoyette 		status = 1;
27917f9a364Spgoyette 		err = 1;	/* so we won't try regexec */
28017f9a364Spgoyette 	}
28117f9a364Spgoyette 
28217f9a364Spgoyette 	if (err != 0) {
28317f9a364Spgoyette 		regfree(&re);
28417f9a364Spgoyette 		return;
28517f9a364Spgoyette 	}
28617f9a364Spgoyette 
28717f9a364Spgoyette 	strcpy(f2copy, f2);
28817f9a364Spgoyette 	fixstr(f2copy);
28917f9a364Spgoyette 
29017f9a364Spgoyette 	if (options('e', f1)&REG_STARTEND) {
29117f9a364Spgoyette 		if (strchr(f2, '(') == NULL || strchr(f2, ')') == NULL)
29217f9a364Spgoyette 			fprintf(stderr, "%d: bad STARTEND syntax\n", line);
29317f9a364Spgoyette 		subs[0].rm_so = strchr(f2, '(') - f2 + 1;
29417f9a364Spgoyette 		subs[0].rm_eo = strchr(f2, ')') - f2;
29517f9a364Spgoyette 	}
29617f9a364Spgoyette 	err = regexec(&re, f2copy, NSUBS, subs, options('e', f1));
29717f9a364Spgoyette 
29817f9a364Spgoyette 	if (err != 0 && (f3 != NULL || err != REG_NOMATCH)) {
29917f9a364Spgoyette 		/* unexpected error or wrong error */
30017f9a364Spgoyette 		len = regerror(err, &re, erbuf, sizeof(erbuf));
30117f9a364Spgoyette 		fprintf(stderr, "%d: %s exec error %s, %d/%d `%s'\n",
30217f9a364Spgoyette 					line, type, eprint(err), len,
30317f9a364Spgoyette 					(int)sizeof(erbuf), erbuf);
30417f9a364Spgoyette 		status = 1;
30517f9a364Spgoyette 	} else if (err != 0) {
30617f9a364Spgoyette 		/* nothing more to check */
30717f9a364Spgoyette 	} else if (f3 == NULL) {
30817f9a364Spgoyette 		/* unexpected success */
30917f9a364Spgoyette 		fprintf(stderr, "%d: %s exec should have failed\n",
31017f9a364Spgoyette 						line, type);
31117f9a364Spgoyette 		status = 1;
31217f9a364Spgoyette 		err = 1;		/* just on principle */
31317f9a364Spgoyette 	} else if (opts&REG_NOSUB) {
31417f9a364Spgoyette 		/* nothing more to check */
31517f9a364Spgoyette 	} else if ((grump = check(f2, subs[0], f3)) != NULL) {
31617f9a364Spgoyette 		fprintf(stderr, "%d: %s %s\n", line, type, grump);
31717f9a364Spgoyette 		status = 1;
31817f9a364Spgoyette 		err = 1;
31917f9a364Spgoyette 	}
32017f9a364Spgoyette 
32117f9a364Spgoyette 	if (err != 0 || f4 == NULL) {
32217f9a364Spgoyette 		regfree(&re);
32317f9a364Spgoyette 		return;
32417f9a364Spgoyette 	}
32517f9a364Spgoyette 
32617f9a364Spgoyette 	for (i = 1; i < NSHOULD; i++)
32717f9a364Spgoyette 		should[i] = NULL;
32817f9a364Spgoyette 	nshould = split(f4, &should[1], NSHOULD-1, ",");
32917f9a364Spgoyette 	if (nshould == 0) {
33017f9a364Spgoyette 		nshould = 1;
33117f9a364Spgoyette 		should[1] = &empty;
33217f9a364Spgoyette 	}
33317f9a364Spgoyette 	for (i = 1; i < NSUBS; i++) {
33417f9a364Spgoyette 		grump = check(f2, subs[i], should[i]);
33517f9a364Spgoyette 		if (grump != NULL) {
33617f9a364Spgoyette 			fprintf(stderr, "%d: %s $%d %s\n", line,
33717f9a364Spgoyette 							type, i, grump);
33817f9a364Spgoyette 			status = 1;
33917f9a364Spgoyette 			err = 1;
34017f9a364Spgoyette 		}
34117f9a364Spgoyette 	}
34217f9a364Spgoyette 
34317f9a364Spgoyette 	regfree(&re);
34417f9a364Spgoyette }
34517f9a364Spgoyette 
34617f9a364Spgoyette /*
34717f9a364Spgoyette  - options - pick options out of a regression-test string
34817f9a364Spgoyette  == int options(int type, char *s);
34917f9a364Spgoyette  */
35017f9a364Spgoyette int
options(int type,char * s)35117f9a364Spgoyette options(int type, char *s)
35217f9a364Spgoyette {
35317f9a364Spgoyette 	char *p;
35417f9a364Spgoyette 	int o = (type == 'c') ? copts : eopts;
355*8621598aSchristos 	const char *legal = (type == 'c') ? "bisnmpg" : "^$#tl";
35617f9a364Spgoyette 
35717f9a364Spgoyette 	for (p = s; *p != '\0'; p++)
35817f9a364Spgoyette 		if (strchr(legal, *p) != NULL)
35917f9a364Spgoyette 			switch (*p) {
36017f9a364Spgoyette 			case 'b':
36117f9a364Spgoyette 				o &= ~REG_EXTENDED;
36217f9a364Spgoyette 				break;
36317f9a364Spgoyette 			case 'i':
36417f9a364Spgoyette 				o |= REG_ICASE;
36517f9a364Spgoyette 				break;
36617f9a364Spgoyette 			case 's':
36717f9a364Spgoyette 				o |= REG_NOSUB;
36817f9a364Spgoyette 				break;
36917f9a364Spgoyette 			case 'n':
37017f9a364Spgoyette 				o |= REG_NEWLINE;
37117f9a364Spgoyette 				break;
37217f9a364Spgoyette 			case 'm':
37317f9a364Spgoyette 				o &= ~REG_EXTENDED;
37417f9a364Spgoyette 				o |= REG_NOSPEC;
37517f9a364Spgoyette 				break;
37617f9a364Spgoyette 			case 'p':
37717f9a364Spgoyette 				o |= REG_PEND;
37817f9a364Spgoyette 				break;
379*8621598aSchristos 			case 'g':
380*8621598aSchristos 				o |= REG_GNU;
3813d2aab13Schristos 				break;
38217f9a364Spgoyette 			case '^':
38317f9a364Spgoyette 				o |= REG_NOTBOL;
38417f9a364Spgoyette 				break;
38517f9a364Spgoyette 			case '$':
38617f9a364Spgoyette 				o |= REG_NOTEOL;
38717f9a364Spgoyette 				break;
38817f9a364Spgoyette 			case '#':
38917f9a364Spgoyette 				o |= REG_STARTEND;
39017f9a364Spgoyette 				break;
39117f9a364Spgoyette 			case 't':	/* trace */
39217f9a364Spgoyette 				o |= REG_TRACE;
39317f9a364Spgoyette 				break;
39417f9a364Spgoyette 			case 'l':	/* force long representation */
39517f9a364Spgoyette 				o |= REG_LARGE;
39617f9a364Spgoyette 				break;
39717f9a364Spgoyette 			case 'r':	/* force backref use */
39817f9a364Spgoyette 				o |= REG_BACKR;
39917f9a364Spgoyette 				break;
40017f9a364Spgoyette 			}
40117f9a364Spgoyette 	return(o);
40217f9a364Spgoyette }
40317f9a364Spgoyette 
40417f9a364Spgoyette /*
40517f9a364Spgoyette  - opt - is a particular option in a regression string?
40617f9a364Spgoyette  == int opt(int c, char *s);
40717f9a364Spgoyette  */
40817f9a364Spgoyette int				/* predicate */
opt(int c,char * s)40917f9a364Spgoyette opt(int c, char *s)
41017f9a364Spgoyette {
41117f9a364Spgoyette 	return(strchr(s, c) != NULL);
41217f9a364Spgoyette }
41317f9a364Spgoyette 
41417f9a364Spgoyette /*
41517f9a364Spgoyette  - fixstr - transform magic characters in strings
41617f9a364Spgoyette  == void fixstr(char *p);
41717f9a364Spgoyette  */
41817f9a364Spgoyette void
fixstr(char * p)41917f9a364Spgoyette fixstr(char *p)
42017f9a364Spgoyette {
42117f9a364Spgoyette 	if (p == NULL)
42217f9a364Spgoyette 		return;
42317f9a364Spgoyette 
42417f9a364Spgoyette 	for (; *p != '\0'; p++)
42517f9a364Spgoyette 		if (*p == 'N')
42617f9a364Spgoyette 			*p = '\n';
42717f9a364Spgoyette 		else if (*p == 'T')
42817f9a364Spgoyette 			*p = '\t';
42917f9a364Spgoyette 		else if (*p == 'S')
43017f9a364Spgoyette 			*p = ' ';
43117f9a364Spgoyette 		else if (*p == 'Z')
43217f9a364Spgoyette 			*p = '\0';
43317f9a364Spgoyette }
43417f9a364Spgoyette 
43517f9a364Spgoyette /*
43617f9a364Spgoyette  * check - check a substring match
43717f9a364Spgoyette  */
43817f9a364Spgoyette char *				/* NULL or complaint */
check(char * str,regmatch_t sub,char * should)43917f9a364Spgoyette check(char *str, regmatch_t sub, char *should)
44017f9a364Spgoyette {
44117f9a364Spgoyette 	int len;
44217f9a364Spgoyette 	int shlen;
44317f9a364Spgoyette 	char *p;
44417f9a364Spgoyette 	static char grump[500];
44517f9a364Spgoyette 	char *at = NULL;
44617f9a364Spgoyette 
44717f9a364Spgoyette 	if (should != NULL && strcmp(should, "-") == 0)
44817f9a364Spgoyette 		should = NULL;
44917f9a364Spgoyette 	if (should != NULL && should[0] == '@') {
45017f9a364Spgoyette 		at = should + 1;
45117f9a364Spgoyette 		should = &empty;
45217f9a364Spgoyette 	}
45317f9a364Spgoyette 
45417f9a364Spgoyette 	/* check rm_so and rm_eo for consistency */
45517f9a364Spgoyette 	if (sub.rm_so > sub.rm_eo || (sub.rm_so == -1 && sub.rm_eo != -1) ||
45617f9a364Spgoyette 				(sub.rm_so != -1 && sub.rm_eo == -1) ||
45717f9a364Spgoyette 				(sub.rm_so != -1 && sub.rm_so < 0) ||
45817f9a364Spgoyette 				(sub.rm_eo != -1 && sub.rm_eo < 0) ) {
45917f9a364Spgoyette 		sprintf(grump, "start %ld end %ld", (long)sub.rm_so,
46017f9a364Spgoyette 							(long)sub.rm_eo);
46117f9a364Spgoyette 		return(grump);
46217f9a364Spgoyette 	}
46317f9a364Spgoyette 
46417f9a364Spgoyette 	/* check for no match */
46517f9a364Spgoyette 	if (sub.rm_so == -1) {
46617f9a364Spgoyette 		if (should == NULL)
46717f9a364Spgoyette 			return(NULL);
46817f9a364Spgoyette 		else {
46917f9a364Spgoyette 			sprintf(grump, "did not match");
47017f9a364Spgoyette 			return(grump);
47117f9a364Spgoyette 		}
47217f9a364Spgoyette 	}
47317f9a364Spgoyette 
47417f9a364Spgoyette 	/* check for in range */
47517f9a364Spgoyette 	if (sub.rm_eo > (ssize_t)strlen(str)) {
47617f9a364Spgoyette 		sprintf(grump, "start %ld end %ld, past end of string",
47717f9a364Spgoyette 					(long)sub.rm_so, (long)sub.rm_eo);
47817f9a364Spgoyette 		return(grump);
47917f9a364Spgoyette 	}
48017f9a364Spgoyette 
48117f9a364Spgoyette 	len = (int)(sub.rm_eo - sub.rm_so);
48217f9a364Spgoyette 	p = str + sub.rm_so;
48317f9a364Spgoyette 
48417f9a364Spgoyette 	/* check for not supposed to match */
48517f9a364Spgoyette 	if (should == NULL) {
48617f9a364Spgoyette 		sprintf(grump, "matched `%.*s'", len, p);
48717f9a364Spgoyette 		return(grump);
48817f9a364Spgoyette 	}
48917f9a364Spgoyette 
49017f9a364Spgoyette 	/* check for wrong match */
49117f9a364Spgoyette 	shlen = (int)strlen(should);
49217f9a364Spgoyette 	if (len != shlen || strncmp(p, should, (size_t)shlen) != 0) {
49317f9a364Spgoyette 		sprintf(grump, "matched `%.*s' instead", len, p);
49417f9a364Spgoyette 		return(grump);
49517f9a364Spgoyette 	}
49617f9a364Spgoyette 	if (shlen > 0)
49717f9a364Spgoyette 		return(NULL);
49817f9a364Spgoyette 
49917f9a364Spgoyette 	/* check null match in right place */
50017f9a364Spgoyette 	if (at == NULL)
50117f9a364Spgoyette 		return(NULL);
50217f9a364Spgoyette 	shlen = strlen(at);
50317f9a364Spgoyette 	if (shlen == 0)
50417f9a364Spgoyette 		shlen = 1;	/* force check for end-of-string */
50517f9a364Spgoyette 	if (strncmp(p, at, shlen) != 0) {
50617f9a364Spgoyette 		sprintf(grump, "matched null at `%.20s'", p);
50717f9a364Spgoyette 		return(grump);
50817f9a364Spgoyette 	}
50917f9a364Spgoyette 	return(NULL);
51017f9a364Spgoyette }
51117f9a364Spgoyette 
51217f9a364Spgoyette /*
51317f9a364Spgoyette  * eprint - convert error number to name
51417f9a364Spgoyette  */
51517f9a364Spgoyette static char *
eprint(int err)51617f9a364Spgoyette eprint(int err)
51717f9a364Spgoyette {
51817f9a364Spgoyette 	static char epbuf[100];
51917f9a364Spgoyette 	size_t len;
52017f9a364Spgoyette 
5212b8aaed8Splunky 	len = regerror(REG_ITOA|err, NULL, epbuf, sizeof(epbuf));
52217f9a364Spgoyette 	assert(len <= sizeof(epbuf));
52317f9a364Spgoyette 	return(epbuf);
52417f9a364Spgoyette }
52517f9a364Spgoyette 
52617f9a364Spgoyette /*
52717f9a364Spgoyette  * efind - convert error name to number
52817f9a364Spgoyette  */
52917f9a364Spgoyette static int
efind(char * name)53017f9a364Spgoyette efind(char *name)
53117f9a364Spgoyette {
53217f9a364Spgoyette 	static char efbuf[100];
53317f9a364Spgoyette 	regex_t re;
53417f9a364Spgoyette 
53517f9a364Spgoyette 	sprintf(efbuf, "REG_%s", name);
53617f9a364Spgoyette 	assert(strlen(efbuf) < sizeof(efbuf));
5373d2aab13Schristos #if REG_ATOI
53817f9a364Spgoyette 	re.re_endp = efbuf;
5393d2aab13Schristos #endif
54017f9a364Spgoyette 	(void) regerror(REG_ATOI, &re, efbuf, sizeof(efbuf));
54117f9a364Spgoyette 	return(atoi(efbuf));
54217f9a364Spgoyette }
543