1*0Sstevel@tonic-gatepackage ExtUtils::Installed; 2*0Sstevel@tonic-gate 3*0Sstevel@tonic-gateuse 5.00503; 4*0Sstevel@tonic-gateuse strict; 5*0Sstevel@tonic-gateuse Carp qw(); 6*0Sstevel@tonic-gateuse ExtUtils::Packlist; 7*0Sstevel@tonic-gateuse ExtUtils::MakeMaker; 8*0Sstevel@tonic-gateuse Config; 9*0Sstevel@tonic-gateuse File::Find; 10*0Sstevel@tonic-gateuse File::Basename; 11*0Sstevel@tonic-gateuse File::Spec; 12*0Sstevel@tonic-gate 13*0Sstevel@tonic-gatemy $Is_VMS = $^O eq 'VMS'; 14*0Sstevel@tonic-gatemy $DOSISH = ($^O =~ /^(MSWin\d\d|os2|dos|mint)$/); 15*0Sstevel@tonic-gate 16*0Sstevel@tonic-gaterequire VMS::Filespec if $Is_VMS; 17*0Sstevel@tonic-gate 18*0Sstevel@tonic-gateuse vars qw($VERSION); 19*0Sstevel@tonic-gate$VERSION = '0.08'; 20*0Sstevel@tonic-gate 21*0Sstevel@tonic-gatesub _is_prefix { 22*0Sstevel@tonic-gate my ($self, $path, $prefix) = @_; 23*0Sstevel@tonic-gate return unless defined $prefix && defined $path; 24*0Sstevel@tonic-gate 25*0Sstevel@tonic-gate if( $Is_VMS ) { 26*0Sstevel@tonic-gate $prefix = VMS::Filespec::unixify($prefix); 27*0Sstevel@tonic-gate $path = VMS::Filespec::unixify($path); 28*0Sstevel@tonic-gate } 29*0Sstevel@tonic-gate 30*0Sstevel@tonic-gate # Sloppy Unix path normalization. 31*0Sstevel@tonic-gate $prefix =~ s{/+}{/}g; 32*0Sstevel@tonic-gate $path =~ s{/+}{/}g; 33*0Sstevel@tonic-gate 34*0Sstevel@tonic-gate return 1 if substr($path, 0, length($prefix)) eq $prefix; 35*0Sstevel@tonic-gate 36*0Sstevel@tonic-gate if ($DOSISH) { 37*0Sstevel@tonic-gate $path =~ s|\\|/|g; 38*0Sstevel@tonic-gate $prefix =~ s|\\|/|g; 39*0Sstevel@tonic-gate return 1 if $path =~ m{^\Q$prefix\E}i; 40*0Sstevel@tonic-gate } 41*0Sstevel@tonic-gate return(0); 42*0Sstevel@tonic-gate} 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gatesub _is_doc { 45*0Sstevel@tonic-gate my ($self, $path) = @_; 46*0Sstevel@tonic-gate my $man1dir = $Config{man1direxp}; 47*0Sstevel@tonic-gate my $man3dir = $Config{man3direxp}; 48*0Sstevel@tonic-gate return(($man1dir && $self->_is_prefix($path, $man1dir)) 49*0Sstevel@tonic-gate || 50*0Sstevel@tonic-gate ($man3dir && $self->_is_prefix($path, $man3dir)) 51*0Sstevel@tonic-gate ? 1 : 0) 52*0Sstevel@tonic-gate} 53*0Sstevel@tonic-gate 54*0Sstevel@tonic-gatesub _is_type { 55*0Sstevel@tonic-gate my ($self, $path, $type) = @_; 56*0Sstevel@tonic-gate return 1 if $type eq "all"; 57*0Sstevel@tonic-gate 58*0Sstevel@tonic-gate return($self->_is_doc($path)) if $type eq "doc"; 59*0Sstevel@tonic-gate 60*0Sstevel@tonic-gate if ($type eq "prog") { 61*0Sstevel@tonic-gate return($self->_is_prefix($path, $Config{prefix} || $Config{prefixexp}) 62*0Sstevel@tonic-gate && 63*0Sstevel@tonic-gate !($self->_is_doc($path)) 64*0Sstevel@tonic-gate ? 1 : 0); 65*0Sstevel@tonic-gate } 66*0Sstevel@tonic-gate return(0); 67*0Sstevel@tonic-gate} 68*0Sstevel@tonic-gate 69*0Sstevel@tonic-gatesub _is_under { 70*0Sstevel@tonic-gate my ($self, $path, @under) = @_; 71*0Sstevel@tonic-gate $under[0] = "" if (! @under); 72*0Sstevel@tonic-gate foreach my $dir (@under) { 73*0Sstevel@tonic-gate return(1) if ($self->_is_prefix($path, $dir)); 74*0Sstevel@tonic-gate } 75*0Sstevel@tonic-gate 76*0Sstevel@tonic-gate return(0); 77*0Sstevel@tonic-gate} 78*0Sstevel@tonic-gate 79*0Sstevel@tonic-gatesub new { 80*0Sstevel@tonic-gate my ($class) = @_; 81*0Sstevel@tonic-gate $class = ref($class) || $class; 82*0Sstevel@tonic-gate my $self = {}; 83*0Sstevel@tonic-gate 84*0Sstevel@tonic-gate my $archlib = $Config{archlibexp}; 85*0Sstevel@tonic-gate my $sitearch = $Config{sitearchexp}; 86*0Sstevel@tonic-gate 87*0Sstevel@tonic-gate # File::Find does not know how to deal with VMS filepaths. 88*0Sstevel@tonic-gate if( $Is_VMS ) { 89*0Sstevel@tonic-gate $archlib = VMS::Filespec::unixify($archlib); 90*0Sstevel@tonic-gate $sitearch = VMS::Filespec::unixify($sitearch); 91*0Sstevel@tonic-gate } 92*0Sstevel@tonic-gate 93*0Sstevel@tonic-gate if ($DOSISH) { 94*0Sstevel@tonic-gate $archlib =~ s|\\|/|g; 95*0Sstevel@tonic-gate $sitearch =~ s|\\|/|g; 96*0Sstevel@tonic-gate } 97*0Sstevel@tonic-gate 98*0Sstevel@tonic-gate # Read the core packlist 99*0Sstevel@tonic-gate $self->{Perl}{packlist} = 100*0Sstevel@tonic-gate ExtUtils::Packlist->new( File::Spec->catfile($archlib, '.packlist') ); 101*0Sstevel@tonic-gate $self->{Perl}{version} = $Config{version}; 102*0Sstevel@tonic-gate 103*0Sstevel@tonic-gate # Read the module packlists 104*0Sstevel@tonic-gate my $sub = sub { 105*0Sstevel@tonic-gate # Only process module .packlists 106*0Sstevel@tonic-gate return if $_ ne ".packlist" || $File::Find::dir eq $archlib; 107*0Sstevel@tonic-gate 108*0Sstevel@tonic-gate # Hack of the leading bits of the paths & convert to a module name 109*0Sstevel@tonic-gate my $module = $File::Find::name; 110*0Sstevel@tonic-gate 111*0Sstevel@tonic-gate $module =~ s!\Q$archlib\E/?auto/(.*)/.packlist!$1!s or 112*0Sstevel@tonic-gate $module =~ s!\Q$sitearch\E/?auto/(.*)/.packlist!$1!s; 113*0Sstevel@tonic-gate my $modfile = "$module.pm"; 114*0Sstevel@tonic-gate $module =~ s!/!::!g; 115*0Sstevel@tonic-gate 116*0Sstevel@tonic-gate # Find the top-level module file in @INC 117*0Sstevel@tonic-gate $self->{$module}{version} = ''; 118*0Sstevel@tonic-gate foreach my $dir (@INC) { 119*0Sstevel@tonic-gate my $p = File::Spec->catfile($dir, $modfile); 120*0Sstevel@tonic-gate if (-r $p) { 121*0Sstevel@tonic-gate $module = _module_name($p, $module) if $Is_VMS; 122*0Sstevel@tonic-gate 123*0Sstevel@tonic-gate require ExtUtils::MM; 124*0Sstevel@tonic-gate $self->{$module}{version} = MM->parse_version($p); 125*0Sstevel@tonic-gate last; 126*0Sstevel@tonic-gate } 127*0Sstevel@tonic-gate } 128*0Sstevel@tonic-gate 129*0Sstevel@tonic-gate # Read the .packlist 130*0Sstevel@tonic-gate $self->{$module}{packlist} = 131*0Sstevel@tonic-gate ExtUtils::Packlist->new($File::Find::name); 132*0Sstevel@tonic-gate }; 133*0Sstevel@tonic-gate 134*0Sstevel@tonic-gate my(@dirs) = grep { -e } ($archlib, $sitearch); 135*0Sstevel@tonic-gate find($sub, @dirs) if @dirs; 136*0Sstevel@tonic-gate 137*0Sstevel@tonic-gate return(bless($self, $class)); 138*0Sstevel@tonic-gate} 139*0Sstevel@tonic-gate 140*0Sstevel@tonic-gate# VMS's non-case preserving file-system means the package name can't 141*0Sstevel@tonic-gate# be reconstructed from the filename. 142*0Sstevel@tonic-gatesub _module_name { 143*0Sstevel@tonic-gate my($file, $orig_module) = @_; 144*0Sstevel@tonic-gate 145*0Sstevel@tonic-gate my $module = ''; 146*0Sstevel@tonic-gate if (open PACKFH, $file) { 147*0Sstevel@tonic-gate while (<PACKFH>) { 148*0Sstevel@tonic-gate if (/package\s+(\S+)\s*;/) { 149*0Sstevel@tonic-gate my $pack = $1; 150*0Sstevel@tonic-gate # Make a sanity check, that lower case $module 151*0Sstevel@tonic-gate # is identical to lowercase $pack before 152*0Sstevel@tonic-gate # accepting it 153*0Sstevel@tonic-gate if (lc($pack) eq lc($orig_module)) { 154*0Sstevel@tonic-gate $module = $pack; 155*0Sstevel@tonic-gate last; 156*0Sstevel@tonic-gate } 157*0Sstevel@tonic-gate } 158*0Sstevel@tonic-gate } 159*0Sstevel@tonic-gate close PACKFH; 160*0Sstevel@tonic-gate } 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gate print STDERR "Couldn't figure out the package name for $file\n" 163*0Sstevel@tonic-gate unless $module; 164*0Sstevel@tonic-gate 165*0Sstevel@tonic-gate return $module; 166*0Sstevel@tonic-gate} 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate 169*0Sstevel@tonic-gate 170*0Sstevel@tonic-gatesub modules { 171*0Sstevel@tonic-gate my ($self) = @_; 172*0Sstevel@tonic-gate 173*0Sstevel@tonic-gate # Bug/feature of sort in scalar context requires this. 174*0Sstevel@tonic-gate return wantarray ? sort keys %$self : keys %$self; 175*0Sstevel@tonic-gate} 176*0Sstevel@tonic-gate 177*0Sstevel@tonic-gatesub files { 178*0Sstevel@tonic-gate my ($self, $module, $type, @under) = @_; 179*0Sstevel@tonic-gate 180*0Sstevel@tonic-gate # Validate arguments 181*0Sstevel@tonic-gate Carp::croak("$module is not installed") if (! exists($self->{$module})); 182*0Sstevel@tonic-gate $type = "all" if (! defined($type)); 183*0Sstevel@tonic-gate Carp::croak('type must be "all", "prog" or "doc"') 184*0Sstevel@tonic-gate if ($type ne "all" && $type ne "prog" && $type ne "doc"); 185*0Sstevel@tonic-gate 186*0Sstevel@tonic-gate my (@files); 187*0Sstevel@tonic-gate foreach my $file (keys(%{$self->{$module}{packlist}})) { 188*0Sstevel@tonic-gate push(@files, $file) 189*0Sstevel@tonic-gate if ($self->_is_type($file, $type) && 190*0Sstevel@tonic-gate $self->_is_under($file, @under)); 191*0Sstevel@tonic-gate } 192*0Sstevel@tonic-gate return(@files); 193*0Sstevel@tonic-gate} 194*0Sstevel@tonic-gate 195*0Sstevel@tonic-gatesub directories { 196*0Sstevel@tonic-gate my ($self, $module, $type, @under) = @_; 197*0Sstevel@tonic-gate my (%dirs); 198*0Sstevel@tonic-gate foreach my $file ($self->files($module, $type, @under)) { 199*0Sstevel@tonic-gate $dirs{dirname($file)}++; 200*0Sstevel@tonic-gate } 201*0Sstevel@tonic-gate return sort keys %dirs; 202*0Sstevel@tonic-gate} 203*0Sstevel@tonic-gate 204*0Sstevel@tonic-gatesub directory_tree { 205*0Sstevel@tonic-gate my ($self, $module, $type, @under) = @_; 206*0Sstevel@tonic-gate my (%dirs); 207*0Sstevel@tonic-gate foreach my $dir ($self->directories($module, $type, @under)) { 208*0Sstevel@tonic-gate $dirs{$dir}++; 209*0Sstevel@tonic-gate my ($last) = (""); 210*0Sstevel@tonic-gate while ($last ne $dir) { 211*0Sstevel@tonic-gate $last = $dir; 212*0Sstevel@tonic-gate $dir = dirname($dir); 213*0Sstevel@tonic-gate last if !$self->_is_under($dir, @under); 214*0Sstevel@tonic-gate $dirs{$dir}++; 215*0Sstevel@tonic-gate } 216*0Sstevel@tonic-gate } 217*0Sstevel@tonic-gate return(sort(keys(%dirs))); 218*0Sstevel@tonic-gate} 219*0Sstevel@tonic-gate 220*0Sstevel@tonic-gatesub validate { 221*0Sstevel@tonic-gate my ($self, $module, $remove) = @_; 222*0Sstevel@tonic-gate Carp::croak("$module is not installed") if (! exists($self->{$module})); 223*0Sstevel@tonic-gate return($self->{$module}{packlist}->validate($remove)); 224*0Sstevel@tonic-gate} 225*0Sstevel@tonic-gate 226*0Sstevel@tonic-gatesub packlist { 227*0Sstevel@tonic-gate my ($self, $module) = @_; 228*0Sstevel@tonic-gate Carp::croak("$module is not installed") if (! exists($self->{$module})); 229*0Sstevel@tonic-gate return($self->{$module}{packlist}); 230*0Sstevel@tonic-gate} 231*0Sstevel@tonic-gate 232*0Sstevel@tonic-gatesub version { 233*0Sstevel@tonic-gate my ($self, $module) = @_; 234*0Sstevel@tonic-gate Carp::croak("$module is not installed") if (! exists($self->{$module})); 235*0Sstevel@tonic-gate return($self->{$module}{version}); 236*0Sstevel@tonic-gate} 237*0Sstevel@tonic-gate 238*0Sstevel@tonic-gate 239*0Sstevel@tonic-gate1; 240*0Sstevel@tonic-gate 241*0Sstevel@tonic-gate__END__ 242*0Sstevel@tonic-gate 243*0Sstevel@tonic-gate=head1 NAME 244*0Sstevel@tonic-gate 245*0Sstevel@tonic-gateExtUtils::Installed - Inventory management of installed modules 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gate=head1 SYNOPSIS 248*0Sstevel@tonic-gate 249*0Sstevel@tonic-gate use ExtUtils::Installed; 250*0Sstevel@tonic-gate my ($inst) = ExtUtils::Installed->new(); 251*0Sstevel@tonic-gate my (@modules) = $inst->modules(); 252*0Sstevel@tonic-gate my (@missing) = $inst->validate("DBI"); 253*0Sstevel@tonic-gate my $all_files = $inst->files("DBI"); 254*0Sstevel@tonic-gate my $files_below_usr_local = $inst->files("DBI", "all", "/usr/local"); 255*0Sstevel@tonic-gate my $all_dirs = $inst->directories("DBI"); 256*0Sstevel@tonic-gate my $dirs_below_usr_local = $inst->directory_tree("DBI", "prog"); 257*0Sstevel@tonic-gate my $packlist = $inst->packlist("DBI"); 258*0Sstevel@tonic-gate 259*0Sstevel@tonic-gate=head1 DESCRIPTION 260*0Sstevel@tonic-gate 261*0Sstevel@tonic-gateExtUtils::Installed provides a standard way to find out what core and module 262*0Sstevel@tonic-gatefiles have been installed. It uses the information stored in .packlist files 263*0Sstevel@tonic-gatecreated during installation to provide this information. In addition it 264*0Sstevel@tonic-gateprovides facilities to classify the installed files and to extract directory 265*0Sstevel@tonic-gateinformation from the .packlist files. 266*0Sstevel@tonic-gate 267*0Sstevel@tonic-gate=head1 USAGE 268*0Sstevel@tonic-gate 269*0Sstevel@tonic-gateThe new() function searches for all the installed .packlists on the system, and 270*0Sstevel@tonic-gatestores their contents. The .packlists can be queried with the functions 271*0Sstevel@tonic-gatedescribed below. 272*0Sstevel@tonic-gate 273*0Sstevel@tonic-gate=head1 FUNCTIONS 274*0Sstevel@tonic-gate 275*0Sstevel@tonic-gate=over 4 276*0Sstevel@tonic-gate 277*0Sstevel@tonic-gate=item new() 278*0Sstevel@tonic-gate 279*0Sstevel@tonic-gateThis takes no parameters, and searches for all the installed .packlists on the 280*0Sstevel@tonic-gatesystem. The packlists are read using the ExtUtils::packlist module. 281*0Sstevel@tonic-gate 282*0Sstevel@tonic-gate=item modules() 283*0Sstevel@tonic-gate 284*0Sstevel@tonic-gateThis returns a list of the names of all the installed modules. The perl 'core' 285*0Sstevel@tonic-gateis given the special name 'Perl'. 286*0Sstevel@tonic-gate 287*0Sstevel@tonic-gate=item files() 288*0Sstevel@tonic-gate 289*0Sstevel@tonic-gateThis takes one mandatory parameter, the name of a module. It returns a list of 290*0Sstevel@tonic-gateall the filenames from the package. To obtain a list of core perl files, use 291*0Sstevel@tonic-gatethe module name 'Perl'. Additional parameters are allowed. The first is one 292*0Sstevel@tonic-gateof the strings "prog", "doc" or "all", to select either just program files, 293*0Sstevel@tonic-gatejust manual files or all files. The remaining parameters are a list of 294*0Sstevel@tonic-gatedirectories. The filenames returned will be restricted to those under the 295*0Sstevel@tonic-gatespecified directories. 296*0Sstevel@tonic-gate 297*0Sstevel@tonic-gate=item directories() 298*0Sstevel@tonic-gate 299*0Sstevel@tonic-gateThis takes one mandatory parameter, the name of a module. It returns a list of 300*0Sstevel@tonic-gateall the directories from the package. Additional parameters are allowed. The 301*0Sstevel@tonic-gatefirst is one of the strings "prog", "doc" or "all", to select either just 302*0Sstevel@tonic-gateprogram directories, just manual directories or all directories. The remaining 303*0Sstevel@tonic-gateparameters are a list of directories. The directories returned will be 304*0Sstevel@tonic-gaterestricted to those under the specified directories. This method returns only 305*0Sstevel@tonic-gatethe leaf directories that contain files from the specified module. 306*0Sstevel@tonic-gate 307*0Sstevel@tonic-gate=item directory_tree() 308*0Sstevel@tonic-gate 309*0Sstevel@tonic-gateThis is identical in operation to directories(), except that it includes all the 310*0Sstevel@tonic-gateintermediate directories back up to the specified directories. 311*0Sstevel@tonic-gate 312*0Sstevel@tonic-gate=item validate() 313*0Sstevel@tonic-gate 314*0Sstevel@tonic-gateThis takes one mandatory parameter, the name of a module. It checks that all 315*0Sstevel@tonic-gatethe files listed in the modules .packlist actually exist, and returns a list of 316*0Sstevel@tonic-gateany missing files. If an optional second argument which evaluates to true is 317*0Sstevel@tonic-gategiven any missing files will be removed from the .packlist 318*0Sstevel@tonic-gate 319*0Sstevel@tonic-gate=item packlist() 320*0Sstevel@tonic-gate 321*0Sstevel@tonic-gateThis returns the ExtUtils::Packlist object for the specified module. 322*0Sstevel@tonic-gate 323*0Sstevel@tonic-gate=item version() 324*0Sstevel@tonic-gate 325*0Sstevel@tonic-gateThis returns the version number for the specified module. 326*0Sstevel@tonic-gate 327*0Sstevel@tonic-gate=back 328*0Sstevel@tonic-gate 329*0Sstevel@tonic-gate=head1 EXAMPLE 330*0Sstevel@tonic-gate 331*0Sstevel@tonic-gateSee the example in L<ExtUtils::Packlist>. 332*0Sstevel@tonic-gate 333*0Sstevel@tonic-gate=head1 AUTHOR 334*0Sstevel@tonic-gate 335*0Sstevel@tonic-gateAlan Burlison <Alan.Burlison@uk.sun.com> 336*0Sstevel@tonic-gate 337*0Sstevel@tonic-gate=cut 338