The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=ab1c6874e5004a8ac4e6b077a4aee307e22628b1
commit ab1c6874e5004a8ac4e6b077a4aee307e22628b1 Author: Dag-Erling Smørgrav <d...@freebsd.org> AuthorDate: 2025-08-06 20:34:13 +0000 Commit: Dag-Erling Smørgrav <d...@freebsd.org> CommitDate: 2025-08-06 20:43:13 +0000 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: 2e0caa7c7e14 Reviewed by: jhb Differential Revision: https://reviews.freebsd.org/D51723 --- lib/libutil/Symbol.map | 1 + lib/libutil/expand_number.3 | 58 +++++++++++++++++++++++--- lib/libutil/expand_number.c | 49 +++++++++++++++++++--- lib/libutil/libutil.h | 8 ++++ lib/libutil/tests/expand_number_test.c | 75 ++++++++++++++++++++++++++++++++++ 5 files changed, 181 insertions(+), 10 deletions(-) diff --git a/lib/libutil/Symbol.map b/lib/libutil/Symbol.map index 8c8fff451cd1..6b8a1ec099bf 100644 --- a/lib/libutil/Symbol.map +++ b/lib/libutil/Symbol.map @@ -13,6 +13,7 @@ FBSD_1.8 { cpuset_parselist; domainset_parselist; expand_number; + expand_unsigned; flopen; flopenat; forkpty; diff --git a/lib/libutil/expand_number.3 b/lib/libutil/expand_number.3 index 1b932400de69..b1833cedf406 100644 --- a/lib/libutil/expand_number.3 +++ b/lib/libutil/expand_number.3 @@ -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 . diff --git a/lib/libutil/expand_number.c b/lib/libutil/expand_number.c index f4c19d7867a3..a3313ba39d98 100644 --- a/lib/libutil/expand_number.c +++ b/lib/libutil/expand_number.c @@ -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); +} diff --git a/lib/libutil/libutil.h b/lib/libutil/libutil.h index d27262e44daf..9b5b2abe7f09 100644 --- a/lib/libutil/libutil.h +++ b/lib/libutil/libutil.h @@ -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, ...); diff --git a/lib/libutil/tests/expand_number_test.c b/lib/libutil/tests/expand_number_test.c index 8e7458994de4..8ff56e1ed01f 100644 --- a/lib/libutil/tests/expand_number_test.c +++ b/lib/libutil/tests/expand_number_test.c @@ -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()); }