# -*- shell-script -*-
# filecache.sh - cache file information
#
# Copyright (C) 2008-2011, 2013-2015, 2018-2019 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.
typeset _Dbg_bogus_file=' A really bogus file'
# Maps a name into its canonic form which can then be looked up in filenames
typeset -A _Dbg_file2canonic
_Dbg_file2canonic=()
# Information about a file.
typeset -A _Dbg_fileinfo
# Keys are the canonic expanded filename. _Dbg_filenames[filename] is
# name of variable which contains text.
typeset -A _Dbg_filenames
_Dbg_filecache_reset() {
_Dbg_filenames=()
_Dbg_fileinfo=()
_Dbg_file2canonic=()
}
_Dbg_filecache_reset
# Check that line $2 is not greater than the number of lines in
# file $1
_Dbg_check_line() {
(( $# != 2 )) && return 1
typeset -i line_number=$1
typeset filename="$2"
typeset -i max_line
max_line=$(_Dbg_get_maxline "$filename")
if (( $? != 0 )) ; then
_Dbg_errmsg "internal error getting number of lines in $filename"
return 1
fi
if (( line_number > max_line )) ; then
(( _Dbg_set_basename )) && filename=${filename##*/}
_Dbg_errmsg "Line $line_number is too large." \
"File $filename has only $max_line lines."
return 1
fi
return 0
}
# Error message for file not read in
function _Dbg_file_not_read_in {
typeset -r filename=$(_Dbg_adjust_filename "$1")
_Dbg_errmsg "File \"$filename\" not found in read-in files."
_Dbg_errmsg "See 'info files' for a list of known files and"
_Dbg_errmsg "'load' to read in a file."
}
# Print the maximum line of filename $1. $1 is expected to be
# read in already and therefore stored in _Dbg_file2canonic.
function _Dbg_get_maxline {
(( $# != 1 )) && return -1
_Dbg_set_source_array_var "$1" || return $?
typeset -r line_count_cmd="line_count=\${#$_Dbg_source_array_var[@]}"
eval $line_count_cmd
eval "typeset last_line; last_line=\${${_Dbg_source_array_var}[$line_count]}"
# If the file had a final newline the last line of the data read in
# is the empty string. We want to count the last line whether or
# not it had a newline.
typeset -i last_not_null=0
# [[ -z $last_line ]] && last_line_is_null=1 || last_line_is_null=0
((line_count=line_count-last_line_is_null))
echo $line_count
return $?
}
# Return text for source line for line $1 of filename $2 in variable
# $_Dbg_source_line.
# If $2 is omitted, use _Dbg_frame_filename, if $1 is omitted use
# _Dbg_frame_last_lineno. The return value is put in _Dbg_source_line.
_Dbg_get_source_line() {
typeset -i lineno
if (( $# == 0 )); then
lineno=$_Dbg_frame_last_lineno
else
lineno=$1
shift
fi
typeset filename
if (( $# == 0 )) ; then
filename="$_Dbg_frame_last_filename"
else
filename="$1"
fi
_Dbg_readin_if_new "$filename"
if [[ -n $_Dbg_set_highlight ]] && [[ -n $_Dbg_highlight_array_var ]]; then
eval "typeset -i count=\${#$_Dbg_highlight_array_var[@]}"
if (( count )) ; then
eval "_Dbg_source_line=\${$_Dbg_highlight_array_var[lineno]}"
else
eval "_Dbg_source_line=\${$_Dbg_source_array_var[$lineno]}"
fi
else
eval "_Dbg_source_line=\${$_Dbg_source_array_var[$lineno]}"
fi
}
# _Dbg_is_file echoes the full filename if $1 is a filename found in files
# '' is echo'd if no file found. Return 0 (in $?) if found, 1 if not.
function _Dbg_is_file {
if (( $# == 0 )) ; then
_Dbg_errmsg "Internal debug error _Dbg_is_file(): null file to find"
echo ''
return 1
fi
# first character might be encoded as \057 == '/',
# find_file:0:1 == "\" , true story
typeset find_file="$(printf "$1")"
typeset try_find_file
if [[ -z $find_file ]] ; then
_Dbg_errmsg "Internal debug error _Dbg_is_file(): file argument null"
echo ''
return 1
fi
if [[ ${find_file:0:1} == '/' ]] ; then
# Absolute file name
try_find_file=$(_Dbg_expand_filename "$find_file")
if [[ -n ${_Dbg_filenames[$try_find_file]} ]] ; then
echo "$try_find_file"
return 0
fi
elif [[ ${find_file:0:1} == '.' ]] ; then
# Relative file name
try_find_file=$(_Dbg_expand_filename "${_Dbg_init_cwd}/$find_file")
# FIXME: turn into common subroutine
if [[ -n ${_Dbg_filenames[$try_find_file]} ]] ; then
echo "$try_find_file"
return 0
fi
else
# Resolve file using _Dbg_dir
typeset -i n=${#_Dbg_dir[@]}
typeset -i i
for (( i=0 ; i < n; i++ )) ; do
typeset basename="${_Dbg_dir[i]}"
if [[ $basename == '\$cdir' ]] ; then
basename=$_Dbg_cdir
elif [[ $basename == '\$cwd' ]] ; then
basename=$(pwd)
fi
try_find_file="$basename/$find_file"
if [[ -f "$try_find_file" ]] ; then
echo "$try_find_file"
return 0
fi
done
fi
echo ''
return 1
}
# Read $1 into _Dbg_source_*n* array where *n* is an entry in
# _Dbg_filenames. Variable _Dbg_source_array_var will be set to
# _Dbg_source_*n* and filename will be saved in array
# _Dbg_filenames. fullname is set to the expanded filename
# 0 is returned if everything went ok.
function _Dbg_readin {
typeset filename
if (($# != 0)) ; then
filename="$1"
else
_Dbg_frame_file
filename="$_Dbg_frame_filename"
fi
typeset -i line_count=0
typeset -i next;
next=${#_Dbg_filenames[@]}
_Dbg_source_array_var="_Dbg_source_${next}"
if [[ -n $_Dbg_set_highlight ]] ; then
_Dbg_highlight_array_var="_Dbg_highlight_${next}"
fi
typeset filevar
typeset source_array
typeset -ri NOT_SMALLFILE=1000
if [[ -z "$filename" ]] || [[ "$filename" == "$_Dbg_bogus_file" ]] ; then
eval "${_Dbg_source_array_var}[0]=\"$Dbg_EXECUTION_STRING\""
else
fullname=$(_Dbg_resolve_expand_filename "$filename")
if [[ -r "$fullname" ]] ; then
typeset -r progress_prefix="Reading $filename"
_Dbg_file2canonic["$filename"]="$fullname"
_Dbg_file2canonic["$fullname"]="$fullname"
# Use readarray which speeds up reading greatly.
typeset -ri BIGFILE=30000
if wc -l < /dev/null >/dev/null 2>&1 ; then
line_count=$(wc -l < "${fullname}")
if (( line_count >= NOT_SMALLFILE )) ; then
_Dbg_msg_nocr "${progress_prefix} "
fi
fi
builtin mapfile -t -O 1 -c $BIGFILE \
-C "_Dbg_progess_show \"${progress_prefix}\" ${line_count}" \
$_Dbg_source_array_var < "$fullname"
if [[ -n $_Dbg_set_highlight ]] ; then
opts="--bg=${_Dbg_set_highlight}"
if [[ -n $_Dbg_set_style ]] ; then
opts="--style=${_Dbg_set_style}"
fi
highlight_cmd="${_Dbg_libdir}/lib/term-highlight.py $opts $fullname"
tempfile=$($highlight_cmd 2>/dev/null)
if (( 0 == $? )) ; then
builtin readarray -t -O 1 -c $BIGFILE \
-C "_Dbg_progess_show \"${progress_prefix}\" ${line_count}" \
$_Dbg_highlight_array_var < "$tempfile"
fi
[[ -r "$tempfile" ]] && rm "$tempfile"
fi
(( line_count > BIGFILE)) && _Dbg_progess_done
else
return 1
fi
fi
(( line_count >= NOT_SMALLFILE )) && _Dbg_msg "done."
# Add $filename to list of all filenames
_Dbg_filenames["$fullname"]=$_Dbg_source_array_var;
return 0
}
# Read in file $1 unless it has already been read in.
# 0 is returned if everything went ok.
_Dbg_readin_if_new() {
(( $# != 1 )) && return 1
typeset filename="$1"
_Dbg_set_source_array_var "$filename"
if [[ -z "$fullname" ]] ; then
_Dbg_readin "$filename"
typeset rc=$?
set +xv
(( $? != 0 )) && return $rc
[[ -z "$fullname" ]] && return 1
_Dbg_set_source_array_var "$filename" || return $?
fi
return 0
}
# Set _Dbg_source_array_var to the variable that contains file lines
# for $1. Variable "fullname" will contain the expanded full filename for $1.
# 0 is returned if everything went ok.
_Dbg_set_source_array_var() {
(( $# != 1 )) && return 1
typeset filename="$1"
[[ -z "$filename" ]] && return 2
fullname="${_Dbg_file2canonic["$filename"]}"
[[ -z "$fullname" ]] && [[ -n "${_Dbg_filenames["$filename"]}" ]] && {
fullname="$filename"
}
[[ -z "$fullname" ]] && return 2
_Dbg_source_array_var=${_Dbg_filenames["$fullname"]}
if [[ -n $_Dbg_set_highlight ]] ; then
_Dbg_highlight_array_var="${_Dbg_source_array_var/_Dbg_source_/_Dbg_highlight_}"
fi
_Dbg_source_array_var=${_Dbg_filenames["$fullname"]}
[[ -z $_Dbg_source_array_var ]] && return 2
return 0
}