/* range.c -- Copyright 1989, 1994, 1995 Liam R. Quin. * All Rights Reserved. * This code is NOT in the public domain. * See the file COPYRIGHT for full details. */ /* $Id: range.c,v 1.6 1996/08/14 17:01:45 lee Exp $ */ #include "error.h" #include #include #include "globals.h" #include #ifdef HAVE_STRING_H # include #else # include #endif #include "emalloc.h" #include "range.h" #include "lqutil.h" #include "liblqtext.h" static char *theFile = __FILE__; /* * LQU_LargerThanRangeTop * Utilities/Numeric Range * * Use for efficiency, to determine whether a given number is larger * than the largest value accepted by the given range. * Passing a range that ended with a hyphen (for example, 1,2,5-7,12-) * will always produce a zero result, even if n falls within a `hole' * in the range, as for 4, 8, 9, 10 and 11 in the example here. * * *
  • 0 if the number is not entirely beyond the given range *
  • 1 otherwise * * * LQU_StringToRange * LQU_NumberWithinRange * */ API int LQU_LargerThanRangeTop(n, Range) CONST int n; CONST t_Range *Range; { register CONST t_Range *rp; for (rp = Range; rp; rp = rp->Next) { if (n <= rp->Last) { return 0; } if (!rp->Next && rp->AndAllAfter == (unsigned int) 1) { return 1; } } return 1; } /* * 0 --> n within range, not wanted * 1 --> n within range and wanted * 0 --> n outside range */ /* * LQU_NumberWithinRange * Utilities/Numeric Range * * Determine whether a given number, n, falls within a given range. *

    A range is a list like `-4,12-30,40,100-', to match

    *

    1, 2, 3, 4, 12, 13...29, 30, 40, 100, 101, 102, ...

    *

    A space can be used instead of a comma. The range "all" * generates the range `-1,2-', matching all numbers

    * * *
  • 1 if the n is within (matched by) the given range *
  • 0 otherwise * * * LQU_StringToRange * LQU_LargerThanRangeTop * */ API int LQU_NumberWithinRange(n, Range) CONST int n; CONST t_Range *Range; { register CONST t_Range *rp; for (rp = Range; rp; rp = rp->Next) { if (n <= rp->Last) { if (n < rp->First) { /* too early, but check to see if the range had a leading * "and everything before..." (AndAllBefore), * e.g. "-12,56-90" would allow numbers less than or * equal to 12. * This obviously only applies to the first element in * the range -- you can't say 1-7,-99,101-200 * as that's the same as "-99,101-200", so we only * check for AndAllBefore if rp is pointing to the * start of the range: */ if (rp == Range) { if (rp->AndAllBefore) { return 1; } else { return 0; } } } else { /* within range */ return 1; } } if (!rp->Next && rp->AndAllAfter == (unsigned int) 1) { return 1; } } return 0; } #define SKIPWHITE \ for (; *p; p++) { \ if (!isspace(*p)) { \ break; \ } \ } PRIVATE void Erange(Severity, Message, RangeString, Point, First, Last) int Severity; char *Message; char *RangeString; char *Point; unsigned long First, Last; { char String[2]; String[0] = (*Point); String[1] = '\0'; if (*Point) { *Point = '\0'; Point++; } Error(E_MULTILINE|Severity, "Error in range %s>>%s<<%s", RangeString, String, Point ); Error(E_MULTILINE|Severity|E_LASTLINE, Message, First, Last ); } PRIVATE unsigned long GetNumber(Stringp) register char **Stringp; { unsigned long Value = 0L; do { Value *= 10; Value += **Stringp - '0'; ++*Stringp; } while (isdigit(**Stringp)); return Value; } /* * LQU_StringToRange * Utilities/Numeric Range * * Converts the given string to a range; integers can subsequently be * matched against the range with LQU_NumberWithinRange. * * *
  • a pointer to a range on success *
  • 0 otherwise * * * A null string argument produces a fatal error. * Syntax errors are also fatal. * * LQU_LargerThanRangeTop * LQU_NumberWithinRange * */ API t_Range * LQU_StringToRange(String) CONST char *String; { t_Range *Result = 0; register t_Range **rpp; char *p; if (!String) { Error(E_FATAL|E_BUG|E_INTERNAL, "LQU_StringToRange: Assertion Failed, %s: %d: NULL String argument", theFile, __LINE__ ); } p = LQU_DownCase(String); /* uses static (but malloc()'d) buffer */ SKIPWHITE if (!*p) { return (t_Range *) 0; } if (STREQ(p, "all")) { Result = (t_Range *) emalloc("Range", sizeof(t_Range)); Result->First = 0L; Result->Last = DEFAULT_RANGE_END; Result->AndAllBefore = (unsigned int) 1; Result->AndAllAfter = (unsigned int) 1; return Result; } rpp = &Result; for (;;) { /* find the first in the range */ char *Start; if (!*p || isspace(*p)) { Error(E_FATAL|E_INTERNAL|E_BUG, "LQU_StringToRange: Assertion failed, %s: %d: *p is %d", theFile,__LINE__, *p ); } *rpp = (t_Range *) emalloc("Range", sizeof(t_Range)); (*rpp)->Next = (t_Range *) 0; (*rpp)->First = 0L; (*rpp)->Last = DEFAULT_RANGE_END; (*rpp)->AndAllBefore = (unsigned int) 0; (*rpp)->AndAllAfter = (unsigned int) 0; /* First digit of the pair */ Start = p; if (isdigit(*p)) { (*rpp)->First = GetNumber(&p); SKIPWHITE; /* check the character after the number: */ switch (*p) { case '\0': (*rpp)->Last = (*rpp)->First; return Result; case ',': ++p; SKIPWHITE if (!*p) { --p; Erange(E_FATAL, "trailing , unexpected, use %d- to match to infinity", String, p, (*rpp)->First, (*rpp)->Last ); } else if (isdigit(*p)) { /* another range follows */ (*rpp)->Last = (*rpp)->First; rpp = &(*rpp)->Next; continue; } else { Erange(E_FATAL, "unexpected character found after a comma", String, p, (*rpp)->First, (*rpp)->Last ); } /*NOTREACHED*/ rpp = &(*rpp)->Next; continue; case '-': break; default: if (isdigit(*p)) { /* e.g. 3 4 5 */ (*rpp)->Last = (*rpp)->First; rpp = &(*rpp)->Next; continue; } else { Erange(E_FATAL, "unexpected character found in range", String, p, (*rpp)->First, (*rpp)->Last ); } } } /* endif isdigit(*p) */ /* now we've dealt with the first number in the range, * let's look for the separator before the second one */ if (*p == '-') { if (p == Start) { if ((*rpp) == Result) { (*rpp)->AndAllBefore = (unsigned int) 1; } else { Erange(E_FATAL, "only the first pair in a range can start with a -", String, p, (*rpp)->First, (*rpp)->Last ); } } if (!p[1]) { /* - at the ned of a range */ (*rpp)->Last = DEFAULT_RANGE_END; (*rpp)->AndAllAfter = (unsigned int) 1; return Result; } ++p; } if (!isdigit(*p)) { /*(-:CANTHAPPEN:-)*/ Erange(E_FATAL, "unexpected end of range", String, p, (*rpp)->First, (*rpp)->Last ); } (*rpp)->Last = GetNumber(&p); if ((*rpp)->First > (*rpp)->Last) { Erange(E_FATAL, "Second number in a pair must be smaller than first, %ld > %ld", String, Start, (*rpp)->First, (*rpp)->Last ); } SKIPWHITE; switch (*p) { case '\0': return Result; case '-': Erange(E_FATAL, "format is [-first],m-n,nnn,[last-] or \"all\"; unexpected -", String, p, (*rpp)->First, (*rpp)->Last ); case ',': ++p; SKIPWHITE; rpp = &(*rpp)->Next; continue; default: if (isdigit(*p)) { /* we must have skipped some space in order * to have got here, or the digit would have been part * of the previous number. */ rpp = &(*rpp)->Next; continue; } Erange(E_FATAL, "format is [-first],m-n,nnn,[last-] or \"all\"", String, p, (*rpp)->First, (*rpp)->Last ); } Error(E_FATAL|E_BUG, "%s: %d: reached a NOTREACHED in LQU_StringToRange", theFile, __LINE__ ); } Error(E_FATAL|E_BUG, "%s: %d: reached a NOTREACHED in LQU_StringToRange", theFile, __LINE__ ); }