1272343Sngie/* $NetBSD: main.c,v 1.2 2011/09/16 16:13:18 plunky Exp $ */ 2272343Sngie 3272343Sngie/*- 4272343Sngie * Copyright (c) 1993 The NetBSD Foundation, Inc. 5272343Sngie * All rights reserved. 6272343Sngie * 7272343Sngie * Redistribution and use in source and binary forms, with or without 8272343Sngie * modification, are permitted provided that the following conditions 9272343Sngie * are met: 10272343Sngie * 1. Redistributions of source code must retain the above copyright 11272343Sngie * notice, this list of conditions and the following disclaimer. 12272343Sngie * 2. Redistributions in binary form must reproduce the above copyright 13272343Sngie * notice, this list of conditions and the following disclaimer in the 14272343Sngie * documentation and/or other materials provided with the distribution. 15272343Sngie * 16272343Sngie * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17272343Sngie * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18272343Sngie * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19272343Sngie * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20272343Sngie * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21272343Sngie * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22272343Sngie * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23272343Sngie * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24272343Sngie * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25272343Sngie * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26272343Sngie * POSSIBILITY OF SUCH DAMAGE. 27272343Sngie */ 28272343Sngie 29272343Sngie#include <assert.h> 30272343Sngie#include <regex.h> 31272343Sngie#include <stdio.h> 32272343Sngie#include <stdlib.h> 33272343Sngie#include <string.h> 34272343Sngie#include <unistd.h> 35272343Sngie 36272343Sngie#include <sys/types.h> 37272343Sngie 38272343Sngie#include "test_regex.h" 39272343Sngie 40272343Sngiechar *progname; 41272343Sngieint debug = 0; 42272343Sngieint line = 0; 43272343Sngieint status = 0; 44272343Sngie 45272343Sngieint copts = REG_EXTENDED; 46272343Sngieint eopts = 0; 47272343Sngieregoff_t startoff = 0; 48272343Sngieregoff_t endoff = 0; 49272343Sngie 50272343Sngiestatic char empty = '\0'; 51272343Sngie 52272343Sngiestatic char *eprint(int); 53272343Sngiestatic int efind(char *); 54272343Sngie 55272343Sngie/* 56272343Sngie * main - do the simple case, hand off to regress() for regression 57272343Sngie */ 58272343Sngieint 59272343Sngiemain(int argc, char *argv[]) 60272343Sngie{ 61272343Sngie regex_t re; 62272343Sngie# define NS 10 63272343Sngie regmatch_t subs[NS]; 64272343Sngie char erbuf[100]; 65272343Sngie int err; 66272343Sngie size_t len; 67272343Sngie int c; 68272343Sngie int errflg = 0; 69272343Sngie int i; 70272343Sngie extern int optind; 71272343Sngie extern char *optarg; 72272343Sngie 73272343Sngie progname = argv[0]; 74272343Sngie 75272343Sngie while ((c = getopt(argc, argv, "c:e:S:E:x")) != -1) 76272343Sngie switch (c) { 77272343Sngie case 'c': /* compile options */ 78272343Sngie copts = options('c', optarg); 79272343Sngie break; 80272343Sngie case 'e': /* execute options */ 81272343Sngie eopts = options('e', optarg); 82272343Sngie break; 83272343Sngie case 'S': /* start offset */ 84272343Sngie startoff = (regoff_t)atoi(optarg); 85272343Sngie break; 86272343Sngie case 'E': /* end offset */ 87272343Sngie endoff = (regoff_t)atoi(optarg); 88272343Sngie break; 89272343Sngie case 'x': /* Debugging. */ 90272343Sngie debug++; 91272343Sngie break; 92272343Sngie case '?': 93272343Sngie default: 94272343Sngie errflg++; 95272343Sngie break; 96272343Sngie } 97272343Sngie if (errflg) { 98272343Sngie fprintf(stderr, "usage: %s ", progname); 99272343Sngie fprintf(stderr, "[-c copt][-C][-d] [re]\n"); 100272343Sngie exit(2); 101272343Sngie } 102272343Sngie 103272343Sngie if (optind >= argc) { 104272343Sngie regress(stdin); 105272343Sngie exit(status); 106272343Sngie } 107272343Sngie 108272343Sngie err = regcomp(&re, argv[optind++], copts); 109272343Sngie if (err) { 110272343Sngie len = regerror(err, &re, erbuf, sizeof(erbuf)); 111272343Sngie fprintf(stderr, "error %s, %zd/%zd `%s'\n", 112272343Sngie eprint(err), len, (size_t)sizeof(erbuf), erbuf); 113272343Sngie exit(status); 114272343Sngie } 115272343Sngie regprint(&re, stdout); 116272343Sngie 117272343Sngie if (optind >= argc) { 118272343Sngie regfree(&re); 119272343Sngie exit(status); 120272343Sngie } 121272343Sngie 122272343Sngie if (eopts®_STARTEND) { 123272343Sngie subs[0].rm_so = startoff; 124272343Sngie subs[0].rm_eo = strlen(argv[optind]) - endoff; 125272343Sngie } 126272343Sngie err = regexec(&re, argv[optind], (size_t)NS, subs, eopts); 127272343Sngie if (err) { 128272343Sngie len = regerror(err, &re, erbuf, sizeof(erbuf)); 129272343Sngie fprintf(stderr, "error %s, %zd/%zd `%s'\n", 130272343Sngie eprint(err), len, (size_t)sizeof(erbuf), erbuf); 131272343Sngie exit(status); 132272343Sngie } 133272343Sngie if (!(copts®_NOSUB)) { 134272343Sngie len = (int)(subs[0].rm_eo - subs[0].rm_so); 135272343Sngie if (subs[0].rm_so != -1) { 136272343Sngie if (len != 0) 137272343Sngie printf("match `%.*s'\n", (int)len, 138272343Sngie argv[optind] + subs[0].rm_so); 139272343Sngie else 140272343Sngie printf("match `'@%.1s\n", 141272343Sngie argv[optind] + subs[0].rm_so); 142272343Sngie } 143272343Sngie for (i = 1; i < NS; i++) 144272343Sngie if (subs[i].rm_so != -1) 145272343Sngie printf("(%d) `%.*s'\n", i, 146272343Sngie (int)(subs[i].rm_eo - subs[i].rm_so), 147272343Sngie argv[optind] + subs[i].rm_so); 148272343Sngie } 149272343Sngie exit(status); 150272343Sngie} 151272343Sngie 152272343Sngie/* 153272343Sngie * regress - main loop of regression test 154272343Sngie */ 155272343Sngievoid 156272343Sngieregress(FILE *in) 157272343Sngie{ 158272343Sngie char inbuf[1000]; 159272343Sngie# define MAXF 10 160272343Sngie char *f[MAXF]; 161272343Sngie int nf; 162272343Sngie int i; 163272343Sngie char erbuf[100]; 164272343Sngie size_t ne; 165272343Sngie const char *badpat = "invalid regular expression"; 166272343Sngie# define SHORT 10 167272343Sngie const char *bpname = "REG_BADPAT"; 168272343Sngie regex_t re; 169272343Sngie 170272343Sngie while (fgets(inbuf, sizeof(inbuf), in) != NULL) { 171272343Sngie line++; 172272343Sngie if (inbuf[0] == '#' || inbuf[0] == '\n') 173272343Sngie continue; /* NOTE CONTINUE */ 174272343Sngie inbuf[strlen(inbuf)-1] = '\0'; /* get rid of stupid \n */ 175272343Sngie if (debug) 176272343Sngie fprintf(stdout, "%d:\n", line); 177272343Sngie nf = split(inbuf, f, MAXF, "\t\t"); 178272343Sngie if (nf < 3) { 179272343Sngie fprintf(stderr, "bad input, line %d\n", line); 180272343Sngie exit(1); 181272343Sngie } 182272343Sngie for (i = 0; i < nf; i++) 183272343Sngie if (strcmp(f[i], "\"\"") == 0) 184272343Sngie f[i] = ∅ 185272343Sngie if (nf <= 3) 186272343Sngie f[3] = NULL; 187272343Sngie if (nf <= 4) 188272343Sngie f[4] = NULL; 189272343Sngie try(f[0], f[1], f[2], f[3], f[4], options('c', f[1])); 190272343Sngie if (opt('&', f[1])) /* try with either type of RE */ 191272343Sngie try(f[0], f[1], f[2], f[3], f[4], 192272343Sngie options('c', f[1]) &~ REG_EXTENDED); 193272343Sngie } 194272343Sngie 195272343Sngie ne = regerror(REG_BADPAT, NULL, erbuf, sizeof(erbuf)); 196272343Sngie if (strcmp(erbuf, badpat) != 0 || ne != strlen(badpat)+1) { 197272343Sngie fprintf(stderr, "end: regerror() test gave `%s' not `%s'\n", 198272343Sngie erbuf, badpat); 199272343Sngie status = 1; 200272343Sngie } 201272343Sngie ne = regerror(REG_BADPAT, NULL, erbuf, (size_t)SHORT); 202272343Sngie if (strncmp(erbuf, badpat, SHORT-1) != 0 || erbuf[SHORT-1] != '\0' || 203272343Sngie ne != strlen(badpat)+1) { 204272343Sngie fprintf(stderr, "end: regerror() short test gave `%s' not `%.*s'\n", 205272343Sngie erbuf, SHORT-1, badpat); 206272343Sngie status = 1; 207272343Sngie } 208272343Sngie ne = regerror(REG_ITOA|REG_BADPAT, NULL, erbuf, sizeof(erbuf)); 209272343Sngie if (strcmp(erbuf, bpname) != 0 || ne != strlen(bpname)+1) { 210272343Sngie fprintf(stderr, "end: regerror() ITOA test gave `%s' not `%s'\n", 211272343Sngie erbuf, bpname); 212272343Sngie status = 1; 213272343Sngie } 214272343Sngie re.re_endp = bpname; 215272343Sngie ne = regerror(REG_ATOI, &re, erbuf, sizeof(erbuf)); 216272343Sngie if (atoi(erbuf) != (int)REG_BADPAT) { 217272343Sngie fprintf(stderr, "end: regerror() ATOI test gave `%s' not `%ld'\n", 218272343Sngie erbuf, (long)REG_BADPAT); 219272343Sngie status = 1; 220272343Sngie } else if (ne != strlen(erbuf)+1) { 221272343Sngie fprintf(stderr, "end: regerror() ATOI test len(`%s') = %ld\n", 222272343Sngie erbuf, (long)REG_BADPAT); 223272343Sngie status = 1; 224272343Sngie } 225272343Sngie} 226272343Sngie 227272343Sngie/* 228272343Sngie - try - try it, and report on problems 229272343Sngie == void try(char *f0, char *f1, char *f2, char *f3, char *f4, int opts); 230272343Sngie */ 231272343Sngievoid 232272343Sngietry(char *f0, char *f1, char *f2, char *f3, char *f4, int opts) 233272343Sngie{ 234272343Sngie regex_t re; 235272343Sngie# define NSUBS 10 236272343Sngie regmatch_t subs[NSUBS]; 237272343Sngie# define NSHOULD 15 238272343Sngie char *should[NSHOULD]; 239272343Sngie int nshould; 240272343Sngie char erbuf[100]; 241272343Sngie int err; 242272343Sngie int len; 243272343Sngie const char *type = (opts & REG_EXTENDED) ? "ERE" : "BRE"; 244272343Sngie int i; 245272343Sngie char *grump; 246272343Sngie char f0copy[1000]; 247272343Sngie char f2copy[1000]; 248272343Sngie 249272343Sngie strcpy(f0copy, f0); 250272343Sngie re.re_endp = (opts®_PEND) ? f0copy + strlen(f0copy) : NULL; 251272343Sngie fixstr(f0copy); 252272343Sngie err = regcomp(&re, f0copy, opts); 253272343Sngie if (err != 0 && (!opt('C', f1) || err != efind(f2))) { 254272343Sngie /* unexpected error or wrong error */ 255272343Sngie len = regerror(err, &re, erbuf, sizeof(erbuf)); 256272343Sngie fprintf(stderr, "%d: %s error %s, %d/%d `%s'\n", 257272343Sngie line, type, eprint(err), len, 258272343Sngie (int)sizeof(erbuf), erbuf); 259272343Sngie status = 1; 260272343Sngie } else if (err == 0 && opt('C', f1)) { 261272343Sngie /* unexpected success */ 262272343Sngie fprintf(stderr, "%d: %s should have given REG_%s\n", 263272343Sngie line, type, f2); 264272343Sngie status = 1; 265272343Sngie err = 1; /* so we won't try regexec */ 266272343Sngie } 267272343Sngie 268272343Sngie if (err != 0) { 269272343Sngie regfree(&re); 270272343Sngie return; 271272343Sngie } 272272343Sngie 273272343Sngie strcpy(f2copy, f2); 274272343Sngie fixstr(f2copy); 275272343Sngie 276272343Sngie if (options('e', f1)®_STARTEND) { 277272343Sngie if (strchr(f2, '(') == NULL || strchr(f2, ')') == NULL) 278272343Sngie fprintf(stderr, "%d: bad STARTEND syntax\n", line); 279272343Sngie subs[0].rm_so = strchr(f2, '(') - f2 + 1; 280272343Sngie subs[0].rm_eo = strchr(f2, ')') - f2; 281272343Sngie } 282272343Sngie err = regexec(&re, f2copy, NSUBS, subs, options('e', f1)); 283272343Sngie 284272343Sngie if (err != 0 && (f3 != NULL || err != REG_NOMATCH)) { 285272343Sngie /* unexpected error or wrong error */ 286272343Sngie len = regerror(err, &re, erbuf, sizeof(erbuf)); 287272343Sngie fprintf(stderr, "%d: %s exec error %s, %d/%d `%s'\n", 288272343Sngie line, type, eprint(err), len, 289272343Sngie (int)sizeof(erbuf), erbuf); 290272343Sngie status = 1; 291272343Sngie } else if (err != 0) { 292272343Sngie /* nothing more to check */ 293272343Sngie } else if (f3 == NULL) { 294272343Sngie /* unexpected success */ 295272343Sngie fprintf(stderr, "%d: %s exec should have failed\n", 296272343Sngie line, type); 297272343Sngie status = 1; 298272343Sngie err = 1; /* just on principle */ 299272343Sngie } else if (opts®_NOSUB) { 300272343Sngie /* nothing more to check */ 301272343Sngie } else if ((grump = check(f2, subs[0], f3)) != NULL) { 302272343Sngie fprintf(stderr, "%d: %s %s\n", line, type, grump); 303272343Sngie status = 1; 304272343Sngie err = 1; 305272343Sngie } 306272343Sngie 307272343Sngie if (err != 0 || f4 == NULL) { 308272343Sngie regfree(&re); 309272343Sngie return; 310272343Sngie } 311272343Sngie 312272343Sngie for (i = 1; i < NSHOULD; i++) 313272343Sngie should[i] = NULL; 314272343Sngie nshould = split(f4, &should[1], NSHOULD-1, ","); 315272343Sngie if (nshould == 0) { 316272343Sngie nshould = 1; 317272343Sngie should[1] = ∅ 318272343Sngie } 319272343Sngie for (i = 1; i < NSUBS; i++) { 320272343Sngie grump = check(f2, subs[i], should[i]); 321272343Sngie if (grump != NULL) { 322272343Sngie fprintf(stderr, "%d: %s $%d %s\n", line, 323272343Sngie type, i, grump); 324272343Sngie status = 1; 325272343Sngie err = 1; 326272343Sngie } 327272343Sngie } 328272343Sngie 329272343Sngie regfree(&re); 330272343Sngie} 331272343Sngie 332272343Sngie/* 333272343Sngie - options - pick options out of a regression-test string 334272343Sngie == int options(int type, char *s); 335272343Sngie */ 336272343Sngieint 337272343Sngieoptions(int type, char *s) 338272343Sngie{ 339272343Sngie char *p; 340272343Sngie int o = (type == 'c') ? copts : eopts; 341272343Sngie const char *legal = (type == 'c') ? "bisnmp" : "^$#tl"; 342272343Sngie 343272343Sngie for (p = s; *p != '\0'; p++) 344272343Sngie if (strchr(legal, *p) != NULL) 345272343Sngie switch (*p) { 346272343Sngie case 'b': 347272343Sngie o &= ~REG_EXTENDED; 348272343Sngie break; 349272343Sngie case 'i': 350272343Sngie o |= REG_ICASE; 351272343Sngie break; 352272343Sngie case 's': 353272343Sngie o |= REG_NOSUB; 354272343Sngie break; 355272343Sngie case 'n': 356272343Sngie o |= REG_NEWLINE; 357272343Sngie break; 358272343Sngie case 'm': 359272343Sngie o &= ~REG_EXTENDED; 360272343Sngie o |= REG_NOSPEC; 361272343Sngie break; 362272343Sngie case 'p': 363272343Sngie o |= REG_PEND; 364272343Sngie break; 365272343Sngie case '^': 366272343Sngie o |= REG_NOTBOL; 367272343Sngie break; 368272343Sngie case '$': 369272343Sngie o |= REG_NOTEOL; 370272343Sngie break; 371272343Sngie case '#': 372272343Sngie o |= REG_STARTEND; 373272343Sngie break; 374272343Sngie case 't': /* trace */ 375272343Sngie o |= REG_TRACE; 376272343Sngie break; 377272343Sngie case 'l': /* force long representation */ 378272343Sngie o |= REG_LARGE; 379272343Sngie break; 380272343Sngie case 'r': /* force backref use */ 381272343Sngie o |= REG_BACKR; 382272343Sngie break; 383272343Sngie } 384272343Sngie return(o); 385272343Sngie} 386272343Sngie 387272343Sngie/* 388272343Sngie - opt - is a particular option in a regression string? 389272343Sngie == int opt(int c, char *s); 390272343Sngie */ 391272343Sngieint /* predicate */ 392272343Sngieopt(int c, char *s) 393272343Sngie{ 394272343Sngie return(strchr(s, c) != NULL); 395272343Sngie} 396272343Sngie 397272343Sngie/* 398272343Sngie - fixstr - transform magic characters in strings 399272343Sngie == void fixstr(char *p); 400272343Sngie */ 401272343Sngievoid 402272343Sngiefixstr(char *p) 403272343Sngie{ 404272343Sngie if (p == NULL) 405272343Sngie return; 406272343Sngie 407272343Sngie for (; *p != '\0'; p++) 408272343Sngie if (*p == 'N') 409272343Sngie *p = '\n'; 410272343Sngie else if (*p == 'T') 411272343Sngie *p = '\t'; 412272343Sngie else if (*p == 'S') 413272343Sngie *p = ' '; 414272343Sngie else if (*p == 'Z') 415272343Sngie *p = '\0'; 416272343Sngie} 417272343Sngie 418272343Sngie/* 419272343Sngie * check - check a substring match 420272343Sngie */ 421272343Sngiechar * /* NULL or complaint */ 422272343Sngiecheck(char *str, regmatch_t sub, char *should) 423272343Sngie{ 424272343Sngie int len; 425272343Sngie int shlen; 426272343Sngie char *p; 427272343Sngie static char grump[500]; 428272343Sngie char *at = NULL; 429272343Sngie 430272343Sngie if (should != NULL && strcmp(should, "-") == 0) 431272343Sngie should = NULL; 432272343Sngie if (should != NULL && should[0] == '@') { 433272343Sngie at = should + 1; 434272343Sngie should = ∅ 435272343Sngie } 436272343Sngie 437272343Sngie /* check rm_so and rm_eo for consistency */ 438272343Sngie if (sub.rm_so > sub.rm_eo || (sub.rm_so == -1 && sub.rm_eo != -1) || 439272343Sngie (sub.rm_so != -1 && sub.rm_eo == -1) || 440272343Sngie (sub.rm_so != -1 && sub.rm_so < 0) || 441272343Sngie (sub.rm_eo != -1 && sub.rm_eo < 0) ) { 442272343Sngie sprintf(grump, "start %ld end %ld", (long)sub.rm_so, 443272343Sngie (long)sub.rm_eo); 444272343Sngie return(grump); 445272343Sngie } 446272343Sngie 447272343Sngie /* check for no match */ 448272343Sngie if (sub.rm_so == -1) { 449272343Sngie if (should == NULL) 450272343Sngie return(NULL); 451272343Sngie else { 452272343Sngie sprintf(grump, "did not match"); 453272343Sngie return(grump); 454272343Sngie } 455272343Sngie } 456272343Sngie 457272343Sngie /* check for in range */ 458272343Sngie if (sub.rm_eo > (ssize_t)strlen(str)) { 459272343Sngie sprintf(grump, "start %ld end %ld, past end of string", 460272343Sngie (long)sub.rm_so, (long)sub.rm_eo); 461272343Sngie return(grump); 462272343Sngie } 463272343Sngie 464272343Sngie len = (int)(sub.rm_eo - sub.rm_so); 465272343Sngie p = str + sub.rm_so; 466272343Sngie 467272343Sngie /* check for not supposed to match */ 468272343Sngie if (should == NULL) { 469272343Sngie sprintf(grump, "matched `%.*s'", len, p); 470272343Sngie return(grump); 471272343Sngie } 472272343Sngie 473272343Sngie /* check for wrong match */ 474272343Sngie shlen = (int)strlen(should); 475272343Sngie if (len != shlen || strncmp(p, should, (size_t)shlen) != 0) { 476272343Sngie sprintf(grump, "matched `%.*s' instead", len, p); 477272343Sngie return(grump); 478272343Sngie } 479272343Sngie if (shlen > 0) 480272343Sngie return(NULL); 481272343Sngie 482272343Sngie /* check null match in right place */ 483272343Sngie if (at == NULL) 484272343Sngie return(NULL); 485272343Sngie shlen = strlen(at); 486272343Sngie if (shlen == 0) 487272343Sngie shlen = 1; /* force check for end-of-string */ 488272343Sngie if (strncmp(p, at, shlen) != 0) { 489272343Sngie sprintf(grump, "matched null at `%.20s'", p); 490272343Sngie return(grump); 491272343Sngie } 492272343Sngie return(NULL); 493272343Sngie} 494272343Sngie 495272343Sngie/* 496272343Sngie * eprint - convert error number to name 497272343Sngie */ 498272343Sngiestatic char * 499272343Sngieeprint(int err) 500272343Sngie{ 501272343Sngie static char epbuf[100]; 502272343Sngie size_t len; 503272343Sngie 504272343Sngie len = regerror(REG_ITOA|err, NULL, epbuf, sizeof(epbuf)); 505272343Sngie assert(len <= sizeof(epbuf)); 506272343Sngie return(epbuf); 507272343Sngie} 508272343Sngie 509272343Sngie/* 510272343Sngie * efind - convert error name to number 511272343Sngie */ 512272343Sngiestatic int 513272343Sngieefind(char *name) 514272343Sngie{ 515272343Sngie static char efbuf[100]; 516272343Sngie regex_t re; 517272343Sngie 518272343Sngie sprintf(efbuf, "REG_%s", name); 519272343Sngie assert(strlen(efbuf) < sizeof(efbuf)); 520272343Sngie re.re_endp = efbuf; 521272343Sngie (void) regerror(REG_ATOI, &re, efbuf, sizeof(efbuf)); 522272343Sngie return(atoi(efbuf)); 523272343Sngie} 524