diff --git a/bin/miaou-bash b/bin/miaou-bash index d18e27b..8cff1a1 100755 --- a/bin/miaou-bash +++ b/bin/miaou-bash @@ -174,17 +174,18 @@ function on_error { last_error="${tmp_error[-1]}" regex2='^[[:space:]]+miaou_trace: source=(.*) lineno=(.*) funcname=(.*)$' if [[ "$last_error" =~ $regex2 ]]; then + miaou_debug "" trace_source="${BASH_REMATCH[1]}" trace_lineno="${BASH_REMATCH[2]}" trace_funcname="${BASH_REMATCH[3]}" extra_source=("$trace_source") extra_lineno=("$trace_lineno") extra_funcname=("$trace_funcname") - miaou_debug "" + # miaou_debug "" # go backward for ((i = ${#tmp_error[@]} - 2; i >= 0; i--)); do content=${tmp_error[$i]} - miaou_debug "" + # miaou_debug "" if [[ "$content" =~ $regex2 ]]; then source="${BASH_REMATCH[1]}" lineno="${BASH_REMATCH[2]}" @@ -192,7 +193,7 @@ function on_error { extra_source=("$source" "${extra_source[@]}") extra_lineno=("$lineno" "${extra_lineno[@]}") extra_funcname=("$funcname" "${extra_funcname[@]}") - miaou_debug '' + # miaou_debug '' else regex2='^(.*): line ([0-9]+): miaou_error: (.*) [0-9]+(.*)$' if [[ "$content" =~ $regex2 ]]; then @@ -203,16 +204,15 @@ function on_error { [[ -n "$optional_payload" ]] && optional_payload="${optional_payload# payload: }" && failure_payload="${failure_payload} ${optional_payload}" - miaou_debug "" + miaou_debug "" else miaou_debug '' fi break fi - miaou_debug '' + # miaou_debug '' done - miaou_debug "" - miaou_debug "" + miaou_debug "" fi fi fi diff --git a/bin/miaou-bash-old b/bin/miaou-bash-old deleted file mode 100755 index e00ceaa..0000000 --- a/bin/miaou-bash-old +++ /dev/null @@ -1,302 +0,0 @@ -#!/usr/bin/env bash - -# CONSTANTS - -MIAOU_BASH_ERROR=$(mktemp -t "$(basename "$0").XXXXXXXX" --tmpdir="/run/user/$(id -u)") -MIAOU_DEBUG=${MIAOU_DEBUG:-false} -readonly MIAOU_BASH_ERROR MIAOU_DEBUG - -# FUNCTIONS - -function is_true { - [[ "${1,,}" =~ ^true$ ]] #${1,,} => Lowercase all chars -} - -# overridden function which permits backtracing of original `return` statement -function false { - >&2 echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]} false" - miaou_debug "overridden false" - builtin false -} - -# overridden function which permits backtracing of original `exit` statement -function exit { - code=${1:-0} - # >&2 echo "${BASH_SOURCE[2]}: line ${BASH_LINENO[0]}: exit $@" - >&2 echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]} exit $code" - miaou_debug "overridden exit $code" - builtin exit "$code" -} - -function on_usr1 { - cleanup - # FIXME: get exit code from MIAOU_BASH_SUBERROR - builtin exit 1 -} - -function on_return { - # regex does not work here unfortunately! - if [[ "${BASH_COMMAND}" == 'return '* ]]; then - code=$(echo "${BASH_COMMAND}" | cut -d' ' -f2) - 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 -} - -function on_exit { - code=${1:-0} - - if [[ $code -gt 0 ]]; then - - 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[@]}" -gt 0 ]] && stack_functions[-1]='
' - - unset 'stack_sources[0]' - unset 'stack_sources[-1]' - - if [[ "${#stack_functions[@]}" -gt 1 && "${stack_functions[1]}" =~ exit|false && "${stack_sources[1]}" == "${BASH_SOURCE[0]}" ]]; then - # the exit or false 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[@]}") - - failure_payload='' - - last_error=$(tail -n1 "$MIAOU_BASH_ERROR") - last_source=${stack_sources[0]} - last_source=${last_source//./"\."} # replace '.' with '\.' - regex1="^$last_source: line ([0-9]+): (.*)\$" - regex2="^$last_source:([0-9]+) (.*)\$" - - # FIXME: too much tricky! (delayed stderr) - if [[ -p /dev/stdout ]]; then - failure_type='SUBSHELL_TERM' - failure_payload="$last_error" - - if [[ "$last_error" =~ $regex1 || "$last_error" =~ $regex2 ]]; then - failure_payload="${BASH_REMATCH[2]}" - fi - last_error="${BASH_SOURCE[1]}:${BASH_LINENO[0]} subshell term $code" - printf "\r" # useful! - kill -USR1 $$ - else - failure_type='UNKNOWN' - fi - - # debug "last_error=[$last_error] <=> ${last_source}" - if [[ "$last_error" =~ $regex1 || "$last_error" =~ $regex2 ]]; then - line=${BASH_REMATCH[1]} - message=${BASH_REMATCH[2]} - regex='^(.*): unbound variable$' - if [[ $code == 1 ]] && [[ "$message" =~ $regex ]]; then - failure_type='UNBOUND_VARIABLE' - failure_payload="${BASH_REMATCH[1]}" - stack_lines[0]=$line - else - if [[ "$message" == 'false' ]]; then - failure_type='FALSE' - else - regex="^return ([0-9]*) from (.*)\$" - if [[ "$message" =~ $regex ]]; then - # return_code="${BASH_REMATCH[1]}" - funcname="${BASH_REMATCH[2]}" - # >&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[@]}") - - failure_type='RETURN' - else - regex='^(.*): command not found$' - if [[ "$message" =~ $regex ]]; then - failure_type='COMMAND_NOT_FOUND' - failure_payload="${BASH_REMATCH[1]}" - else - regex='^exit' - if [[ "$message" =~ $regex ]]; then - failure_type='EXIT' - else - regex='^(.*): No such file or directory$' - if [[ "$message" =~ $regex ]]; then - failure_payload="${BASH_REMATCH[1]}" - if [[ $code == 1 ]]; then - failure_type='SOURCE_NOT_FOUND' - stack_lines[0]=$line - code=128 # change code to 128 to remember this embedded 'source not found' issue! - else - if [[ $code == 127 ]]; then - failure_type='FILE_NOT_FOUND' - regex='\.sh$|\.bash$' - if [[ "$failure_payload" =~ $regex ]]; then - stack_functions[0]="${stack_functions[0]} (hint: prefer source than subshell:1)" - code=129 # change code to 128 to remember this embedded 'source not found' issue! - # >&2 echo "${last_error}" - else - : - # >&2 echo "${last_error}" - fi - else - failure_type='UNKNOWN_NOT_FOUND' - fi - fi - fi - fi - fi - fi - fi - fi - else - failure_payload="${last_error}" - regex="╟─⟶" - if [[ "$last_error" =~ $regex ]]; then - last_trim=false # keep the last error for next stacktrace - error_block=false - failure_type='SUBSHELL_ERROR' - # - # >&4 echo "SUBSHELL_ERROR caught up ${BASH_SOURCE[*]} ${BASH_LINENO[*]}" - # - stack_functions[0]="${stack_functions[0]} (hint: prefer source than subshell)" - [[ "${code}" == 128 ]] && failure_type='SUBSHELL_SOURCE_NOT_FOUND' - else - failure_type='COMMAND_ERROR' - fi - fi - - dump_failure "${last_trim:-true}" "${error_block:-true}" - else - dump_success - fi - - # FIXME: be aware of errors during the on_exit function because it does not pop up! - miaou_debug "--end of on_exit => $code --" - - builtin exit "$code" -} - -function miaou_debug { - is_true "${MIAOU_DEBUG}" && >/dev/tty builtin echo -e "*********** DEBUG: $*" || true -} - -function ansi_block_continuation { - tput sc - tput cuu 1 - BG=RED FG=BLACK style_ansi "\t╦" - tput rc - FG=RED style_ansi "\t║\n" -} - -function ansi_block_error { - FG=BLACK - BG=RED - local content detail exit_code - - content=$(style_ansi "$1") - content=$(PADDING=2 style_padding "$content") - - detail="$2" - [[ -n "$detail" ]] && detail=$(BG=BLACK FG=WHITE PADDING=2 style_padding "$2") - - exit_code=$(FG=CYAN PADDING=2 style_padding "$3") - builtin echo -en "\r" - BLOCK_CONTINUATION_COL=5 style_block "$content$detail$exit_code" - ansi_reset - FG=RED style_ansi " ║" && builtin echo -} - -function ansi_traces { - local optional location sources lines functions - declare -n sources=$1 - declare -n lines=$2 - declare -n functions=$3 - optional=false - for i in "${!sources[@]}"; do - FG=RED style_ansi " ╟─⟶ " - location=$(printf "%40s" "${sources[$i]}:${lines[$i]}") - is_true "$optional" && - location=$(FG=GRAY STYLE=ITALIC style_ansi "$location") || - location=$(STYLE=ITALIC style_ansi "$location") - builtin echo "${location} ${functions[$i]}" - optional=true - done -} -function dump_failure { - declare -F style_ansi >/dev/null || source "$MIAOU_BASH_DIR/lib/ansi.bash" - dump_stderr "${1:-true}" - - local error_block="${2:-true}" - if is_true $error_block; then - miaou_debug "---error box ${failure_type:-} ---" - if [[ ${failure_type} == 'SUBSHELL SOURCE NOT FOUND' ]]; then - : - else - >&4 ansi_block_error "$failure_type" "$failure_payload" "$code" - fi - miaou_debug '---end of error box---' - fi - - miaou_debug '---stacktrace---' - >&4 ansi_traces stack_sources stack_lines stack_functions - miaou_debug '---end of stacktrace---' - - cleanup -} - -function dump_stderr { - last_trim=${1:-false} - mapfile -t tmp_error <"$MIAOU_BASH_ERROR" - is_true "$last_trim" && unset 'tmp_error[-1]' && tmp_error=("${tmp_error[@]}") - if [[ "${#tmp_error[@]}" -gt 0 ]]; then - miaou_debug '---stderr---' - >&4 printf '\r' - for i in "${tmp_error[@]}"; do - >&4 echo -e "$i" - done - else - miaou_debug '---no stderr---' - fi -} - -function dump_success { - dump_stderr - cleanup -} - -function cleanup { - trap - ERR TERM EXIT DEBUG USR1 - exec 3>&- 4>&- - rm "$MIAOU_BASH_ERROR" -} - -# MAIN - -set -TEue -o pipefail -[[ $# -lt 1 ]] && >&2 echo 'ERROR: script expected!' && builtin exit 1 - -# trap 'on_exit $?' ERR TERM INT EXIT -trap 'on_exit $?' ERR EXIT -trap 'on_return' DEBUG -trap 'on_usr1' USR1 -BASH_ARGV0="$1" && shift # to pretend script runs by itself - -# magic FD + delayed tee => /dev/fd/3 means delayed stderr, /dev/fd/4 remains original stderr -exec 3> >(tee "$MIAOU_BASH_ERROR" >/dev/null) 4>/dev/stderr - -miaou_debug "---stdout--$$ $BASHPID $PPID--" -# shellcheck source=/dev/null -source "$BASH_ARGV0" 2>&3 -dump_success diff --git a/test/miaou-bash-old.test.bash b/test/miaou-bash-old.test.bash deleted file mode 100755 index 25b060f..0000000 --- a/test/miaou-bash-old.test.bash +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env bash - -# CONSTANTS - -TEST_COUNT=0 -SUCCESS_COUNT=0 -# functions - -function assert { - ((TEST_COUNT++)) - if ./test/stub/scenario.bash "$1" >/dev/null; then - ((SUCCESS_COUNT++)) - else - (echo "error in: $1" && exit 1) - fi - -} - -function strip_ansi { - echo "$1" | sed -E 's/\x1b\[[0-9;]*[a-zA-Z]//g' -} - -function fail_type { - ((TEST_COUNT++)) - local i line first error_start_above regex hint_message - hint_message="${3:-}" - mapfile -t stderr <<<"$(./test/stub/scenario.bash "$1" 2>&1 >/dev/null)" - # echo "stderr countains ${#stderr[@]}" - error_start_above=false - success=false - regex="║[[:blank:]]+([A-Z|_]+)" - for i in "${!stderr[@]}"; do - line=$(strip_ansi "${stderr[$i]}") - first="${line:1:1}" - [[ $first == ╔ ]] && error_start_above=true && continue - if [[ "$error_start_above" == true ]]; then - [[ $line =~ $regex ]] && [[ ${BASH_REMATCH[1]} == "$2" ]] && success=true - break - fi - done - - if [[ $success == true && -n "${hint_message}" ]]; then - success=false - regex="╟─⟶.*(\(.*\))$" - for j in $(seq "$((i + 1))" "$((${#stderr[@]} - 1))"); do - line=$(strip_ansi "${stderr[$j]}") - if [[ $line =~ $regex ]]; then - [[ "${BASH_REMATCH[1]}" == "${hint_message}" ]] && success=true - break - fi - done - fi - if [[ $success == true ]]; then - ((SUCCESS_COUNT++)) - else - echo "error in: $1" && return 1 - fi -} - -# main -set -uo pipefail - -assert success -fail_type subshell_term SUBSHELL_TERM -fail_type unbound_variable UNBOUND_VARIABLE -fail_type false FALSE -fail_type return RETURN -fail_type command_not_found COMMAND_NOT_FOUND -fail_type exit EXIT -fail_type source_not_found SOURCE_NOT_FOUND -fail_type file_not_found FILE_NOT_FOUND -fail_type subshell_error COMMAND_NOT_FOUND '(hint: prefer source than subshell)' -fail_type command_error COMMAND_ERROR - -failed=$((TEST_COUNT - SUCCESS_COUNT)) - -if [[ $failed -gt 0 ]]; then - ratio=$((100 * SUCCESS_COUNT / TEST_COUNT)) - echo "${ratio}% passed, $SUCCESS_COUNT succeeded, $failed failed!" - exit 1 -else - echo 100% passed! -fi diff --git a/test/stub/extra/onemore.bash b/test/stub/extra/onemore.bash index 435dbdf..256934c 100755 --- a/test/stub/extra/onemore.bash +++ b/test/stub/extra/onemore.bash @@ -9,9 +9,9 @@ function onemore { function do_next { echo do_next - echo $nope exit 10 # youpi grep nope nope + echo $nope command_not_found exit return 7