File Coverage

blib/lib/HTML/Perlinfo/Modules.pm
Criterion Covered Total %
statement 30 282 10.6
branch 0 148 0.0
condition 0 68 0.0
subroutine 10 23 43.4
pod 1 12 8.3
total 41 533 7.6


" if $count++ % 5 == 0; " if (($count >= 5 && $count % 5 == 0)||($count >= @{$color_specs}));
line stmt bran cond sub pod time code
1             package HTML::Perlinfo::Modules;
2              
3 1     1   4 use strict;
  1         1  
  1         45  
4 1     1   5 use warnings;
  1         1  
  1         412  
5 1     1   4 use File::Find;
  1         1  
  1         51  
6 1     1   4 use File::Spec;
  1         1  
  1         14  
7 1     1   3 use Carp ();
  1         0  
  1         14  
8 1     1   3 use Config qw(%Config);
  1         0  
  1         28  
9 1     1   6 use base qw(HTML::Perlinfo::Base);
  1         1  
  1         373  
10 1     1   5 use CGI qw(escapeHTML);
  1         1  
  1         7  
11 1     1   99 use HTML::Perlinfo::Common;
  1         1  
  1         729  
12              
13             our $VERSION = '1.17';
14              
15              
16             sub new {
17              
18 0     0 0   my ($class, %params) = @_;
19 0 0         $params{'title'} = exists $params{'title'} ? $params{'title'} : 'Perl Modules';
20              
21 0           $class->SUPER::new(%params);
22              
23             }
24              
25              
26             sub module_color_check {
27              
28 0     0 0   my ($module_name, $color_specs) = @_;
29 0 0 0       if (defined $color_specs && ref($color_specs->[0]) eq 'ARRAY') {
30 0           foreach (@{ $color_specs }) {
  0            
31 0 0         return $_->[0] if (match_string($module_name,$_->[1])==1);
32             }
33             }
34             else {
35 0 0 0       return $color_specs->[0] if (defined $color_specs && match_string($module_name,$color_specs->[1])==1);
36             }
37 0           return 0;
38             }
39              
40             # get_modinfo
41             # This sub was created for the files_in option.
42             # Returns found_mod reference
43             ######################################
44              
45             sub get_files_in {
46              
47 0     0 0   my ($file_path) = @_;
48            
49 0 0         return 0 unless $file_path =~ m/\.pm$/;
50 0           my $mod_info = module_info($file_path, undef);
51 0           return $mod_info;
52              
53             }
54              
55              
56             sub sort_modules {
57              
58 0     0 0   my ($modules, $sort_by) = @_;
59 0           my @sorted_modules;
60              
61 0 0         if ($sort_by eq 'name') {
    0          
62 0           foreach my $key (sort {lc $a cmp lc $b} keys %$modules) {
  0            
63             # Check for duplicate modules
64 0 0         if (ref($modules->{$key}) eq 'ARRAY') {
65 0           foreach (@{ $modules->{$key} }) {
  0            
66 0           push @sorted_modules, $_;
67             }
68             }
69             else {
70 0           push @sorted_modules, $modules->{$key};
71             }
72             }
73             }
74             elsif ($sort_by eq 'version') {
75 0           foreach my $key (keys %$modules) {
76 0 0         if (ref($modules->{$key}) eq 'ARRAY') {
77 0           @{ $modules->{$key} } = sort {$a->{'version'} cmp $b->{'version'}}@{ $modules->{$key} };
  0            
  0            
  0            
78 0           for (@{ $modules->{$key}}) {
  0            
79 0           push @sorted_modules, $_;
80             }
81             }
82             else {
83 0           push @sorted_modules, $modules->{$key};
84             }
85             }
86 0           @sorted_modules = sort {$a->{'version'} cmp $b->{'version'}}@sorted_modules;
  0            
87             }
88 0           return @sorted_modules;
89             }
90              
91             sub html_setup {
92            
93 0     0 0   my ($self, $columns, $color_specs, $section, $full_page) = @_;
94            
95 0           my $html;
96              
97 0 0         $html .= $self->print_htmlhead if $full_page;
98              
99 0           my %show_columns = (
100             'name' => 'Module name',
101             'version' => 'Version',
102             'path' => 'Location',
103             'core' => 'Core',
104             'desc' => 'Description'
105             );
106              
107 0 0         $html .= $section ? print_section($section) : '';
108 0 0 0       $html .= print_color_codes($color_specs) if $color_specs && $color_specs->[2];
109 0           $html .= print_table_start();
110 0           $html .= print_table_header(scalar @$columns, map{ $show_columns{$_} }@$columns);
  0            
111 0           return $html;
112             }
113              
114             sub module_info {
115 0     0 0   my ($module_path, $show_only) = @_;
116              
117 0           ( $module_path ) = $module_path =~ /^(.*)$/;
118            
119 0           my ($mod_name, $mod_version, $mod_desc);
120            
121 1     1   6 no warnings 'all'; # silence warnings
  1         1  
  1         2209  
122 0 0         open(MOD, $module_path) or return 0;
123 0           while () {
124            
125 0 0         unless ($mod_name) {
126 0 0         if (/^ *package +(\S+);/) {
127 0           $mod_name = $1;
128             }
129             }
130            
131 0 0         unless ($mod_version) {
132            
133 0 0         if (/([\$*])(([\w\:\']*)\bVERSION)\b.*\=/) {
134            
135 0           my $line = substr $_, index($_, $1);
136 0           my $eval = qq{
137             package HTML::Perlinfo::_version;
138             no strict;
139              
140             local $1$2;
141             \$$2=undef; do {
142             $line
143             }; \$$2
144             };
145            
146 0           ( $eval ) = $eval =~ /^(.*)$/sm;
147 0           $mod_version = eval($eval);
148             # Again let us be nice here.
149 0 0 0       $mod_version = 'unknown' if (not defined $mod_version) || ($@);
150 0           $mod_version =~ s/^\s+|\s+$//;
151             }
152             }
153              
154 0 0         unless ($mod_desc) {
155 0 0         if (/=head\d\s+NAME/) {
156 0           local $/ = '';
157 0           local $_;
158 0           chomp($_ = );
159 0           ($mod_desc) = /^.*?-+\s*(.*?)$/ism;
160             }
161             }
162            
163 0 0 0       last if $mod_name && $mod_version && $mod_desc;
      0        
164            
165             }
166            
167 0           close (MOD);
168 0 0 0       return 0 if (! $mod_name || $show_only && ref $show_only && (match_string($mod_name, $show_only) == 0));
      0        
      0        
169 0 0 0       $mod_version = 'unknown' if !($mod_version) || ($mod_version !~ /^[\.\d+_]+$/);
170 0 0         $mod_desc = escapeHTML($mod_desc) if $mod_desc;
171 0 0         $mod_desc = "No description found" unless $mod_desc;
172 0           return { 'name' => $mod_name, 'version' => $mod_version, 'desc' => $mod_desc };
173             }
174              
175             sub print_color_codes {
176 0     0 0   my $color_specs = shift;
177 0           my ($html, $label);
178 0           $html .= print_table_start();
179 0           $html .= print_table_header(1, "Module Color Codes");
180 0           $html .= print_table_color_start();
181              
182 0 0         if (ref($color_specs->[0]) eq 'ARRAY') {
183 0           my $count = 0;
184 0           foreach (@{ $color_specs }) {
  0            
185 0 0         $html .= "
186 0   0       $label = $_->[2] || $_->[1];
187 0           $html .= print_color_box($_->[0], $label);
188 0 0 0       $html .= "
  0   0        
189             }
190             }
191             else {
192 0   0       $label = $color_specs->[2] || $color_specs->[1];
193 0           $html .= print_color_box($color_specs->[0], $label);
194             }
195              
196 0           $html .= print_table_color_end();
197 0           $html .= print_table_end();
198 0           return $html;
199             }
200              
201             sub print_module_results {
202              
203 0     0 0   my ($mod_dir, $mod_count, $from, $overall_total, $show_dir) = @_;
204              
205 0           my ($html, $total_amount, $searched, @mod_dir, @bad_dir, %seen);
206            
207 0 0         if ($show_dir) {
208              
209 0           $html .= print_table_start();
210 0           $html .= print_table_header(2, "Directory", "Number of Modules");
211 0           for my $dir (keys %{$mod_count}) {
  0            
212 0           my $amount_found = $mod_count->{$dir};
213 0 0         push (@mod_dir, $dir) if $amount_found;
214             }
215            
216 0           for my $dir1 (@mod_dir) {
217 0           for my $dir2 (@mod_dir) {
218 0 0 0       if ($dir1 ne $dir2 && $dir2 =~ /^$dir1/) {
219 0           push @bad_dir, $dir2;
220             }
221             }
222             }
223 0           for my $top_dir (@mod_dir) {
224 0 0         unless (grep{$_ eq $top_dir }@bad_dir) {
  0            
225 0           $html .= print_table_row(2, add_link('local', File::Spec->canonpath($top_dir)), $mod_count->{$top_dir});
226             }
227             }
228 0           $html .= print_table_end();
229             }
230             else {
231             # Print out directories not in @INC
232 0 0 0       @mod_dir = grep { -d $_ && -r $_ && !$seen{$_}++ } map {File::Spec->canonpath($_)}@INC;
  0            
  0            
233 0           my @module_paths = grep { not exists $seen{$_} }@$mod_dir;
  0            
234              
235 0 0         if (@module_paths >= 1) {
236 0           $html .= print_table_start();
237 0           $html .= print_table_header(3, "Directory", "Searched", "Number of Modules");
238              
239 0           for my $dir (map{ File::Spec->canonpath($_) }@module_paths) {
  0            
240 0 0         $searched = (grep { $_ eq $dir } @$mod_dir) ? "yes" : "no";
  0            
241 0 0         my $amount_found = ($searched eq 'yes') ? $mod_count->{$dir} : 'unknown';
242 0           $html .= print_table_row(3, add_link('local', File::Spec->canonpath($dir)), $searched, $amount_found);
243             }
244 0           $html .= print_table_end();
245             }
246            
247            
248 0           $html .= print_table_start();
249 0           $html .= print_table_header(3, "Include path (INC) directories", "Searched", "Number of Modules");
250 0           for my $dir (@mod_dir) {
251 0 0         $searched = exists $mod_count->{$dir} ? 'yes' : 'no';
252 0 0         my $amount_found = ($searched eq 'yes') ? $mod_count->{$dir} : 'unknown';
253 0           $html .= print_table_row(3, add_link('local', File::Spec->canonpath($dir)), $searched, $amount_found);
254             }
255              
256 0           $html .= print_table_end();
257             }
258            
259 0           $html .= print_table_start();
260             #my $view = ($from eq 'all') ? 'installed' :
261             # ($from eq 'core') ? 'core' : 'found';
262              
263 0           $html .= print_table_row(2, "Total modules", $overall_total);
264 0           $html .= print_table_end();
265              
266 0           return $html;
267              
268             }
269              
270             sub search_dir {
271              
272 0     0 0   my ($from, $show_only, $core_dir1, $core_dir2) = @_;
273              
274            
275 0           my %seen = ();
276            
277 0 0 0       my @user_dir = (ref($from) eq 'ARRAY') && $show_only ne 'core' ? @{$from} :
  0 0          
278             ($show_only eq 'core') ? ($core_dir1, $core_dir2) : $from;
279              
280             # Make sure only unique entries and readable directories in @mod_dir
281 0 0 0       my @mod_dir = grep { -d $_ && -r $_ && !$seen{$_}++ } map {File::Spec->canonpath($_)}@user_dir;
  0            
  0            
282 0 0         if (@mod_dir != @user_dir) {
283              
284             # Looks like there might have been a problem with the directories given to us.
285             # Or maybe not. @user_dir could have duplicate values and that's ok.
286             # But let's still warn about any unreadable or non-directories given
287              
288 0           my @debug;
289 0           %seen = ();
290 0           @user_dir = grep { !$seen{$_}++ } map {File::Spec->canonpath($_)}@user_dir;
  0            
  0            
291 0 0         if (@user_dir > @mod_dir) {
292             #%seen = map {$_ => undef} @mod_dir;
293 0           %seen = ();
294 0           @seen{@mod_dir} = ();
295 0           my @difference = grep { !$seen{$_}++ }@user_dir;
  0            
296 0           foreach my $element (@difference) {
297 0 0         if (! -d $element) {
    0          
298 0 0         if ( grep {$_ eq $element} map {File::Spec->canonpath($_)}@INC) {
  0            
  0            
299 0           warn "$element is in the Perl include path, but is not a directory";
300             }
301             else {
302 0           warn "$element is not a directory";
303             }
304 0           push @debug, $element;
305             }
306             elsif (! -r $element) {
307 0 0         if ( grep {$_ eq $element} map {File::Spec->canonpath($_)}@INC) {
  0            
  0            
308 0           warn "$element is in the Perl include path, but is not readable";
309             }
310             else {
311 0           warn "$element is not a readable directory";
312             }
313              
314 0           push @debug, $element;
315             }
316             }
317             }
318             }
319              
320 0 0         error_msg("Search directories are invalid") unless @mod_dir >= 1;
321              
322 0           return @mod_dir;
323             }
324              
325             sub get_input {
326              
327 0     0 0   my $self = shift;
328 0           my $args = process_args(@_, \&check_module_args);
329 0           my %input = ();
330 0   0       $input{'files_in'} = $args->{'files_in'} || undef;
331 0   0       $input{'sort_by'} = $args->{'sort_by'} || 'name';
332 0   0       $input{'from'} = $args->{'from'} || \@INC;
333 0   0       $input{'show_only'} = $args->{'show_only'} || "";
334 0           $input{'color_specs'} = $args->{'color'};
335 0           $input{'link'} = $args->{'link'};
336 0 0         $input{'section'} = exists $args->{'section'} ? $args->{'section'} :
    0          
337             $input{'show_only'} eq 'core' ? 'Core Perl modules installed' : '';
338 0 0         $input{'full_page'} = exists $args->{'full_page'} ? $args->{'full_page'} : $self->{'full_page'};
339 0 0         $input{'show_inc'} = exists $args->{'show_inc'} ? $args->{'show_inc'} : 1;
340 0 0         $input{'show_dir'} = exists $args->{'show_dir'} ? $args->{'show_dir'} : 0;
341 0 0         $input{'columns'} = exists $args->{'columns'} ? $args->{'columns'} : ['name','version','desc'];
342 0           return %input;
343             }
344              
345             sub print_modules {
346            
347 0     0 1   my %input = get_input(@_);
348            
349 0           my ($found_mod, $mod_count, $overall_total, @mod_dir, $core_dir1, $core_dir2);
350            
351             # Check to see if a search is even needed
352 0 0         if (defined $input{'files_in'}) {
353            
354 0           my @files = @{ $input{'files_in'} };
  0            
355 0           my %found_mod = ();
356            
357 0           foreach my $file_path (@files) {
358            
359 0           my $mod_info = get_files_in($file_path);
360 0 0         next unless (ref $mod_info eq 'HASH');
361 0           $found_mod{$mod_info->{'name'}} = $mod_info;
362             }
363 0 0         return undef unless (keys %found_mod > 0);
364 0           $found_mod = \%found_mod;
365             }
366             else {
367            
368             # Get ready to search
369 0           $core_dir1 = File::Spec->canonpath($Config{installarchlib});
370 0           $core_dir2 = File::Spec->canonpath($Config{installprivlib});
371            
372 0           @mod_dir = search_dir($input{'from'}, $input{'show_only'}, $core_dir1, $core_dir2);
373              
374 0           ($overall_total, $found_mod, $mod_count) = find_modules($input{'show_only'}, \@mod_dir);
375            
376 0 0         return undef unless $overall_total;
377              
378             }
379            
380 0           my @sorted_modules = sort_modules($found_mod, $input{'sort_by'});
381            
382 0           my $html .= html_setup( $_[0],
383             $input{'columns'},
384             $input{'color_specs'},
385             $input{'section'},
386             $input{'full_page'}
387             );
388              
389 0           my $numberof_columns = scalar @{$input{'columns'}};
  0            
390            
391 0           foreach my $module (@sorted_modules) {
392            
393             $html .= print_table_row_color( $numberof_columns,
394             module_color_check($module->{'name'}, $input{'color_specs'}),
395             map{
396 0 0         if ($_ eq 'name') {
  0 0          
    0          
397 0           add_link('cpan', $module->{'name'}, $input{'link'});
398             }
399             elsif ($_ eq 'core') {
400 0 0         (grep File::Spec->rel2abs($module->{'path'}) =~ /\Q$_/, ($core_dir1, $core_dir2)) ? 'yes' : 'no';
401             }
402             elsif ($_ eq 'path') {
403 0           add_link('local', $module->{'path'});
404             }
405             else {
406 0           $module->{$_};
407             }
408            
409 0           } @{$input{'columns'}} );
410             }
411            
412 0           $html .= print_table_end();
413            
414 0 0 0       unless (defined $input{'files_in'} && ref $input{'files_in'} eq 'ARRAY') {
415 0 0         $html .= print_module_results( \@mod_dir,
416             $mod_count,
417             $input{'from'},
418             $overall_total,
419             $input{'show_dir'}) if $input{'show_inc'};
420             }
421            
422 0 0         $html .= "" if $input{'full_page'};
423            
424 0 0         defined wantarray ? return $html : print $html;
425            
426             }
427              
428             sub find_modules {
429              
430 0     0 0   my ($show_only, $mod_dir) = @_;
431              
432 0           my ($overall_total, $module, $base, $start_dir, $new_val, $mod_info);
433             # arrays
434 0           my (@modinfo_array, @mod_dir);
435             # hashes
436 0           my ( %path, %inc_path, %mod_count, %found_mod);
437 0           @mod_dir = @$mod_dir;
438            
439 0           @path{@mod_dir} = ();
440 0           @inc_path{@INC} = ();
441 0           for $base (@mod_dir) {
442            
443             find ({ wanted => sub {
444 0     0     for (@INC, @mod_dir) {
445 0 0         if (index($File::Find::name, $_) == 0) {
446             # lets record it unless we already have hit the dir
447 0 0         $mod_count{$_} = 0 unless exists $mod_count{$_};
448             }
449             }
450             # This prevents mod_dir dirs from being searched again when you have a dir within a dir
451 0 0 0       $File::Find::prune = 1, return if exists $path{$File::Find::name} && $File::Find::name ne $File::Find::topdir;
452              
453             # make sure we are dealing with a module
454 0 0         return unless $File::Find::name =~ m/\.pm$/;
455 0           $mod_info = module_info($File::Find::name, $show_only);
456 0 0         return unless ref ($mod_info) eq 'HASH';
457              
458             # update the counts.
459 0           for (@INC, grep{not exists $inc_path{$_}}@mod_dir) {
  0            
460 0 0         if (index($File::Find::name, $_) == 0) {
461 0           $mod_count{$_}++;
462             }
463             }
464 0           $overall_total++;
465              
466 0           $mod_info->{'path'} = File::Spec->canonpath($File::Find::dir);
467             # Check for duplicate modules
468 0 0         if (exists $found_mod{$mod_info->{'name'}}) {
469 0 0         @modinfo_array = ref( $found_mod{$mod_info->{'name'}} ) eq 'ARRAY' ? @{$found_mod{$mod_info->{'name'}}} : $found_mod{$mod_info->{'name'}};
  0            
470 0           push @modinfo_array, $mod_info;
471 0           $new_val = [@modinfo_array];
472 0           $found_mod{$mod_info->{'name'}} = $new_val;
473             }
474             else {
475 0           $found_mod{$mod_info->{'name'}} = $mod_info;
476             }
477            
478 0           },untaint => 1, untaint_pattern => qr|^([-+@\s\S\w./]+)$|}, $base);
479             } # end of for loop
480              
481 0           return ($overall_total, \%found_mod, \%mod_count);
482              
483             }
484              
485             1;
486             __END__