xref: /openbsd-src/regress/lib/libc/regex/main.c (revision d7259957e8a5d4370d76bfccd4a30d5d1fe80f38)
1*d7259957Scheloha /*	$OpenBSD: main.c,v 1.12 2022/12/04 23:50:46 cheloha Exp $	*/
2df930be7Sderaadt /*	$NetBSD: main.c,v 1.2 1995/04/20 22:39:51 cgd Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt #include <stdio.h>
507ea8d15Smillert #include <stdlib.h>
6df930be7Sderaadt #include <string.h>
7df930be7Sderaadt #include <sys/types.h>
8df930be7Sderaadt #include <regex.h>
9df930be7Sderaadt #include <assert.h>
1007ea8d15Smillert #include <unistd.h>
11df930be7Sderaadt 
12df930be7Sderaadt #include "main.ih"
13df930be7Sderaadt 
14df930be7Sderaadt char *progname;
15df930be7Sderaadt int debug = 0;
16df930be7Sderaadt int line = 0;
17df930be7Sderaadt int status = 0;
18df930be7Sderaadt 
19df930be7Sderaadt int copts = REG_EXTENDED;
20df930be7Sderaadt int eopts = 0;
21df930be7Sderaadt regoff_t startoff = 0;
22df930be7Sderaadt regoff_t endoff = 0;
23df930be7Sderaadt 
24df930be7Sderaadt 
25db3296cfSderaadt extern int split(char *, char *[], int, char *);
26db3296cfSderaadt extern void regprint(regex_t *, FILE *);
27df930be7Sderaadt 
28df930be7Sderaadt /*
29df930be7Sderaadt  - main - do the simple case, hand off to regress() for regression
30df930be7Sderaadt  */
3107ea8d15Smillert int
main(int argc,char * argv[])32db3296cfSderaadt main(int argc, char *argv[])
33db3296cfSderaadt 
34df930be7Sderaadt {
35df930be7Sderaadt 	regex_t re;
36df930be7Sderaadt #	define	NS	10
37df930be7Sderaadt 	regmatch_t subs[NS];
38df930be7Sderaadt 	char erbuf[100];
39df930be7Sderaadt 	int err;
40df930be7Sderaadt 	size_t len;
41df930be7Sderaadt 	int c;
42df930be7Sderaadt 	int errflg = 0;
43df930be7Sderaadt 	register int i;
44df930be7Sderaadt 
45df930be7Sderaadt 	progname = argv[0];
46df930be7Sderaadt 
47b14dacd1Sguenther 	while ((c = getopt(argc, argv, "c:E:e:S:x")) != -1)
48df930be7Sderaadt 		switch (c) {
49df930be7Sderaadt 		case 'c':	/* compile options */
50df930be7Sderaadt 			copts = options('c', optarg);
51df930be7Sderaadt 			break;
52b14dacd1Sguenther 		case 'E':	/* end offset */
53b14dacd1Sguenther 			endoff = (regoff_t)atoi(optarg);
54b14dacd1Sguenther 			break;
55df930be7Sderaadt 		case 'e':	/* execute options */
56df930be7Sderaadt 			eopts = options('e', optarg);
57df930be7Sderaadt 			break;
58df930be7Sderaadt 		case 'S':	/* start offset */
59df930be7Sderaadt 			startoff = (regoff_t)atoi(optarg);
60df930be7Sderaadt 			break;
61df930be7Sderaadt 		case 'x':	/* Debugging. */
62df930be7Sderaadt 			debug++;
63df930be7Sderaadt 			break;
64df930be7Sderaadt 		default:
65df930be7Sderaadt 			errflg++;
66df930be7Sderaadt 			break;
67df930be7Sderaadt 		}
68df930be7Sderaadt 	if (errflg) {
69df930be7Sderaadt 		fprintf(stderr, "usage: %s ", progname);
70b14dacd1Sguenther 		fprintf(stderr, "[-x] [-c copt] [-E endoff] [-e eopt] [-S startoff] [re]\n");
71df930be7Sderaadt 		exit(2);
72df930be7Sderaadt 	}
73df930be7Sderaadt 
74df930be7Sderaadt 	if (optind >= argc) {
75df930be7Sderaadt 		regress(stdin);
76df930be7Sderaadt 		exit(status);
77df930be7Sderaadt 	}
78df930be7Sderaadt 
79df930be7Sderaadt 	err = regcomp(&re, argv[optind++], copts);
80df930be7Sderaadt 	if (err) {
81df930be7Sderaadt 		len = regerror(err, &re, erbuf, sizeof(erbuf));
828e8b6a3bSotto 		fprintf(stderr, "error %s, %zu/%zu `%s'\n",
83df930be7Sderaadt 			eprint(err), len, sizeof(erbuf), erbuf);
84df930be7Sderaadt 		exit(status);
85df930be7Sderaadt 	}
86df930be7Sderaadt 	regprint(&re, stdout);
87df930be7Sderaadt 
88df930be7Sderaadt 	if (optind >= argc) {
89df930be7Sderaadt 		regfree(&re);
90df930be7Sderaadt 		exit(status);
91df930be7Sderaadt 	}
92df930be7Sderaadt 
93df930be7Sderaadt 	if (eopts&REG_STARTEND) {
94df930be7Sderaadt 		subs[0].rm_so = startoff;
95df930be7Sderaadt 		subs[0].rm_eo = strlen(argv[optind]) - endoff;
96df930be7Sderaadt 	}
97df930be7Sderaadt 	err = regexec(&re, argv[optind], (size_t)NS, subs, eopts);
98df930be7Sderaadt 	if (err) {
99df930be7Sderaadt 		len = regerror(err, &re, erbuf, sizeof(erbuf));
1008e8b6a3bSotto 		fprintf(stderr, "error %s, %zu/%zu `%s'\n",
101df930be7Sderaadt 			eprint(err), len, sizeof(erbuf), erbuf);
102df930be7Sderaadt 		exit(status);
103df930be7Sderaadt 	}
104df930be7Sderaadt 	if (!(copts&REG_NOSUB)) {
10507ea8d15Smillert 		len = (size_t)(subs[0].rm_eo - subs[0].rm_so);
106df930be7Sderaadt 		if (subs[0].rm_so != -1) {
107df930be7Sderaadt 			if (len != 0)
10807ea8d15Smillert 				printf("match `%.*s'\n", (int)len,
109df930be7Sderaadt 					argv[optind] + subs[0].rm_so);
110df930be7Sderaadt 			else
111df930be7Sderaadt 				printf("match `'@%.1s\n",
112df930be7Sderaadt 					argv[optind] + subs[0].rm_so);
113df930be7Sderaadt 		}
114df930be7Sderaadt 		for (i = 1; i < NS; i++)
115df930be7Sderaadt 			if (subs[i].rm_so != -1)
116df930be7Sderaadt 				printf("(%d) `%.*s'\n", i,
117df930be7Sderaadt 					(int)(subs[i].rm_eo - subs[i].rm_so),
118df930be7Sderaadt 					argv[optind] + subs[i].rm_so);
119df930be7Sderaadt 	}
120df930be7Sderaadt 	exit(status);
121df930be7Sderaadt }
122df930be7Sderaadt 
123df930be7Sderaadt /*
124df930be7Sderaadt  - regress - main loop of regression test
125df930be7Sderaadt  == void regress(FILE *in);
126df930be7Sderaadt  */
127df930be7Sderaadt void
regress(in)128df930be7Sderaadt regress(in)
129df930be7Sderaadt FILE *in;
130df930be7Sderaadt {
131df930be7Sderaadt 	char inbuf[1000];
132df930be7Sderaadt #	define	MAXF	10
133df930be7Sderaadt 	char *f[MAXF];
134df930be7Sderaadt 	int nf;
135df930be7Sderaadt 	int i;
136df930be7Sderaadt 	char erbuf[100];
137df930be7Sderaadt 	size_t ne;
138df930be7Sderaadt 	char *badpat = "invalid regular expression";
139df930be7Sderaadt #	define	SHORT	10
140df930be7Sderaadt 	char *bpname = "REG_BADPAT";
141df930be7Sderaadt 	regex_t re;
142df930be7Sderaadt 
143df930be7Sderaadt 	while (fgets(inbuf, sizeof(inbuf), in) != NULL) {
144df930be7Sderaadt 		line++;
145df930be7Sderaadt 		if (inbuf[0] == '#' || inbuf[0] == '\n')
146df930be7Sderaadt 			continue;			/* NOTE CONTINUE */
1472ed2be6eSchl 		inbuf[strcspn(inbuf, "\n")] = '\0';	/* get rid of stupid \n */
148df930be7Sderaadt 		if (debug)
149df930be7Sderaadt 			fprintf(stdout, "%d:\n", line);
150df930be7Sderaadt 		nf = split(inbuf, f, MAXF, "\t\t");
151df930be7Sderaadt 		if (nf < 3) {
152df930be7Sderaadt 			fprintf(stderr, "bad input, line %d\n", line);
153df930be7Sderaadt 			exit(1);
154df930be7Sderaadt 		}
155df930be7Sderaadt 		for (i = 0; i < nf; i++)
156df930be7Sderaadt 			if (strcmp(f[i], "\"\"") == 0)
157df930be7Sderaadt 				f[i] = "";
158df930be7Sderaadt 		if (nf <= 3)
159df930be7Sderaadt 			f[3] = NULL;
160df930be7Sderaadt 		if (nf <= 4)
161df930be7Sderaadt 			f[4] = NULL;
162df930be7Sderaadt 		try(f[0], f[1], f[2], f[3], f[4], options('c', f[1]));
163df930be7Sderaadt 		if (opt('&', f[1]))	/* try with either type of RE */
164df930be7Sderaadt 			try(f[0], f[1], f[2], f[3], f[4],
165df930be7Sderaadt 					options('c', f[1]) &~ REG_EXTENDED);
166df930be7Sderaadt 	}
167df930be7Sderaadt 
168df930be7Sderaadt 	ne = regerror(REG_BADPAT, (regex_t *)NULL, erbuf, sizeof(erbuf));
169df930be7Sderaadt 	if (strcmp(erbuf, badpat) != 0 || ne != strlen(badpat)+1) {
170df930be7Sderaadt 		fprintf(stderr, "end: regerror() test gave `%s' not `%s'\n",
171df930be7Sderaadt 							erbuf, badpat);
172df930be7Sderaadt 		status = 1;
173df930be7Sderaadt 	}
174df930be7Sderaadt 	ne = regerror(REG_BADPAT, (regex_t *)NULL, erbuf, (size_t)SHORT);
175df930be7Sderaadt 	if (strncmp(erbuf, badpat, SHORT-1) != 0 || erbuf[SHORT-1] != '\0' ||
176df930be7Sderaadt 						ne != strlen(badpat)+1) {
177df930be7Sderaadt 		fprintf(stderr, "end: regerror() short test gave `%s' not `%.*s'\n",
178df930be7Sderaadt 						erbuf, SHORT-1, badpat);
179df930be7Sderaadt 		status = 1;
180df930be7Sderaadt 	}
181df930be7Sderaadt 	ne = regerror(REG_ITOA|REG_BADPAT, (regex_t *)NULL, erbuf, sizeof(erbuf));
182df930be7Sderaadt 	if (strcmp(erbuf, bpname) != 0 || ne != strlen(bpname)+1) {
183df930be7Sderaadt 		fprintf(stderr, "end: regerror() ITOA test gave `%s' not `%s'\n",
184df930be7Sderaadt 						erbuf, bpname);
185df930be7Sderaadt 		status = 1;
186df930be7Sderaadt 	}
187df930be7Sderaadt 	re.re_endp = bpname;
188df930be7Sderaadt 	ne = regerror(REG_ATOI, &re, erbuf, sizeof(erbuf));
189df930be7Sderaadt 	if (atoi(erbuf) != (int)REG_BADPAT) {
190df930be7Sderaadt 		fprintf(stderr, "end: regerror() ATOI test gave `%s' not `%ld'\n",
191df930be7Sderaadt 						erbuf, (long)REG_BADPAT);
192df930be7Sderaadt 		status = 1;
193df930be7Sderaadt 	} else if (ne != strlen(erbuf)+1) {
194df930be7Sderaadt 		fprintf(stderr, "end: regerror() ATOI test len(`%s') = %ld\n",
195df930be7Sderaadt 						erbuf, (long)REG_BADPAT);
196df930be7Sderaadt 		status = 1;
197df930be7Sderaadt 	}
198df930be7Sderaadt }
199df930be7Sderaadt 
200df930be7Sderaadt /*
201df930be7Sderaadt  - try - try it, and report on problems
202df930be7Sderaadt  == void try(char *f0, char *f1, char *f2, char *f3, char *f4, int opts);
203df930be7Sderaadt  */
204df930be7Sderaadt void
try(f0,f1,f2,f3,f4,opts)205df930be7Sderaadt try(f0, f1, f2, f3, f4, opts)
206df930be7Sderaadt char *f0;
207df930be7Sderaadt char *f1;
208df930be7Sderaadt char *f2;
209df930be7Sderaadt char *f3;
210df930be7Sderaadt char *f4;
211df930be7Sderaadt int opts;			/* may not match f1 */
212df930be7Sderaadt {
213df930be7Sderaadt 	regex_t re;
214df930be7Sderaadt #	define	NSUBS	10
215df930be7Sderaadt 	regmatch_t subs[NSUBS];
216df930be7Sderaadt #	define	NSHOULD	15
217df930be7Sderaadt 	char *should[NSHOULD];
218df930be7Sderaadt 	int nshould;
219df930be7Sderaadt 	char erbuf[100];
220df930be7Sderaadt 	int err;
221df930be7Sderaadt 	int len;
222df930be7Sderaadt 	char *type = (opts & REG_EXTENDED) ? "ERE" : "BRE";
223df930be7Sderaadt 	register int i;
224df930be7Sderaadt 	char *grump;
225df930be7Sderaadt 	char f0copy[1000];
226df930be7Sderaadt 	char f2copy[1000];
227df930be7Sderaadt 
228112e1910Sderaadt 	strlcpy(f0copy, f0, sizeof f0copy);
229df930be7Sderaadt 	re.re_endp = (opts&REG_PEND) ? f0copy + strlen(f0copy) : NULL;
230df930be7Sderaadt 	fixstr(f0copy);
231df930be7Sderaadt 	err = regcomp(&re, f0copy, opts);
232df930be7Sderaadt 	if (err != 0 && (!opt('C', f1) || err != efind(f2))) {
233df930be7Sderaadt 		/* unexpected error or wrong error */
234df930be7Sderaadt 		len = regerror(err, &re, erbuf, sizeof(erbuf));
2358e8b6a3bSotto 		fprintf(stderr, "%d: %s error %s, %d/%zu `%s'\n",
236df930be7Sderaadt 					line, type, eprint(err), len,
237df930be7Sderaadt 					sizeof(erbuf), erbuf);
238df930be7Sderaadt 		status = 1;
239df930be7Sderaadt 	} else if (err == 0 && opt('C', f1)) {
240df930be7Sderaadt 		/* unexpected success */
241df930be7Sderaadt 		fprintf(stderr, "%d: %s should have given REG_%s\n",
242df930be7Sderaadt 						line, type, f2);
243df930be7Sderaadt 		status = 1;
244df930be7Sderaadt 		err = 1;	/* so we won't try regexec */
245df930be7Sderaadt 	}
246df930be7Sderaadt 
247df930be7Sderaadt 	if (err != 0) {
248df930be7Sderaadt 		regfree(&re);
249df930be7Sderaadt 		return;
250df930be7Sderaadt 	}
251df930be7Sderaadt 
252112e1910Sderaadt 	strlcpy(f2copy, f2, sizeof f2copy);
253df930be7Sderaadt 	fixstr(f2copy);
254df930be7Sderaadt 
255df930be7Sderaadt 	if (options('e', f1)&REG_STARTEND) {
256df930be7Sderaadt 		if (strchr(f2, '(') == NULL || strchr(f2, ')') == NULL)
257df930be7Sderaadt 			fprintf(stderr, "%d: bad STARTEND syntax\n", line);
258df930be7Sderaadt 		subs[0].rm_so = strchr(f2, '(') - f2 + 1;
259df930be7Sderaadt 		subs[0].rm_eo = strchr(f2, ')') - f2;
260880d559dSschwarze 		/* the preceding character is relevant with REG_NOTBOL */
261880d559dSschwarze 		f2copy[subs[0].rm_so - 1] = subs[0].rm_so > 1 ?
262880d559dSschwarze 		    f2copy[subs[0].rm_so - 2] : 'X';
263df930be7Sderaadt 	}
264df930be7Sderaadt 	err = regexec(&re, f2copy, NSUBS, subs, options('e', f1));
265df930be7Sderaadt 
266df930be7Sderaadt 	if (err != 0 && (f3 != NULL || err != REG_NOMATCH)) {
267df930be7Sderaadt 		/* unexpected error or wrong error */
268df930be7Sderaadt 		len = regerror(err, &re, erbuf, sizeof(erbuf));
2698e8b6a3bSotto 		fprintf(stderr, "%d: %s exec error %s, %d/%zu `%s'\n",
270df930be7Sderaadt 					line, type, eprint(err), len,
271df930be7Sderaadt 					sizeof(erbuf), erbuf);
272df930be7Sderaadt 		status = 1;
273df930be7Sderaadt 	} else if (err != 0) {
274df930be7Sderaadt 		/* nothing more to check */
275df930be7Sderaadt 	} else if (f3 == NULL) {
276df930be7Sderaadt 		/* unexpected success */
277df930be7Sderaadt 		fprintf(stderr, "%d: %s exec should have failed\n",
278df930be7Sderaadt 						line, type);
279df930be7Sderaadt 		status = 1;
280df930be7Sderaadt 		err = 1;		/* just on principle */
281df930be7Sderaadt 	} else if (opts&REG_NOSUB) {
282df930be7Sderaadt 		/* nothing more to check */
283df930be7Sderaadt 	} else if ((grump = check(f2, subs[0], f3)) != NULL) {
284df930be7Sderaadt 		fprintf(stderr, "%d: %s %s\n", line, type, grump);
285df930be7Sderaadt 		status = 1;
286df930be7Sderaadt 		err = 1;
287df930be7Sderaadt 	}
288df930be7Sderaadt 
289df930be7Sderaadt 	if (err != 0 || f4 == NULL) {
290df930be7Sderaadt 		regfree(&re);
291df930be7Sderaadt 		return;
292df930be7Sderaadt 	}
293df930be7Sderaadt 
294df930be7Sderaadt 	for (i = 1; i < NSHOULD; i++)
295df930be7Sderaadt 		should[i] = NULL;
296df930be7Sderaadt 	nshould = split(f4, should+1, NSHOULD-1, ",");
297df930be7Sderaadt 	if (nshould == 0) {
298df930be7Sderaadt 		nshould = 1;
299df930be7Sderaadt 		should[1] = "";
300df930be7Sderaadt 	}
301df930be7Sderaadt 	for (i = 1; i < NSUBS; i++) {
302df930be7Sderaadt 		grump = check(f2, subs[i], should[i]);
303df930be7Sderaadt 		if (grump != NULL) {
304df930be7Sderaadt 			fprintf(stderr, "%d: %s $%d %s\n", line,
305df930be7Sderaadt 							type, i, grump);
306df930be7Sderaadt 			status = 1;
307df930be7Sderaadt 			err = 1;
308df930be7Sderaadt 		}
309df930be7Sderaadt 	}
310df930be7Sderaadt 
311df930be7Sderaadt 	regfree(&re);
312df930be7Sderaadt }
313df930be7Sderaadt 
314df930be7Sderaadt /*
315df930be7Sderaadt  - options - pick options out of a regression-test string
316df930be7Sderaadt  == int options(int type, char *s);
317df930be7Sderaadt  */
318df930be7Sderaadt int
options(type,s)319df930be7Sderaadt options(type, s)
320df930be7Sderaadt int type;			/* 'c' compile, 'e' exec */
321df930be7Sderaadt char *s;
322df930be7Sderaadt {
323df930be7Sderaadt 	register char *p;
324df930be7Sderaadt 	register int o = (type == 'c') ? copts : eopts;
325df930be7Sderaadt 	register char *legal = (type == 'c') ? "bisnmp" : "^$#tl";
326df930be7Sderaadt 
327df930be7Sderaadt 	for (p = s; *p != '\0'; p++)
328df930be7Sderaadt 		if (strchr(legal, *p) != NULL)
329df930be7Sderaadt 			switch (*p) {
330df930be7Sderaadt 			case 'b':
331df930be7Sderaadt 				o &= ~REG_EXTENDED;
332df930be7Sderaadt 				break;
333df930be7Sderaadt 			case 'i':
334df930be7Sderaadt 				o |= REG_ICASE;
335df930be7Sderaadt 				break;
336df930be7Sderaadt 			case 's':
337df930be7Sderaadt 				o |= REG_NOSUB;
338df930be7Sderaadt 				break;
339df930be7Sderaadt 			case 'n':
340df930be7Sderaadt 				o |= REG_NEWLINE;
341df930be7Sderaadt 				break;
342df930be7Sderaadt 			case 'm':
343df930be7Sderaadt 				o &= ~REG_EXTENDED;
344df930be7Sderaadt 				o |= REG_NOSPEC;
345df930be7Sderaadt 				break;
346df930be7Sderaadt 			case 'p':
347df930be7Sderaadt 				o |= REG_PEND;
348df930be7Sderaadt 				break;
349df930be7Sderaadt 			case '^':
350df930be7Sderaadt 				o |= REG_NOTBOL;
351df930be7Sderaadt 				break;
352df930be7Sderaadt 			case '$':
353df930be7Sderaadt 				o |= REG_NOTEOL;
354df930be7Sderaadt 				break;
355df930be7Sderaadt 			case '#':
356df930be7Sderaadt 				o |= REG_STARTEND;
357df930be7Sderaadt 				break;
358df930be7Sderaadt 			case 't':	/* trace */
359df930be7Sderaadt 				o |= REG_TRACE;
360df930be7Sderaadt 				break;
361df930be7Sderaadt 			case 'l':	/* force long representation */
362df930be7Sderaadt 				o |= REG_LARGE;
363df930be7Sderaadt 				break;
364df930be7Sderaadt 			case 'r':	/* force backref use */
365df930be7Sderaadt 				o |= REG_BACKR;
366df930be7Sderaadt 				break;
367df930be7Sderaadt 			}
368df930be7Sderaadt 	return(o);
369df930be7Sderaadt }
370df930be7Sderaadt 
371df930be7Sderaadt /*
372df930be7Sderaadt  - opt - is a particular option in a regression string?
373df930be7Sderaadt  == int opt(int c, char *s);
374df930be7Sderaadt  */
375df930be7Sderaadt int				/* predicate */
opt(c,s)376df930be7Sderaadt opt(c, s)
377df930be7Sderaadt int c;
378df930be7Sderaadt char *s;
379df930be7Sderaadt {
380df930be7Sderaadt 	return(strchr(s, c) != NULL);
381df930be7Sderaadt }
382df930be7Sderaadt 
383df930be7Sderaadt /*
384df930be7Sderaadt  - fixstr - transform magic characters in strings
385df930be7Sderaadt  == void fixstr(register char *p);
386df930be7Sderaadt  */
387df930be7Sderaadt void
fixstr(p)388df930be7Sderaadt fixstr(p)
389df930be7Sderaadt register char *p;
390df930be7Sderaadt {
391df930be7Sderaadt 	if (p == NULL)
392df930be7Sderaadt 		return;
393df930be7Sderaadt 
394df930be7Sderaadt 	for (; *p != '\0'; p++)
395df930be7Sderaadt 		if (*p == 'N')
396df930be7Sderaadt 			*p = '\n';
397df930be7Sderaadt 		else if (*p == 'T')
398df930be7Sderaadt 			*p = '\t';
399df930be7Sderaadt 		else if (*p == 'S')
400df930be7Sderaadt 			*p = ' ';
401df930be7Sderaadt 		else if (*p == 'Z')
402df930be7Sderaadt 			*p = '\0';
403df930be7Sderaadt }
404df930be7Sderaadt 
405df930be7Sderaadt /*
406df930be7Sderaadt  - check - check a substring match
407df930be7Sderaadt  == char *check(char *str, regmatch_t sub, char *should);
408df930be7Sderaadt  */
409df930be7Sderaadt char *				/* NULL or complaint */
check(str,sub,should)410df930be7Sderaadt check(str, sub, should)
411df930be7Sderaadt char *str;
412df930be7Sderaadt regmatch_t sub;
413df930be7Sderaadt char *should;
414df930be7Sderaadt {
415df930be7Sderaadt 	register int len;
416df930be7Sderaadt 	register int shlen;
417df930be7Sderaadt 	register char *p;
418df930be7Sderaadt 	static char grump[500];
419df930be7Sderaadt 	register char *at = NULL;
420df930be7Sderaadt 
421df930be7Sderaadt 	if (should != NULL && strcmp(should, "-") == 0)
422df930be7Sderaadt 		should = NULL;
423df930be7Sderaadt 	if (should != NULL && should[0] == '@') {
424df930be7Sderaadt 		at = should + 1;
425df930be7Sderaadt 		should = "";
426df930be7Sderaadt 	}
427df930be7Sderaadt 
428df930be7Sderaadt 	/* check rm_so and rm_eo for consistency */
429df930be7Sderaadt 	if (sub.rm_so > sub.rm_eo || (sub.rm_so == -1 && sub.rm_eo != -1) ||
430df930be7Sderaadt 				(sub.rm_so != -1 && sub.rm_eo == -1) ||
431df930be7Sderaadt 				(sub.rm_so != -1 && sub.rm_so < 0) ||
432df930be7Sderaadt 				(sub.rm_eo != -1 && sub.rm_eo < 0) ) {
433db3296cfSderaadt 		snprintf(grump, sizeof grump,
434db3296cfSderaadt 		    "start %ld end %ld", (long)sub.rm_so,
435df930be7Sderaadt 		    (long)sub.rm_eo);
436df930be7Sderaadt 		return(grump);
437df930be7Sderaadt 	}
438df930be7Sderaadt 
439df930be7Sderaadt 	/* check for no match */
440df930be7Sderaadt 	if (sub.rm_so == -1 && should == NULL)
441df930be7Sderaadt 		return(NULL);
442df930be7Sderaadt 	if (sub.rm_so == -1)
443df930be7Sderaadt 		return("did not match");
444df930be7Sderaadt 
445df930be7Sderaadt 	/* check for in range */
446df930be7Sderaadt 	if (sub.rm_eo > strlen(str)) {
447db3296cfSderaadt 		snprintf(grump, sizeof grump,
448db3296cfSderaadt 			"start %ld end %ld, past end of string",
449df930be7Sderaadt 			(long)sub.rm_so, (long)sub.rm_eo);
450df930be7Sderaadt 		return(grump);
451df930be7Sderaadt 	}
452df930be7Sderaadt 
453df930be7Sderaadt 	len = (int)(sub.rm_eo - sub.rm_so);
454df930be7Sderaadt 	p = str + sub.rm_so;
455df930be7Sderaadt 
456df930be7Sderaadt 	/* check for not supposed to match */
457df930be7Sderaadt 	if (should == NULL) {
458db3296cfSderaadt 		snprintf(grump, sizeof grump, "matched `%.*s'", len, p);
459df930be7Sderaadt 		return(grump);
460df930be7Sderaadt 	}
461df930be7Sderaadt 
462df930be7Sderaadt 	/* check for wrong match */
4637d7ae660Sschwarze 	shlen = (int)strlen(should);
464df930be7Sderaadt 	if (len != shlen || strncmp(p, should, (size_t)shlen) != 0) {
465db3296cfSderaadt 		snprintf(grump, sizeof grump, "matched `%.*s' instead", len, p);
466df930be7Sderaadt 		return(grump);
467df930be7Sderaadt 	}
468df930be7Sderaadt 	if (shlen > 0)
469df930be7Sderaadt 		return(NULL);
470df930be7Sderaadt 
471df930be7Sderaadt 	/* check null match in right place */
472df930be7Sderaadt 	if (at == NULL)
473df930be7Sderaadt 		return(NULL);
474df930be7Sderaadt 	shlen = strlen(at);
475df930be7Sderaadt 	if (shlen == 0)
476df930be7Sderaadt 		shlen = 1;	/* force check for end-of-string */
477df930be7Sderaadt 	if (strncmp(p, at, shlen) != 0) {
478db3296cfSderaadt 		snprintf(grump, sizeof grump, "matched null at `%.20s'", p);
479df930be7Sderaadt 		return(grump);
480df930be7Sderaadt 	}
481df930be7Sderaadt 	return(NULL);
482df930be7Sderaadt }
483df930be7Sderaadt 
484df930be7Sderaadt /*
485df930be7Sderaadt  - eprint - convert error number to name
486df930be7Sderaadt  == static char *eprint(int err);
487df930be7Sderaadt  */
488df930be7Sderaadt static char *
eprint(err)489df930be7Sderaadt eprint(err)
490df930be7Sderaadt int err;
491df930be7Sderaadt {
492df930be7Sderaadt 	static char epbuf[100];
493df930be7Sderaadt 	size_t len;
494df930be7Sderaadt 
495df930be7Sderaadt 	len = regerror(REG_ITOA|err, (regex_t *)NULL, epbuf, sizeof(epbuf));
496df930be7Sderaadt 	assert(len <= sizeof(epbuf));
497df930be7Sderaadt 	return(epbuf);
498df930be7Sderaadt }
499df930be7Sderaadt 
500df930be7Sderaadt /*
501df930be7Sderaadt  - efind - convert error name to number
502df930be7Sderaadt  == static int efind(char *name);
503df930be7Sderaadt  */
504df930be7Sderaadt static int
efind(name)505df930be7Sderaadt efind(name)
506df930be7Sderaadt char *name;
507df930be7Sderaadt {
508df930be7Sderaadt 	static char efbuf[100];
509df930be7Sderaadt 	regex_t re;
510df930be7Sderaadt 
511db3296cfSderaadt 	snprintf(efbuf, sizeof efbuf, "REG_%s", name);
512df930be7Sderaadt 	assert(strlen(efbuf) < sizeof(efbuf));
513df930be7Sderaadt 	re.re_endp = efbuf;
514df930be7Sderaadt 	(void) regerror(REG_ATOI, &re, efbuf, sizeof(efbuf));
515df930be7Sderaadt 	return(atoi(efbuf));
516df930be7Sderaadt }
517