mirror of
https://github.com/opnsense/src.git
synced 2026-06-20 22:19:13 -04:00
libutil: Backward compatibility for expand_number()
Reimplement expand_number() in terms of expand_unsigned(), which takes
a pointer to uint64_t like expand_number() did before. Provide a macro
that picks the correct version based on the type of the argument.
Fixes: 2e0caa7c7e
Reviewed by: jhb
Differential Revision: https://reviews.freebsd.org/D51723
This commit is contained in:
parent
ad43e7f472
commit
ab1c6874e5
5 changed files with 181 additions and 10 deletions
|
|
@ -13,6 +13,7 @@ FBSD_1.8 {
|
|||
cpuset_parselist;
|
||||
domainset_parselist;
|
||||
expand_number;
|
||||
expand_unsigned;
|
||||
flopen;
|
||||
flopenat;
|
||||
forkpty;
|
||||
|
|
|
|||
|
|
@ -24,11 +24,12 @@
|
|||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd July 25, 2025
|
||||
.Dd August 6, 2025
|
||||
.Dt EXPAND_NUMBER 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm expand_number
|
||||
.Nm expand_number ,
|
||||
.Nm expand_unsigned
|
||||
.Nd parse a number from human readable form
|
||||
.Sh LIBRARY
|
||||
.Lb libutil
|
||||
|
|
@ -38,6 +39,10 @@
|
|||
.Fo expand_number
|
||||
.Fa "const char *buf" "int64_t *num"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo expand_unsigned
|
||||
.Fa "const char *buf" "uint64_t *num"
|
||||
.Fc
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fn expand_number
|
||||
|
|
@ -48,6 +53,17 @@ quantity in the location pointed to by its
|
|||
.Fa *num
|
||||
argument.
|
||||
.Pp
|
||||
The
|
||||
.Fn expand_unsigned
|
||||
function is similar to
|
||||
.Fn expand_number ,
|
||||
but accepts only positive numbers in the range
|
||||
.Bq 0, Ns Dv UINT64_MAX .
|
||||
.Pp
|
||||
Both functions interpret the input
|
||||
.Dq -0
|
||||
as 0.
|
||||
.Pp
|
||||
The input string must consist of a decimal number, optionally preceded
|
||||
by a
|
||||
.Sq +
|
||||
|
|
@ -81,20 +97,38 @@ is interpreted as 5, and
|
|||
.Dq 5kb
|
||||
is interpreted as 5,120).
|
||||
However, the usage of this suffix is discouraged.
|
||||
.Pp
|
||||
For backward compatibility reasons, if the compiler supports generic
|
||||
selection, a macro is provided which automatically replaces calls to
|
||||
.Fn expand_number
|
||||
with calls to
|
||||
.Fn expand_unsigned
|
||||
if the type of the actual
|
||||
.Va num
|
||||
argument is compatible with
|
||||
.Vt uint64_t * .
|
||||
.Sh RETURN VALUES
|
||||
.Rv -std
|
||||
.Sh ERRORS
|
||||
The
|
||||
.Fn expand_number
|
||||
function will fail if:
|
||||
and
|
||||
.Fn expand_unsigned
|
||||
functions will fail if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EINVAL
|
||||
The given string does not contain a valid number.
|
||||
.It Bq Er EINVAL
|
||||
An unrecognized suffix was encountered.
|
||||
.It Bq Er ERANGE
|
||||
The given string represents a number which does not fit into a
|
||||
.Vt int64_t .
|
||||
The given string represents a number which does not fit into an
|
||||
.Vt int64_t
|
||||
(for
|
||||
.Fn expand_number )
|
||||
or
|
||||
.Vt uint64_t
|
||||
(for
|
||||
.Fn expand_unsigned ) .
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr humanize_number 3
|
||||
|
|
@ -103,3 +137,17 @@ The
|
|||
.Fn expand_number
|
||||
function first appeared in
|
||||
.Fx 6.3 .
|
||||
The original implementation did not handle negative numbers correctly,
|
||||
and it was switched to taking a
|
||||
.Vt uint64_t *
|
||||
and accepting only positive numbers in
|
||||
.Fx 9.0 .
|
||||
The
|
||||
.Fn expand_unsigned
|
||||
function was added,
|
||||
and
|
||||
.Fn expand_number
|
||||
switched back to
|
||||
.Vt int64_t * ,
|
||||
in
|
||||
.Fx 15.0 .
|
||||
|
|
|
|||
|
|
@ -37,13 +37,12 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int
|
||||
expand_number(const char *buf, int64_t *num)
|
||||
static int
|
||||
expand_impl(const char *buf, uint64_t *num, bool *neg)
|
||||
{
|
||||
char *endptr;
|
||||
uintmax_t number;
|
||||
unsigned int shift;
|
||||
bool neg;
|
||||
int serrno;
|
||||
|
||||
/*
|
||||
|
|
@ -52,10 +51,10 @@ expand_number(const char *buf, int64_t *num)
|
|||
while (isspace((unsigned char)*buf))
|
||||
buf++;
|
||||
if (*buf == '-') {
|
||||
neg = true;
|
||||
*neg = true;
|
||||
buf++;
|
||||
} else {
|
||||
neg = false;
|
||||
*neg = false;
|
||||
if (*buf == '+')
|
||||
buf++;
|
||||
}
|
||||
|
|
@ -127,6 +126,22 @@ expand_number(const char *buf, int64_t *num)
|
|||
}
|
||||
number <<= shift;
|
||||
|
||||
*num = number;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
(expand_number)(const char *buf, int64_t *num)
|
||||
{
|
||||
uint64_t number;
|
||||
bool neg;
|
||||
|
||||
/*
|
||||
* Parse the number.
|
||||
*/
|
||||
if (expand_impl(buf, &number, &neg) != 0)
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
* Apply the sign and check for overflow.
|
||||
*/
|
||||
|
|
@ -146,3 +161,27 @@ expand_number(const char *buf, int64_t *num)
|
|||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
expand_unsigned(const char *buf, uint64_t *num)
|
||||
{
|
||||
uint64_t number;
|
||||
bool neg;
|
||||
|
||||
/*
|
||||
* Parse the number.
|
||||
*/
|
||||
if (expand_impl(buf, &number, &neg) != 0)
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
* Negative numbers are out of range.
|
||||
*/
|
||||
if (neg && number > 0) {
|
||||
errno = ERANGE;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
*num = number;
|
||||
return (0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,6 +89,14 @@ __BEGIN_DECLS
|
|||
void clean_environment(const char * const *_white,
|
||||
const char * const *_more_white);
|
||||
int expand_number(const char *_buf, int64_t *_num);
|
||||
int expand_unsigned(const char *_buf, uint64_t *_num);
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || \
|
||||
__has_extension(c_generic_selections)
|
||||
#define expand_number(_buf, _num) \
|
||||
_Generic((_num), \
|
||||
uint64_t *: expand_unsigned, \
|
||||
default: expand_number)((_buf), (_num))
|
||||
#endif
|
||||
int extattr_namespace_to_string(int _attrnamespace, char **_string);
|
||||
int extattr_string_to_namespace(const char *_string, int *_attrnamespace);
|
||||
int flopen(const char *_path, int _flags, ...);
|
||||
|
|
|
|||
|
|
@ -206,10 +206,85 @@ ATF_TC_BODY(expand_number__bad, tp)
|
|||
require_error(" + 1", EINVAL);
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(expand_unsigned);
|
||||
ATF_TC_BODY(expand_unsigned, tp)
|
||||
{
|
||||
static struct tc {
|
||||
const char *str;
|
||||
uint64_t num;
|
||||
int error;
|
||||
} tcs[] = {
|
||||
{ "0", 0, 0 },
|
||||
{ "+0", 0, 0 },
|
||||
{ "-0", 0, 0 },
|
||||
{ "1", 1, 0 },
|
||||
{ "+1", 1, 0 },
|
||||
{ "-1", 0, ERANGE },
|
||||
{ "18446744073709551615", UINT64_MAX, 0 },
|
||||
{ "+18446744073709551615", UINT64_MAX, 0 },
|
||||
{ "-18446744073709551615", 0, ERANGE },
|
||||
{ 0 },
|
||||
};
|
||||
struct tc *tc;
|
||||
uint64_t num;
|
||||
int error, ret;
|
||||
|
||||
for (tc = tcs; tc->str != NULL; tc++) {
|
||||
ret = expand_number(tc->str, &num);
|
||||
error = errno;
|
||||
if (tc->error == 0) {
|
||||
ATF_REQUIRE_EQ_MSG(0, ret,
|
||||
"%s ret = %d", tc->str, ret);
|
||||
ATF_REQUIRE_EQ_MSG(tc->num, num,
|
||||
"%s num = %ju", tc->str, (uintmax_t)num);
|
||||
} else {
|
||||
ATF_REQUIRE_EQ_MSG(-1, ret,
|
||||
"%s ret = %d", tc->str, ret);
|
||||
ATF_REQUIRE_EQ_MSG(tc->error, error,
|
||||
"%s errno = %d", tc->str, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(expand_generic);
|
||||
ATF_TC_BODY(expand_generic, tp)
|
||||
{
|
||||
uint64_t uint64;
|
||||
int64_t int64;
|
||||
size_t size;
|
||||
off_t off;
|
||||
|
||||
ATF_REQUIRE_EQ(0, expand_number("18446744073709551615", &uint64));
|
||||
ATF_REQUIRE_EQ(UINT64_MAX, uint64);
|
||||
ATF_REQUIRE_EQ(-1, expand_number("-1", &uint64));
|
||||
ATF_REQUIRE_EQ(ERANGE, errno);
|
||||
|
||||
ATF_REQUIRE_EQ(0, expand_number("9223372036854775807", &int64));
|
||||
ATF_REQUIRE_EQ(INT64_MAX, int64);
|
||||
ATF_REQUIRE_EQ(-1, expand_number("9223372036854775808", &int64));
|
||||
ATF_REQUIRE_EQ(ERANGE, errno);
|
||||
ATF_REQUIRE_EQ(0, expand_number("-9223372036854775808", &int64));
|
||||
ATF_REQUIRE_EQ(INT64_MIN, int64);
|
||||
|
||||
ATF_REQUIRE_EQ(0, expand_number("18446744073709551615", &size));
|
||||
ATF_REQUIRE_EQ(UINT64_MAX, size);
|
||||
ATF_REQUIRE_EQ(-1, expand_number("-1", &size));
|
||||
ATF_REQUIRE_EQ(ERANGE, errno);
|
||||
|
||||
ATF_REQUIRE_EQ(0, expand_number("9223372036854775807", &off));
|
||||
ATF_REQUIRE_EQ(INT64_MAX, off);
|
||||
ATF_REQUIRE_EQ(-1, expand_number("9223372036854775808", &off));
|
||||
ATF_REQUIRE_EQ(ERANGE, errno);
|
||||
ATF_REQUIRE_EQ(0, expand_number("-9223372036854775808", &off));
|
||||
ATF_REQUIRE_EQ(INT64_MIN, off);
|
||||
}
|
||||
|
||||
ATF_TP_ADD_TCS(tp)
|
||||
{
|
||||
ATF_TP_ADD_TC(tp, expand_number__ok);
|
||||
ATF_TP_ADD_TC(tp, expand_number__bad);
|
||||
ATF_TP_ADD_TC(tp, expand_unsigned);
|
||||
ATF_TP_ADD_TC(tp, expand_generic);
|
||||
|
||||
return (atf_no_error());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue