192986Sobrien#include <sys/cdefs.h> 292986Sobrien__FBSDID("$FreeBSD$"); 392986Sobrien 462856Sdcs#include <stdio.h> 562856Sdcs#include <string.h> 662856Sdcs#include <sys/types.h> 762856Sdcs#include <regex.h> 862856Sdcs#include <assert.h> 962856Sdcs 1062856Sdcs#include "main.ih" 1162856Sdcs 1262856Sdcschar *progname; 1362856Sdcsint debug = 0; 1462856Sdcsint line = 0; 1562856Sdcsint status = 0; 1662856Sdcs 1762856Sdcsint copts = REG_EXTENDED; 1862856Sdcsint eopts = 0; 1962856Sdcsregoff_t startoff = 0; 2062856Sdcsregoff_t endoff = 0; 2162856Sdcs 2262856Sdcs 2362856Sdcsextern int split(); 2462856Sdcsextern void regprint(); 2562856Sdcs 2662856Sdcs/* 2762856Sdcs - main - do the simple case, hand off to regress() for regression 2862856Sdcs */ 2962856Sdcsmain(argc, argv) 3062856Sdcsint argc; 3162856Sdcschar *argv[]; 3262856Sdcs{ 3362856Sdcs regex_t re; 3462856Sdcs# define NS 10 3562856Sdcs regmatch_t subs[NS]; 3662856Sdcs char erbuf[100]; 3762856Sdcs int err; 3862856Sdcs size_t len; 3962856Sdcs int c; 4062856Sdcs int errflg = 0; 4192889Sobrien int i; 4262856Sdcs extern int optind; 4362856Sdcs extern char *optarg; 4462856Sdcs 4562856Sdcs progname = argv[0]; 4662856Sdcs 47176380Skevlo while ((c = getopt(argc, argv, "c:e:S:E:x")) != -1) 4862856Sdcs switch (c) { 4962856Sdcs case 'c': /* compile options */ 5062856Sdcs copts = options('c', optarg); 5162856Sdcs break; 5262856Sdcs case 'e': /* execute options */ 5362856Sdcs eopts = options('e', optarg); 5462856Sdcs break; 5562856Sdcs case 'S': /* start offset */ 5662856Sdcs startoff = (regoff_t)atoi(optarg); 5762856Sdcs break; 5862856Sdcs case 'E': /* end offset */ 5962856Sdcs endoff = (regoff_t)atoi(optarg); 6062856Sdcs break; 6162856Sdcs case 'x': /* Debugging. */ 6262856Sdcs debug++; 6362856Sdcs break; 6462856Sdcs case '?': 6562856Sdcs default: 6662856Sdcs errflg++; 6762856Sdcs break; 6862856Sdcs } 6962856Sdcs if (errflg) { 7062856Sdcs fprintf(stderr, "usage: %s ", progname); 7162856Sdcs fprintf(stderr, "[-c copt][-C][-d] [re]\n"); 7262856Sdcs exit(2); 7362856Sdcs } 7462856Sdcs 7562856Sdcs if (optind >= argc) { 7662856Sdcs regress(stdin); 7762856Sdcs exit(status); 7862856Sdcs } 7962856Sdcs 8062856Sdcs err = regcomp(&re, argv[optind++], copts); 8162856Sdcs if (err) { 8262856Sdcs len = regerror(err, &re, erbuf, sizeof(erbuf)); 8362856Sdcs fprintf(stderr, "error %s, %d/%d `%s'\n", 8462856Sdcs eprint(err), len, sizeof(erbuf), erbuf); 8562856Sdcs exit(status); 8662856Sdcs } 8762856Sdcs regprint(&re, stdout); 8862856Sdcs 8962856Sdcs if (optind >= argc) { 9062856Sdcs regfree(&re); 9162856Sdcs exit(status); 9262856Sdcs } 9362856Sdcs 9462856Sdcs if (eopts®_STARTEND) { 9562856Sdcs subs[0].rm_so = startoff; 9662856Sdcs subs[0].rm_eo = strlen(argv[optind]) - endoff; 9762856Sdcs } 9862856Sdcs err = regexec(&re, argv[optind], (size_t)NS, subs, eopts); 9962856Sdcs if (err) { 10062856Sdcs len = regerror(err, &re, erbuf, sizeof(erbuf)); 10162856Sdcs fprintf(stderr, "error %s, %d/%d `%s'\n", 10262856Sdcs eprint(err), len, sizeof(erbuf), erbuf); 10362856Sdcs exit(status); 10462856Sdcs } 10562856Sdcs if (!(copts®_NOSUB)) { 10662856Sdcs len = (int)(subs[0].rm_eo - subs[0].rm_so); 10762856Sdcs if (subs[0].rm_so != -1) { 10862856Sdcs if (len != 0) 10962856Sdcs printf("match `%.*s'\n", len, 11062856Sdcs argv[optind] + subs[0].rm_so); 11162856Sdcs else 11262856Sdcs printf("match `'@%.1s\n", 11362856Sdcs argv[optind] + subs[0].rm_so); 11462856Sdcs } 11562856Sdcs for (i = 1; i < NS; i++) 11662856Sdcs if (subs[i].rm_so != -1) 11762856Sdcs printf("(%d) `%.*s'\n", i, 11862856Sdcs (int)(subs[i].rm_eo - subs[i].rm_so), 11962856Sdcs argv[optind] + subs[i].rm_so); 12062856Sdcs } 12162856Sdcs exit(status); 12262856Sdcs} 12362856Sdcs 12462856Sdcs/* 12562856Sdcs - regress - main loop of regression test 12662856Sdcs == void regress(FILE *in); 12762856Sdcs */ 12862856Sdcsvoid 12962856Sdcsregress(in) 13062856SdcsFILE *in; 13162856Sdcs{ 13262856Sdcs char inbuf[1000]; 13362856Sdcs# define MAXF 10 13462856Sdcs char *f[MAXF]; 13562856Sdcs int nf; 13662856Sdcs int i; 13762856Sdcs char erbuf[100]; 13862856Sdcs size_t ne; 13962856Sdcs char *badpat = "invalid regular expression"; 14062856Sdcs# define SHORT 10 14162856Sdcs char *bpname = "REG_BADPAT"; 14262856Sdcs regex_t re; 14362856Sdcs 14462856Sdcs while (fgets(inbuf, sizeof(inbuf), in) != NULL) { 14562856Sdcs line++; 14662856Sdcs if (inbuf[0] == '#' || inbuf[0] == '\n') 14762856Sdcs continue; /* NOTE CONTINUE */ 14862856Sdcs inbuf[strlen(inbuf)-1] = '\0'; /* get rid of stupid \n */ 14962856Sdcs if (debug) 15062856Sdcs fprintf(stdout, "%d:\n", line); 15162856Sdcs nf = split(inbuf, f, MAXF, "\t\t"); 15262856Sdcs if (nf < 3) { 15362856Sdcs fprintf(stderr, "bad input, line %d\n", line); 15462856Sdcs exit(1); 15562856Sdcs } 15662856Sdcs for (i = 0; i < nf; i++) 15762856Sdcs if (strcmp(f[i], "\"\"") == 0) 15862856Sdcs f[i] = ""; 15962856Sdcs if (nf <= 3) 16062856Sdcs f[3] = NULL; 16162856Sdcs if (nf <= 4) 16262856Sdcs f[4] = NULL; 16362856Sdcs try(f[0], f[1], f[2], f[3], f[4], options('c', f[1])); 16462856Sdcs if (opt('&', f[1])) /* try with either type of RE */ 16562856Sdcs try(f[0], f[1], f[2], f[3], f[4], 16662856Sdcs options('c', f[1]) &~ REG_EXTENDED); 16762856Sdcs } 16862856Sdcs 16962856Sdcs ne = regerror(REG_BADPAT, (regex_t *)NULL, erbuf, sizeof(erbuf)); 17062856Sdcs if (strcmp(erbuf, badpat) != 0 || ne != strlen(badpat)+1) { 17162856Sdcs fprintf(stderr, "end: regerror() test gave `%s' not `%s'\n", 17262856Sdcs erbuf, badpat); 17362856Sdcs status = 1; 17462856Sdcs } 17562856Sdcs ne = regerror(REG_BADPAT, (regex_t *)NULL, erbuf, (size_t)SHORT); 17662856Sdcs if (strncmp(erbuf, badpat, SHORT-1) != 0 || erbuf[SHORT-1] != '\0' || 17762856Sdcs ne != strlen(badpat)+1) { 17862856Sdcs fprintf(stderr, "end: regerror() short test gave `%s' not `%.*s'\n", 17962856Sdcs erbuf, SHORT-1, badpat); 18062856Sdcs status = 1; 18162856Sdcs } 18262856Sdcs ne = regerror(REG_ITOA|REG_BADPAT, (regex_t *)NULL, erbuf, sizeof(erbuf)); 18362856Sdcs if (strcmp(erbuf, bpname) != 0 || ne != strlen(bpname)+1) { 18462856Sdcs fprintf(stderr, "end: regerror() ITOA test gave `%s' not `%s'\n", 18562856Sdcs erbuf, bpname); 18662856Sdcs status = 1; 18762856Sdcs } 18862856Sdcs re.re_endp = bpname; 18962856Sdcs ne = regerror(REG_ATOI, &re, erbuf, sizeof(erbuf)); 19062856Sdcs if (atoi(erbuf) != (int)REG_BADPAT) { 19162856Sdcs fprintf(stderr, "end: regerror() ATOI test gave `%s' not `%ld'\n", 19262856Sdcs erbuf, (long)REG_BADPAT); 19362856Sdcs status = 1; 19462856Sdcs } else if (ne != strlen(erbuf)+1) { 19562856Sdcs fprintf(stderr, "end: regerror() ATOI test len(`%s') = %ld\n", 19662856Sdcs erbuf, (long)REG_BADPAT); 19762856Sdcs status = 1; 19862856Sdcs } 19962856Sdcs} 20062856Sdcs 20162856Sdcs/* 20262856Sdcs - try - try it, and report on problems 20362856Sdcs == void try(char *f0, char *f1, char *f2, char *f3, char *f4, int opts); 20462856Sdcs */ 20562856Sdcsvoid 20662856Sdcstry(f0, f1, f2, f3, f4, opts) 20762856Sdcschar *f0; 20862856Sdcschar *f1; 20962856Sdcschar *f2; 21062856Sdcschar *f3; 21162856Sdcschar *f4; 21262856Sdcsint opts; /* may not match f1 */ 21362856Sdcs{ 21462856Sdcs regex_t re; 21562856Sdcs# define NSUBS 10 21662856Sdcs regmatch_t subs[NSUBS]; 21762856Sdcs# define NSHOULD 15 21862856Sdcs char *should[NSHOULD]; 21962856Sdcs int nshould; 22062856Sdcs char erbuf[100]; 22162856Sdcs int err; 22262856Sdcs int len; 22362856Sdcs char *type = (opts & REG_EXTENDED) ? "ERE" : "BRE"; 22492889Sobrien int i; 22562856Sdcs char *grump; 22662856Sdcs char f0copy[1000]; 22762856Sdcs char f2copy[1000]; 22862856Sdcs 22962856Sdcs strcpy(f0copy, f0); 23062856Sdcs re.re_endp = (opts®_PEND) ? f0copy + strlen(f0copy) : NULL; 23162856Sdcs fixstr(f0copy); 23262856Sdcs err = regcomp(&re, f0copy, opts); 23362856Sdcs if (err != 0 && (!opt('C', f1) || err != efind(f2))) { 23462856Sdcs /* unexpected error or wrong error */ 23562856Sdcs len = regerror(err, &re, erbuf, sizeof(erbuf)); 23662856Sdcs fprintf(stderr, "%d: %s error %s, %d/%d `%s'\n", 23762856Sdcs line, type, eprint(err), len, 23862856Sdcs sizeof(erbuf), erbuf); 23962856Sdcs status = 1; 24062856Sdcs } else if (err == 0 && opt('C', f1)) { 24162856Sdcs /* unexpected success */ 24262856Sdcs fprintf(stderr, "%d: %s should have given REG_%s\n", 24362856Sdcs line, type, f2); 24462856Sdcs status = 1; 24562856Sdcs err = 1; /* so we won't try regexec */ 24662856Sdcs } 24762856Sdcs 24862856Sdcs if (err != 0) { 24962856Sdcs regfree(&re); 25062856Sdcs return; 25162856Sdcs } 25262856Sdcs 25362856Sdcs strcpy(f2copy, f2); 25462856Sdcs fixstr(f2copy); 25562856Sdcs 25662856Sdcs if (options('e', f1)®_STARTEND) { 25762856Sdcs if (strchr(f2, '(') == NULL || strchr(f2, ')') == NULL) 25862856Sdcs fprintf(stderr, "%d: bad STARTEND syntax\n", line); 25962856Sdcs subs[0].rm_so = strchr(f2, '(') - f2 + 1; 26062856Sdcs subs[0].rm_eo = strchr(f2, ')') - f2; 26162856Sdcs } 26262856Sdcs err = regexec(&re, f2copy, NSUBS, subs, options('e', f1)); 26362856Sdcs 26462856Sdcs if (err != 0 && (f3 != NULL || err != REG_NOMATCH)) { 26562856Sdcs /* unexpected error or wrong error */ 26662856Sdcs len = regerror(err, &re, erbuf, sizeof(erbuf)); 26762856Sdcs fprintf(stderr, "%d: %s exec error %s, %d/%d `%s'\n", 26862856Sdcs line, type, eprint(err), len, 26962856Sdcs sizeof(erbuf), erbuf); 27062856Sdcs status = 1; 27162856Sdcs } else if (err != 0) { 27262856Sdcs /* nothing more to check */ 27362856Sdcs } else if (f3 == NULL) { 27462856Sdcs /* unexpected success */ 27562856Sdcs fprintf(stderr, "%d: %s exec should have failed\n", 27662856Sdcs line, type); 27762856Sdcs status = 1; 27862856Sdcs err = 1; /* just on principle */ 27962856Sdcs } else if (opts®_NOSUB) { 28062856Sdcs /* nothing more to check */ 28162856Sdcs } else if ((grump = check(f2, subs[0], f3)) != NULL) { 28262856Sdcs fprintf(stderr, "%d: %s %s\n", line, type, grump); 28362856Sdcs status = 1; 28462856Sdcs err = 1; 28562856Sdcs } 28662856Sdcs 28762856Sdcs if (err != 0 || f4 == NULL) { 28862856Sdcs regfree(&re); 28962856Sdcs return; 29062856Sdcs } 29162856Sdcs 29262856Sdcs for (i = 1; i < NSHOULD; i++) 29362856Sdcs should[i] = NULL; 29462856Sdcs nshould = split(f4, should+1, NSHOULD-1, ","); 29562856Sdcs if (nshould == 0) { 29662856Sdcs nshould = 1; 29762856Sdcs should[1] = ""; 29862856Sdcs } 29962856Sdcs for (i = 1; i < NSUBS; i++) { 30062856Sdcs grump = check(f2, subs[i], should[i]); 30162856Sdcs if (grump != NULL) { 30262856Sdcs fprintf(stderr, "%d: %s $%d %s\n", line, 30362856Sdcs type, i, grump); 30462856Sdcs status = 1; 30562856Sdcs err = 1; 30662856Sdcs } 30762856Sdcs } 30862856Sdcs 30962856Sdcs regfree(&re); 31062856Sdcs} 31162856Sdcs 31262856Sdcs/* 31362856Sdcs - options - pick options out of a regression-test string 31462856Sdcs == int options(int type, char *s); 31562856Sdcs */ 31662856Sdcsint 31762856Sdcsoptions(type, s) 31862856Sdcsint type; /* 'c' compile, 'e' exec */ 31962856Sdcschar *s; 32062856Sdcs{ 32192889Sobrien char *p; 32292889Sobrien int o = (type == 'c') ? copts : eopts; 32392889Sobrien char *legal = (type == 'c') ? "bisnmp" : "^$#tl"; 32462856Sdcs 32562856Sdcs for (p = s; *p != '\0'; p++) 32662856Sdcs if (strchr(legal, *p) != NULL) 32762856Sdcs switch (*p) { 32862856Sdcs case 'b': 32962856Sdcs o &= ~REG_EXTENDED; 33062856Sdcs break; 33162856Sdcs case 'i': 33262856Sdcs o |= REG_ICASE; 33362856Sdcs break; 33462856Sdcs case 's': 33562856Sdcs o |= REG_NOSUB; 33662856Sdcs break; 33762856Sdcs case 'n': 33862856Sdcs o |= REG_NEWLINE; 33962856Sdcs break; 34062856Sdcs case 'm': 34162856Sdcs o &= ~REG_EXTENDED; 34262856Sdcs o |= REG_NOSPEC; 34362856Sdcs break; 34462856Sdcs case 'p': 34562856Sdcs o |= REG_PEND; 34662856Sdcs break; 34762856Sdcs case '^': 34862856Sdcs o |= REG_NOTBOL; 34962856Sdcs break; 35062856Sdcs case '$': 35162856Sdcs o |= REG_NOTEOL; 35262856Sdcs break; 35362856Sdcs case '#': 35462856Sdcs o |= REG_STARTEND; 35562856Sdcs break; 35662856Sdcs case 't': /* trace */ 35762856Sdcs o |= REG_TRACE; 35862856Sdcs break; 35962856Sdcs case 'l': /* force long representation */ 36062856Sdcs o |= REG_LARGE; 36162856Sdcs break; 36262856Sdcs case 'r': /* force backref use */ 36362856Sdcs o |= REG_BACKR; 36462856Sdcs break; 36562856Sdcs } 36662856Sdcs return(o); 36762856Sdcs} 36862856Sdcs 36962856Sdcs/* 37062856Sdcs - opt - is a particular option in a regression string? 37162856Sdcs == int opt(int c, char *s); 37262856Sdcs */ 37362856Sdcsint /* predicate */ 37462856Sdcsopt(c, s) 37562856Sdcsint c; 37662856Sdcschar *s; 37762856Sdcs{ 37862856Sdcs return(strchr(s, c) != NULL); 37962856Sdcs} 38062856Sdcs 38162856Sdcs/* 38262856Sdcs - fixstr - transform magic characters in strings 38392889Sobrien == void fixstr(char *p); 38462856Sdcs */ 38562856Sdcsvoid 38662856Sdcsfixstr(p) 38792889Sobrienchar *p; 38862856Sdcs{ 38962856Sdcs if (p == NULL) 39062856Sdcs return; 39162856Sdcs 39262856Sdcs for (; *p != '\0'; p++) 39362856Sdcs if (*p == 'N') 39462856Sdcs *p = '\n'; 39562856Sdcs else if (*p == 'T') 39662856Sdcs *p = '\t'; 39762856Sdcs else if (*p == 'S') 39862856Sdcs *p = ' '; 39962856Sdcs else if (*p == 'Z') 40062856Sdcs *p = '\0'; 40162856Sdcs} 40262856Sdcs 40362856Sdcs/* 40462856Sdcs - check - check a substring match 40562856Sdcs == char *check(char *str, regmatch_t sub, char *should); 40662856Sdcs */ 40762856Sdcschar * /* NULL or complaint */ 40862856Sdcscheck(str, sub, should) 40962856Sdcschar *str; 41062856Sdcsregmatch_t sub; 41162856Sdcschar *should; 41262856Sdcs{ 41392889Sobrien int len; 41492889Sobrien int shlen; 41592889Sobrien char *p; 41662856Sdcs static char grump[500]; 41792889Sobrien char *at = NULL; 41862856Sdcs 41962856Sdcs if (should != NULL && strcmp(should, "-") == 0) 42062856Sdcs should = NULL; 42162856Sdcs if (should != NULL && should[0] == '@') { 42262856Sdcs at = should + 1; 42362856Sdcs should = ""; 42462856Sdcs } 42562856Sdcs 42662856Sdcs /* check rm_so and rm_eo for consistency */ 42762856Sdcs if (sub.rm_so > sub.rm_eo || (sub.rm_so == -1 && sub.rm_eo != -1) || 42862856Sdcs (sub.rm_so != -1 && sub.rm_eo == -1) || 42962856Sdcs (sub.rm_so != -1 && sub.rm_so < 0) || 43062856Sdcs (sub.rm_eo != -1 && sub.rm_eo < 0) ) { 43162856Sdcs sprintf(grump, "start %ld end %ld", (long)sub.rm_so, 43262856Sdcs (long)sub.rm_eo); 43362856Sdcs return(grump); 43462856Sdcs } 43562856Sdcs 43662856Sdcs /* check for no match */ 43762856Sdcs if (sub.rm_so == -1 && should == NULL) 43862856Sdcs return(NULL); 43962856Sdcs if (sub.rm_so == -1) 44062856Sdcs return("did not match"); 44162856Sdcs 44262856Sdcs /* check for in range */ 44362856Sdcs if (sub.rm_eo > strlen(str)) { 44462856Sdcs sprintf(grump, "start %ld end %ld, past end of string", 44562856Sdcs (long)sub.rm_so, (long)sub.rm_eo); 44662856Sdcs return(grump); 44762856Sdcs } 44862856Sdcs 44962856Sdcs len = (int)(sub.rm_eo - sub.rm_so); 45062856Sdcs shlen = (int)strlen(should); 45162856Sdcs p = str + sub.rm_so; 45262856Sdcs 45362856Sdcs /* check for not supposed to match */ 45462856Sdcs if (should == NULL) { 45562856Sdcs sprintf(grump, "matched `%.*s'", len, p); 45662856Sdcs return(grump); 45762856Sdcs } 45862856Sdcs 45962856Sdcs /* check for wrong match */ 46062856Sdcs if (len != shlen || strncmp(p, should, (size_t)shlen) != 0) { 46162856Sdcs sprintf(grump, "matched `%.*s' instead", len, p); 46262856Sdcs return(grump); 46362856Sdcs } 46462856Sdcs if (shlen > 0) 46562856Sdcs return(NULL); 46662856Sdcs 46762856Sdcs /* check null match in right place */ 46862856Sdcs if (at == NULL) 46962856Sdcs return(NULL); 47062856Sdcs shlen = strlen(at); 47162856Sdcs if (shlen == 0) 47262856Sdcs shlen = 1; /* force check for end-of-string */ 47362856Sdcs if (strncmp(p, at, shlen) != 0) { 47462856Sdcs sprintf(grump, "matched null at `%.20s'", p); 47562856Sdcs return(grump); 47662856Sdcs } 47762856Sdcs return(NULL); 47862856Sdcs} 47962856Sdcs 48062856Sdcs/* 48162856Sdcs - eprint - convert error number to name 48262856Sdcs == static char *eprint(int err); 48362856Sdcs */ 48462856Sdcsstatic char * 48562856Sdcseprint(err) 48662856Sdcsint err; 48762856Sdcs{ 48862856Sdcs static char epbuf[100]; 48962856Sdcs size_t len; 49062856Sdcs 49162856Sdcs len = regerror(REG_ITOA|err, (regex_t *)NULL, epbuf, sizeof(epbuf)); 49262856Sdcs assert(len <= sizeof(epbuf)); 49362856Sdcs return(epbuf); 49462856Sdcs} 49562856Sdcs 49662856Sdcs/* 49762856Sdcs - efind - convert error name to number 49862856Sdcs == static int efind(char *name); 49962856Sdcs */ 50062856Sdcsstatic int 50162856Sdcsefind(name) 50262856Sdcschar *name; 50362856Sdcs{ 50462856Sdcs static char efbuf[100]; 50562856Sdcs size_t n; 50662856Sdcs regex_t re; 50762856Sdcs 50862856Sdcs sprintf(efbuf, "REG_%s", name); 50962856Sdcs assert(strlen(efbuf) < sizeof(efbuf)); 51062856Sdcs re.re_endp = efbuf; 51162856Sdcs (void) regerror(REG_ATOI, &re, efbuf, sizeof(efbuf)); 51262856Sdcs return(atoi(efbuf)); 51362856Sdcs} 514