home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume15
/
lwf
/
range.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-05-24
|
8KB
|
351 lines
/* 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);
}