# -*- shell-script -*- # filecache.sh - cache file information # # Copyright (C) 2008-2011, 2013-2015 Rocky Bernstein # # # 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 setglobal _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 proc _Dbg_filecache_reset { setglobal _Dbg_filenames = ''() setglobal _Dbg_fileinfo = ''() setglobal _Dbg_file2canonic = ''() } _Dbg_filecache_reset # Check that line $2 is not greater than the number of lines in # file $1 proc _Dbg_check_line { sh-expr ' $# != 2 ' && return 1 typeset -i line_number=$1 typeset filename="$2" typeset -i max_line setglobal max_line = $[_Dbg_get_maxline $filename] if sh-expr ' $? != 0 ' { _Dbg_errmsg "internal error getting number of lines in $filename" return 1 } if sh-expr ' line_number > max_line ' { sh-expr ' _Dbg_set_basename ' && setglobal filename = $(filename##*/) _Dbg_errmsg "Line $line_number is too large." \ "File $filename has only $max_line lines." return 1 } return 0 } # Error message for file not read in proc _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. proc _Dbg_get_maxline { sh-expr ' $# != 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 sh-expr '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. proc _Dbg_get_source_line { typeset -i lineno if sh-expr ' $# == 0 ' { setglobal lineno = $_Dbg_frame_last_lineno } else { setglobal lineno = $1 shift } typeset filename if sh-expr ' $# == 0 ' { setglobal filename = $_Dbg_frame_last_filename } else { setglobal filename = $1 } _Dbg_readin_if_new $filename if [[ -n $_Dbg_set_highlight ]] && [[ -n $_Dbg_highlight_array_var ]] { eval "typeset -i count=\${#$_Dbg_highlight_array_var[@]}" if sh-expr ' count ' { eval "_Dbg_source_line=\${$_Dbg_highlight_array_var[lineno]}" } else { eval "_Dbg_source_line=\${$_Dbg_source_array_var[$lineno]}" } } else { eval "_Dbg_source_line=\${$_Dbg_source_array_var[$lineno]}" } } # _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. proc _Dbg_is_file { if sh-expr ' $# == 0 ' { _Dbg_errmsg "Internal debug error _Dbg_is_file(): null file to find" echo '' return 1 } typeset find_file="$1" typeset try_find_file if [[ -z $find_file ]] { _Dbg_errmsg "Internal debug error _Dbg_is_file(): file argument null" echo '' return 1 } if [[ ${find_file:0:1} == '/' ]] { # Absolute file name setglobal try_find_file = $[_Dbg_expand_filename $find_file] if [[ -n ${_Dbg_filenames[$try_find_file]} ]] { echo $try_find_file return 0 } } elif [[ ${find_file:0:1} == '.' ]] { # Relative file name setglobal try_find_file = $[_Dbg_expand_filename "$(_Dbg_init_cwd)/$find_file] # FIXME: turn into common subroutine if [[ -n ${_Dbg_filenames[$try_find_file]} ]] { echo $try_find_file return 0 } } 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 } 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. proc _Dbg_readin { typeset filename if sh-expr '$# != 0' { setglobal filename = $1 } else { _Dbg_frame_file setglobal filename = $_Dbg_frame_filename } typeset -i line_count=0 typeset -i next; setglobal next = $(#_Dbg_filenames[@]) setglobal _Dbg_source_array_var = ""_Dbg_source_$(next)"" if [[ -n $_Dbg_set_highlight ]] { setglobal _Dbg_highlight_array_var = ""_Dbg_highlight_$(next)"" } typeset filevar typeset source_array typeset -ri NOT_SMALLFILE=1000 if [[ -z $filename ]] || [[ $filename == "$_Dbg_bogus_file" ]] { eval "$(_Dbg_source_array_var)[0]=\"$Dbg_EXECUTION_STRING\"" } else { setglobal fullname = $[_Dbg_resolve_expand_filename $filename] if [[ -r $fullname ]] { typeset -r progress_prefix="Reading $filename" compat array-assign _Dbg_file2canonic '$filename' $fullname compat array-assign _Dbg_file2canonic '$fullname' $fullname # Use readarray which speeds up reading greatly. typeset -ri BIGFILE=30000 if wc -l < /dev/null >/dev/null !2 > !1 { setglobal line_count = $[wc -l < $(fullname)] if sh-expr ' line_count >= NOT_SMALLFILE ' { _Dbg_msg_nocr "$(progress_prefix) " } } builtin readarray -t -O 1 -c $BIGFILE \ -C "_Dbg_progess_show \"$(progress_prefix)\" $(line_count)" \ $_Dbg_source_array_var < $fullname if [[ -n $_Dbg_set_highlight ]] { setglobal opts = ""--bg=$(_Dbg_set_highlight)"" if [[ -n $_Dbg_set_style ]] { setglobal opts = ""--style=$(_Dbg_set_style)"" } setglobal highlight_cmd = ""$(_Dbg_libdir)/lib/term-highlight.py $opts $fullname"" setglobal tempfile = $[$highlight_cmd !2 >/dev/null] if sh-expr ' 0 == $? ' { builtin readarray -t -O 1 -c $BIGFILE \ -C "_Dbg_progess_show \"$(progress_prefix)\" $(line_count)" \ $_Dbg_highlight_array_var < $tempfile } [[ -r $tempfile ]] && rm $tempfile } sh-expr ' line_count > BIGFILE' && _Dbg_progess_done } else { return 1 } } typeset -r line_count_cmd="line_count=\${#$(_Dbg_source_array_var[@])}" eval $line_count_cmd sh-expr ' line_count >= NOT_SMALLFILE ' && _Dbg_msg "done." # Add $filename to list of all filenames compat array-assign _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. proc _Dbg_readin_if_new { sh-expr ' $# != 1 ' && return 1 typeset filename="$1" _Dbg_set_source_array_var $filename if [[ -z "$fullname" ]] { _Dbg_readin $filename typeset rc=$Status set +xv sh-expr ' $? != 0 ' && return $rc [[ -z $fullname ]] && return 1 _Dbg_set_source_array_var $filename || return $? } 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. proc _Dbg_set_source_array_var { sh-expr ' $# != 1 ' && return 1 typeset filename="$1" [[ -z $filename ]] && return 2 setglobal fullname = $(_Dbg_file2canonic[$filename]) [[ -z $fullname ]] && [[ -n ${_Dbg_filenames[$filename]} ]] && do { setglobal fullname = $filename } [[ -z $fullname ]] && return 2 setglobal _Dbg_source_array_var = $(_Dbg_filenames[$fullname]) if [[ -n $_Dbg_set_highlight ]] { setglobal _Dbg_highlight_array_var = $(_Dbg_source_array_var/_Dbg_source_/_Dbg_highlight_) } setglobal _Dbg_source_array_var = $(_Dbg_filenames[$fullname]) [[ -z $_Dbg_source_array_var ]] && return 2 return 0 }