Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F160346867
D51542.id159148.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
D51542.id159148.diff
View Options
diff --git a/lib/libutil/expand_number.3 b/lib/libutil/expand_number.3
--- a/lib/libutil/expand_number.3
+++ b/lib/libutil/expand_number.3
@@ -1,5 +1,6 @@
.\" Copyright (c) 2007 Eric Anderson <anderson@FreeBSD.org>
.\" Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+.\" Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -23,43 +24,52 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd June 13, 2023
+.Dd July 25, 2025
.Dt EXPAND_NUMBER 3
.Os
.Sh NAME
.Nm expand_number
-.Nd format a number from human readable form
+.Nd parse a number from human readable form
.Sh LIBRARY
.Lb libutil
.Sh SYNOPSIS
.In libutil.h
.Ft int
.Fo expand_number
-.Fa "const char *buf" "uint64_t *num"
+.Fa "const char *buf" "int64_t *num"
.Fc
.Sh DESCRIPTION
The
.Fn expand_number
-function parses the
+function parses the number in the string pointed to by its
.Fa buf
-string and stores a unsigned 64-bit quantity at
-.Fa *num .
+argument and stores the number it represents as a signed 64-bit
+quantity in the location pointed to by its
+.Fa *num
+argument.
.Pp
-The
-.Fn expand_number
-function
-is case-insensitive and
-follows the SI power of two convention.
+The input string must consist of a number recognizable by
+.Xr strtoumax 8
+with
+.Fa base
+set to 0, optionally preceded by a
+.Sq +
+or
+.Sq -
+sign, and optionally followed, without intervening whitespace, by a
+suffix indicating a power-of-two multiplier to apply.
+Any amount of whitespace at the beginning of the string will be
+ignored.
.Pp
-The suffixes are:
+Recognized suffixes are:
.Bl -column "Suffix" "Description" "1000000000000000000" -offset indent
.It Sy "Suffix" Ta Sy "Description" Ta Sy "Multiplier"
-.It Li K Ta No kilo Ta 1024
-.It Li M Ta No mega Ta 1048576
-.It Li G Ta No giga Ta 1073741824
-.It Li T Ta No tera Ta 1099511627776
-.It Li P Ta No peta Ta 1125899906842624
-.It Li E Ta No exa Ta 1152921504606846976
+.It Li K Ta No kilo Ta 1,024
+.It Li M Ta No mega Ta 1,048,576
+.It Li G Ta No giga Ta 1,073,741,824
+.It Li T Ta No tera Ta 1,099,511,627,776
+.It Li P Ta No peta Ta 1,125,899,906,842,624
+.It Li E Ta No exa Ta 1,152,921,504,606,846,976
.El
.Pp
For historical reasons, the
@@ -68,7 +78,11 @@
.Dq B
suffix at the end of the
.Fa buf
-string.
+string (i.e.
+.Dq 5b
+is interpreted as 5, and
+.Dq 5kb
+is interpreted as 5,120).
However, the usage of this suffix is discouraged.
.Sh RETURN VALUES
.Rv -std
@@ -78,11 +92,12 @@
function will fail if:
.Bl -tag -width Er
.It Bq Er EINVAL
-The given string contains no digits.
+The given string does not contain a valid number.
.It Bq Er EINVAL
-An unrecognized suffix was given.
+An unrecognized suffix was encountered.
.It Bq Er ERANGE
-Result doesn't fit into 64 bits.
+The given string represents a number which does not fit into a
+.Vt int64_t .
.El
.Sh SEE ALSO
.Xr humanize_number 3
diff --git a/lib/libutil/expand_number.c b/lib/libutil/expand_number.c
--- a/lib/libutil/expand_number.c
+++ b/lib/libutil/expand_number.c
@@ -3,6 +3,7 @@
*
* Copyright (c) 2007 Eric Anderson <anderson@FreeBSD.org>
* Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,79 +29,120 @@
*/
#include <sys/types.h>
+
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <libutil.h>
+#include <stdbool.h>
#include <stdint.h>
int
-expand_number(const char *buf, uint64_t *num)
+expand_number(const char *buf, int64_t *num)
{
char *endptr;
- uintmax_t umaxval;
- uint64_t number;
- unsigned shift;
+ uintmax_t number;
+ unsigned int shift;
+ bool neg;
int serrno;
+ /*
+ * Skip whitespace and optional sign.
+ */
+ while (isspace((unsigned char)*buf))
+ buf++;
+ if (*buf == '-') {
+ neg = true;
+ buf++;
+ } else {
+ neg = false;
+ if (*buf == '+')
+ buf++;
+ }
+
+ /*
+ * The next character is either the first digit of the number or
+ * the 0 of its binary / octal / hexadecimal prefix. If we don't
+ * check this, strtoumax() will allow whitespace and a sign.
+ */
+ if (!isdigit((unsigned char)*buf)) {
+ errno = EINVAL;
+ return (-1);
+ }
+
serrno = errno;
errno = 0;
- umaxval = strtoumax(buf, &endptr, 0);
- if (umaxval > UINT64_MAX)
- errno = ERANGE;
+ number = strtoumax(buf, &endptr, 0);
if (errno != 0)
return (-1);
errno = serrno;
- number = umaxval;
switch (tolower((unsigned char)*endptr)) {
case 'e':
shift = 60;
+ endptr++;
break;
case 'p':
shift = 50;
+ endptr++;
break;
case 't':
shift = 40;
+ endptr++;
break;
case 'g':
shift = 30;
+ endptr++;
break;
case 'm':
shift = 20;
+ endptr++;
break;
case 'k':
shift = 10;
+ endptr++;
break;
- case 'b':
- shift = 0;
- break;
- case '\0': /* No unit. */
- *num = number;
- return (0);
default:
- /* Unrecognized unit. */
- errno = EINVAL;
- return (-1);
+ shift = 0;
}
/*
* Treat 'b' as an ignored suffix for all unit except 'b',
* otherwise there should be no remaining character(s).
*/
- endptr++;
- if (shift != 0 && tolower((unsigned char)*endptr) == 'b')
+ if (tolower((unsigned char)*endptr) == 'b')
endptr++;
if (*endptr != '\0') {
errno = EINVAL;
return (-1);
}
+ /*
+ * Apply the shift and check for overflow.
+ */
if ((number << shift) >> shift != number) {
/* Overflow */
errno = ERANGE;
return (-1);
}
- *num = number << shift;
+ number <<= shift;
+
+ /*
+ * Apply the sign and check for overflow.
+ */
+ if (neg) {
+ if (number > 0x8000000000000000LLU /* -INT64_MIN */) {
+ errno = ERANGE;
+ return (-1);
+ }
+ *num = -number;
+ } else {
+ if (number > INT64_MAX) {
+ errno = ERANGE;
+ return (-1);
+ }
+ *num = number;
+ }
+
return (0);
}
diff --git a/lib/libutil/libutil.h b/lib/libutil/libutil.h
--- a/lib/libutil/libutil.h
+++ b/lib/libutil/libutil.h
@@ -89,7 +89,7 @@
char *auth_getval(const char *_name);
void clean_environment(const char * const *_white,
const char * const *_more_white);
-int expand_number(const char *_buf, uint64_t *_num);
+int expand_number(const char *_buf, int64_t *_num);
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, ...);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Jun 24, 1:12 PM (12 h, 21 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34284014
Default Alt Text
D51542.id159148.diff (6 KB)
Attached To
Mode
D51542: libutil: Really fix expand_number(3)
Attached
Detach File
Event Timeline
Log In to Comment