home *** CD-ROM | disk | FTP | other *** search
- /* vi: set tabstop=4 : */
-
- /*
- * Return 1 if the given number is in the specified range,
- * -1 if there is an error in the range specification,
- * 0 otherwise
- *
- * Ranges have a similar (we use a colon instead of a dash) form to that
- * used by [nt]roff; i.e., a comma separated list of specifiers of the
- * form:
- * 1) n means x such that x = n
- * 2) :n means all x such that x <= n
- * 3) n: means all x such that x >= n
- * 4) n:m means all x such that n <= x <= m
- * 5) : means all x
- * n is an int
- *
- * Problems:
- * The routine prints an error message if the range is strange - this
- * might not always be desirable.
- *
- * Jul/86 BJB
- */
-
- /*
- * ===================================================================
- *
- * Permission is given to freely copy and distribute this software
- * providing:
- *
- * 1) You do not sell it,
- * 2) You do not use it for commercial advantage, and
- * 3) This notice accompanies the distribution
- *
- * Copyright (c) 1988
- * Barry Brachman
- * Dept. of Computer Science
- * Univ. of British Columbia
- * Vancouver, B.C. V6T 1W5
- *
- * .. {ihnp4!alberta, uw-beaver, uunet}!ubc-vision!ubc-csgrads!brachman
- * brachman@grads.cs.ubc.cdn
- * brachman%ubc.csnet@csnet-relay.arpa
- * brachman@ubc.csnet
- * ====================================================================
- */
-
- #include <ctype.h>
- #include <stdio.h>
-
- #define streq(a, b) (!strcmp((a), (b)))
- #define smalloc(a, t, s) ((a = (t) malloc((unsigned) (s))) == NULL)
- #define srealloc(a, t, b, s) ((a = (t) realloc(b, (unsigned) (s))) == NULL)
- #define max(a, b) ((a) >= (b) ? (a) : (b))
-
- #define SEP_CHAR ':'
- #define SEP_T 0 /* separator token */
- #define NUM_T 1 /* number token */
- #define BAD_T 2 /* token for bad character */
-
- #define STR_ALLOC 80
-
- struct range_header {
- char *range_str; /* range character string */
- int range_str_alloc; /* length in bytes */
- int nranges; /* number of range entries */
- struct range *range;
- } range_header = {
- NULL, 0, NULL
- };
-
- /*
- * If hflag (lflag) is non-zero then the high (low) value is present
- */
- struct range {
- char hflag; /* high value present */
- char lflag; /* low value present */
- int high; /* high part of range */
- int low; /* low part of range */
- };
-
- #ifdef RANGE_DEBUG
-
- /*
- * This is a program for demonstrating and debugging the range checking
- * code
- * Enter a range when prompted
- * (If there is a previous range shown you may enter <return> to
- * reselect it)
- * Enter a value
- * The program will indicate whether the value is in the given range
- */
-
- char buf[BUFSIZ], range[BUFSIZ];
-
- main(argc, argv)
- int argc;
- char **argv;
- {
- register int i;
- char *p;
- struct range_header *rh;
- struct range *r;
- FILE *fp;
- char *gets(), *index(), *strcpy();
-
- buf[0] = range[0] = '\0';
- if (argc == 2) {
- if ((fp = fopen(argv[1], "r")) == NULL) {
- (void) fprintf(stderr, "Can't open %s\n", argv[1]);
- exit(1);
- /*NOTREACHED*/
- }
- }
- else
- fp = stdin;
-
- if (fp == stdin)
- (void) printf("Range? ");
- while (fgets(buf, sizeof(buf), fp) != NULL) {
- if ((p = index(buf, '\n')) != NULL)
- *p = '\0';
- if (buf[0] != '\0') {
- (void) strcpy(range, buf);
- if (checkrange(range)) {
- if (fp == stdin)
- (void) printf("Range? ");
- continue;
- }
- rh = &range_header;
- (void) printf("%s (%d alloc) (%d ranges):\n",
- rh->range_str, rh->range_str_alloc, rh->nranges);
- for (r = rh->range, i = 0; i < rh->nranges; i++, r++)
- (void) printf("hflag=%d lflag=%d high=%d low=%d\n",
- r->hflag, r->lflag, r->high, r->low);
- }
- if (fp != stdin)
- continue;
- (void) printf("Value? ");
- if (gets(buf) == NULL)
- break;
- i = inrange(atoi(buf), range);
- if (i == 0)
- (void) printf("\tno\n");
- else if (i == 1)
- (void) printf("\tyes\n");
- else if (i == -1)
- (void) printf("\terror\n");
- else
- (void) printf("\tbad result\n");
- (void) printf("Range ['%s']? ", range);
- }
- (void) printf("\n");
- }
- #endif RANGE_DEBUG
-
- /*
- * Check and compile the given range specification and then determine if
- * the number is in the range
- * Return -1 if there is a compilation error, 1 if the number is in the
- * range, or 0 if the number isn't in the range
- */
- inrange(num, range_spec)
- int num;
- char *range_spec;
- {
- register int i, rc;
- register struct range_header *rh;
- register struct range *r;
-
- if (checkrange(range_spec))
- return(-1);
- rh = &range_header;
- rc = 0;
- for (r = rh->range, i = 0; rc == 0 && i < rh->nranges; i++, r++) {
- if (r->hflag) {
- if (num > r->high)
- continue;
- if (r->lflag && num < r->low)
- continue;
- rc = 1;
- }
- else if (r->lflag) {
- if (num >= r->low)
- rc = 1;
- }
- else /* both unset -> ":" */
- rc = 1;
- }
- return(rc);
- }
-
- /*
- * Check and compile a range specification
- * Print a message and return -1 on error; return 0 oth.
- *
- * Could be more efficient by allocating more structures at a time... SMOP
- */
- checkrange(range_spec)
- char *range_spec;
- {
- register struct range_header *rh;
- register struct range *r;
- int len;
- int ltype, lval, rtype, rval;
- char *p;
- char *malloc(), *realloc(), *strcpy();
-
- rh = &range_header;
- /*
- * Check if the previous range is being used
- */
- if (rh->range_str != NULL && streq(range_spec, rh->range_str))
- return(0);
-
- /*
- * New range spec
- * If there is enough space, reuse it; oth. allocate enough
- * (amount allocated never shrinks)
- */
- len = max(strlen(range_spec) + 1, STR_ALLOC);
- if (rh->range_str != NULL && len > rh->range_str_alloc) {
- free(rh->range_str);
- rh->range_str = (char *) malloc((unsigned) len);
- rh->range_str_alloc = len;
- }
- else if (rh->range_str == NULL) {
- rh->range_str = (char *) malloc((unsigned) len);
- rh->range_str_alloc = len;
- }
- (void) strcpy(rh->range_str, range_spec);
- if (rh->range != NULL)
- free((char *) rh->range);
- rh->range = NULL;
-
- p = range_spec;
- while (1) {
- lval = getnum(&p, <ype);
- if (ltype == BAD_T) {
- (void) fprintf(stderr, "range: bad first number\n");
- *rh->range_str = '\0'; /* invalidate */
- return(-1);
- }
-
- if (rh->range == NULL) {
- smalloc(r, struct range *, sizeof(struct range));
- rh->nranges = 1;
- }
- else {
- len = sizeof(struct range) * ++(rh->nranges);
- srealloc(r, struct range *, (char *) rh->range, len);
- }
- rh->range = r;
- r += rh->nranges - 1; /* point to new one */
- r->hflag = r->lflag = 0;
- r->high = r->low = 0;
-
- /*
- * If ltype != NUM_T there is no lval
- */
- if (ltype == NUM_T) {
- r->lflag = 1;
- r->low = lval;
- }
-
- switch (*p) {
- case ',': /* single number */
- r->hflag = 1;
- r->high = lval;
- p++;
- continue;
-
- case '\0': /* single number at end */
- r->hflag = 1;
- r->high = lval;
- return(0);
-
- case ':':
- p++;
- if (*p == '\0') /* no rval */
- return(0);
- if (*p == ',') { /* no rval */
- p++;
- break;
- }
-
- rval = getnum(&p, &rtype);
- if (rtype == BAD_T) {
- (void) fprintf(stderr, "range: bad second number\n");
- *rh->range_str = '\0';
- return(-1);
- }
-
- if (lval > rval) {
- (void) fprintf(stderr, "range: values reversed\n");
- *rh->range_str = '\0'; /* invalidate */
- return(-1);
- }
- r->hflag = 1;
- r->high = rval;
- if (*p == '\0')
- return(0);
- if (*p == ',')
- p++;
- break;
-
- default:
- (void) fprintf(stderr, "range: bad character\n");
- *rh->range_str = '\0'; /* invalidate */
- return(-1);
- }
- }
- }
-
- static
- getnum(pp, type)
- char **pp;
- int *type;
- {
- register int sign, val;
- register char *p;
-
- p = *pp;
- if (!isdigit(*p) && *p != '-') {
- if (*p == SEP_CHAR)
- *type = SEP_T;
- else
- *type = BAD_T;
- return(0);
- }
- sign = 1;
- if (*p == '-') {
- sign = -1;
- p++;
- }
- if (!isdigit(*p)) {
- *type = BAD_T;
- return(0);
- }
- for (val = 0; isdigit(*p) && *p != '\0'; p++)
- val = val * 10 + *p - '0';
- if (*p != '\0' && *p != ',' && *p != SEP_CHAR) {
- *type = BAD_T;
- return(0);
- }
- *pp = p;
- *type = NUM_T;
- return(sign * val);
- }
-
-