home *** CD-ROM | disk | FTP | other *** search
- /**************************************************************************
- * ranges.c: Given a numeric range (such as "12-67"), create a bit
- * vector that has its appropriate bits (in this case, bits
- * 12 through 67 inclusive) turned on, and all others turned
- * off.
- * Uses a finite automaton.
- * A part of OberSuite for the Commodore Amiga.
- *
- * Author: Daniel Barrett, barrett@cs.umass.edu.
- * Version: 1.0.
- * Copyright: None! This program is in the Public Domain.
- * Please share it with others.
- ***************************************************************************/
-
- #include "decl.h"
- #include "oberheim.h"
- #include "bits.h"
-
- /***************************************************************************
- * A dash character is used to separate 2 integers, forming a range.
- ***************************************************************************/
-
- #define RANGECHAR '-'
-
- /***************************************************************************
- * Ranges may contain only non-negative integers. Here is an impossible
- * value that we may use to indicate an error condition or uninitialized
- * value.
- ***************************************************************************/
-
- #define GARBAGE_NUMBER (-1)
-
- /***************************************************************************
- * Convert a digit character to its numeric value.
- ***************************************************************************/
- #define CH_TO_NUM(ch) ((ch) - '0')
-
- /***************************************************************************
- * We assume base 10.
- ***************************************************************************/
-
- #define BASE 10
-
- /***************************************************************************
- * The states for our finite automaton. Described below.
- ***************************************************************************/
-
- #define RANGE_START 0
- #define RANGE_INFIRST 1
- #define RANGE_ENDFIRST 2
- #define RANGE_INSECOND 3
- #define RANGE_DONE 4
- #define RANGE_ERROR 5
-
- /***************************************************************************
- * Upper and lower limits for range values.
- ***************************************************************************/
-
- #define UPPER_LIMIT LAST_PATCH
- #define LOWER_LIMIT FIRST_PATCH
-
-
- /****************************************************************************
- * MakeRange: An automaton that breaks a numeric range (xx-yy) into its
- * two bounds "first" and "second". Return value is 0 on success, >0 else.
- *
- * If the range is a single integer, then both "first" and "second" are set
- * to its value. This is an acceptable "degenerate" range.
- *
- * The 6 automaton states are:
- * RANGE_START: Nothing has been read yet.
- * RANGE_INFIRST: At least one digit has been found.
- * RANGE_ENDFIRST: A dash has been encountered.
- * RANGE_INSECOND: At least one digit has been found after the dash.
- * RANGE_ERROR: An illegal character was found.
- * RANGE_DONE: Terminating state.
- ***************************************************************************/
-
- int MakeRange(char *str, int *first, int *second)
- {
- int state = RANGE_START;
- int error = 0;
-
- (*first) = (*second) = GARBAGE_NUMBER;
-
- while ((state != RANGE_DONE))
- {
- switch (state)
- {
- /*
- * Our first character MUST be a digit.
- * Get its value and switch to RANGE_INFIRST state.
- */
- case RANGE_START:
- if (isdigit(*str))
- {
- (*first) = CH_TO_NUM(*str);
- str++;
- state = RANGE_INFIRST;
- }
- else
- state = RANGE_ERROR;
- break;
-
- /*
- * We have already read a digit, but have not encountered a RANGECHAR.
- *
- * If we encounter another digit, increase the value of *first.
- * If we find a RANGECHAR, we are done with the first number.
- * If we reach the end of the string, there is no second number, and
- * we have a degenerate range (1 integer).
- * Anything else is an error.
- */
- case RANGE_INFIRST:
- if (isdigit(*str))
- {
- (*first) *= BASE;
- (*first) += CH_TO_NUM(*str);
- str++;
- }
- else if (*str == RANGECHAR)
- {
- state = RANGE_ENDFIRST;
- str++;
- }
- else if (*str == '\0')
- {
- (*second) = (*first);
- state = RANGE_DONE;
- }
- else
- state = RANGE_ERROR;
- break;
-
- /*
- * We have just read the RANGECHAR separator.
- * We MUST find a digit as the next character.
- */
- case RANGE_ENDFIRST:
- if (isdigit(*str))
- {
- (*second) = CH_TO_NUM(*str);
- str++;
- state = RANGE_INSECOND;
- }
- else
- state = RANGE_ERROR;
- break;
-
- /*
- * We are in the middle of the second integer.
- * Similar logic to that of RANGE_INFIRST, but a RANGECHAR is no
- * longer an acceptable input.
- */
- case RANGE_INSECOND:
- if (isdigit(*str))
- {
- (*second) *= BASE;
- (*second) += CH_TO_NUM(*str);
- str++;
- }
- else if (*str == '\0')
- state = RANGE_DONE;
- else
- state = RANGE_ERROR;
- break;
-
- case RANGE_ERROR:
- error++;
- state = RANGE_DONE;
- break;
-
- case RANGE_DONE:
- break;
-
- default: /* This should never happen!! */
- break;
- }
- }
-
- return(!error);
- }
-
-
- /***************************************************************************
- * Check that both "first" and "second" lie within the legal limits.
- * Then, turn on all bits in "bitfield" between first and second, inclusive.
- * It doesn't matter which of "first" or "second" is larger; the range
- * is still valid.
- ***************************************************************************/
-
- int AddToRange(BITS bitfield[], int first, int second)
- {
- int i;
-
- if (!Between(first, LOWER_LIMIT, UPPER_LIMIT)
- || !Between(second, LOWER_LIMIT, UPPER_LIMIT))
- return(0);
- else if (first <= second)
- for (i=first; i<=second; i++)
- FlipOn(bitfield, i);
- else
- for (i=first; i>=second; i--)
- FlipOn(bitfield, i);
-
- return(1);
- }
-
-
- /***************************************************************************
- * A test program to make sure that ranges are working correctly.
- * Compile with -DTEST_RANGE_PROGRAM and link with bits.o and parse.o.
- ***************************************************************************/
-
- #ifdef TEST_RANGE_PROGRAM
- void PrintBitfield(BITS bitfield[], int numBits);
-
- main(argc, argv)
- int argc; char *argv[];
- {
- int i, upper, lower;
- BITS bitfield[BITFIELD_LENGTH];
-
- ClearBitfield(bitfield);
-
- for (i=1; i<argc; i++)
- if (MakeRange(argv[i], &upper, &lower))
- AddToRange(bitfield, upper, lower);
-
- PrintBitfield(bitfield, UPPER_LIMIT+1);
- }
-
- void PrintBitfield(BITS bitfield[], int numBits)
- {
- int i;
-
- for (i=0; i<numBits; i++)
- if (BitOn(bitfield, i))
- printf(" %d", i);
- printf("\n");
- }
- #endif /* TEST_RANGE_PROGRAM */
-