tech-userlevel archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: Reuse strtonum(3) and reallocarray(3) from OpenBSD
In article <20141129135653.GA1626%britannica.bec.de@localhost>,
Joerg Sonnenberger <joerg%britannica.bec.de@localhost> wrote:
>On Sat, Nov 29, 2014 at 04:18:24AM +0100, Kamil Rytarowski wrote:
>> I've prepared a small patch adding strtonum(3)
>
>Have you bothered to search the list archive why strtonum(3) was
>rejected the first time? I don't think any of the problems has been
>addressed by OpenBSD. Just because people want to reinvent strfry(3)
>doesn't mean it should be used.
With the helpful feedback from rmind, joerg, etc. I've written a function
which I call strtoi() (and strtou()) which tries to fix the API issues with
strtonum() and provides strtonum() as a wrapper:
#include <stdio.h>
#include <errno.h>
#include <inttypes.h>
/*
* 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;
}
Home |
Main Index |
Thread Index |
Old Index