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