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