# -*- shell-script -*-
# Call Stack routines
#
# Copyright (C) 2002-2006, 2008-2010, 2014, 2017
# Rocky Bernstein <rocky@gnu.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 59 Temple Place, Suite 330, Boston,
# MA 02111 USA.
#================ VARIABLE INITIALIZATIONS ====================#
# _Dbg_stack_size: the number of entries on the call stack at the time
# the hook was entered. Note that bash updates the stack inside the
# debugger so it is important to save this value on entry. Also
# note that the most recent entries are pushed or at position 0.
# Thus to get position 0 of the debugged program we need to ignore leading
# any debugger frames.
typeset -i _Dbg_stack_size
# Where are we in stack? This can be changed by "up", "down" or
# "frame" commands. 0 means the most recent stack frame with respect
# to the debugged program and _Dbg_stack_size is the
# least-recent. Note that inside the debugger the stack is still
# updated. On debugger entry, the value is set to 0
typeset -i _Dbg_stack_pos
# Save the last-entered frame for to determine stopping when
# "set force" or step+ is in effect.
typeset _Dbg_frame_last_filename=''
typeset -i _Dbg_frame_last_lineno=0
#======================== FUNCTIONS ============================#
function _Dbg_frame_adjust {
(($# != 2)) && return 255
typeset -i count=$1
typeset -i signum=$2
typeset -i retval
_Dbg_frame_int_setup $count || return 2
typeset -i pos
if (( signum==0 )) ; then
if (( count < 0 )) ; then
((pos = _Dbg_stack_size + count - 1))
else
((pos = count))
fi
else
((pos=_Dbg_stack_pos+(count*signum)))
fi
if (( pos < 0 )) ; then
_Dbg_errmsg 'Would be beyond bottom-most (most recent) entry.'
return 1
elif (( pos >= _Dbg_stack_size - 1 )) ; then
_Dbg_errmsg 'Would be beyond top-most (least recent) entry.'
return 1
fi
typeset -i adjusted_pos
adjusted_pos=$(_Dbg_frame_adjusted_pos $pos)
_Dbg_stack_pos=$pos
## DEBUG
## typeset -p pos
## typeset -p adjusted_pos
## typeset -p BASH_LINENO
## typeset -p BASH_SOURCE
_Dbg_listline="${BASH_LINENO[adjusted_pos-1]}"
# _Dbg_frame_last_lineno="$_Dbg_listline"
_Dbg_frame_last_filename="${BASH_SOURCE[adjusted_pos]}"
typeset filename; filename="$(_Dbg_file_canonic "$_Dbg_frame_last_filename")"
_Dbg_frame_print '->' $_Dbg_stack_pos '' "$filename" $_Dbg_listline ''
_Dbg_print_location_and_command "$_Dbg_listline"
return 0
}
# Set $_Dbg_frame_filename to be frame file for the call stack at
# given position $1 or _Dbg_stack_pos if $1 is omitted. If $2 is
# given, it indicates if we want the basename only. Otherwise the
# $_Dbg_set_basename setting is used. 0 is returned if no error,
# nonzero means some sort of error.
_Dbg_frame_file() {
(($# > 2)) && return 2
# FIXME check to see that $1 doesn't run off the end.
typeset -i pos=${1:-$_Dbg_stack_pos}
typeset -i basename_only=${2:-$_Dbg_set_basename}
_Dbg_frame_filename=${BASH_SOURCE[pos]}
(( basename_only )) && _Dbg_frame_filename=${_Dbg_frame_filename##*/}
return 0
}
# Set $_Dbg_frame_filename to be frame line for the call stack at
# given position $1 or _Dbg_stack_pos if $1 is omitted. 0 is returned
# if no error, nonzero means some sort of error.
_Dbg_frame_line() {
(($# > 1)) && return 2
# FIXME check to see that $1 doesn't run off the end.
typeset -i pos=${1:-$_Dbg_stack_pos}
_Dbg_frame_last_lineno="${BASH_LINENO[pos]}"
return 0
}
# Tests for a signed integer parameter and set global retval
# if everything is okay. Retval is set to 1 on error
_Dbg_frame_int_setup() {
_Dbg_not_running && return 1
eval "$_seteglob"
if [[ $1 != '' && $1 != $_Dbg_signed_int_pat ]] ; then
_Dbg_errmsg "Bad integer parameter: $1"
eval "$_resteglob"
return 1
fi
eval "$_resteglob"
return 0
}
# Turn position $1 which uses 0 to represent the most-recent stack entry
# into which may have additional internal debugger frames pushed on.
function _Dbg_frame_adjusted_pos
{
if (($# != 1)) ; then
echo -n '-1'
return 1
fi
typeset -i pos
((pos=${#FUNCNAME[@]} - _Dbg_stack_size + $1))
echo -n $pos
return 0
}
# Creates a parameter string for return in non-local variable
# _Dbg_parm_str. This is obtained from BASH_ARGC and BASH_ARGV. On
# entry, _Dbg_next_argc, and _Dbg_next_argv should be set. These
# variables and _Dbg_parm_str are updated on exit. _Dbg_next_argc is
# and integer index into BASH_ARGC and _Dbg_next_argv is and index
# into BASH_ARGV. On return
_Dbg_frame_fn_param_str() {
(($# == 0)) || return 1
_Dbg_is_int "$_Dbg_next_argc" || return 2
_Dbg_is_int "$_Dbg_next_argv" || return 3
# add 1 to argument count to compensate for this call (of zero
# parameters) and at the same time we update _Dbg_next_argc for the
# next call.
#
((_Dbg_next_argc++))
typeset -i arg_count=BASH_ARGC[$_Dbg_next_argc]
if ((arg_count == 0)) ; then
_Dbg_parm_str=''
else
typeset -i i
_Dbg_parm_str="\"${BASH_ARGV[$_Dbg_next_argv+arg_count-1]}\""
for (( i=1; i <= arg_count-1; i++ )) ; do
_Dbg_parm_str+=", \"${BASH_ARGV[$_Dbg_next_argv+arg_count-i-1]}\""
done
((_Dbg_next_argv+=arg_count))
fi
return 0
}
_Dbg_frame_set_fn_param() {
(($# == 1)) || return 1
typeset -i skip_count=$1
# Set to ignore this call in computation
_Dbg_next_argc=1
_Dbg_next_argv=1
typeset -i i
for (( i=1; i <= skip_count; i++ )) ; do
typeset -i arg_count=${BASH_ARGC[$i]}
((_Dbg_next_argv+=arg_count))
done
# After this function returns argv will be one greater. So adjust
# for that now.
((_Dbg_next_argc=skip_count))
((_Dbg_next_argv--))
## Debug:
## typeset -p BASH_ARGC
## typeset -p BASH_ARGV
## typeset -p FUNCNAME
## typeset -p _Dbg_next_argc
## typeset -p _Dbg_next_argv
}
# Print "##" or "->" depending on whether or not $1 (POS) is a number
# between 0 and _Dbg_stack_size-1. For POS, 0 is the top-most
# (newest) entry. For _Dbg_stack_pos, 0 is the bottom-most entry.
# 0 is returnd on success, nonzero on failure.
function _Dbg_frame_prefix {
typeset prefix='??'
typeset -i rc=0
if (($# == 1)) ; then
typeset -i pos=$1
if ((pos < 0)) ; then
rc=2
elif ((pos >= _Dbg_stack_size)) ; then
rc=3
elif (( pos == _Dbg_stack_pos )) ; then
prefix='->'
else
prefix='##'
fi
else
rc=1
fi
echo -n $prefix
return $rc
}
# Print one line in a call stack
function _Dbg_frame_print {
typeset prefix=$1
typeset -i pos=$2
typeset fn=$3
typeset filename="$4"
typeset -i line=$5
typeset args="$6"
typeset callstr=$fn
[[ -n $args ]] && callstr="$callstr($args)"
_Dbg_msg "$prefix$pos in file \`$filename' at line $line"
}