1# -*- shell-script -*- 2# filecache.sh - cache file information 3# 4# Copyright (C) 2008-2011, 2013-2015, 2018-2019 Rocky Bernstein 5# <rocky@gnu.org> 6# 7# This program is free software; you can redistribute it and/or 8# modify it under the terms of the GNU General Public License as 9# published by the Free Software Foundation; either version 2, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15# General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License along 18# with this program; see the file COPYING. If not, write to the Free Software 19# Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. 20 21typeset _Dbg_bogus_file=' A really bogus file' 22 23# Maps a name into its canonic form which can then be looked up in filenames 24typeset -A _Dbg_file2canonic 25_Dbg_file2canonic=() 26 27# Information about a file. 28typeset -A _Dbg_fileinfo 29 30# Keys are the canonic expanded filename. _Dbg_filenames[filename] is 31# name of variable which contains text. 32typeset -A _Dbg_filenames 33 34_Dbg_filecache_reset() { 35 _Dbg_filenames=() 36 _Dbg_fileinfo=() 37 _Dbg_file2canonic=() 38} 39_Dbg_filecache_reset 40 41# Check that line $2 is not greater than the number of lines in 42# file $1 43_Dbg_check_line() { 44 (( $# != 2 )) && return 1 45 typeset -i line_number=$1 46 typeset filename="$2" 47 typeset -i max_line 48 max_line=$(_Dbg_get_maxline "$filename") 49 if (( $? != 0 )) ; then 50 _Dbg_errmsg "internal error getting number of lines in $filename" 51 return 1 52 fi 53 54 if (( line_number > max_line )) ; then 55 (( _Dbg_set_basename )) && filename=${filename##*/} 56 _Dbg_errmsg "Line $line_number is too large." \ 57 "File $filename has only $max_line lines." 58 return 1 59 fi 60 return 0 61} 62 63# Error message for file not read in 64function _Dbg_file_not_read_in { 65 typeset -r filename=$(_Dbg_adjust_filename "$1") 66 _Dbg_errmsg "File \"$filename\" not found in read-in files." 67 _Dbg_errmsg "See 'info files' for a list of known files and" 68 _Dbg_errmsg "'load' to read in a file." 69} 70 71# Print the maximum line of filename $1. $1 is expected to be 72# read in already and therefore stored in _Dbg_file2canonic. 73function _Dbg_get_maxline { 74 (( $# != 1 )) && return -1 75 _Dbg_set_source_array_var "$1" || return $? 76 typeset -r line_count_cmd="line_count=\${#$_Dbg_source_array_var[@]}" 77 eval $line_count_cmd 78 eval "typeset last_line; last_line=\${${_Dbg_source_array_var}[$line_count]}" 79 # If the file had a final newline the last line of the data read in 80 # is the empty string. We want to count the last line whether or 81 # not it had a newline. 82 typeset -i last_not_null=0 83 # [[ -z $last_line ]] && last_line_is_null=1 || last_line_is_null=0 84 ((line_count=line_count-last_line_is_null)) 85 echo $line_count 86 return $? 87} 88 89# Return text for source line for line $1 of filename $2 in variable 90# $_Dbg_source_line. 91 92# If $2 is omitted, use _Dbg_frame_filename, if $1 is omitted use 93# _Dbg_frame_last_lineno. The return value is put in _Dbg_source_line. 94_Dbg_get_source_line() { 95 typeset -i lineno 96 if (( $# == 0 )); then 97 lineno=$_Dbg_frame_last_lineno 98 else 99 lineno=$1 100 shift 101 fi 102 typeset filename 103 if (( $# == 0 )) ; then 104 filename="$_Dbg_frame_last_filename" 105 else 106 filename="$1" 107 fi 108 _Dbg_readin_if_new "$filename" 109 if [[ -n $_Dbg_set_highlight ]] && [[ -n $_Dbg_highlight_array_var ]]; then 110 eval "typeset -i count=\${#$_Dbg_highlight_array_var[@]}" 111 if (( count )) ; then 112 eval "_Dbg_source_line=\${$_Dbg_highlight_array_var[lineno]}" 113 else 114 eval "_Dbg_source_line=\${$_Dbg_source_array_var[$lineno]}" 115 fi 116 else 117 eval "_Dbg_source_line=\${$_Dbg_source_array_var[$lineno]}" 118 fi 119} 120 121# _Dbg_is_file echoes the full filename if $1 is a filename found in files 122# '' is echo'd if no file found. Return 0 (in $?) if found, 1 if not. 123function _Dbg_is_file { 124 if (( $# == 0 )) ; then 125 _Dbg_errmsg "Internal debug error _Dbg_is_file(): null file to find" 126 echo '' 127 return 1 128 fi 129 # first character might be encoded as \057 == '/', 130 # find_file:0:1 == "\" , true story 131 typeset find_file="$(printf "$1")" 132 typeset try_find_file 133 134 if [[ -z $find_file ]] ; then 135 _Dbg_errmsg "Internal debug error _Dbg_is_file(): file argument null" 136 echo '' 137 return 1 138 fi 139 140 if [[ ${find_file:0:1} == '/' ]] ; then 141 # Absolute file name 142 try_find_file=$(_Dbg_expand_filename "$find_file") 143 if [[ -n ${_Dbg_filenames[$try_find_file]} ]] ; then 144 echo "$try_find_file" 145 return 0 146 fi 147 elif [[ ${find_file:0:1} == '.' ]] ; then 148 # Relative file name 149 try_find_file=$(_Dbg_expand_filename "${_Dbg_init_cwd}/$find_file") 150 # FIXME: turn into common subroutine 151 if [[ -n ${_Dbg_filenames[$try_find_file]} ]] ; then 152 echo "$try_find_file" 153 return 0 154 fi 155 else 156 # Resolve file using _Dbg_dir 157 typeset -i n=${#_Dbg_dir[@]} 158 typeset -i i 159 for (( i=0 ; i < n; i++ )) ; do 160 typeset basename="${_Dbg_dir[i]}" 161 if [[ $basename == '\$cdir' ]] ; then 162 basename=$_Dbg_cdir 163 elif [[ $basename == '\$cwd' ]] ; then 164 basename=$(pwd) 165 fi 166 try_find_file="$basename/$find_file" 167 if [[ -f "$try_find_file" ]] ; then 168 echo "$try_find_file" 169 return 0 170 fi 171 done 172 fi 173 echo '' 174 return 1 175} 176 177# Read $1 into _Dbg_source_*n* array where *n* is an entry in 178# _Dbg_filenames. Variable _Dbg_source_array_var will be set to 179# _Dbg_source_*n* and filename will be saved in array 180# _Dbg_filenames. fullname is set to the expanded filename 181# 0 is returned if everything went ok. 182function _Dbg_readin { 183 typeset filename 184 if (($# != 0)) ; then 185 filename="$1" 186 else 187 _Dbg_frame_file 188 filename="$_Dbg_frame_filename" 189 fi 190 191 typeset -i line_count=0 192 193 typeset -i next; 194 next=${#_Dbg_filenames[@]} 195 _Dbg_source_array_var="_Dbg_source_${next}" 196 if [[ -n $_Dbg_set_highlight ]] ; then 197 _Dbg_highlight_array_var="_Dbg_highlight_${next}" 198 fi 199 200 typeset filevar 201 typeset source_array 202 typeset -ri NOT_SMALLFILE=1000 203 204 if [[ -z "$filename" ]] || [[ "$filename" == "$_Dbg_bogus_file" ]] ; then 205 eval "${_Dbg_source_array_var}[0]=\"$Dbg_EXECUTION_STRING\"" 206 else 207 fullname=$(_Dbg_resolve_expand_filename "$filename") 208 if [[ -r "$fullname" ]] ; then 209 typeset -r progress_prefix="Reading $filename" 210 _Dbg_file2canonic["$filename"]="$fullname" 211 _Dbg_file2canonic["$fullname"]="$fullname" 212 # Use readarray which speeds up reading greatly. 213 typeset -ri BIGFILE=30000 214 if wc -l < /dev/null >/dev/null 2>&1 ; then 215 line_count=$(wc -l < "${fullname}") 216 if (( line_count >= NOT_SMALLFILE )) ; then 217 _Dbg_msg_nocr "${progress_prefix} " 218 fi 219 fi 220 builtin mapfile -t -O 1 -c $BIGFILE \ 221 -C "_Dbg_progess_show \"${progress_prefix}\" ${line_count}" \ 222 $_Dbg_source_array_var < "$fullname" 223 if [[ -n $_Dbg_set_highlight ]] ; then 224 opts="--bg=${_Dbg_set_highlight}" 225 if [[ -n $_Dbg_set_style ]] ; then 226 opts="--style=${_Dbg_set_style}" 227 fi 228 highlight_cmd="${_Dbg_libdir}/lib/term-highlight.py $opts $fullname" 229 tempfile=$($highlight_cmd 2>/dev/null) 230 if (( 0 == $? )) ; then 231 builtin readarray -t -O 1 -c $BIGFILE \ 232 -C "_Dbg_progess_show \"${progress_prefix}\" ${line_count}" \ 233 $_Dbg_highlight_array_var < "$tempfile" 234 fi 235 [[ -r "$tempfile" ]] && rm "$tempfile" 236 fi 237 (( line_count > BIGFILE)) && _Dbg_progess_done 238 else 239 return 1 240 fi 241 fi 242 243 (( line_count >= NOT_SMALLFILE )) && _Dbg_msg "done." 244 245 # Add $filename to list of all filenames 246 _Dbg_filenames["$fullname"]=$_Dbg_source_array_var; 247 return 0 248} 249 250# Read in file $1 unless it has already been read in. 251# 0 is returned if everything went ok. 252_Dbg_readin_if_new() { 253 (( $# != 1 )) && return 1 254 typeset filename="$1" 255 _Dbg_set_source_array_var "$filename" 256 if [[ -z "$fullname" ]] ; then 257 _Dbg_readin "$filename" 258 typeset rc=$? 259 set +xv 260 (( $? != 0 )) && return $rc 261 [[ -z "$fullname" ]] && return 1 262 _Dbg_set_source_array_var "$filename" || return $? 263 fi 264 return 0 265} 266 267# Set _Dbg_source_array_var to the variable that contains file lines 268# for $1. Variable "fullname" will contain the expanded full filename for $1. 269# 0 is returned if everything went ok. 270_Dbg_set_source_array_var() { 271 (( $# != 1 )) && return 1 272 typeset filename="$1" 273 [[ -z "$filename" ]] && return 2 274 fullname="${_Dbg_file2canonic["$filename"]}" 275 [[ -z "$fullname" ]] && [[ -n "${_Dbg_filenames["$filename"]}" ]] && { 276 fullname="$filename" 277 } 278 [[ -z "$fullname" ]] && return 2 279 _Dbg_source_array_var=${_Dbg_filenames["$fullname"]} 280 if [[ -n $_Dbg_set_highlight ]] ; then 281 _Dbg_highlight_array_var="${_Dbg_source_array_var/_Dbg_source_/_Dbg_highlight_}" 282 fi 283 _Dbg_source_array_var=${_Dbg_filenames["$fullname"]} 284 [[ -z $_Dbg_source_array_var ]] && return 2 285 return 0 286} 287