more accurate interactive trace, refactor
authorIan Kelling <iank@fsf.org>
Wed, 6 Nov 2019 00:57:34 +0000 (19:57 -0500)
committerIan Kelling <iank@fsf.org>
Wed, 6 Nov 2019 00:57:34 +0000 (19:57 -0500)
err

diff --git a/err b/err
index 3be16713f9b27546cef83e7edbd2802dc13cd0e3..816207083ce11fdd47e9f1f2d9c0fbe18f090707 100644 (file)
--- a/err
+++ b/err
@@ -62,7 +62,7 @@ err-catch() {
   # this is sourced from a startup file, and you use a login shell to
   # run a command. Avoid doing that if you want function arguments in
   # your trace.
-  if [[ $- != *c* ]] && ! shopt login_shell >/dev/null; then
+  if [[ $- != *c* ]] || ! shopt login_shell >/dev/null; then
     shopt -s extdebug
   fi
   _err-trap() {
@@ -88,53 +88,67 @@ err-catch() {
 }
 
 
+#######################################
+# Internal function for err-catch-interactive.
+# Prints stack trace from interactive shell trap.
+# Usage: see err-catch-interactive
+#######################################
+
+_err-bash-trace-interactive() {
+  local ret bash_command argc pattern i
+  # We have these passed to us because they are lost inside the
+  # function.
+  ret=$1
+  bash_command="$2"
+  argc=$(( $3 - 1 ))
+  shift 3
+  argv=("$@")
+  for pattern in "${err_catch_ignore[@]}"; do
+    # shellcheck disable=SC2053
+    if [[ ${BASH_SOURCE[0]} == $pattern ]]; then
+      return 0
+    fi
+  done
+  if (( ${#FUNCNAME[@]} > _err_func_last )); then
+    echo ERR: \`$bash_command\' returned $ret
+  fi
+  _err_func_last=${#FUNCNAME[@]}
+  if (( _err_func_last > 1 )); then
+    printf "  from \`%s" "${FUNCNAME[1]}"
+    if shopt extdebug >/dev/null; then
+      for ((i=argc; i >= 0; i--)); do
+        printf " %s" "${argv[i]}"
+      done
+    fi
+    printf "\' defined at %s:%s\n" "${BASH_SOURCE[1]}" "$(declare -F "${FUNCNAME[1]}"|awk "{print \$2}")"
+    return $ret
+  fi
+}
+
 #######################################
 # For interactive shells: on error, print stack trace and return
 #
+# Note: calling line number is not available, so we print function
+# definition lines.
+#
 # Globals:
 #   err_catch_ignore  Array containing glob patterns to test against filenames to ignore
 #                     errors from. Initialized to ignore bash-completion scripts on debian
 #                     based systems.
-#   _err_func_last  Used internally.
-#   _err_catch_err  Used internally.
-#   _err_catch_i    Used internally.
-#   _err_catch_ignore Used internally.
+#   _err_func_last    Used internally in err-bash-trace-interactive
 #
-# misc: All shellcheck disables for this function are false positives.
 #######################################
-# shellcheck disable=SC2120
 err-catch-interactive() {
-  err_catch_ignore=(
-    '/etc/bash_completion.d/*'
-  )
-  # shellcheck disable=SC2034
+  if ! test ${err_catch_ignore+defined}; then
+    err_catch_ignore=(
+      '/etc/bash_completion.d/*'
+      '*/bash-completion/*'
+    )
+  fi
   declare -i _err_func_last=0
   set -E; shopt -s extdebug
   # shellcheck disable=SC2154
-  trap '_err_catch_err=$? _trap_bc="$BASH_COMMAND"
-  _err_catch_ignore=false
-  for _err_catch_i in "${err_catch_ignore[@]}"; do
-    if [[ ${BASH_SOURCE[0]} == $_err_catch_i ]]; then
-      _err_catch_ignore=true
-      break
-    fi
-  done
-  if ! $_err_catch_ignore; then
-    if (( ${#FUNCNAME[@]} > _err_func_last )); then
-      echo ERR: \`$_trap_bc'"\'"' returned $_err_catch_err
-    fi
-    _err_func_last=${#FUNCNAME[@]}
-    if (( _err_func_last )); then
-      printf "  from %s:%s:in \`%s" "${BASH_SOURCE[0]}" "$(declare -F "${FUNCNAME[0]}"|awk "{print \$2}")" "${FUNCNAME[0]}"
-      if shopt extdebug >/dev/null; then
-        for ((_err_catch_i=${BASH_ARGC[0]}-1; _err_catch_i >= 0; _err_catch_i--)); do
-          printf " %s" "${BASH_ARGV[_err_catch_i]}"
-        done
-      fi
-      echo '"\'"'
-      return $_err_catch_err
-    fi
-  fi' ERR
+  trap '_err-bash-trace-interactive $? "$BASH_COMMAND" ${BASH_ARGC[0]} "${BASH_ARGV[@]}"' ERR
   set -o pipefail
 }