1233541Sjchandra:
2233541Sjchandra# SPDX-License-Identifier: BSD-2-Clause
3233541Sjchandra
4233541Sjchandra# NAME:
5233541Sjchandra#	debug.sh - selectively debug scripts
6233541Sjchandra#
7233541Sjchandra# SYNOPSIS:
8233541Sjchandra#	$_DEBUG_SH . debug.sh
9233541Sjchandra#	DebugOn [-eo] "tag" ...
10233541Sjchandra#	DebugOff [-eo] [rc="rc"] "tag" ...
11233541Sjchandra#	Debugging
12233541Sjchandra#	DebugEcho ...
13233541Sjchandra#	DebugLog ...
14233541Sjchandra#	DebugShell "tag" ...
15233541Sjchandra#	DebugTrace ...
16233541Sjchandra#	Debug "tag" ...
17233541Sjchandra#
18233541Sjchandra#	$DEBUG_SKIP echo skipped when Debug "tag" is true.
19233541Sjchandra#	$DEBUG_DO echo only done when Debug "tag" is true.
20233541Sjchandra#
21233541Sjchandra# DESCRIPTION:
22233541Sjchandra#	debug.sh provides the following functions to facilitate
23233541Sjchandra#	flexible run-time tracing of complicated shell scripts.
24233541Sjchandra#
25233541Sjchandra#	DebugOn turns tracing on if any "tag" is found in "DEBUG_SH".
26233541Sjchandra#	It turns tracing off if "!tag" is found in "DEBUG_SH".
27233541Sjchandra#	It also sets "DEBUG_ON" to the "tag" that caused tracing to be
28233541Sjchandra#	enabled, or "DEBUG_OFF" if we matched "!tag".
29233541Sjchandra#	If '-e' option given returns 1 if no "tag" matched.
30233541Sjchandra#	If the '-o' flag is given, tracing is turned off unless there
31233541Sjchandra#	was a matched "tag", useful for functions too noisy to tace.
32233541Sjchandra#
33233541Sjchandra#	DebugOff turns tracing on if any "tag" matches "DEBUG_OFF" or
34233541Sjchandra#	off if any "tag" matches "DEBUG_ON". This allows nested
35233541Sjchandra#	functions to not interfere with each other.
36233541Sjchandra#
37233541Sjchandra#	DebugOff accepts but ignores the '-e' and '-o' options.
38233541Sjchandra#	The optional "rc" value will be returned rather than the
39233541Sjchandra#	default of 0. Thus if DebugOff is the last operation in a
40233541Sjchandra#	function, "rc" will be the return code of that function.
41233541Sjchandra#
42233541Sjchandra#	DebugEcho is just shorthand for:
43233541Sjchandra#.nf
44233541Sjchandra#	$DEBUG_DO echo "$@"
45233541Sjchandra#.fi
46233541Sjchandra#
47233541Sjchandra#	Debugging returns true if tracing is enabled.
48233541Sjchandra#	It is useful for bounding complex debug actions, rather than
49233541Sjchandra#	using lots of "DEBUG_DO" lines.
50233541Sjchandra#
51233541Sjchandra#	DebugShell runs an interactive shell if any "tag" is found in
52233541Sjchandra#	"DEBUG_INTERACTIVE", and there is a tty available.
53233541Sjchandra#	The shell used is defined by "DEBUG_SHELL" or "SHELL" and
54233541Sjchandra#	defaults to '/bin/sh'.
55233541Sjchandra#
56233541Sjchandra#	Debug calls DebugOn and if that does not turn tracing on, it
57233541Sjchandra#	calls DebugOff to turn it off.
58233541Sjchandra#
59233541Sjchandra#	The variables "DEBUG_SKIP" and "DEBUG_DO" are set so as to
60233541Sjchandra#	enable/disable code that should be skipped/run when debugging
61233541Sjchandra#	is turned on. "DEBUGGING" is the same as "DEBUG_SKIP" for
62233541Sjchandra#	backwards compatability.
63233541Sjchandra#
64233541Sjchandra#	The use of $_DEBUG_SH is to prevent multiple inclusion, though
65233541Sjchandra#	it does no harm in this case.
66233541Sjchandra#
67233541Sjchandra# BUGS:
68233541Sjchandra#	Does not work with some versions of ksh.
69233541Sjchandra#	If a function turns tracing on, ksh turns it off when the
70233541Sjchandra#	function returns - useless.
71233541Sjchandra#	PD ksh works ok ;-)
72233541Sjchandra#
73233541Sjchandra# AUTHOR:
74233541Sjchandra#	Simon J. Gerraty <sjg@crufty.net>
75233541Sjchandra
76233541Sjchandra# RCSid:
77233541Sjchandra#	$Id: debug.sh,v 1.35 2024/02/03 19:04:47 sjg Exp $
78233541Sjchandra#
79233541Sjchandra#	@(#) Copyright (c) 1994-2024 Simon J. Gerraty
80233541Sjchandra#
81233541Sjchandra#	This file is provided in the hope that it will
82233541Sjchandra#	be of use.  There is absolutely NO WARRANTY.
83233541Sjchandra#	Permission to copy, redistribute or otherwise
84233541Sjchandra#	use this file is hereby granted provided that
85233541Sjchandra#	the above copyright notice and this notice are
86233541Sjchandra#	left intact.
87233541Sjchandra#
88233541Sjchandra#	Please send copies of changes and bug-fixes to:
89233541Sjchandra#	sjg@crufty.net
90233541Sjchandra#
91233541Sjchandra
92233541Sjchandra_DEBUG_SH=:
93233541Sjchandra
94233541SjchandraMyname=${Myname:-`basename $0 .sh`}
95233541Sjchandra
96233541SjchandraDEBUGGING=
97233541SjchandraDEBUG_DO=:
98233541SjchandraDEBUG_SKIP=
99233541Sjchandraexport DEBUGGING DEBUG_DO DEBUG_SKIP
100233541Sjchandra
101233541Sjchandra_debugOn() {
102233541Sjchandra	DEBUG_OFF=
103233541Sjchandra	DEBUG_DO=
104233541Sjchandra	DEBUG_SKIP=:
105233541Sjchandra	DEBUG_X=-x
106233541Sjchandra	set -x
107233541Sjchandra	DEBUG_ON=$1
108233541Sjchandra}
109233541Sjchandra
110233541Sjchandra_debugOff() {
111233541Sjchandra	DEBUG_OFF=$1
112233541Sjchandra	set +x
113233541Sjchandra	DEBUG_ON=$2
114233541Sjchandra	DEBUG_DO=:
115233541Sjchandra	DEBUG_SKIP=
116233541Sjchandra	DEBUG_X=
117233541Sjchandra}
118233541Sjchandra
119233541SjchandraDebugEcho() {
120233541Sjchandra	$DEBUG_DO echo "$@"
121233541Sjchandra}
122233541Sjchandra
123233541SjchandraDebugging() {
124233541Sjchandra	test "$DEBUG_SKIP"
125233541Sjchandra}
126233541Sjchandra
127233541SjchandraDebugLog() {
128233541Sjchandra	$DEBUG_SKIP return 0
129233541Sjchandra	echo `date '+@ %s [%Y-%m-%d %H:%M:%S %Z]'` "$@"
130233541Sjchandra}
131233541Sjchandra
132233541Sjchandra# something hard to miss when wading through huge -x output
133233541SjchandraDebugTrace() {
134233541Sjchandra	$DEBUG_SKIP return 0
135233541Sjchandra	set +x
136233541Sjchandra	echo "@ ==================== [ $DEBUG_ON ] ===================="
137233541Sjchandra	DebugLog "$@"
138233541Sjchandra	echo "@ ==================== [ $DEBUG_ON ] ===================="
139233541Sjchandra	set -x
140233541Sjchandra}
141233541Sjchandra
142233541Sjchandra# Turn on debugging if appropriate
143233541SjchandraDebugOn() {
144233541Sjchandra	_rc=0			# avoid problems with set -e
145233541Sjchandra	_off=:
146233541Sjchandra	while :
147233541Sjchandra	do
148233541Sjchandra		case "$1" in
149233541Sjchandra		-e) _rc=1; shift;; # caller ok with return 1
150233541Sjchandra		-o) _off=; shift;; # off unless we have a match
151233541Sjchandra		*) break;;
152233541Sjchandra		esac
153233541Sjchandra	done
154233541Sjchandra	case ",${DEBUG_SH:-$DEBUG}," in
155233541Sjchandra	,,)	return $_rc;;
156233541Sjchandra	*,[Dd]ebug,*) ;;
157233541Sjchandra	*) $DEBUG_DO set +x;;		# reduce the noise
158	esac
159	_match=
160	# if debugging is off because of a !e
161	# don't add 'all' to the On list.
162	case "$_off$DEBUG_OFF" in
163	:)	_e=all;;
164	*)	_e=;;
165	esac
166	for _e in ${*:-$Myname} $_e
167	do
168		: $_e in ,${DEBUG_SH:-$DEBUG},
169		case ",${DEBUG_SH:-$DEBUG}," in
170		*,!$_e,*|*,!$Myname:$_e,*)
171			# only turn it off if it was on
172			_rc=0
173			$DEBUG_DO _debugOff $_e $DEBUG_ON
174			break
175			;;
176		*,$_e,*|*,$Myname:$_e,*)
177			# only turn it on if it was off
178			_rc=0
179			_match=$_e
180			$DEBUG_SKIP _debugOn $_e
181			break
182			;;
183		esac
184	done
185	if test -z "$_off$_match"; then
186		# off unless explicit match, but
187		# only turn it off if it was on
188		$DEBUG_DO _debugOff $_e $DEBUG_ON
189	fi
190	DEBUGGING=$DEBUG_SKIP	# backwards compatability
191	$DEBUG_DO set -x	# back on if needed
192	$DEBUG_DO set -x	# make sure we see it in trace
193	return $_rc
194}
195
196# Only turn debugging off if one of our args was the reason it
197# was turned on.
198# We normally return 0, but caller can pass rc=$? as first arg
199# so that we preserve the status of last statement.
200DebugOff() {
201	case ",${DEBUG_SH:-$DEBUG}," in
202	*,[Dd]ebug,*) ;;
203	*) $DEBUG_DO set +x;;		# reduce the noise
204	esac
205	_rc=0			# always happy
206	while :
207	do
208		case "$1" in
209		-[eo]) shift;;	# ignore it
210		rc=*) eval "_$1"; shift;;
211		*) break;;
212		esac
213	done
214	for _e in $*
215	do
216		: $_e==$DEBUG_OFF DEBUG_OFF
217		case "$DEBUG_OFF" in
218		"")	break;;
219		$_e)	_debugOn $DEBUG_ON; return $_rc;;
220		esac
221	done
222	for _e in $*
223	do
224		: $_e==$DEBUG_ON DEBUG_ON
225		case "$DEBUG_ON" in
226		"")	break;;
227		$_e)	_debugOff; return $_rc;;
228		esac
229	done
230	DEBUGGING=$DEBUG_SKIP	# backwards compatability
231	$DEBUG_DO set -x	# back on if needed
232	$DEBUG_DO set -x	# make sure we see it in trace
233	return $_rc
234}
235
236_TTY=${_TTY:-`test -t 0 && tty`}; export _TTY
237
238# override this if you like
239_debugShell() {
240	{
241		echo DebugShell "$@"
242		echo "Type 'exit' to continue..."
243	} > $_TTY
244	${DEBUG_SHELL:-${SHELL:-/bin/sh}} < $_TTY > $_TTY 2>&1
245}
246
247# Run an interactive shell if appropriate
248# Note: you can use $DEBUG_SKIP DebugShell ... to skip unless debugOn
249DebugShell() {
250	case "$_TTY%${DEBUG_INTERACTIVE}" in
251	*%|%*) return 0;;	# no tty or no spec
252	esac
253	for _e in ${*:-$Myname} all
254	do
255		case ",${DEBUG_INTERACTIVE}," in
256		*,!$_e,*|*,!$Myname:$_e,*)
257			return 0
258			;;
259		*,$_e,*|*,$Myname:$_e,*)
260			# Provide clues as to why/where
261			_debugShell "$_e: $@"
262			return $?
263			;;
264		esac
265	done
266	return 0
267}
268
269# For backwards compatability
270Debug() {
271	case "${DEBUG_SH:-$DEBUG}" in
272	"")	;;
273	*)	DEBUG_ON=${DEBUG_ON:-_Debug}
274		DebugOn -e $* || DebugOff $DEBUG_LAST
275		DEBUGGING=$DEBUG_SKIP
276		;;
277	esac
278}
279