#include #include #include /* * in inttypes.h: */ intmax_t strtoi(const char * __restrict, char ** __restrict, int, intmax_t, intmax_t, int *); uintmax_t strtou(const char * __restrict, char ** __restrict, int, uintmax_t, uintmax_t, int *); #ifdef _OPENBSD_SOURCE long long strtonum(const char *, long long, long long, const char **) #endif /* * in libc. */ /* * Problems with the strtonum(3) API: * - will return 0 on failure; 0 might not be in range, so * that necessitates an error check even if you want to avoid it. * - does not differentiate 'illegal' returns, so we can't tell * the difference between partial and no conversions. * - returns english strings * - can't set the base, or find where the conversion ended */ long long strtonum(const char *ptr, long long lo, long long hi, const char **res) { int e; intmax_t rv; const char *resp; if (res == NULL) res = &resp; rv = strtoi(ptr, NULL, 0, lo, hi, &e); if (e == 0) { *res = NULL; return rv; } *res = e != ERANGE ? "invalid" : (rv == hi ? "too large" : "too small"); return 0; } /* * - Always returns numbers in range. Errors are indicated in *rerror: * - 0 -> OK * - ENOTSUP -> the string conversion was incomplere * - EINVAL -> the base was not supported * - ERANGE -> the value returned from the conversion was out of range * and was corrected to be in range. * * - Does not need to reset or deal with errno. Simple error checking: * int e; * type num = (type)strtol(buf, NULL, 0, lo, hi, &e) * if (e) * warnx("Bad number `%s' (%s)", buf, strerror(e)); * Or more precise: * switch (e) { * case 0: * break; * case ENOTSUP: * warnx("Bad number `%s'", buf); * break; * case ERANGE: * warnx("Too %s number `%s'", buf, num == hi ? "big" : "small"); * break; * default: * abort(); // EINVAL can't happen here * } * * - Ignore all information: strtoi("foo", NULL, 0, 10, 20, NULL), but return * "reasonable" result even on error: This returns 10, since "foo" could * not be converted, and the default unconverted result from strtoimax(3) * is 0. * * - Doesn't suffer from i17n * * - Same basic functionality as other strto*() functions (includes eptr, base) */ intmax_t strtoi(const char * __restrict ptr, char ** __restrict eptr, int base, intmax_t lo, intmax_t hi, int *rerror) { int serrno; intmax_t im; char *ep; int rep; if (*eptr == NULL) eptr = &ep; if (rerror == NULL) rerror = &rep; serrno = errno; errno = 0; im = strtoimax(ptr, eptr, base); *rerror = errno; errno = serrno; if (*rerror == 0 && (ptr == *eptr || *eptr)) *rerror = ENOTSUP; if (im < lo) { if (*rerror == 0) *rerror = ERANGE; return lo; } if (im > hi) { if (*rerror == 0) *rerror = ERANGE; return hi; } return im; }