#!/usr/bin/env perl # # Copyright (C) 2011, 2012 Rocky Bernstein # # use Digest::SHA; use Scalar::Util; use version; $VERSION = '0.2'; package DB; # FIXME: Figure out where to put this # *pod # # I => I # # Evaluate I<$code> and return true if there's no error. # *cut sub eval_ok ($) { my $code = shift; no strict; no warnings; $DB::namespace_package = 'package main' unless $DB::namespace_package; my $wrapped = "$DB::namespace_package; sub { $code }"; eval $wrapped; # print $@, "\n" if $@; return !$@; } package DB::LineCache; =pod =head1 NAME DB::LineCache DB::LineCache - package to read and cache lines of a Perl program. =head1 SYNOPSIS The LineCache package allows one to get any line from any file, caching lines of the file on first access to the file. Although the file may be any file, the common use is when the file is a Perl script since parsing of the file is done to figure out where the statement boundaries are. The routines here may be is useful when a small random sets of lines are read from a single file, in particular in a debugger to show source lines. use DB::LineCache; $lines = DB::LineCache::getlines('/tmp/myperl.pl') # The following lines have same effect as the above. unshift @INC, '/tmp'; $lines = DB::LineCache::getlines('myperl.pl'); shift @INC; chdir '/tmp'; $lines = DB::LineCache::getlines('myperl.pl') $line = DB::LineCache::getline('/tmp/myperl.pl', 6) # Note lines[6] == line (if /tmp/myperl.pl has 6 lines) DB::LineCache::clear_file_cache DB::LineCache::update_cache # Check for modifications of all cached files. =cut use English qw( -no_match_vars ); use vars qw(%file_cache %script_cache); use strict; use warnings; no warnings 'once'; no warnings 'redefine'; use Cwd 'abs_path'; use File::Basename; use File::Spec; use File::stat; use rlib '../..'; ## FIXME:: Make conditional use Devel::Trepan::DB::Colors; my $perl_formatter = Devel::Trepan::DB::Colors::setup(); ## struct(stat => '$', lines => '%', path => '$', sha1 => '$'); # The file cache. The key is a name as would be given by Perl for # __FILE__. The value is a LineCacheInfo object. # Maps a string filename (a String) to a key in %file_cache (a # String). # # One important use of %file2file_remap is mapping the a full path # of a file into the name stored in %file_cache or given by Perl's # __FILE__. Applications such as those that get input from users, # may want canonicalize a file name before looking it up. This map # gives a way to do that. # # Another related use is when a template system is used. Here we'll # probably want to remap not only the file name but also line # ranges. Will probably use this for that, but I'm not sure. my %file2file_remap; my %file2file_remap_lines; my %script2file; my @tempfiles; =pod =head1 SUBROUTINES I in what follows we use I<$file_or_script> to refer to either a filename which generally should be a Perl file, or a psuedo-file name that is created in an I string. Often, the filename does not have to be fully qualified. In some cases I<@INC> will be used to find the file. =cut sub remove_temps() { for my $filename (values %script2file) { unlink($filename) if -f $filename; } for my $filename (@tempfiles) { unlink($filename) if -f $filename; } } END { $DB::ready = 0; remove_temps }; =pod =head2 clear_file_cache B B)> Clear the file cache of I<$filename>. If I<$filename> is not given, clear all files in the cache. =cut sub clear_file_cache(;$) { if (scalar @_ == 1) { my $filename = shift; if ($file_cache{$filename}) { delete $file_cache{$filename}; } } else { %file_cache = {}; %file2file_remap = {}; %file2file_remap_lines = {}; } } =pod =head2 clear_file_format_cache B Remove syntax-formatted lines in the cache. Use this when you change the L colors and want to redo how files may have previously been syntax marked. =cut sub clear_file_format_cache() { while (my ($fname, $cache_info) = each %file_cache) { while (my($format, $lines) = each %{$cache_info->{lines_href}}) { next if 'plain' eq $format; my $ref = $file_cache{$fname}; $ref->{lines_href}->{$format} = undef; } } } =pod =head2 clear_script_cache B Clear the script cache entirely. =cut sub clear_script_cache() { %script_cache = {}; } =pod =head2 cached_files B => I Return an array of cached file names =cut sub cached_files() { keys %file_cache; } =pod =head2 checkcache B => I B [, $opts])> => I Discard cache entries that are out of date. If I<$filename>is I, all entries in the file cache are checked. If we did not previously have I information about a file, it will be added. Return a list of invalidated filenames. I is returned if a filename was given but not found cached. =cut sub checkcache(;$$) { my ($filename, $opts) = @_; $opts = {} unless defined $opts; my $use_perl_d_file = $opts->{use_perl_d_file}; my @filenames; if (defined $filename) { @filenames = keys %file_cache; } elsif (exists $file_cache{$filename}) { @filenames = ($filename); } else { return undef; } my @result = (); for my $filename (@filenames) { next unless exists $file_cache{$filename}; my $path = $file_cache{$filename}{path}; if (-f $path) { my $cache_info = $file_cache{$filename}{stat}; my $stat = File::stat::stat($path); if ($cache_info) { if ($stat && ($cache_info->{size} != $stat->size or $cache_info->{mtime} != $stat->mtime)) { push @result, $filename; update_cache($filename, $opts); } } } else { push @result, $filename; update_cache($filename, $opts); } } return @result; } =pod =head2 cache_script B [, I<$opts>]) > => I