commit: 2500778920f533c56fa55798ec8d381276ae84d1
Author: Kerin Millar <kfm <AT> plushkava <DOT> net>
AuthorDate: Wed Aug 7 17:45:02 2024 +0000
Commit: Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Sun Aug 11 10:10:57 2024 +0000
URL:
https://gitweb.gentoo.org/proj/gentoo-functions.git/commit/?id=25007789
Have srandom() employ an upper bound of 2^31-1
In the case of some shells - mksh, at least - the maximum value of an
integer is 2147483647. Such is a consequence of implementing integers as
signed int rather than signed long, even though doing so contravenes the
specification.
Reduce the output range of srandom() so as to be between 0 and
2147483647, rather than 0 and 4294967295. A change of this scope would
normally justify incrementing GENFUN_API_LEVEL but I shall not do so on
this occasion. My rationale is that >=gentoo-functions-1.7 has not yet
had enough exposure for srandom() to be in use by other projects.
Additionally, have test-functions test srandom() 10 times instead of 5.
Signed-off-by: Kerin Millar <kfm <AT> plushkava.net>
Signed-off-by: Sam James <sam <AT> gentoo.org>
functions.sh | 42 +++++++++++++++++++++++++++++++++++++-----
test-functions | 12 ++++++++----
2 files changed, 45 insertions(+), 9 deletions(-)
diff --git a/functions.sh b/functions.sh
index b591acb..1c55b3d 100644
--- a/functions.sh
+++ b/functions.sh
@@ -463,7 +463,10 @@ quote_args()
}
#
-# Generates a random uint32 with the assistance of the kernel CSPRNG.
+# Generates a random number between 0 and 2147483647 (2^31-1) with the
+# assistance of the kernel CSPRNG. Upon success, the number shall be printed to
+# the standard output along with a trailing <newline>. Otherwise, the return
+# value shall be greater than 0.
#
srandom()
{
@@ -471,14 +474,43 @@ srandom()
if [ "${BASH_VERSINFO:-0}" -ge 5 ]; then
srandom()
{
- printf '%d\n' "${SRANDOM}"
+ printf '%d\n' "$(( SRANDOM >> 1 ))"
+ }
+ elif [ -c /dev/urandom ] && [ "$(( 1 << 31 == -2147483648 ))" -eq 1 ];
then
+ # The shell implements integers as signed int rather than signed
+ # long, contrary to the specification. Therefore, bit shifting
+ # cannot be a viable strategy. Instead, use awk to generate a
+ # number that is immediately within range.
+ srandom()
+ {
+ local hex
+
+ hex=$(
+ LC_ALL=
+ LC_CTYPE=C
+ od -vAn -N256 -tx1 /dev/urandom | awk '
+ {
+ gsub(/[[:space:]]/, "")
+ hex = hex $0
+ }
+ END {
+ if (match(hex,
/[0-7][[:xdigit:]]{7}/)) {
+ print substr(hex,
RSTART, RLENGTH)
+ } else {
+ exit 1
+ }
+ }
+ '
+ ) &&
+ printf '%d\n' "0x${hex}"
}
elif [ -c /dev/urandom ]; then
srandom()
{
- printf '%d\n' "0x$(
- LC_ALL=C od -vAn -N4 -tx1 /dev/urandom | tr -d
'[:space:]'
- )"
+ local hex
+
+ hex=$(LC_ALL=C od -vAn -N4 -tx1 /dev/urandom | tr -d
'[:space:]')
+ [ "${hex}" ] && printf '%d\n' "$(( 0x${hex} >> 1 ))"
}
else
warn "srandom: /dev/urandom doesn't exist as a character device"
diff --git a/test-functions b/test-functions
index 4b2f7f9..96781f2 100755
--- a/test-functions
+++ b/test-functions
@@ -415,21 +415,25 @@ test_yesno() {
test_srandom() {
set -- \
+ eq 0 \
+ eq 0 \
+ eq 0 \
+ eq 0 \
+ eq 0 \
eq 0 \
eq 0 \
eq 0 \
eq 0 \
eq 0
- row=0
-
callback() {
number=$(srandom)
- test_description="srandom ($(( row += 1 ))/5: ${number:-blank})"
+ test_description="srandom ($(( row += 1 ))/10:
${number:-blank})"
is_int "${number}" \
- && awk -v "n=${number}" 'BEGIN { exit !(n >= 0 && n <=
4294967295) }'
+ && awk -v "n=${number}" 'BEGIN { exit !(n >= 0 && n <=
2147483647) }'
}
+ row=0
iterate_tests 2 "$@"
}