home *** CD-ROM | disk | FTP | other *** search
/ Audio 4.94 - Over 11,000 Files / audio-11000.iso / amiga / midi / obrst103.lha / OberSuite-1.03 / SourceCode / ranges.c < prev    next >
C/C++ Source or Header  |  1993-01-23  |  7KB  |  243 lines

  1. /**************************************************************************
  2. * ranges.c:    Given a numeric range (such as "12-67"), create a bit 
  3. *         vector that has its appropriate bits (in this case, bits 
  4. *         12 through 67 inclusive) turned on, and all others turned
  5. *         off.
  6. *        Uses a finite automaton.
  7. *        A part of OberSuite for the Commodore Amiga.
  8. *
  9. * Author:    Daniel Barrett, barrett@cs.umass.edu.
  10. * Version:    1.0.
  11. * Copyright:    None!  This program is in the Public Domain.
  12. *        Please share it with others.
  13. ***************************************************************************/
  14.  
  15. #include "decl.h"
  16. #include "oberheim.h"
  17. #include "bits.h"
  18.  
  19. /***************************************************************************
  20. * A dash character is used to separate 2 integers, forming a range.
  21. ***************************************************************************/
  22.  
  23. #define    RANGECHAR    '-'
  24.  
  25. /***************************************************************************
  26. * Ranges may contain only non-negative integers.  Here is an impossible
  27. * value that we may use to indicate an error condition or uninitialized
  28. * value.
  29. ***************************************************************************/
  30.  
  31. #define    GARBAGE_NUMBER    (-1)
  32.  
  33. /***************************************************************************
  34. * Convert a digit character to its numeric value.
  35. ***************************************************************************/
  36. #define    CH_TO_NUM(ch)    ((ch) - '0')
  37.  
  38. /***************************************************************************
  39. * We assume base 10.
  40. ***************************************************************************/
  41.  
  42. #define    BASE        10
  43.  
  44. /***************************************************************************
  45. * The states for our finite automaton.  Described below.
  46. ***************************************************************************/
  47.  
  48. #define    RANGE_START    0
  49. #define    RANGE_INFIRST    1
  50. #define    RANGE_ENDFIRST    2
  51. #define    RANGE_INSECOND    3
  52. #define    RANGE_DONE    4
  53. #define    RANGE_ERROR    5
  54.  
  55. /***************************************************************************
  56. * Upper and lower limits for range values.
  57. ***************************************************************************/
  58.  
  59. #define    UPPER_LIMIT    LAST_PATCH
  60. #define    LOWER_LIMIT    FIRST_PATCH
  61.  
  62.  
  63. /****************************************************************************
  64.  * MakeRange:  An automaton that breaks a numeric range (xx-yy) into its
  65.  * two bounds "first" and "second".  Return value is 0 on success, >0 else.
  66.  *
  67.  * If the range is a single integer, then both "first" and "second" are set
  68.  * to its value.  This is an acceptable "degenerate" range.
  69.  *
  70.  * The 6 automaton states are:
  71.  *    RANGE_START:    Nothing has been read yet.
  72.  *    RANGE_INFIRST:    At least one digit has been found.
  73.  *    RANGE_ENDFIRST:    A dash has been encountered.
  74.  *    RANGE_INSECOND:    At least one digit has been found after the dash.
  75.  *    RANGE_ERROR:    An illegal character was found.
  76.  *    RANGE_DONE:    Terminating state.
  77.  ***************************************************************************/
  78.  
  79. int MakeRange(char *str, int *first, int *second)
  80. {
  81.     int state = RANGE_START;
  82.     int error = 0;
  83.  
  84.     (*first) = (*second) = GARBAGE_NUMBER;
  85.     
  86.     while ((state != RANGE_DONE))
  87.     {
  88.         switch (state)
  89.         {
  90.    /* 
  91.     * Our first character MUST be a digit.
  92.     * Get its value and switch to RANGE_INFIRST state.
  93.     */
  94.             case RANGE_START:
  95.                 if (isdigit(*str))
  96.                 {
  97.                     (*first) = CH_TO_NUM(*str);
  98.                     str++;
  99.                     state = RANGE_INFIRST;
  100.                 }
  101.                 else
  102.                     state = RANGE_ERROR;
  103.                 break;
  104.  
  105.    /*
  106.     * We have already read a digit, but have not encountered a RANGECHAR.
  107.     *
  108.     * If we encounter another digit, increase the value of *first.
  109.     * If we find a RANGECHAR, we are done with the first number.
  110.     * If we reach the end of the string, there is no second number, and
  111.     *  we have a degenerate range (1 integer).
  112.     * Anything else is an error.
  113.     */
  114.             case RANGE_INFIRST:
  115.                 if (isdigit(*str))
  116.                 {
  117.                     (*first) *= BASE;
  118.                     (*first) += CH_TO_NUM(*str);
  119.                     str++;
  120.                 }
  121.                 else if (*str == RANGECHAR)
  122.                 {
  123.                     state = RANGE_ENDFIRST;
  124.                     str++;
  125.                 }
  126.                 else if (*str == '\0')
  127.                 {
  128.                     (*second) = (*first);
  129.                     state = RANGE_DONE;
  130.                 }
  131.                 else
  132.                     state = RANGE_ERROR;
  133.                 break;
  134.  
  135.    /*
  136.     * We have just read the RANGECHAR separator.
  137.     * We MUST find a digit as the next character.
  138.     */
  139.             case RANGE_ENDFIRST:
  140.                 if (isdigit(*str))
  141.                 {
  142.                     (*second) = CH_TO_NUM(*str);
  143.                     str++;
  144.                     state = RANGE_INSECOND;
  145.                 }
  146.                 else
  147.                     state = RANGE_ERROR;
  148.                 break;
  149.  
  150.    /*
  151.     * We are in the middle of the second integer.
  152.     * Similar logic to that of RANGE_INFIRST, but a RANGECHAR is no
  153.     *  longer an acceptable input.
  154.     */
  155.             case RANGE_INSECOND:
  156.                 if (isdigit(*str))
  157.                 {
  158.                     (*second) *= BASE;
  159.                     (*second) += CH_TO_NUM(*str);
  160.                     str++;
  161.                 }
  162.                 else if (*str == '\0')
  163.                     state = RANGE_DONE;
  164.                 else
  165.                     state = RANGE_ERROR;
  166.                 break;
  167.  
  168.             case RANGE_ERROR:
  169.                 error++;
  170.                 state = RANGE_DONE;
  171.                 break;
  172.  
  173.             case RANGE_DONE:
  174.                 break;
  175.  
  176.             default:    /* This should never happen!! */
  177.                 break;
  178.         }
  179.     }
  180.  
  181.     return(!error);
  182. }
  183.  
  184.  
  185. /***************************************************************************
  186. * Check that both "first" and "second" lie within the legal limits.
  187. * Then, turn on all bits in "bitfield" between first and second, inclusive.
  188. * It doesn't matter which of "first" or "second" is larger; the range
  189. *  is still valid.
  190. ***************************************************************************/
  191.  
  192. int AddToRange(BITS bitfield[], int first, int second)
  193. {
  194.     int i;
  195.  
  196.     if (!Between(first, LOWER_LIMIT, UPPER_LIMIT)
  197.     ||  !Between(second, LOWER_LIMIT, UPPER_LIMIT))
  198.         return(0);
  199.     else if (first <= second)
  200.         for (i=first; i<=second; i++)
  201.             FlipOn(bitfield, i);
  202.     else
  203.         for (i=first; i>=second; i--)
  204.             FlipOn(bitfield, i);
  205.  
  206.     return(1);
  207. }
  208.  
  209.  
  210. /***************************************************************************
  211. * A test program to make sure that ranges are working correctly.
  212. * Compile with -DTEST_RANGE_PROGRAM and link with bits.o and parse.o.
  213. ***************************************************************************/
  214.  
  215. #ifdef TEST_RANGE_PROGRAM
  216. void PrintBitfield(BITS bitfield[], int numBits);
  217.  
  218. main(argc, argv)
  219. int argc; char *argv[];
  220. {
  221.     int i, upper, lower;
  222.     BITS bitfield[BITFIELD_LENGTH];
  223.  
  224.     ClearBitfield(bitfield);
  225.     
  226.     for (i=1; i<argc; i++)
  227.         if (MakeRange(argv[i], &upper, &lower))
  228.             AddToRange(bitfield, upper, lower);
  229.  
  230.     PrintBitfield(bitfield, UPPER_LIMIT+1);
  231. }
  232.  
  233. void PrintBitfield(BITS bitfield[], int numBits)
  234. {
  235.     int i;
  236.     
  237.     for (i=0; i<numBits; i++)
  238.         if (BitOn(bitfield, i))
  239.             printf(" %d", i);
  240.     printf("\n");
  241. }
  242. #endif /* TEST_RANGE_PROGRAM */
  243.