commit:     3de0f4f80587a58d93eb4d67dc0d536035cbd566
Author:     Kerin Millar <kfm <AT> plushkava <DOT> net>
AuthorDate: Mon May 20 07:37:15 2024 +0000
Commit:     Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Mon May 20 09:28:19 2024 +0000
URL:        
https://gitweb.gentoo.org/proj/gentoo-functions.git/commit/?id=3de0f4f8

Render argument checking more stringent; standardise diagnostics

Have esyslog(), is_older_than(), veend(), vewend() and yesno() die for
invalid arguments rather than issue a warning.

Have yesno() die if given no arguments, just as several other functions
already do.

Introduce a helper function named _throw_invalid_args() to assist. It
calls upon _print_args() to safely display any offending argument(s)
while also helping to standardise the diagnostics. To that end, the
diagnostic messages concerning wrong argument counts have been adjusted
to follow suit. Below are some examples.

test-functions: is_older_than: too few arguments (got 0, expected at least 2)
test-functions: is_older_than: too few arguments (got 1, expected at least 2)
test-functions: esyslog: too few arguments (got 0, expected at least 2)
test-functions: esyslog: too few arguments (got 1, expected at least 2)
test-functions: yesno: invalid argument: 'not-a-valid-nameref'
test-functions: yesno: invalid argument: '_"; set -- yes # code injection'

Signed-off-by: Kerin Millar <kfm <AT> plushkava.net>

 functions.sh   | 45 +++++++++++++++++++++++++++++++--------------
 test-functions | 11 +++++------
 2 files changed, 36 insertions(+), 20 deletions(-)

diff --git a/functions.sh b/functions.sh
index 100bd30..c82c229 100644
--- a/functions.sh
+++ b/functions.sh
@@ -216,7 +216,7 @@ eqatag() {
                case ${arg} in
                        [!=/]*=?*)
                                if [ "${positional}" -eq 1 ]; then
-                                       die "eqatag: invalid argument in 
positional context -- ${arg}"
+                                       _throw_invalid_args eqatag "${arg}"
                                fi
                                set -- "$@" --arg "${arg%%=*}" "${arg#*=}"
                                ;;
@@ -228,7 +228,7 @@ eqatag() {
                                set -- "$@" "${arg}"
                                ;;
                        *)
-                               die "eqatag: invalid argument -- ${arg}"
+                               _throw_invalid_args eqatag "${arg}"
                esac
        done
        shift "${argc}"
@@ -263,8 +263,7 @@ esyslog()
        local pri tag msg
 
        if [ "$#" -lt 2 ]; then
-               ewarn "Too few arguments for esyslog (got $#, expected at least 
2)"
-               return 1
+               die "esyslog: too few arguments (got $#, expected at least 2)"
        elif yesno "${EINFO_LOG}" && hash logger 2>/dev/null; then
                pri=$1
                tag=$2
@@ -376,8 +375,7 @@ is_older_than()
        local ref has_gfind
 
        if [ "$#" -lt 2 ]; then
-               ewarn "Too few arguments for is_older_than (got $#, expected at 
least 2)"
-               return 1
+               die "is_older_than: too few arguments (got $#, expected at 
least 2)"
        elif [ -e "$1" ]; then
                ref=$1
        else
@@ -424,7 +422,7 @@ veend()
        if yesno "${EINFO_VERBOSE}"; then
                GENFUN_CALLER=veend eend "$@"
        elif [ "$#" -gt 0 ] && { ! is_int "$1" || [ "$1" -lt 0 ]; }; then
-               ewarn "Invalid argument given to veend (the exit status code 
must be an integer >= 0)"
+               _throw_invalid_args veend "$1"
        else
                return "$1"
        fi
@@ -435,7 +433,7 @@ vewend()
        if yesno "${EINFO_VERBOSE}"; then
                GENFUN_CALLER=vewend ewend "$@"
        elif [ "$#" -gt 0 ] && { ! is_int "$1" || [ "$1" -lt 0 ]; }; then
-               ewarn "Invalid argument given to vewend (the exit status code 
must be an integer >= 0)"
+               _throw_invalid_args vewend "$1"
        else
                return "$1"
        fi
@@ -449,8 +447,14 @@ vewend()
 #
 yesno()
 {
+       local arg
+
+       if [ "$#" -eq 0 ]; then
+               die "yesno: too few arguments (got $#, expected 1)"
+       fi
+       arg=$1
        for _ in 1 2; do
-               case $1 in
+               case ${arg} in
                        [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0|'')
                                return 1
                                ;;
@@ -462,9 +466,9 @@ yesno()
                else
                        # The value appears to be a legal variable name. Treat
                        # it as a name reference and try again, once only.
-                       eval "set -- \"\$$1\""
+                       eval "arg=\$$1"
                fi
-       done || vewarn "Invalid argument given to yesno (expected a 
boolean-like or a legal name)"
+       done || _throw_invalid_args yesno "$1"
        return 1
 }
 
@@ -481,9 +485,7 @@ _eend()
        if [ "$#" -eq 0 ]; then
                retval=0
        elif ! is_int "$1" || [ "$1" -lt 0 ]; then
-               ewarn "Invalid argument given to ${GENFUN_CALLER} (the exit 
status code must be an integer >= 0)"
-               retval=0
-               msg=
+               _throw_invalid_args "${GENFUN_CALLER}" "$1"
        else
                retval=$1
                shift
@@ -650,6 +652,21 @@ _print_args() {
        EOF
 }
 
+#
+# Prints a diganostic message concerning invalid function arguments then exits.
+# The first argument shall be taken as a function identifier. The remaining
+# arguments shall be safely rendered as a part of the diagnostic.
+#
+_throw_invalid_args()
+{
+       local ident plural
+
+       ident=$1
+       shift
+       [ "$#" -gt 1 ] && plural=s || plural=
+       die "${ident}: invalid argument${plural}: $(_print_args "$@")"
+}
+
 #
 # Determines whether the terminal on STDIN is able to report its dimensions.
 # Upon success, the number of columns shall be stored in genfun_cols.

diff --git a/test-functions b/test-functions
index 7eb5981..40beded 100755
--- a/test-functions
+++ b/test-functions
@@ -196,7 +196,7 @@ test_is_older_than() {
        callback() {
                shift
                test_description="is_older_than $(_print_args "$@")"
-               is_older_than "$@"
+               ( is_older_than "$@" )
        }
 
        iterate_tests 4 "$@"
@@ -239,18 +239,17 @@ test_esyslog() {
        logger() {
                # esyslog() ignores empty messages. By overriding logger(1), it
                # can be determined whether a message would have been logged.
-               logged=$((logged + 1))
+               printf '1\n'
        }
 
        callback() {
                should_log=$2
                shift 2
-               logged=0
                test_description="esyslog $(_print_args "$@")"
-               EINFO_LOG=1 esyslog "$@" 2>/dev/null
+               logged=$(EINFO_LOG=1 esyslog "$@")
                case $? in
                        0)
-                               test "${logged}" -eq "${should_log}"
+                               test "${logged:-0}" -eq "${should_log}"
                                ;;
                        *)
                                return "$?"
@@ -393,7 +392,7 @@ test_yesno() {
                callback() {
                        shift
                        test_description="yesno $(_print_args "$@")"
-                       yesno "$@"
+                       ( yesno "$@" )
                }
 
                iterate_tests 3 "$@"

Reply via email to