libc/stdlib: Port strtonumx() from Illumos

Add strtonumx(), a companion to strtonum(3) that preserves its safety
and error-reporting semantics while allowing the caller to specify a
conversion base, similar to the strtol(3) family of functions.

Reviewed by:	emaste, kib, ziaee
Obtained from:	https://www.illumos.org/issues/15365
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D54270
This commit is contained in:
Hans Rosenfeld
2025-12-16 17:04:30 +01:00
committed by Joseph Mingrone
parent 73c921ef1d
commit 6a6f230d31
5 changed files with 82 additions and 23 deletions
+2
View File
@@ -333,6 +333,8 @@ int sradixsort(const unsigned char **, int, const unsigned char *,
void srandomdev(void);
long long
strtonum(const char *, long long, long long, const char **);
long long
strtonumx(const char *, long long, long long, const char **, int);
/* Deprecated interfaces, to be removed. */
__int64_t
+1
View File
@@ -142,6 +142,7 @@ MLINKS+=strtod.3 strtof.3 \
MLINKS+=strtol.3 strtoll.3 \
strtol.3 strtoq.3 \
strtol.3 strtoimax.3
MLINKS+=strtonum.3 strtonumx.3
MLINKS+=strtoul.3 strtoull.3 \
strtoul.3 strtouq.3 \
strtoul.3 strtoumax.3
+1
View File
@@ -134,6 +134,7 @@ FBSD_1.8 {
FBSD_1.9 {
memalignment;
recallocarray;
strtonumx;
tdestroy;
};
+57 -17
View File
@@ -1,4 +1,5 @@
.\" Copyright (c) 2004 Ted Unangst
.\" Copyright 2023 Oxide Computer Company
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@@ -14,11 +15,12 @@
.\"
.\" $OpenBSD: strtonum.3,v 1.13 2006/04/25 05:15:42 tedu Exp $
.\"
.Dd April 29, 2004
.Dd January 15, 2026
.Dt STRTONUM 3
.Os
.Sh NAME
.Nm strtonum
.Nm strtonum ,
.Nm strtonumx
.Nd "reliably convert string value to an integer"
.Sh SYNOPSIS
.In stdlib.h
@@ -29,26 +31,33 @@
.Fa "long long maxval"
.Fa "const char **errstr"
.Fc
.Ft long long
.Fo strtonumx
.Fa "const char *nptr"
.Fa "long long minval"
.Fa "long long maxval"
.Fa "const char **errstr"
.Fa "int base"
.Fc
.Sh DESCRIPTION
The
.Fn strtonum
function converts the string in
and
.Fn strtonumx
functions convert the string in
.Fa nptr
to a
.Vt "long long"
value.
The
.Fn strtonum
function was designed to facilitate safe, robust programming
and overcome the shortcomings of the
These functions were designed to facilitate safe, robust programming and
overcome the shortcomings of the
.Xr atoi 3
and
.Xr strtol 3
family of interfaces.
.Pp
The string may begin with an arbitrary amount of whitespace
(as determined by
.Xr isspace 3 )
.Pq as determined by Xr isspace 3
followed by a single optional
.Ql +
or
@@ -57,7 +66,10 @@ sign.
.Pp
The remainder of the string is converted to a
.Vt "long long"
value according to base 10.
value according to base 10
.Pq for Fn strtonum
or the provided base
.Pq for Fn strtonumx .
.Pp
The value obtained is then checked against the provided
.Fa minval
@@ -68,13 +80,30 @@ If
.Fa errstr
is non-null,
.Fn strtonum
stores an error string in
and
.Fn strtonumx
store an error string in
.Fa *errstr
indicating the failure.
.Pp
For
.Fn strtonumx
the value of
.Ar base
is interpreted in the same way as described in
.Xr strtoll 3 .
In particular, if the value of
.Ar base
is 0, then the expected form of
.Ar nptr
is that of a decimal constant, octal constant or hexadecimal constant, any of
which may be preceded by a + or - sign.
.Sh RETURN VALUES
The
.Fn strtonum
function returns the result of the conversion,
and
.Fn strtonumx
functions return the result of the conversion,
unless the value would exceed the provided bounds or is invalid.
On error, 0 is returned,
.Va errno
@@ -90,6 +119,8 @@ a successful return of 0 from an error.
.Sh EXAMPLES
Using
.Fn strtonum
and
.Fn strtonumx
correctly is meant to be simpler than the alternative functions.
.Bd -literal -offset indent
int iterations;
@@ -107,7 +138,10 @@ The above example will guarantee that the value of iterations is between
.It Bq Er ERANGE
The given string was out of range.
.It Bq Er EINVAL
The given string did not consist solely of digit characters.
The given string did not consist solely of digit characters
.Pq for Fn strtonum ,
or characters which are valid in the given base
.Pq for Fn strtonumx .
.It Bq Er EINVAL
The supplied
.Fa minval
@@ -120,12 +154,15 @@ If an error occurs,
will be set to one of the following strings:
.Pp
.Bl -tag -width ".Li too large" -compact
.It Li "too large"
.It Qq too large
The result was larger than the provided maximum value.
.It Li "too small"
.It Qq too small
The result was smaller than the provided minimum value.
.It Li invalid
The string did not consist solely of digit characters.
.It Qq invalid
The string did not consist solely of characters valid in the specified base
.Pq or base 10 for Fn strtonum .
.It Qq unparsable; invalid base specified
The specified base was outside the permitted range.
.El
.Sh SEE ALSO
.Xr atof 3 ,
@@ -152,3 +189,6 @@ The
.Fn strtonum
function first appeared in
.Ox 3.6 .
The
.Fn strtonumx
function first appeared in illumos in 2023.
+21 -6
View File
@@ -2,6 +2,8 @@
* Copyright (c) 2004 Ted Unangst and Todd Miller
* All rights reserved.
*
* Copyright 2023 Oxide Computer Company
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
@@ -24,10 +26,13 @@
#define INVALID 1
#define TOOSMALL 2
#define TOOLARGE 3
#define BADBASE 4
#define MBASE ('z' - 'a' + 1 + 10)
long long
strtonum(const char *numstr, long long minval, long long maxval,
const char **errstrp)
strtonumx(const char *numstr, long long minval, long long maxval,
const char **errstrp, int base)
{
long long ll = 0;
int error = 0;
@@ -35,20 +40,23 @@ strtonum(const char *numstr, long long minval, long long maxval,
struct errval {
const char *errstr;
int err;
} ev[4] = {
} ev[5] = {
{ NULL, 0 },
{ "invalid", EINVAL },
{ "too small", ERANGE },
{ "too large", ERANGE },
{ "unparsable; invalid base specified", EINVAL },
};
ev[0].err = errno;
errno = 0;
if (minval > maxval) {
error = INVALID;
} else if (base < 0 || base > MBASE || base == 1) {
error = BADBASE;
} else {
ll = strtoll(numstr, &ep, 10);
if (errno == EINVAL || numstr == ep || *ep != '\0')
ll = strtoll(numstr, &ep, base);
if (numstr == ep || *ep != '\0')
error = INVALID;
else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
error = TOOSMALL;
@@ -58,8 +66,15 @@ strtonum(const char *numstr, long long minval, long long maxval,
if (errstrp != NULL)
*errstrp = ev[error].errstr;
errno = ev[error].err;
if (error)
if (error != 0)
ll = 0;
return (ll);
}
long long
strtonum(const char *numstr, long long minval, long long maxval,
const char **errstrp)
{
return (strtonumx(numstr, minval, maxval, errstrp, 10));
}