minor comment
[errhandle.git] / err
1 #!/bin/bash
2 # Copyright 2018 Ian Kelling
3
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7
8 # http://www.apache.org/licenses/LICENSE-2.0
9
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16
17 # Commentary: Bash stack trace and error handling functions. This file
18 # is meant to be sourced. It loads some functions which you may want to
19 # call manually (see the comments at the start of each one), and then
20 # runs err-catch. See the README file for a slightly longer explanation.
21
22
23 #######################################
24 # Print stack trace
25 #
26 # usage: err-bash-trace [FRAME_START]
27 #
28 # This function is called by the other functions which print stack
29 # traces.
30 #
31 # It does not show function args unless you first run:
32 # shopt -s extdebug
33 # which err-catch & err-print do for you.
34 #
35 # FRAME_START The frame to start printing on. default=0. Useful when
36 # printing from an ERR trap function to avoid printing
37 # that function.
38 #######################################
39 err-bash-trace() {
40 local -i argc_index=0 frame i start=${1:-0} max_indent=8 indent
41 local source
42 local extdebug=false
43 if [[ $(shopt -p extdebug) == *-s* ]]; then
44 extdebug=true
45 fi
46 for ((frame=0; frame < ${#FUNCNAME[@]}-1; frame++)); do
47 argc=${BASH_ARGC[frame]}
48 argc_index+=$argc
49 ((frame < start)) && continue
50 if (( ${#BASH_SOURCE[@]} > 1 )); then
51 source="${BASH_SOURCE[frame+1]}:${BASH_LINENO[frame]}:"
52 fi
53 indent=$((frame-start + 1))
54 indent=$((indent < max_indent ? indent : max_indent))
55 printf "%${indent}s↳%sin \`%s" '' "$source" "${FUNCNAME[frame]}"
56 if $extdebug; then
57 for ((i=argc_index-1; i >= argc_index-argc; i--)); do
58 printf " %s" "${BASH_ARGV[i]}"
59 done
60 fi
61 echo \'
62 done
63 return 0
64 }
65
66 #######################################
67 # On error print stack trace and exit
68 #
69 # Globals:
70 # ${_errcatch_cleanup[@]} Optional command & args that will run before exiting
71 #######################################
72 err-catch() {
73 set -E; shopt -s extdebug
74 _err-trap() {
75 err=$?
76 exec >&2
77 set +x
78 echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \`$BASH_COMMAND' returned $err"
79 err-bash-trace 2
80 set -e # err trap does not work within an error trap
81 # note :-: makes this compatible with set -u. shellcheck doesn't understand.
82 "${_errcatch_cleanup[@]:-:}"
83 echo "$0: exiting with code $err"
84 exit $err
85 }
86 trap _err-trap ERR
87 set -o pipefail
88 }
89
90 #######################################
91 # Undoes err-catch. turns off exit and stack trace on error.
92 #######################################
93 err-allow() {
94 set +E +o pipefail; trap ERR
95 }
96
97 #######################################
98 # On error, print stack trace
99 #######################################
100 err-print() {
101 # help: on errors: print stack trace
102 #
103 # This function depends on err-bash-trace.
104
105 set -E; shopt -s extdebug
106 _err-trap() {
107 err=$?
108 exec >&2
109 set +x
110 echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \`$BASH_COMMAND' returned $err"
111 err-bash-trace 2
112 }
113 trap _err-trap ERR
114 set -o pipefail
115 }
116
117
118 #######################################
119 # Print stack trace and exit
120 #
121 # Use this instead of the exit command to be more informative.
122 #
123 # usage: err-exit [EXIT_CODE] [MESSAGE]
124 #
125 # EXIT_CODE Default is 1.
126 # MESSAGE Print MESSAGE to stderr. If only one of EXIT_CODE
127 # and MESSAGE is given, we consider it to be an
128 # exit code if it is a number.
129 #######################################
130 err-exit() {
131 exec >&2
132 code=1
133 if [[ "$*" ]]; then
134 if [[ ${1/[^0-9]/} == "$1" ]]; then
135 code=$1
136 if [[ $2 ]]; then
137 printf '%s\n' "$2" >&2
138 fi
139 else
140 printf '%s\n' "$0: $1" >&2
141 fi
142 fi
143 echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}"
144 err-bash-trace 2
145 echo "$0: exiting with code $code"
146 exit $err
147 }
148
149 # We want this more often than not, so run it now.
150 err-catch