This updates the edo function to behave similar to its possible
implementation in the package manager for EAPI 9:

- Backslash-escaping (as einfo does via "echo -e") is not wanted.

- Arguments should be printed in a quoted format that could be reused
  as shell input. We quote arguments using the "@Q" operator of Bash
  parameter expansion, but avoid excessive quoting by testing each
  argument for shell metacharacters. The resulting output should be
  very similar to that of the trace obtained with "set -x".

  Note that the @Q operator did not exist in Bash 4.2, therefore
  arguments are printed literally in EAPI 7.

Bug: https://bugs.gentoo.org/744880#c13
Suggested-by: Eli Schwartz <eschwa...@gentoo.org>
Reviewed-by: Eli Schwartz <eschwa...@gentoo.org>
Signed-off-by: Ulrich Müller <u...@gentoo.org>
---
Changes in v2:
- Test number of arguments
- Add some test cases

 eclass/edo.eclass   | 24 +++++++++++++++++++++---
 eclass/tests/edo.sh | 25 ++++++++++++++++++++++++-
 2 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/eclass/edo.eclass b/eclass/edo.eclass
index 5fd77a676a8b..a308851aca7f 100644
--- a/eclass/edo.eclass
+++ b/eclass/edo.eclass
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Gentoo Authors
+# Copyright 2022-2025 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 # @ECLASS: edo.eclass
@@ -36,8 +36,26 @@ _EDO_ECLASS=1
 # Executes a short 'command' with any given arguments and exits on failure
 # unless called under 'nonfatal'.
 edo() {
-       einfo "$@"
-       "$@" || die -n "Failed to run command: $@"
+       # list of special characters taken from sh_contains_shell_metas
+       # in shquote.c (bash-5.2)
+       local a out regex='[] '\''"\|&;()<>!{}*[?^$`]|^[#~]|[=:]~'
+
+       [[ $# -ge 1 ]] || die "edo: at least one argument needed"
+
+       if [[ ${EAPI} = 7 ]]; then
+               # no @Q in bash-4.2
+               out=" $*"
+       else
+               for a; do
+                       # quote if (and only if) necessary
+                       [[ ${a} =~ ${regex} || ! ${a} =~ ^[[:print:]]+$ ]] && 
a=${a@Q}
+                       out+=" ${a}"
+               done
+       fi
+
+       einfon
+       printf '%s\n' "${out:1}" >&2
+       "$@" || die -n "Failed to run command: ${1}"
 }
 
 # @FUNCTION: edob
diff --git a/eclass/tests/edo.sh b/eclass/tests/edo.sh
index cac03e0401ba..1fa8a7a9a026 100755
--- a/eclass/tests/edo.sh
+++ b/eclass/tests/edo.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright 1999-2024 Gentoo Authors
+# Copyright 1999-2025 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 EAPI=8
@@ -13,6 +13,18 @@ make_some_noise() {
        echo "EoN"
 }
 
+test_edo() {
+       local exp_ret=${1} exp_out=${2} cmd=("${@:3}")
+       local have_ret have_out
+       tbegin "edo -> ret: ${exp_ret}, out: ${exp_out}"
+       have_out=$(edo "${cmd[@]}" 2>&1)
+       have_ret=$?
+       have_out=${have_out%%$'\n'*}
+       have_out=${have_out# \* }
+       [[ ${have_ret} -eq ${exp_ret} && ${have_out} == "${exp_out}" ]]
+       tend $? "returned: ${have_ret}, output: ${have_out}"
+}
+
 test_edob_simple() {
        tbegin "edob with output test"
        (
@@ -105,6 +117,17 @@ test_edob_failure() {
        tend $? "Unexpected output, found \"${fourth_line_of_edob_out}\", 
expected \"quz\""
 }
 
+test_edo 0 "/bin/true foo"              /bin/true foo
+test_edo 1 "/bin/false bar"             /bin/false bar
+test_edo 0 "make_some_noise baz"        make_some_noise baz
+test_edo 0 ": 'foo bar' 'baz  quux'"    : 'foo bar' 'baz  quux'
+test_edo 0 ": @%:+,-=._ '\$'"           : '@%:+,-=._' '$'
+test_edo 0 ": 'foo;bar' 'baz*quux'"     : 'foo;bar' 'baz*quux'
+test_edo 0 ": '#foo' bar#baz 'qu=~ux'"  : '#foo' 'bar#baz' 'qu=~ux'
+test_edo 0 ": '\"' \\' 'foo'\\''bar'"   : '"' "'" "foo'bar"
+test_edo 0 ": '' ' ' \$'\\t' \$'\\001'" : '' ' ' $'\t' $'\x01'
+test_edo 0 ": äöü"                      : 'äöü'
+
 test_edob_simple
 test_edob_explicit_log_name
 test_edob_explicit_message
-- 
2.49.0


Reply via email to