You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
3.0 KiB
121 lines
3.0 KiB
#!/usr/bin/env bash
|
|
|
|
# CONSTANTS
|
|
DEBUG=true
|
|
TMP_ERROR=$(mktemp -t "$(basename $0).XXXXXXXX" --tmpdir=/run/user/$(id -u))
|
|
SCRIPT=$1
|
|
|
|
# FUNCTIONS
|
|
|
|
function on_return {
|
|
if [[ "${BASH_COMMAND}" == return* ]]; then
|
|
code=$(echo "${BASH_COMMAND}" | cut -d' ' -f2) # regex does not work here!
|
|
if [[ -n "$code" ]] && [[ $code -ne 0 ]]; then
|
|
trap - DEBUG
|
|
>&2 echo "${BASH_SOURCE[2]}: line ${BASH_LINENO[0]}: return $code from ${FUNCNAME[1]}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# overridden function which permits stacktracing of original `return` statement
|
|
function exit {
|
|
code=${1:-0}
|
|
|
|
# TODO: prefer this output, but change the exit case scenario message in stacktrace first!
|
|
# >&2 echo "${BASH_SOURCE[2]}:${BASH_LINENO[0]} exit $@"
|
|
|
|
>&2 echo "${BASH_SOURCE[2]}: line ${BASH_LINENO[0]}: exit $@"
|
|
builtin exit $code
|
|
}
|
|
|
|
function on_exit {
|
|
code=${1:-0}
|
|
>&4 dump_stderr
|
|
|
|
stack_lines=("${BASH_LINENO[@]}")
|
|
stack_functions=("${FUNCNAME[@]}")
|
|
stack_sources=("${BASH_SOURCE[@]}")
|
|
|
|
unset stack_lines[-1]
|
|
unset stack_lines[-1]
|
|
unset stack_functions[0]
|
|
unset stack_functions[-1]
|
|
stack_functions[-1]='<main>'
|
|
unset stack_sources[0]
|
|
unset stack_sources[-1]
|
|
|
|
if [[ "${stack_functions[1]}" == exit && "${stack_sources[1]}" == "${BASH_SOURCE[0]}" ]]; then
|
|
# the exit case scenario
|
|
unset stack_functions[1]
|
|
unset stack_sources[1]
|
|
unset stack_lines[0]
|
|
fi
|
|
|
|
stack_lines=("${stack_lines[@]}")
|
|
stack_functions=("${stack_functions[@]}")
|
|
stack_sources=("${stack_sources[@]}")
|
|
|
|
if [[ $code -gt 0 ]]; then
|
|
$DEBUG && >/dev/tty echo '---stacktrace---'
|
|
last_error=$(tail -n1 $TMP_ERROR)
|
|
last_source=${stack_sources[0]}
|
|
last_source=${last_source//./"\."} # replace . by \.
|
|
regex="^$last_source: line ([0-9]+): (.*)\$"
|
|
if [[ "$last_error" =~ $regex ]]; then
|
|
line=${BASH_REMATCH[1]}
|
|
message=${BASH_REMATCH[2]}
|
|
regex=': unbound variable$'
|
|
[[ $code == 1 ]] && [[ "$message" =~ $regex ]] && stack_lines[0]=$line
|
|
|
|
regex="^return [0-9]* from (.*)\$"
|
|
if [[ "$message" =~ $regex ]]; then
|
|
funcname="${BASH_REMATCH[1]}"
|
|
# >&4 echo "RETURN DETECTED line ${stack_sources[0]} $line $funcname"
|
|
stack_sources=("${stack_sources[0]}" "${stack_sources[@]}")
|
|
stack_lines=("$line" "${stack_lines[@]}")
|
|
stack_functions=("$funcname" "${stack_functions[@]}")
|
|
fi
|
|
|
|
>&4 echo -e "ERROR $code: [$message]"
|
|
else
|
|
>&4 echo "ERROR $code: <return>"
|
|
fi
|
|
|
|
for i in "${!stack_functions[@]}"; do
|
|
>&4 printf "\t%30s\t%s\n" "${stack_sources[$i]}:${stack_lines[$i]}" "${stack_functions[$i]}"
|
|
done
|
|
fi
|
|
|
|
cleanup
|
|
builtin exit $code
|
|
}
|
|
|
|
function dump_stderr {
|
|
if [[ -s $TMP_ERROR ]]; then
|
|
$DEBUG && >/dev/tty echo '---stderr---'
|
|
cat $TMP_ERROR
|
|
fi
|
|
}
|
|
|
|
function success {
|
|
>&2 dump_stderr
|
|
cleanup
|
|
}
|
|
|
|
function cleanup {
|
|
trap - ERR TERM INT EXIT
|
|
exec 3>&- 4>&-
|
|
rm $TMP_ERROR
|
|
}
|
|
|
|
# MAIN
|
|
|
|
set -TEue -o pipefail
|
|
trap 'on_exit $?' ERR TERM INT EXIT
|
|
trap 'on_return' DEBUG
|
|
|
|
# magic FD
|
|
exec 3>$TMP_ERROR 4>/dev/stderr
|
|
$DEBUG && >/dev/tty echo '---stdout---'
|
|
source $SCRIPT 2>&3
|
|
success
|