better docs, consistent output to stderr
[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 # Undoes err-catch. turns off exit and stack trace on error.
25 #######################################
26 err-allow() {
27 set +E +o pipefail; trap ERR
28 }
29
30
31 #######################################
32 # Print stack trace
33 #
34 # usage: err-bash-trace [FRAME_START]
35 #
36 # It does not show function args unless you first run:
37 # shopt -s extdebug
38 # which err-catch & err-print does for you.
39 #
40 # FRAME_START The frame to start printing on. default=0. Useful when
41 # printing from an ERR trap function to avoid printing
42 # that function.
43 #######################################
44 err-bash-trace() {
45 local -i argc_index=0 frame i start=${1:-0} max_indent=8 indent
46 local source
47 local extdebug=false
48 if [[ $(shopt -p extdebug) == *-s* ]]; then
49 extdebug=true
50 fi
51 for ((frame=0; frame < ${#FUNCNAME[@]}-1; frame++)); do
52 argc=${BASH_ARGC[frame]}
53 argc_index+=$argc
54 ((frame <= start)) && continue
55 if (( ${#BASH_SOURCE[@]} > 1 )); then
56 source="${BASH_SOURCE[frame+1]}:${BASH_LINENO[frame]}:"
57 fi
58 indent=$((frame-start))
59 indent=$((indent < max_indent ? indent : max_indent))
60 printf "%${indent}s↳%sin \`%s" '' "$source" "${FUNCNAME[frame]}"
61 if $extdebug; then
62 for ((i=argc_index-1; i >= argc_index-argc; i--)); do
63 printf " %s" "${BASH_ARGV[i]}"
64 done
65 fi
66 echo \'
67 done
68 return 0
69 }
70
71 #######################################
72 # On error print stack trace and exit
73 #
74 # Globals:
75 # ${_errcatch_cleanup[@]} Optional command & args that will run before exiting
76 #######################################
77 err-catch() {
78 set -E; shopt -s extdebug
79 _err-trap() {
80 err=$?
81 exec >&2
82 set +x
83 echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \`$BASH_COMMAND' returned $err"
84 err-bash-trace 2
85 set -e # err trap does not work within an error trap
86 "${_errcatch_cleanup[@]:-:}" # note :-: is to be compatible with set -u
87 echo "$0: exiting with code $err"
88 exit $err
89 }
90 trap _err-trap ERR
91 set -o pipefail
92 }
93
94 #######################################
95 # On error, print stack trace
96 #######################################
97 err-print() {
98 # help: on errors: print stack trace
99 #
100 # This function depends on err-bash-trace.
101
102 set -E; shopt -s extdebug
103 _err-trap() {
104 err=$?
105 exec >&2
106 set +x
107 echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \`$BASH_COMMAND' returned $err"
108 err-bash-trace 2
109 }
110 trap _err-trap ERR
111 set -o pipefail
112 }
113
114
115 #######################################
116 # On error, print stack trace
117 #
118 # Use this instead of the exit command to be more informative.
119 #
120 # usage: err-exit [EXIT_CODE] [MESSAGE]
121 #
122 # EXIT_CODE Default is 1.
123 # MESSAGE Print MESSAGE to stderr. If only one of EXIT_CODE
124 # and MESSAGE is given, we consider it to be an
125 # exit code if it is a number.
126 #######################################
127 err-exit() {
128 exec >&2
129 code=1
130 if [[ "$*" ]]; then
131 if [[ ${1/[^0-9]/} == "$1" ]]; then
132 code=$1
133 if [[ $2 ]]; then
134 printf '%s\n' "$2" >&2
135 fi
136 else
137 printf '%s\n' "$0: $1" >&2
138 fi
139 fi
140 echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}"
141 err-bash-trace 2
142 echo "$0: exiting with code $code"
143 exit $err
144 }
145
146 # We want this more often than not, so run it now.
147 err-catch