/usr/share/perl5/Getopt/Complete/Cache.pm is in libgetopt-complete-perl 0.26-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | package Getopt::Complete::Cache;
our $VERSION = $Getopt::Complete::VERSION;
use strict;
use warnings;
our $cache_path;
our $cache_is_stale;
sub import {
my $self = shift;
my %args = @_;
my $class = delete $args{class};
my $file = delete $args{file} if exists $args{file};
my $above = 0; $above = delete $args{above} if exists $args{above};
my $dynamic_caching = 0; $dynamic_caching = delete $args{dynamic_caching} if exists $args{dynamic_caching};
my $comp_cword = $ENV{COMP_CWORD}; $comp_cword = delete $args{comp_cword} if exists $args{comp_cword};
if (%args) {
require Data::Dumper;
die "Unknown params passed to " . __PACKAGE__ .
Data::Dumper::Dumper(\@_);
}
if ($class and $file) {
die __PACKAGE__ . " received both a class and file param: $class, $file!";
}
return unless ($comp_cword);
# doesn't detect
my $module_path;
if ($class) {
($module_path, $cache_path) = $self->module_and_cache_paths_for_package($class, $above);
$cache_path ||= $module_path . '.opts';
}
else {
use FindBin;
$module_path = $FindBin::RealBin . '/' . $FindBin::RealScript;
$cache_path = $file || $module_path . '.opts';
}
# TODO: This does not quite work yet.
if ($dynamic_caching && -e $cache_path) {
my $my_mtime = (stat($module_path))[9];
# if the module has a directory with changes newer than the module,
# use its mtime as the change time
my $module_dir = $module_path;
$module_dir =~ s/.pm$//;
if (-e $module_dir) {
if ((stat($module_dir))[9] > $my_mtime) {
$my_mtime = (stat($module_dir))[9];
}
}
my $cache_mtime = (stat($cache_path))[9];
unless ($cache_mtime >= $my_mtime) {
print STDERR "\nstale completion cache: refreshing $cache_path...\n";
unlink $cache_path;
}
}
$cache_path = $file if ($file);
if ( -f $cache_path && -s $cache_path) {
my $fh;
open($fh, $cache_path);
if ($fh) {
my $src = join('', <$fh>);
require Getopt::Complete;
my $spec = eval $src;
if (@$spec) {
Getopt::Complete->import(@$spec);
}
}
return 1;
}
else {
die "Unable to open file: $cache_path\n";
}
}
sub module_and_cache_paths_for_package {
my $self = shift;
my $class = shift;
my $above = shift;
my $path = $class;
$path =~ s/::/\//g;
$path = '/' . $path . '.pm';
my ($mod_path, $opt_path);
# if above, check cwd upwards for class location
if ($above) {
require Cwd;
$mod_path = Cwd::cwd();
$mod_path =~ s/([^\/])$/$1\//;
until (-e "$mod_path$path" || $mod_path eq '/') {
$mod_path =~ s/[^\/]+\/$//; # remove last folder and try again
}
$mod_path .= $path;
$opt_path = $mod_path . '.opts';
}
# otherwise search perl's path
unless ($mod_path && -e "$mod_path") {
my (@mod_paths) = map { ($_ . $path) } @INC;
($mod_path) = grep { -e $_ } @mod_paths;
}
unless ($opt_path && -e "$opt_path") {
my (@opt_paths) = map { ($_ . $path . '.opts' ) } @INC;
($opt_path) = grep { -e $_ } @opt_paths;
}
return ($mod_path, $opt_path);
}
sub generate {
print STDERR "ending\n";
eval {
print STDERR "evaling $cache_path\n";
unless (-e $cache_path) {
print STDERR "found $cache_path\n";
no warnings;
my $a = $Getopt::Complete::ARGS;
print STDERR "args are $a\n";
use warnings;
if ($a) {
print STDERR ">> got args $a\n";
if (my $o = $a->options) {
print STDERR ">> got opts $o\n";
my $c = $o->{completion_handlers};
my @modules;
if ($c) {
print STDERR ">> got completions $c\n";
my $has_callbacks = 0;
for my $key (keys %$c) {
my $completions = $c->{$key};
if (ref($completions) eq 'SCALAR') {
push @modules, $$completions;
}
elsif(ref($completions) eq 'CODE') {
warn "cannot use cached completions with anonymous callbacks!";
$has_callbacks = 1;
}
}
unless ($has_callbacks) {
my $fh;
open($fh,$cache_path);
if ($fh) {
warn "caching options for $cache_path...\n";
my $src = Data::Dumper::Dumper($c);
#$src =~ s/^\$VAR1/\$${class}::OPTS_SPEC/;
#print STDERR ">> $src\n";
$fh->print($src);
#require Data::Dumper;
#my $src = Data::Dumper::Dumper($c);
}
}
for my $module (@modules) {
print STDERR "trying mod $module\n";
local $ENV{GETOPT_COMPLETE_CACHE} = 1;
eval "use $module";
die $@ if $@;
no strict;
no warnings;
$spec = ${ $class . '::OPTS_SPEC' };
my ($other_module_path,$other_cache_path) = $self->module_and_cache_paths_for_package($module);
$other_cache_path ||= $other_module_path . '.opts';
my $fh;
open($fh,$other_cache_path);
if ($fh) {
warn "caching options for $module at $other_cache_path...\n";
my $src = Data::Dumper::Dumper($c);
$src =~ s/^\$VAR1/\$${class}::OPTS_SPEC/;
#print STDERR ">> $src\n";
$fh->print($src);
#require Data::Dumper;
#my $src = Data::Dumper::Dumper($c);
}
}
}
}
}
}
};
print STDERR ">>>> $@\n" if $@;
}
1;
=pod
=head1 NAME
Getopt::Complete::Cache - cache options next-to the command they apply-to
=head1 VERSION
This document describes Getopt::Complete::Cache 0.26.
=head1 SYNOPSIS
Presuming MyApp.pm isa Command, and "myapp" is an executable like:
use MyApp;
MyApp->execute_with_shell_params_and_exit();
Add this BEFORE using the MyApp module:
use Getopt::Complete::Cache class => 'MyApp';
use MyApp;
MyApp->execute_with_shell_params_and_exit();
Now the shell will look for MyApp.pm.opts during completion
and will never actually load the MyApp.pm module during tab-completion.
The .opts file is autogenerated upon the first attempt to find it.
=head1 DESCRIPTION
This module is for the obscure case in which:
1. the compile time on an executable is sluggish, and we don't want to have sluggish tab-completion
2. the command-line should be cached relative to a given module name
This is most useful with classes implementing the Command.pm API. Since these modules may form a
large command tree, the caching occurs at individual levels in the tree separately.
=cut
|