#include #include #include #include #include #include #include #include "int.h" #include "girstring.h" #include "casprintf.h" #include "string_parser.h" static const char * strippedSubstring(const char * const string) { const char * p; for (p = &string[0]; isspace(*p); ++p); return p; } void interpretUll(const char * const string, uint64_t * const ullP, const char ** const errorP) { /* strtoull() has the same disappointing weaknesses of strtoul(). See interpretUint(). */ const char * const strippedString = strippedSubstring(string); if (strippedString[0] == '\0') casprintf(errorP, "Null (or all whitespace) string."); else if (!isdigit(strippedString[0])) casprintf(errorP, "First non-blank character is '%c', not a digit.", strippedString[0]); else { /* strtoull() does a bizarre thing where if the number is out of range, it returns a clamped value but tells you about it by setting errno = ERANGE. If it is not out of range, strtoull() leaves errno alone. */ char * tail; errno = 0; /* So we can tell if strtoull() overflowed */ *ullP = strtoull(strippedString, &tail, 10); if (tail[0] != '\0') casprintf(errorP, "Non-digit stuff in string: %s", tail); else if (errno == ERANGE) casprintf(errorP, "Number too large"); else *errorP = NULL; } } void interpretLl(const char * const string, int64_t * const llP, const char ** const errorP) { if (string[0] == '\0') casprintf(errorP, "Null string."); else { /* strtoll() does a bizarre thing where if the number is out of range, it returns a clamped value but tells you about it by setting errno = ERANGE. If it is not out of range, strtoll() leaves errno alone. */ char * tail; errno = 0; /* So we can tell if strtoll() overflowed */ *llP = strtoll(string, &tail, 10); if (tail[0] != '\0') casprintf(errorP, "Non-digit stuff in string: %s", tail); else if (errno == ERANGE) casprintf(errorP, "Number too large"); else *errorP = NULL; } } void interpretUint(const char * const string, unsigned int * const uintP, const char ** const errorP) { /* strtoul() does a lousy job of dealing with invalid numbers. A null string is just zero; a negative number is a large positive one; a positive (cf unsigned) number is accepted. strtoul is inconsistent in its treatment of the tail; if there is no valid number at all, it returns the entire string as the tail, including leading white space and sign, which are not themselves invalid. */ const char * const strippedString = strippedSubstring(string); if (strippedString[0] == '\0') casprintf(errorP, "Null (or all whitespace) string."); else if (!isdigit(strippedString[0])) casprintf(errorP, "First non-blank character is '%c', not a digit.", strippedString[0]); else { /* strtoul() does a bizarre thing where if the number is out of range, it returns a clamped value but tells you about it by setting errno = ERANGE. If it is not out of range, strtoul() leaves errno alone. */ char * tail; unsigned long ulongValue; errno = 0; /* So we can tell if strtoul() overflowed */ ulongValue = strtoul(strippedString, &tail, 10); if (tail[0] != '\0') casprintf(errorP, "Non-digit stuff in string: %s", tail); else if (errno == ERANGE) casprintf(errorP, "Number too large"); else if (ulongValue > UINT_MAX) casprintf(errorP, "Number too large"); else { *uintP = ulongValue; *errorP = NULL; } } } void interpretInt(const char * const string, int * const intP, const char ** const errorP) { if (string[0] == '\0') casprintf(errorP, "Null string."); else { /* strtol() does a bizarre thing where if the number is out of range, it returns a clamped value but tells you about it by setting errno = ERANGE. If it is not out of range, strtol() leaves errno alone. */ char * tail; long longValue; errno = 0; /* So we can tell if strtol() overflowed */ longValue = strtol(string, &tail, 10); if (tail[0] != '\0') casprintf(errorP, "Non-digit stuff in string: %s", tail); else if (errno == ERANGE) casprintf(errorP, "Number too large"); else if (longValue > INT_MAX) casprintf(errorP, "Number too large"); else if (longValue < INT_MIN) casprintf(errorP, "Number too negative"); else { *intP = longValue; *errorP = NULL; } } } void interpretBinUint(const char * const string, uint64_t * const valueP, const char ** const errorP) { char * tailptr; long const mantissa_long = strtol(string, &tailptr, 10); if (errno == ERANGE) casprintf(errorP, "Numeric value out of range for computation: '%s'. " "Try a smaller number with a K, M, G, etc. suffix.", string); else { int64_t const mantissa = mantissa_long; int64_t argNumber; *errorP = NULL; /* initial assumption */ if (*tailptr == '\0') /* There's no suffix. A pure number */ argNumber = mantissa * 1; else if (stripcaseeq(tailptr, "K")) argNumber = mantissa * 1024; else if (stripcaseeq(tailptr, "M")) argNumber = mantissa * 1024 * 1024; else if (stripcaseeq(tailptr, "G")) argNumber = mantissa * 1024 * 1024 * 1024; else if (stripcaseeq(tailptr, "T")) argNumber = mantissa * 1024 * 1024 * 1024 * 1024; else if (stripcaseeq(tailptr, "P")) argNumber = mantissa * 1024 * 1024 * 1024 * 1024 * 1024; else { argNumber = 0; /* quiet compiler warning */ casprintf(errorP, "Garbage suffix '%s' on number", tailptr); } if (!*errorP) { if (argNumber < 0) casprintf(errorP, "Unsigned numeric value is " "negative: %" PRId64, argNumber); else *valueP = (uint64_t) argNumber; } } }