#!/bin/bash
-# Copyright 2018 Ian Kelling
-
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-
-# http://www.apache.org/licenses/LICENSE-2.0
-
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
+# Copyright (C) 2019 Ian Kelling
+# SPDX-License-Identifier: GPL-3.0-or-later
# Commentary: Bash stack trace and error handling functions. This file
# is meant to be sourced. It loads some functions which you may want to
# On error print stack trace and exit
#
# Globals:
-# ${_errcatch_cleanup[@]} Optional command & args that will run before exiting
+# errcatch-cleanup If set, this command will run just before exiting.
#######################################
err-catch() {
- set -E; shopt -s extdebug
+ set -E;
+ # This condition avoids starting the bash debugger in the case that
+ # this is sourced from a startup file, and you use a login shell to
+ # run a command. eg: bash -l some-command. Avoid doing that if you want
+ # function arguments in your trace.
+ if ! shopt login_shell >/dev/null; then
+ shopt -s extdebug
+ fi
_err-trap() {
err=$?
exec >&2
echo "$msg"
fi
set -e # err trap does not work within an error trap
- # note :-: makes this compatible with set -u, assigns : if unset, but shellcheck
- # doesn't understand that.
- # shellcheck disable=SC2154
- "${_errcatch_cleanup[@]:-:}"
+ if type -t errcatch-cleanup >/dev/null; then
+ errcatch-cleanup
+ fi
echo "$0: exiting with status $err"
exit $err
}
}
+#######################################
+# Internal function for err-catch-interactive.
+# Prints stack trace from interactive shell trap.
+# Usage: see err-catch-interactive
+#######################################
+
+_err-bash-trace-interactive() {
+ if (( ${#FUNCNAME[@]} <= 1 )); then
+ return 0
+ fi
+
+ for pattern in "${err_catch_ignore[@]}"; do
+ # shellcheck disable=SC2053
+ if [[ ${BASH_SOURCE[1]} == $pattern ]]; then
+ return 0
+ fi
+ done
+
+ local ret bash_command argc pattern i last
+ last=$_err_func_last
+ _err_func_last=${#FUNCNAME[@]}
+ # We have these passed to us because they are lost inside the
+ # function.
+ ret=$1
+ bash_command="$2"
+ argc=$(( $3 - 1 ))
+ shift 3
+ argv=("$@")
+ # The trap returns a nonzero, then gets called again. This condition
+ # tells us if we are the first.
+ if (( _err_func_last > last )); then
+ echo ERR: \`$bash_command\' returned $ret
+ fi
+ 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
+}
+
#######################################
# 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_func_last Used internally.
-# _err_catch_err Used internally.
-# _err_catch_i Used internally.
+# 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 in err-bash-trace-interactive
#
-# misc: All shellcheck disables for this function are false positives.
#######################################
-# shellcheck disable=SC2120
err-catch-interactive() {
- # 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"
- 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' ERR
+ trap '_err-bash-trace-interactive $? "$BASH_COMMAND" ${BASH_ARGC[0]} "${BASH_ARGV[@]}" || return $?' ERR
set -o pipefail
}
#######################################
-# Undoes err-catch. turns off exit and stack trace on error.
+# Undo err-catch/err-catch-interactive
#######################################
err-allow() {
- set +E +o pipefail; trap ERR
+ shopt -u extdebug
+ set +E +o pipefail
+ trap ERR
}
#######################################