mirror of
https://github.com/edeproject/ede.git
synced 2023-08-10 21:13:03 +03:00
227 lines
6.7 KiB
C
227 lines
6.7 KiB
C
|
#include <sys/types.h>
|
||
|
#include <string.h>
|
||
|
#include <ctype.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <errno.h>
|
||
|
#include <limits.h>
|
||
|
|
||
|
#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;
|
||
|
}
|
||
|
}
|
||
|
}
|