File Coverage

blib/lib/Tree/PseudoIncLib.pm
Criterion Covered Total %
statement 665 728 91.3
branch 174 254 68.5
condition 26 51 50.9
subroutine 50 50 100.0
pod 20 20 100.0
total 935 1103 84.7


'; '} @hdr_list; '."\n"; '; '; '; '; '; # first mandatory tag '; ' '; '; '; '; '; '; '; '; '; ';
line stmt bran cond sub pod time code
1             package Tree::PseudoIncLib;
2              
3             # This package is developed primarily as a part of Apache::App::PerlLibTree web application.
4             # It encapsulates the object of description of perl library defined by @INC array.
5             # Internal description of the library exists in the form of internal array of hashes.
6             # It can be exported as either XML or DHTML files.
7             # A reference to the internal description can be exported too.
8              
9             # Description instance can be created "from scratch" only,
10              
11             # Logging information:
12             # --------------------
13             # I use full-scale Log::Log4perl. Log configurattion file is storied in data directory.
14              
15 3     3   2761 use 5.006;
  3         10  
  3         122  
16 3     3   14 use strict;
  3         5  
  3         82  
17 3     3   15 use warnings;
  3         4  
  3         78  
18 3     3   2694 use File::Listing;
  3         31852  
  3         196  
19 3     3   35 use File::Basename;
  3         7  
  3         350  
20 3     3   2995 use File::chdir;
  3         12910  
  3         402  
21 3     3   3007 use POSIX qw(strftime);
  3         23562  
  3         25  
22 3     3   3681 use Cwd;
  3         8  
  3         225  
23 3     3   3298 use UNIVERSAL qw(isa);
  3         40  
  3         23  
24 3     3   5898 use Log::Log4perl;
  3         202552  
  3         22  
25              
26 3     3   152 use vars qw($VERSION);
  3         5  
  3         197  
27             $VERSION = "0.05";
28              
29 3     3   15 use constant APPLICATION_DIRECTORY => '/app/pltree/'; # URL mask from the Apache Document_Root
  3         6  
  3         147  
30 3     3   16 use constant TREE_ID_DEFAULT => 'Default_Tree';
  3         6  
  3         121  
31 3     3   18 use constant LIB_INDEX_PREFIX => 'lib'; # default prefix for root library name
  3         5  
  3         114  
32 3     3   17 use constant MIN_LIMIT_NODES => 15; # min value for max_nodes setting validation
  3         5  
  3         136  
33 3     3   14 use constant LIMIT_NODES => 15000;# default for max_nodes
  3         5  
  3         114  
34 3     3   14 use constant RPM_TYPE => 'RPM';# default type of packaging system, debian for instance is different
  3         7  
  3         128  
35 3     3   13 use constant NO_RPM_OWNER => undef; # '-' is not that convenient...
  3         5  
  3         122  
36 3     3   14 use constant SKIP_EMPTY_DIR_DEFAULT => 1; # true
  3         5  
  3         2689  
37 3     3   183 use constant SKIP_MODE_DEFAULT => 0; # false
  3         6  
  3         1506  
38 3     3   15 use constant SKIP_OWNER_DEFAULT => 0; # false
  3         6  
  3         1179  
39 3     3   553 use constant SKIP_GROUP_DEFAULT => 0; # false
  3         46  
  3         25463  
40              
41             sub new { # class/instance constructor, ready for sub-classing
42 9     9 1 42706 my $proto = shift;
43 9   33     114 my $class = ref($proto) || $proto;
44 9         29 my $self = {};
45 9         56 bless ($self, $class);
46              
47             # instance identification should include:
48 9         52 $self->{tree_id} = TREE_ID_DEFAULT; # to display user-friendly
49 9         31 $self->{application_directory} = APPLICATION_DIRECTORY; # default
50              
51 9         22 $self->{max_nodes} = LIMIT_NODES; # to decrement foreach documented node
52 9         23 $self->{skip_empty_dir} = SKIP_EMPTY_DIR_DEFAULT;
53 9         23 $self->{skip_mode} = SKIP_MODE_DEFAULT;
54 9         20 $self->{skip_owner} = SKIP_OWNER_DEFAULT;
55 9         27 $self->{skip_group} = SKIP_GROUP_DEFAULT;
56              
57 9         31 $self->{descript} = undef; # a reference to the array of hashes finally...
58              
59             # all simple keys have to be defined in order to be restorable from DBI when necessary...
60 9         49 $self->{descript_internal_start_time} = undef;
61 9         24 $self->{descript_internal_finish_time} = undef;
62 9         25 $self->{descript_start_time_text} = undef;
63 9         18 $self->{descript_finish_time_text} = undef;
64              
65 9         27 $self->{rpm_type} = RPM_TYPE;
66 9         24 $self->{rpm_active} = 1; # TRUE might be for known RPM types only...
67 9         24 $self->{lib_index_prefix} = LIB_INDEX_PREFIX; # default for internal names
68              
69             # default @INC comes from my old development machine:
70 9         63 $self->{p_INC} = [
71             '/usr/lib/perl5/5.6.1/i386-linux',
72             '/usr/lib/perl5/5.6.1',
73             '/usr/lib/perl5/site_perl/5.6.1/i386-linux',
74             '/usr/lib/perl5/site_perl/5.6.1',
75             '/usr/lib/perl5/site_perl/5.6.0',
76             '/usr/lib/perl5/site_perl',
77             '/usr/lib/perl5/vendor_perl/5.6.1/i386-linux',
78             '/usr/lib/perl5/vendor_perl/5.6.1',
79             '/usr/lib/perl5/vendor_perl'
80             ];
81              
82             # default array of allowed for keeping files:
83 9         155 $self->{allow_files} = [
84             { mask => '.pm$', icon => 'file.gif',
85             name_on_click_action => 'source',
86             icon_on_click_action => 'pod2html',
87             name_mouse_over_prompt => 'source',
88             icon_mouse_over_prompt => 'documentation',},
89             { mask => '.pod$', icon => 'file_note.gif',
90             name_on_click_action => 'source',
91             icon_on_click_action => 'pod2html',
92             name_mouse_over_prompt => 'source',
93             icon_mouse_over_prompt => 'document',},
94             { mask => '.html$', icon => 'file_html.gif',
95             name_on_click_action => 'source',
96             icon_on_click_action => 'source',
97             name_mouse_over_prompt => 'no prompt',
98             icon_mouse_over_prompt => 'no prompt',},
99             { mask => '.htm$', icon => 'htm_file.jpg',
100             name_on_click_action => 'source',
101             icon_on_click_action => 'source',
102             name_mouse_over_prompt => 'no prompt',
103             icon_mouse_over_prompt => 'no prompt',},
104             ];
105              
106 9         134 $self->{plog} = Log::Log4perl->get_logger(); # __PACKAGE__ might be featured in log
107              
108             # optional parameters:
109 9         1371 my $parm = { @_ }; # a reference to the hash
110 9 50       37 if ( $parm ) {
111 9 50       51 $self->application_directory ($parm->{application_directory}) if defined $parm->{application_directory};
112 9 50       39 $self->tree_id ($parm->{tree_id}) if defined $parm->{tree_id};
113 9 100       61 $self->max_nodes ($parm->{max_nodes}) if defined $parm->{max_nodes};
114 9 100       81 $self->pseudo_INC ($parm->{p_INC}) if defined $parm->{p_INC};
115 9 100       58 $self->skip_empty_dir ($parm->{skip_empty_dir}) if defined $parm->{skip_empty_dir};
116 9 50       35 $self->skip_mode ($parm->{skip_mode}) if defined $parm->{skip_mode};
117 9 50       24 $self->skip_owner ($parm->{skip_owner}) if defined $parm->{skip_owner};
118 9 50       26 $self->skip_group ($parm->{skip_group}) if defined $parm->{skip_group};
119 9 50       39 $self->allow_files ($parm->{allow_files}) if defined $parm->{allow_files};
120              
121             # a group of RPM settings is not quite independent:
122 9 50       34 $self->rpm_type ($parm->{rpm_type}) if defined $parm->{rpm_type}; # even empty
123 9 50       46 $self->rpm_active ($parm->{rpm_active}) if defined $parm->{rpm_active}; # overwrite
124             }
125              
126             # log if/what necessary:
127 9         19 my $incoming_parameters = join("\n\t",map{$_.' => '.$parm->{$_}}(sort keys %{$parm}));
  21         79  
  9         52  
128 9 100       47 $incoming_parameters = "\n\t".$incoming_parameters if $incoming_parameters;
129 9         42 my $message = "( $incoming_parameters ); an instance of $class is created.\n";
130 9         233 $self->{plog}->debug($message.$self->status_as_string);
131 9         102 return $self;
132             }
133              
134             sub status_as_string { # internal data
135 10     10 1 20 my $self = shift;
136 10         19 my $simple_key_data = 'Internals:'."\n";
137             # I got tied over here to fight with
138             # map { $simple_key_data .= "\t".$_.' => '.eval{$self->{$_}}."\n" } @{$self->list_simple_keys};
139             # that complained about the
140             # Use of uninitialized value in concatenation (.) or string at
141             # blib/lib/Apache/App/ModPerlLibTree/AppLib/OneLibInitialDescription.pm line 141.
142 10         14 foreach (@{$self->list_simple_keys}){
  10         46  
143 150 100       254 if (!defined $self->{$_}){
144 50         94 $simple_key_data .= "\t".$_.' => undef'."\n";
145             } else {
146 100         248 $simple_key_data .= "\t".$_.' => '.$self->{$_}."\n";
147             }
148             }
149 10         37 my $current_inc = 'Pseudo-@INC:'."\n";
150 10         19 map { $current_inc .= "\t".$_."\n" } @{ $self->{p_INC} };
  31         95  
  10         24  
151 10         19 my $curr_allow = 'Allowed for Storage Files:';
152 40         53 map {my $i=$_; $curr_allow.= "\n\tmask => $_->{mask}\t".join "\t",
  10         22  
153 10 100       16 map {if($_ eq 'mask'){} else {"$_ => $i->{$_}"}} sort keys %$_ } @{$self->{allow_files}};
  40         190  
  240         367  
  200         583  
154 10         151 return $simple_key_data.$current_inc.$curr_allow;
155             }
156              
157             sub allow_files {
158 2     2 1 4 my $self = shift;
159 2         3 my $p_r = shift; # a reference to a new version of array of hashes
160 2 100       8 if ($p_r) {
161 1 50       8 unless (isa($p_r, 'ARRAY')){
162 0         0 $self->{plog}->error("($p_r); parameter must be a reference to ARRAY\n");
163 0         0 return undef;
164             }
165 1         2 $self->{allow_files} = $p_r;
166 1         12 my $message = "($p_r); internal reference is updated.\n";
167 1         3 my $curr_allow = 'Allowed for Storage Files:';
168 1         3 map {my $i=$_; $curr_allow.= "\n\tmask => $_->{mask}\t".join "\t",
  1         3  
169 1 100       8 map {if($_ eq 'mask'){} else {"$_ => $i->{$_}"}} sort keys %$_ } @{$self->{allow_files}};
  1         10  
  2         12  
  1         5  
170 1         7 $self->{plog}->debug($message.$curr_allow);
171             }
172 2         18 return $self->{allow_files};
173             }
174              
175             sub application_directory {
176 2     2 1 4 my $self = shift;
177 2         4 my $pr = shift;
178 2 100       7 if ($pr) {
179 1         4 $self->{application_directory} = $pr;
180 1         8 $self->{plog}->debug("($pr); value is updated\n");
181             }
182 2         16 return $self->{application_directory};
183             }
184              
185             sub tree_id {
186 2     2 1 2056 my $self = shift;
187 2         3 my $pr = shift; # a new value for ID
188 2 100       9 if ($pr) {
189 1         2 $self->{tree_id} = $pr;
190 1         8 $self->{plog}->debug("($pr); value is updated\n");
191             }
192 2         14 return $self->{tree_id};
193             }
194              
195             sub pseudo_INC {
196 10     10 1 1730 my $self = shift;
197 10         24 my $p_r = shift; # a reference to a new version of pseudo_INC array
198 10 100       30 if ($p_r) {
199 9 50       65 unless (isa($p_r, 'ARRAY')){
200 0         0 $self->{plog}->error("($p_r); parameter must be a reference to ARRAY\n");
201 0         0 return undef;
202             }
203 9         20 $self->{p_INC} = $p_r;
204 9         30 my $current_inc = 'Pseudo-@INC:'."\n";
205 9         13 map { $current_inc .= "\t".$_."\n" } @{ $self->{p_INC} };
  14         45  
  9         105  
206 9         55 $self->{plog}->debug("($p_r); internal reference is updated. $current_inc");
207             }
208 10         86 return $self->{p_INC};
209             }
210              
211             sub rpm_type { # one optional parameter:
212 4     4 1 509 my $self = shift;
213 4         7 my $val = shift;
214 4 100       17 if ( defined $val ){ # might be empty string
215 3         5 $self->{rpm_type} = $val;
216 3         21 $self->{plog}->debug("($val); rpm_type is changed to $self->{rpm_type}\n");
217 3 100       30 unless ($self->{rpm_type}){
218 1         2 $self->{rpm_active} = 0;
219 1         5 $self->{plog}->debug('rpm_type disables the rpm_active'."\n");
220             }
221             }
222 4         23 return $self->{rpm_type};
223             }
224              
225             sub rpm_active { # one optional parameter:
226 5     5 1 9 my $self = shift;
227 5         7 my $val = shift;
228 5 100       23 if ( defined $val ){ # might be 0
229 3 100 66     15 if ( !$self->{rpm_type} && $val ){ # error
230 1         9 $self->{plog}->error("($val); unable to set up rpm_active for unknown rpm_type\n");
231 1         422 $self->{rpm_active} = 0;
232 1         6 return 0;
233             }
234 2         3 $self->{rpm_active} = $val;
235 2         11 $self->{plog}->debug("($val); rpm_active is changed to $self->{rpm_active}\n");
236             }
237 4         48 return $self->{rpm_active};
238             }
239              
240             sub skip_empty_dir { # one optional parameter:
241 8     8 1 16 my $self = shift;
242 8         13 my $val = shift;
243 8 100       30 if ( defined $val ){ # might be 0
244 7         18 $self->{skip_empty_dir} = $val;
245 7         41 $self->{plog}->debug("( $val ); skip_empty_dir is changed to $self->{skip_empty_dir}\n");
246             }
247 8         66 return $self->{skip_empty_dir};
248             }
249              
250             sub skip_mode { # one optional parameter:
251 3     3 1 5 my $self = shift;
252 3         3 my $val = shift;
253 3 100       9 if ( defined $val ){ # might be 0
254 2         3 $self->{skip_mode} = $val;
255 2         14 $self->{plog}->debug("( $val ); skip_mode is changed to $self->{skip_mode}\n");
256             }
257 3         25 return $self->{skip_mode};
258             }
259              
260             sub skip_owner { # one optional parameter:
261 3     3 1 6 my $self = shift;
262 3         15 my $val = shift;
263 3 100       8 if ( defined $val ){ # might be 0
264 2         5 $self->{skip_owner} = $val;
265 2         13 $self->{plog}->debug("($val); skip_owner is changed to $self->{skip_owner}\n");
266             }
267 3         29 return $self->{skip_owner};
268             }
269              
270             sub skip_group { # one optional parameter:
271 3     3 1 6 my $self = shift;
272 3         5 my $val = shift;
273 3 100       10 if ( defined $val ){ # might be 0
274 2         4 $self->{skip_group} = $val;
275 2         16 $self->{plog}->debug("($val); skip_group is changed to $self->{skip_group}\n");
276             }
277 3         31 return $self->{skip_group};
278             }
279              
280             sub max_nodes { # one optional parameter:
281 11     11 1 1226 my $self = shift;
282 11         18 my $val = shift;
283             # max_nodes value has to be integer > 1 if defined
284 11 100       31 if ( $val ){
285 10 100       41 if ( $val < MIN_LIMIT_NODES ){ # error?
286 2         25 $self->{plog}->warn("($val); must be not less than ".MIN_LIMIT_NODES."\n");
287 2         1193 $val = MIN_LIMIT_NODES;
288             }
289 10         27 $self->{max_nodes} = $val;
290 10         80 $self->{plog}->debug("($val); max_nodes is changed to $self->{max_nodes}\n");
291             }
292 11         104 return $self->{max_nodes};
293             }
294              
295             sub _dir_description {
296             # This member-function is used by from_scratch member function in order to
297             # create a primary description of so-called 'root directory' using recursion.
298             # The result is a pretty complicated structure of arrays and hashes.
299             # Primarily, it is an array of hashes, where some keys might reference another (child)
300             # arrays of hashes, and so on... Upon success _dir_description returns a reference to the array of hashes.
301              
302             # Every file/directory/symlink is described with the hash using the following set of keys:
303             #
304             # {type} - can be 'd', 'l', or 'f' (stand for 'directory', 'link', or 'file');
305             # {inode} - associated with the item;
306             # {permissions_octal_text} - like '0755'
307             # {size} - in bytes
308             # {owner} - name of the owner;
309             # {group} - name of the group;
310             # {level} - depth in the tree (since 1 for the names listed in @INC);
311             # {name} - local name of the file/link/directory (inside the parent directory);
312             # {full_name} - absolute path-and-name like /full/path/to/the/file
313             # {pseudo_cpan_name} - makes sense for the .pm file only; indeed is generated recursively;
314             # {last_mod_time_text} - date/time of last modification in format "%B %d, %Y at %H:%M"
315             # {parent_index} - unique name of the parent node/object;
316             # {self_index} - unique name for the self node/object;
317             # {child_dir_list} - a reference to the array of children descriptions;
318             # {rpm_package_name} - for real files only;
319             # {allow_index} - for real files only;
320              
321             # all children in every array are sorted by the name alphabetically.
322              
323             # Input hash keys:
324             #
325             # {root_dir} - absolute name of the directory to explore (the trailing slash / might be skipped);
326             # {pseudo_cpan_root_name} - estimation of the CPAN name for root_dir;
327             # {parent_index} - unique object name for the root_dir;
328             # {parent_depth_level} - depth level of root_dir inside the result tree;
329             # {prior_libs} - a reference to the array of prior library names those should not be repeated again;
330             # {inc_lib} - name of current library in @INC;
331             # {allow_masks} - a reference to the array of masks for allow-files
332 38     38   204 my $self = shift;
333             # and input parameters:
334 38         517 my $params = { @_ }; # a reference to the hash
335             # real parameters of the call are important for debug:
336 38         145 my $message = '(';
337 38         85 my $incoming_parameters = join("\n\t",map{$_.' => '.$params->{$_}}(sort keys %{$params}));
  266         1437  
  38         767  
338 38 50       323 $incoming_parameters = "\n\t".$incoming_parameters if $incoming_parameters;
339 38         146 $message .= $incoming_parameters.'); started'."\n";
340 38         266 $self->{plog}->debug($message);
341              
342             # incoming data validation...
343 38 50 33     878 unless ( defined $params->{root_dir} && $params->{root_dir} ){
344 0         0 $self->{plog}->error('no incoming root_dir'."\n");
345 0         0 return undef;
346             }
347 38         98 my $dir_path = $params->{root_dir};
348 38 50       126 unless ( defined $params->{pseudo_cpan_root_name} ){
349 0         0 $self->{plog}->error('undefined incoming pseudo_cpan_root_name'."\n");
350 0         0 return undef;
351             }
352 38         93 my $pseudo_cpan_root_name = $params->{pseudo_cpan_root_name};
353 38 50 33     423 unless ( defined $params->{parent_index} && $params->{parent_index} ){
354 0         0 $self->{plog}->error('no incoming parent_index'."\n");
355 0         0 return undef;
356             }
357 38         73 my $parent_index = $params->{parent_index}; # unique part of parent js object
358 38 50       142 unless ( defined $params->{parent_depth_level} ){
359 0         0 $self->{plog}->error('undefined incoming parent_depth_level'."\n");
360 0         0 return undef;
361             }
362 38         115 my $depth_level = $params->{parent_depth_level} + 1;
363 38 50 33     683 unless ( defined $params->{prior_libs} && $params->{prior_libs} ){
364 0         0 $self->{plog}->error('no incoming prior_libs'."\n");
365 0         0 return undef;
366             }
367 38         87 my $prior_libs = $params->{prior_libs};
368 38 50       205 unless (isa($prior_libs, 'ARRAY')){
369 0         0 $self->{plog}->error('prior_libs must be a reference to ARRAY'."\n");
370 0         0 return undef;
371             }
372 38 50 33     554 unless ( defined $params->{inc_lib} && $params->{inc_lib} ){
373 0         0 $self->{plog}->error('no incoming inc_lib'."\n");
374 0         0 return undef;
375             }
376 38         161 my $inc_lib = $params->{inc_lib};
377 38 50 33     442 unless ( defined $params->{allow_masks} && $params->{allow_masks} ){
378 0         0 $self->{plog}->error('no incoming allow_masks'."\n");
379 0         0 return undef;
380             }
381 38         75 my $allow_masks = $params->{allow_masks};
382 38 50       496 unless (isa($allow_masks, 'ARRAY')){
383 0         0 $self->{plog}->error('allow_masks must be a reference to ARRAY'."\n");
384 0         0 return undef;
385             }
386              
387             # check for repeatition:
388 38         69 foreach ( @{$prior_libs} ){
  38         160  
389 20 50       109 if ( $_ eq $dir_path ) {
390             # this should not be considered an error or abnormal anyway...
391 0         0 $self->{plog}->debug('skipping the repeatition of '.$_."\n");
392 0         0 return undef;
393             }
394             }
395             # make sure the $dir_path is referencing the directory:
396 38 50       420 $dir_path .= '/' unless $dir_path =~ /\/$/;
397              
398 38         115 my $common_array = []; # to store the result
399 38         74 my $internal_index = 0;
400 38         484105 for (parse_dir(`ls -l $dir_path`)) {
401 83         35415 $internal_index += 1; # nodes in one diredtory ???
402              
403 83         364 my $row = {}; # hash to store the description of one file/sub-directory
404              
405 83         668 $row->{parent_index} = $parent_index;
406 83         1004 $row->{inc_lib} = $inc_lib; # the same for all levels
407 83         642 $row->{level} = $depth_level;
408              
409             # rule to create {self_index} in string form:
410 83         631 my $self_index = $parent_index.'_'.$internal_index;
411 83         260 $row->{self_index} = $self_index;
412              
413 83         471 my ($name, $type, $size, $m_mtime, $m_mode) = @$_;
414             # on this stage the $size is undefined for sub-directory...
415 83         393 $row->{name} = $name;
416              
417             # It was a warning over here: Use of uninitialized value in join or string at
418             # /usr/lib/perl5/site_perl/5.6.1/Apache/App/ModPerlLibTree.pm line 175.
419             # for the initial operator:
420             # my $pseudo_cpan_name = join ('::', $pseudo_cpan_root_name, $name);
421             #
422             # I made this working:
423 83         272 my $pseudo_cpan_name = $pseudo_cpan_root_name;
424 83 100       241 if ( $pseudo_cpan_root_name ) {
425 12         80 $pseudo_cpan_name .= '::'.$name;
426             } else {
427 71         507 $pseudo_cpan_name = $name;
428             }
429 83         391 $row->{pseudo_cpan_name} = $pseudo_cpan_name;
430              
431 83         372 $row->{type} = $type;
432              
433 83         7964 my $now_string = strftime "%B %d, %Y at %H:%M", localtime ($m_mtime);
434 83         614 $row->{last_mod_time_text} = $now_string;
435              
436 83 50       410 unless ($self->{skip_mode}){
437 83         474 my $permissions = sprintf "%04o", $m_mode & 07777;
438 83         408 $row->{permissions_octal_text} = $permissions;
439             }
440              
441 83         280 my $full_file_name = $dir_path.$name;
442 83         190 $row->{full_name} = $full_file_name;
443              
444             # retrieve the rest of details from the stat:
445 83         4382 my ( $dev, # device number of filesystem
446             $ino, # inode number
447             $mode, # file mode (type and permissions)
448             $nlink, # number of (hard) links to the file
449             $uid, # numeric user ID of file's owner
450             $gid, # numeric group ID of file's owner
451             $rdev, # the device identifier (special files only)
452             $size_2, # total size of file, in bytes
453             $atime, # last access time in seconds since the epoch
454             $mtime, # last modify time in seconds since the epoch
455             $ctime, # inode change time (NOT creation time!) in seconds since the epoch
456             $blksize, # preferred block size for file system I/O
457             $blocks # actual number of blocks allocated
458             ) = stat ($full_file_name);
459              
460             # on this stage the sub-directory has some (fictive in my understanding) size...
461 83         409 $row->{size} = $size_2;
462 83         385 $row->{inode} = $ino;
463 83 50       10937 $row->{owner} = getpwuid($uid) unless $self->{skip_owner};
464 83 50       8403 $row->{group} = getgrgid($gid) unless $self->{skip_group};
465              
466 83 100       567 if ($type eq 'd') {
    100          
467             # one directory might have multiple rpm-owners like:
468             # [slava@PBC110 slava]$ rpm -qf /usr/lib/perl5/5.6.1/i386-linux
469             # perl-5.6.1-34.99.6
470             # perl-DBI-1.21-1
471             # perl-DBD-Pg-1.01-8
472             # perl-DBD-MySQL-1.2219-6
473             # I care about the rpm-owners of particular files only:
474              
475             # recursion into the sub-directory:
476              
477 25         381 my $child = $self->_dir_description (
478             root_dir => $full_file_name,
479             prior_libs => $prior_libs,
480             pseudo_cpan_root_name => $pseudo_cpan_name,
481             parent_index => $self_index,
482             inc_lib => $inc_lib,
483             parent_depth_level => $depth_level,
484             allow_masks => $allow_masks );
485              
486 25 100 50     538 if ( $child && scalar(@{$child}) ){ # successfully created
  25 100       240  
487              
488 12         70 $row->{child_dir_list} = $child; # a reference to the array
489             # of child's description
490 12         37 push @{$common_array}, $row;
  12         43  
491 12         35 $self->{max_nodes} -= 1;
492 12 50       170 last if $self->{max_nodes} < 1;
493              
494             } elsif ( !$self->{skip_empty_dir} ) { # keep it storied
495              
496 10         47 push @{$common_array}, $row;
  10         84  
497 10         61 $self->{max_nodes} -= 1;
498 10 50       165 last if $self->{max_nodes} < 1;
499              
500             } else {
501             # skip empty directory (with no children) but log this...
502 3         26 $self->{plog}->debug("skips empty directory $full_file_name\n");
503             }
504              
505             } elsif ($type eq 'f') {
506              
507             # I limit files to be stored by the rule of 'allowed only':
508 45         78 my $keepit = 0; # false initially
509 45         163 my $allow_index = 0;
510 45         109 foreach (@{$self->{allow_files}}){
  45         359  
511 110         665 my $mask = $_->{mask};
512 110 100       3611 if ( $name =~ /$mask/i ){
513 32         87 $row->{allow_index} = $allow_index; # to get the action later
514 32         54 $keepit = 1;
515 32         129 last; # the first allowed is a right one
516             }
517 78         230 $allow_index++;
518             }
519 45 100       133 if ($keepit) {
520             # no child reference for the file:
521 32         659 $row->{child_dir_list} = undef;
522              
523             # determine the rpm package when appropriate:
524 32 50       275 if ( $self->{rpm_active} ) {
525             # I have Red Hat RPM only: rpm --version
526             # RPM version 4.0.4
527 32         210385 my $rpm_name = `rpm -qf $full_file_name`;
528             # as an example, in my tests I get initially on Red Hat:
529             # file /some/real/full/name/file_1.pm is not owned by any package
530             # I use simple mask: /^file \// to recognize no-rpm right away
531             # in order to save some storage memory:
532             # my $no_rpm_mask = '^file /';
533 32         7317 chomp $rpm_name;
534 32 50       3972 $row->{rpm_package_name} = ($rpm_name =~ /^file \//o)
535             ? NO_RPM_OWNER : $rpm_name; # =~ m/(\S.*\S)/;
536             }
537              
538 32         394 push @{$common_array}, $row;
  32         341  
539 32         304 $self->{max_nodes} -= 1;
540 32 50       1163 last if $self->{max_nodes} < 1;
541             } else {
542             # I skip all other files but log this...
543 13         107 my $message = 'skips '.$full_file_name." due to unknown type\n";
544 13         498 $self->{plog}->debug($message);
545             }
546              
547             } else {
548             # this is supposed to be a link:
549              
550             # In my test for real symlink I have for example:
551             # type=>l file_3.txt
552             # name=>file_4.htm
553             # on Red Hat Linux 9.0 after:
554             # ln -s file_3.txt file_4.htm
555             # having:
556             # lrwxrwxrwx 1 slava group 10 Aug 7 09:08 file_4.htm -> file_3.txt
557              
558 13         47 $row->{child_dir_list} = undef;
559 13         145 $row->{link_target} = substr($type, 2); # check this for other platforms!
560 13         40 $row->{type} = 'l'; # make it clear for the further use
561              
562             # I don't follow symlinks in order to avoid loops
563              
564 13         97 $self->{plog}->debug('has a link called '.$name."\n");
565 13         122 push @{$common_array}, $row;
  13         34  
566 13         35 $self->{max_nodes} -= 1;
567 13 50       81 last if $self->{max_nodes} < 1;
568             }
569             }
570             # common_array is created.
571              
572 38         3235 @{$common_array} = sort { $a->{name} cmp $b->{name} } @{$common_array};
  38         110  
  70         913  
  38         362  
573              
574 38         830 $self->{plog}->debug('done on level='.$depth_level.' in '.$dir_path."\n");
575 38         1946 return $common_array;
576             }
577              
578             sub from_scratch {
579             # A member function that creates the discription of perl-library defined by {p_INC} reference.
580             # no incoming parameters
581             # The result reference is stored internally in {descript} and is returned upon success.
582 5     5 1 31 my $self = shift;
583              
584 5         11 my $internal_start_time = time;
585             # this time will be assigned as a time of the creation of description
586 5         13 $self->{descript_internal_start_time} = $internal_start_time;
587 5         438 my $now_string = strftime "%A, %B %e, %Y at %H:%M:%S", localtime($internal_start_time);
588 5         21 $self->{descript_start_time_text} = $now_string;
589              
590 5         36 $self->{plog}->info('started on '.$now_string."\n");
591              
592             # I need to create this array ones for all nested calls:
593 5         2899 my $allow_masks = []; # to select files
594 5         10 map { push @{$allow_masks},$_->{mask} } @{$self->{allow_files}};
  20         25  
  20         44  
  5         13  
595              
596 5         10 my $depth_level = 1; # to control the depth of the tree,
597             # I have the list of @INC names on level 1...
598 5         9 my $lib_list_ref = []; # a reference to the array of hashes; every hash describes one library:
599              
600             # {parent_index} - unique name of the parent node/object;
601             # {self_index} - unique name for the self node/object;
602             # {name} - name of the file/link/directory;
603             # {pseudo_cpan_name} - makes sense for the .pm file only; indeed is generated recursively;
604             # {type} - can be 'd', 'l', or 'f'; However, see features of 'l'...
605             # {last_mod_time_text} - date/time of last modification in format "%B %d, %Y at %H:%M"
606             # {permissions_octal_text} - like '0755'
607             # {full_name} - absolute name like /full/path/to/the/file
608             # {size} - in bytes
609             # {owner} - name of the owner;
610             # {group} - name of the group;
611             # {child_dir_list} - a reference to the array of children descriptions;
612             # {inode} - associated with the item;
613             # {level} - depth in the tree (=1 for the names listed in @INC);
614              
615             # I don't want to have stupid repititions in the tree structure.
616             # For example, in Red Hat distribution 7.3 you might have:
617             #
618             # @INC =
619             # /usr/lib/perl5/5.6.1/i386-linux
620             # /usr/lib/perl5/5.6.1
621             # /usr/lib/perl5/site_perl/5.6.1/i386-linux
622             # /usr/lib/perl5/site_perl/5.6.1
623             # /usr/lib/perl5/site_perl/5.6.0
624             # /usr/lib/perl5/site_perl
625             # /usr/lib/perl5/vendor_perl/5.6.1/i386-linux
626             # /usr/lib/perl5/vendor_perl/5.6.1
627             # /usr/lib/perl5/vendor_perl
628             # . !!! This is '/' for mod_perl !!!
629             # /etc/httpd/ !!! Loop is here !!!
630             # /etc/httpd/lib/perl !!! Does not exist on my machine !!!
631             #
632             # It is not supposed to make a real sence in terms of pseudo-cpan names...
633              
634 5         9 my $prior_libs = []; # a reference to the array of already explored libraries
635              
636 5         13 my $local_index = 0; # to create unique names
637              
638 5         14 foreach (@{ $self->{p_INC} }) {
  5         17  
639              
640 10         30 $local_index += 1;
641 10         27 my $lib_descr = {};
642 10         57 $lib_descr->{level} = $depth_level;
643              
644 10         77 my $lib_index_name = $self->{lib_index_prefix}.'_'.$local_index;
645 10         32 $lib_descr->{self_index} = $lib_index_name;
646 10         36 $lib_descr->{parent_index} = undef;
647 10         25 my $dir = $_;
648              
649 10         41 my $message = 'serves $INC['.$local_index.'] = '.$dir." named $lib_index_name\n";
650 10         57 $self->{plog}->debug($message);
651              
652 10         107 $lib_descr->{name} = $dir;
653 10         59 $lib_descr->{type} = 'd'; # always directory in @INC
654             # retrieve the rest of details from the stat:
655 10         43 my $dir_path = $dir;
656 10 50       66 $dir_path .= '/' unless $dir =~ /\/$/;
657 10         434 my ( $dev, # device number of filesystem
658             $ino, # inode number
659             $mode, # file mode (type and permissions)
660             $nlink, # number of (hard) links to the file
661             $uid, # numeric user ID of file's owner
662             $gid, # numeric group ID of file's owner
663             $rdev, # the device identifier (special files only)
664             $size_2, # total size of file, in bytes
665             $atime, # last access time in seconds since the epoch
666             $mtime, # last modify time in seconds since the epoch
667             $ctime, # inode change time (NOT creation time!) in seconds since the epoch
668             $blksize, # preferred block size for file system I/O
669             $blocks # actual number of blocks allocated
670             ) = stat ($dir_path);
671             # on this stage the sub-directory has some (fictive in my understanding) size...
672 10         51 $lib_descr->{size} = $size_2;
673              
674 10         711 my $now_string = strftime "%B %d, %Y at %H:%M", localtime ($mtime);
675 10         74 $lib_descr->{last_mod_time_text} = $now_string;
676              
677 10         38 $lib_descr->{full_name} = $dir;
678              
679 10 50       43 unless ($self->{skip_mode}){
680 10         68 my $permissions = sprintf "%04o", $mode & 07777;
681 10         30 $lib_descr->{permissions_octal_text} = $permissions;
682             }
683              
684 10 50       3339 $lib_descr->{owner} = getpwuid($uid) unless $self->{skip_owner};
685 10 50       691 $lib_descr->{group} = getgrgid($gid) unless $self->{skip_group};
686 10         48 $lib_descr->{inode} = $ino;
687              
688 10         69 $lib_descr->{child_dir_list} = $self->_dir_description (
689             root_dir => $dir,
690             prior_libs => $prior_libs,
691             pseudo_cpan_root_name => '', # it warns in debug when I use undef over here
692             parent_index => $lib_index_name,
693             inc_lib => $dir,
694             parent_depth_level => $depth_level,
695             allow_masks => $allow_masks );
696              
697             # never skip the root (level 1) directory, even empty...
698              
699             # when the limit on global number of nodes is exceeded in _dir_description
700             # it can return undef. This should be safe for the following push...
701 10 50 33     101 if ( defined($lib_descr->{child_dir_list})
  10         172  
702             && scalar( @{$lib_descr->{child_dir_list}} ) eq 0 ){
703 0         0 $lib_descr->{child_dir_list} = undef;
704             }
705 10         34 push @{$lib_list_ref}, $lib_descr;
  10         40  
706 10         32 $self->{max_nodes} -= 1;
707 10 50       39 last if $self->{max_nodes} < 1;
708 10         20 push @{$prior_libs}, $dir;
  10         99  
709             }
710             # time stamp of the finish:
711 5         37 my $internal_finish_time = time;
712 5         600 my $now_finish_string = strftime "%A, %B %e, %Y at %H:%M:%S", localtime($internal_finish_time);
713 5         24 $self->{descript_internal_finish_time} = $internal_finish_time;
714 5         26 $self->{descript_finish_time_text} = $now_finish_string;
715              
716             # create a simple list of all accumulated items:
717              
718 5         57 $self->{descript} = $self->_object_list ($lib_list_ref);
719 5         40 $self->_mark_shaded_names();
720              
721 5 50       21 if ( $self->{max_nodes} < 1 ){ # ERROR
722             # terminating this late, I keep the accumulated result viewable
723 0         0 $self->{plog}->error('ERROR termination: max_nodes exceeded'."\n");
724 0         0 return undef;
725             }
726 5         11 my $duration = $internal_finish_time - $internal_start_time;
727              
728             # I will clean up the following mess later...
729 5         17 my $hh = int($duration/3600);
730 5         14 my $mm = int(($duration - 3600 * $hh)/60);
731 5         13 my $ss = $duration - 60 * $mm - 3600 * $hh;
732 5         131 my $duration_text = sprintf "%02d:%02d:%02d", $hh,$mm,$ss;
733              
734 5         31 $self->{plog}->info('done on '.$now_finish_string." duration=$duration_text\n");
735 5         32093 return scalar(@{$self->{descript}});
  5         83  
736             }
737              
738             sub _object_list {
739             # transforms the description tree structure
740             # to the simple (regular) array of simple hashes:
741              
742 28     28   67 my $self = shift;
743 28         50 my $source = shift; # a reference to the array of dir descriptions
744             # source data validation:
745             #
746             # I can take an empty incoming array when the directory is empty;
747             # that's fine, I will respond with the reference to an empty array then...
748             # The problem could appear if the $source is udefined,
749             # or is referencing something that is not an array...
750 28 50       266 unless (isa($source, 'ARRAY')){
751 0         0 $self->{plog}->error('incoming parameter must be a reference to ARRAY'."\n");
752 0         0 return undef;
753             }
754 28         47 my $result = []; # a reference to return
755              
756             # 09/10/04: a bug appears over here: $source->[0]->{level} is undef ocasionaly.
757 28         96 my $in_size = scalar @{$source};
  28         52  
758 28 50       103 unless ( defined $source->[0]->{level} ){
759 0         0 $self->{plog}->warn("undefined level value when the size=$in_size\n");
760 0         0 return $result; # empty...
761             }
762 28         43 my $dbg_nodes = []; # to drill into the bug
763              
764 28         45 my $current_level = $source->[0]->{level};
765 28         301 $self->{plog}->debug("start level=$current_level size=$in_size\n");
766              
767 28         233 foreach ( @{$source} ) {
  28         61  
768              
769 71         103 my $lib_descr = {}; # my very simple hash for one row
770              
771             # this is not a full list of incoming keys:
772              
773 71         186 $lib_descr->{pseudo_cpan_name} = $_->{pseudo_cpan_name};
774 71         215 $lib_descr->{level} = $_->{level};
775 71         125 $lib_descr->{inc_lib} = $_->{inc_lib};
776 71         151 $lib_descr->{parent_obj_name} = $_->{parent_index};
777 71         146 $lib_descr->{self_obj_name} = $_->{self_index};
778 71         149 $lib_descr->{name} = $_->{name};
779 71         202 $lib_descr->{type} = $_->{type};
780 71         316 $lib_descr->{size} = $_->{size};
781 71         144 $lib_descr->{last_mod_time_text} = $_->{last_mod_time_text};
782 71         165 $lib_descr->{full_name} = $_->{full_name};
783 71         135 $lib_descr->{inode} = $_->{inode};
784 71 50       451 $lib_descr->{permissions_octal_text}=$_->{permissions_octal_text} if $_->{permissions_octal_text};
785 71 100       167 $lib_descr->{owner} = $_->{owner} if $_->{owner};
786 71 50       284 $lib_descr->{group} = $_->{group} if $_->{group};
787 71 100       174 $lib_descr->{allow_index} = $_->{allow_index} if defined $_->{allow_index};# files only
788 71 50       232 $lib_descr->{rpm_package_name} = $_->{rpm_package_name} if $_->{rpm_package_name};
789 71 100       179 $lib_descr->{link_target} = $_->{link_target} if $_->{link_target};
790              
791 71         84 push @{$result}, $lib_descr;
  71         129  
792 71         81 push @{$dbg_nodes}, $_->{inode};
  71         236  
793              
794 71 100       192 if ( $_->{child_dir_list} ) {
795              
796             # this is a good place for
797              
798             # recursion inside the same namespace/class omly:
799 22         116 my $subset = _object_list($self, $_->{child_dir_list});
800 22         37 push @{$result}, @{$subset};
  22         39  
  22         58  
801              
802             # this is a good place for
803              
804 22         218 $_->{child_dir_list} = undef; # release the memory
805             };
806             }
807 28         163 $self->{plog}->debug("done level=$current_level for nodes:\n\t"
808 28         166 .join("\n\t",@{$dbg_nodes})."\n");
809 28         237 return $result;
810             }
811              
812             sub _mark_shaded_names {
813             # creates extended descriptions for shaded .pm files.
814              
815             # Since Aug 13, 2004 I extend the same record with additional keys
816             # (instead of referencing additional hash in previous versions)
817             # in order to simplify the main data structure, XML representation,
818             # and serialization/deserialization mechanism.
819              
820             # no parameters:
821 5     5   15 my $self = shift;
822 5         21 $self->{plog}->debug("start\n");
823              
824 5         33 my %first; # to store pseudo_cpan_name's
825 5         8 foreach ( @{ $self->{descript} } ){
  5         18  
826              
827 65 100 100     453 next unless $_->{type} eq 'f' and lc $_->{name} =~ /\.pm$/;
828              
829 15         28 my $actual_file_name = $_->{pseudo_cpan_name};
830 15 100       43 if ( $first{$actual_file_name} ){
831              
832             # this file is shaded
833 5         14 $_->{shaded_by_lib} = $first{$actual_file_name}->{lib};
834 5         46 $_->{shaded_by_inode} = $first{$actual_file_name}->{inode};
835 5         18 $_->{shaded_by_last_modified} = $first{$actual_file_name}->{last_modified};
836              
837             } else {
838 10         23 my $details = {};
839 10         37 $details->{lib} = $_->{inc_lib};
840 10         32 $details->{inode} = $_->{inode};
841 10         21 $details->{last_modified} = $_->{last_mod_time_text};
842              
843 10         60 $first{$actual_file_name} = $details; # to check other files
844             }
845             }
846              
847 5         13 my $shaded_cpan_names = [];
848 5 100       10 map {push @{$shaded_cpan_names},$_->{pseudo_cpan_name} if $_->{shaded_by_lib} } @{$self->{descript}};
  65         252  
  5         23  
  5         29  
849 5         16 $self->{plog}->debug("Shaded Files:\n".join(', ', @{$shaded_cpan_names} ));
  5         63  
850              
851 5         51 $self->{plog}->debug('done'."\n");
852 5         48 return $shaded_cpan_names;
853             }
854              
855             sub list_simple_keys {
856             # returns a reference to the array that contains a
857             # sorted alphabetically set of names of simple keys of the object.
858 12     12 1 25 my $self = shift;
859              
860 12         22 my $ref_keys = []; # final array of key names
861 12         34 foreach (sort keys %{$self}){
  12         187  
862 216 100 100     1130 if (!defined $self->{$_}){
    100          
863 54         56 push @{$ref_keys},$_;
  54         98  
864             } elsif ($self->{$_}=~/HASH/ or $self->{$_}=~/ARRAY/){
865 38         61 next;
866             } else {
867 124         129 push @{$ref_keys},$_;
  124         322  
868             }
869             }
870 12         43 $self->{plog}->debug( "Outgoing List:\n\t".join("\n\t",@{$ref_keys})."\n" );
  12         99  
871              
872 12         186 return $ref_keys;
873             }
874              
875             sub list_descript_keys {
876             # returns a reference to the array that contains
877             # sorted alphabetically names of keys used anywhere inside descriptions.
878 2     2 1 100 my $self = shift;
879              
880 2         10 my $ref_descript_keys = []; # final array of all keys
881 2         10 my %r; # to fill out with full set of available description keys (no duplications):
882 2         5 map { map{ $r{$_} = 1 } keys %{$_} } @{$self->{descript}};
  13         18  
  181         294  
  13         64  
  2         10  
883             # sorted list of all keys:
884 2         19 map { push @{$ref_descript_keys},$_ } sort keys %r;
  19         27  
  19         34  
885 2         8 $self->{plog}->debug( "Outgoing List:\n\t".join("\n\t",@{$ref_descript_keys})."\n" );
  2         23  
886              
887 2         121 return $ref_descript_keys;
888             }
889              
890             ######################### HTML ##################
891             sub w3c_doctype {
892 1     1 1 3 my $self = shift;
893              
894 1         9 $self->{plog}->debug('started'."\n");
895              
896 1         15 my $parms = { @_ }; # a reference to the hash
897             # 1 mandatory parameter:
898 1         3 my $type = $parms->{type};
899 1 50       4 $self->{plog}->error('has no incoming type') unless $type;
900 1 50       4 return undef unless $type;
901              
902 1         4 my $res = ''; # to output
903 1 50       24 if ( $type =~ /xhtml/i ){
    50          
904 0         0 $res =<
905            
906             "http://www.w3.org/TR/xhtml1/DTD/strict.dtd">
907            
908             TOP_PART
909             } elsif ( $type =~ /html/i ){
910 1         5 $res =<
911            
912             TOP_PART
913             } else {
914 0         0 $self->{plog}->error('has unknown type: '.$type);
915 0         0 return undef;
916             }
917 1         5 $self->{plog}->debug('done'."\n");
918 1         8 return $res;
919             }
920              
921             sub inline_CSS {
922             # no parameters ?
923              
924 1     1 1 5 my $res =<
925            
948             END
949 1         4 return $res;
950             }
951              
952             sub _html_head {
953 1     1   3 my $self = shift;
954              
955 1         5 $self->{plog}->debug('started'."\n");
956              
957 1         123 my $parms = { @_ }; # a reference to the hash
958             # 3 parameters:
959 1         3 my $title = $parms->{title};
960 1         2 my $jslib = $parms->{jslib};
961 1         2 my $css = $parms->{css};
962 1         1 my $overLib = $parms->{overLib};
963              
964 1         4 my $res =<
965            
966             $title
967             END
968 1 50 33     13 if ($css and ($css eq 'inline')){
    0          
969 1         4 $res .= $self->inline_CSS;
970             } elsif ($css) {
971 0         0 $res .=<
972            
973             END
974             } # scip css otherwise...
975              
976 1 50       4 if ($jslib){
977 0         0 $res .=<
978            
979             END
980             }
981 1 50       3 if ($overLib){
982 1         3 $res .=<
983            
984             END
985             }
986 1         1 $res .=<
987            
988             END
989 1         4 $self->{plog}->debug('done'."\n");
990 1         14 return $res;
991             }
992              
993             sub inc_html_table {
994             # list content of pseudo-inc linking names to the main descripts
995             # make human readable HTML format:
996 1     1 1 2 my $self = shift;
997              
998 1         4 $self->{plog}->debug('started'."\n");
999              
1000 1         7 my $parms = { @_ }; # a reference to the hash
1001             # 1 parameter:
1002 1         2 my $title = $parms->{title};
1003 1         3 my $res =<
1004             '."\n";
1005            
$title
1006             END
1007 1         1 my $loc_ind = 1;
1008 1         3 foreach ( @{$self->{p_INC}} ){
  1         6  
1009 2         7 my $link = $self->{lib_index_prefix}.'_'.$loc_ind; # create it here:
1010 2         12 $res .= "\t".'
'.$_.'
1011 2         5 $loc_ind += 1;
1012             }
1013 1         5 $self->{plog}->debug('done'."\n");
1014 1         13 return $res.'
'."\n";
1015             }
1016              
1017             sub _descript_html_table_head_row {
1018 1     1   2 my $self = shift;
1019 1         3 $self->{plog}->debug('started'."\n");
1020              
1021 1         8 my $res = '
1022             # header row of the table:
1023 1         4 my @hdr_list;
1024 1 50       5 push @hdr_list, 'mode' unless $self->{skip_mode};
1025 1 50       5 push @hdr_list, 'owner' unless $self->{skip_owner};
1026 1 50       7 push @hdr_list, 'group' unless $self->{skip_group};
1027 1         7 push @hdr_list, 'inode', 'tree', 'size', 'last_modified', 'use_model';
1028 1 50       5 push @hdr_list, 'package' if $self->{rpm_active};
1029 1         4 map {$res .= ''.$_.'
  9         19  
1030 1         5 $self->{plog}->debug('done'."\n");
1031 1         14 return $res.'
1032             }
1033              
1034             sub export_to_DHTML {
1035             # create a multi-string of dynamic HTML page
1036 1     1 1 22 my $self = shift;
1037 1         5 $self->{plog}->debug('started'."\n");
1038              
1039 1         36 my $parm = { @_ }; # a reference to the hash
1040 1         3 my $title = $parm->{title};
1041             # all following parameters should be object properties?..
1042 1         3 my $image_dir = $parm->{'image_dir'};
1043 1 50       5 unless ( $image_dir ){
1044 0         0 $self->{plog}->error('has no image_dir'."\n");
1045 0         0 return undef;
1046             }
1047 1         3 my $icon_shaded = $parm->{'icon_shaded'};
1048 1 50       4 unless ( $icon_shaded ){
1049 0         0 $self->{plog}->error('has no icon_shaded'."\n");
1050 0         0 return undef;
1051             }
1052 1         2 my $icon_folder_opened = $parm->{'icon_folder_opened'};
1053 1 50       4 unless ( $icon_folder_opened ){
1054 0         0 $self->{plog}->error('has no icon_folder_opened'."\n");
1055 0         0 return undef;
1056             }
1057 1         2 my $icon_symlink =$parm->{'icon_symlink'};
1058 1 50       12 unless ( $icon_symlink ){
1059 0         0 $self->{plog}->error('has no icon_symlink'."\n");
1060 0         0 return undef;
1061             }
1062 1         2 my $tree_intend = $parm->{'tree_intend'};
1063 1 50       5 $self->{plog}->warn('has undefined tree_intend'."\n") unless defined $tree_intend;
1064 1   50     5 my $row_class = $parm->{'row_class'} || 'r0';
1065              
1066 1   50     2891 my $css =$parm->{'css'} || 'inline';
1067 1   50     14 my $jslib =$parm->{'jslib'} || '';
1068 1         4 my $overlib =$parm->{'overlib'};
1069 1 50       4 unless ( $overlib ){
1070 0         0 $self->{plog}->error('has no overlib'."\n");
1071 0         0 return undef;
1072             }
1073              
1074 1         15 my $res = $self->w3c_doctype( type => 'html' );
1075 1         4 $res .=<
1076            
1077             END
1078 1         6 $res .= $self->_html_head(
1079             title => $title,
1080             css => $css,
1081             jslib => $jslib,
1082             overLib => $overlib,
1083             );
1084 1         2 $res .=<
1085            
1086            
1087             END
1088 1         3 my $start = $self->{descript_start_time_text};
1089 1 50       91 my $v = $^V ? sprintf "v%vd", $^V : $];
1090 1         8 $res .=<
1091            

Perl $v
$self->{tree_id}
created on $start

1092            

INC array:

1093             END
1094 1         4 $res .= $self->inc_html_table(title => 'Library');
1095 1         2 $res .=<
1096            

Tree of Libraries:

1097            
1098             END
1099 1         4 $res .= $self->_descript_html_table_head_row();
1100             # list all descriptions:
1101 1         4 $self->{lib_index} = 1; # to link pseudo_INC list to right rows of description
1102 1         2 foreach ( @{$self->{descript}} ) {
  1         3  
1103             # 11/19/03: need to make a flexible input for the _data_row_HTML:
1104 13         34 $res .= $self->_data_row_HTML(
1105             current_row_description => $_,
1106             image_dir => $image_dir,
1107             icon_shaded => $icon_shaded,
1108             icon_folder_opened => $icon_folder_opened,
1109             icon_symlink => $icon_symlink,
1110             tree_intend => $tree_intend,
1111             row_class => $row_class,
1112             )."\n";
1113             }
1114 1         3 $self->{lib_index} = undef; # release this temporary key from possible saving operations
1115 1         3 $res .=<
1116            
1117            



1118            
1119            
1120             REST
1121 1         4 $self->{plog}->debug('done'."\n");
1122 1         39 return $res;
1123             }
1124              
1125             sub _data_row_HTML {
1126             # this method creates one regular row only,
1127             # it does not serve the root (and I have no root row anymore...)
1128 13     13   15 my $self = shift;
1129 13         39 $self->{plog}->debug('started'."\n");
1130              
1131 13         114 my $parm = { @_ };
1132 13         20 my $source = $parm->{'current_row_description'};
1133 13 50       27 unless ( $source ){
1134 0         0 $self->{plog}->error('has no current_row_description'."\n");
1135 0         0 return undef;
1136             }
1137              
1138             # all following parameters should be object properties?..
1139 13         15 my $image_dir = $parm->{'image_dir'};
1140 13 50       21 unless ( $image_dir ){
1141 0         0 $self->{plog}->error('has no image_dir'."\n");
1142 0         0 return undef;
1143             }
1144 13         12 my $icon_shaded = $parm->{'icon_shaded'};
1145 13 50       20 unless ( $icon_shaded ){
1146 0         0 $self->{plog}->error('has no icon_shaded'."\n");
1147 0         0 return undef;
1148             }
1149 13         14 my $icon_folder_opened = $parm->{'icon_folder_opened'};
1150 13 50       23 unless ( $icon_folder_opened ){
1151 0         0 $self->{plog}->error('has no icon_folder_opened'."\n");
1152 0         0 return undef;
1153             }
1154 13         14 my $icon_symlink =$parm->{'icon_symlink'};
1155 13 50       20 unless ( $icon_symlink ){
1156 0         0 $self->{plog}->error('has no icon_symlink'."\n");
1157 0         0 return undef;
1158             }
1159 13         12 my $tree_intend = $parm->{'tree_intend'};
1160 13 50       27 $self->{plog}->warn('has undefined tree_intend'."\n") unless defined $tree_intend;
1161 13   50     23 my $row_class = $parm->{'row_class'} || 'r0';
1162              
1163             # a level==1 directory should be accomplished with a local link anchor:
1164 13         10 my $anchor = '';
1165 13 100       40 if ( $source->{level} eq 1 ) {
1166 2         5 $anchor = '';
1167 2         3 $self->{lib_index} += 1;
1168             }
1169 13         20 my $result = '
1170 13 50       27 unless ( $self->{skip_mode} ){
1171 13         23 $result .= ''.$anchor.$source->{permissions_octal_text}.'
1172 13         18 $anchor = '';
1173             }
1174 13 50       23 unless ( $self->{skip_owner} ){
1175 13         725 $result .= ''.$anchor.$source->{owner}.'
1176 13         27 $anchor = '';
1177             }
1178 13 50       32 unless ( $self->{skip_group} ){
1179 13         28 $result .= ''.$anchor.$source->{group}.'
1180 13         18 $anchor = '';
1181             }
1182 13         33 $result .= ''.$anchor.$source->{inode}.'
1183              
1184             # tree sell:
1185 13         14 $result .= ''; '; '; '; '; '; ';
1186 13 50       23 if ( $source->{level} ) {
1187 13         28 $result .= ' 
1188             }
1189              
1190 13         15 my $icon = $image_dir;
1191 13 100       32 if ( $source->{type} eq 'f'){
    100          
1192 5 100       15 $icon .= ($source->{shaded_by_lib}) ?
1193             $icon_shaded : $self->{allow_files}->[$source->{allow_index}]->{icon};
1194             } elsif ( $source->{type} eq 'd'){
1195 6         9 $icon .= $icon_folder_opened;
1196             } else { # $source->{type} eq 'l':
1197 2         3 $icon .= $icon_symlink;
1198             }
1199              
1200 13         19 my $application_directory = $self->{application_directory};
1201              
1202 13 100       19 if ( $source->{shaded_by_lib} ){
1203             # make the message to display by overLib on_mouse_over:
1204 1   50     4 my $ollibname = $source->{shaded_by_lib} || 'Unknown';
1205 1         3 my $olinode = $source->{shaded_by_inode};
1206 1         2 my $ollast_mod = $source->{shaded_by_last_modified};
1207 1         11 my $olMessage = 'Click to view this document
'
1208             .'shaded by:
' '
library:'
1209             .$ollibname.'
1210             .'
inode:'.$olinode.' 
1211             .'
modified_on:'.$ollast_mod.' 
';
1212 1         2 my $allow_index = $source->{allow_index};
1213 1         6 $result .= ''.$self->_link_icon_overLib (
1214             icon_src => $icon,
1215             # on_click_href => '/display-document'.$source->{full_name},
1216             on_click_href => $application_directory.$self->{allow_files}->[$allow_index]->{icon_on_click_action}.$source->{full_name},
1217             on_mouse_over_message => $olMessage,
1218             hspace => 1,
1219             align => 'absmiddle',
1220             border => 0 ).'
1221             } else {
1222 12 100       26 if ( $source->{type} eq 'f' ){
1223 4         5 my $allow_index = $source->{allow_index};
1224 4 50       9 unless ( defined $allow_index ) { # zerro is fine
1225 0         0 $self->{plog}->error($source->{full_name}.' has no allow_index'."\n");
1226 0         0 return undef;
1227             }
1228 4         29 $result .= ''.$self->_link_icon_overLib (
1229             icon_src => $icon,
1230             on_click_href => $application_directory.$self->{allow_files}->[$allow_index]->{icon_on_click_action}.$source->{full_name},
1231             on_mouse_over_message => $self->{allow_files}->[$allow_index]->{icon_mouse_over_prompt},
1232             hspace => 1,
1233             align => 'absmiddle',
1234             border => 0 ).'
1235             } else { # this is a directory or a link:
1236 8         18 $result .= '
1237             }
1238             }
1239              
1240             # short name for the item:
1241 13 100       26 if ( $source->{type} eq 'f' ){
1242 5         7 my $allow_index = $source->{allow_index};
1243 5         5 my $left_space = ''; # default for .pod icon that has own space...
1244 5         10 my $olMessage = $self->{allow_files}->[$allow_index]->{name_mouse_over_prompt};
1245 5         6 $left_space = '  ';
1246 5         21 $result .= ''.$left_space.$self->_link_text_overLib (
1247             text => $source->{name},
1248             href => $application_directory.$self->{allow_files}->[$allow_index]->{name_on_click_action}.$source->{full_name},
1249             on_mouse_over_message => $olMessage ).'
1250             } else {
1251             # no links for directory or symlink:
1252 8         19 $result .= ' '.$source->{name}.'
1253             }
1254 13         17 $result .= '
1255              
1256             # output the rest of the row:
1257 13 100 100     80 if ( $source->{type} eq 'f' ){
    100          
    100          
1258 5         16 $result .= ''.$source->{size}.' 
1259             .''.$source->{last_mod_time_text}.' 
1260 5 100       22 if ( lc $source->{name} =~ /\.pm$/ ){
1261 3         12 my $real_name = substr($source->{pseudo_cpan_name}, 0, -3);
1262 3         7 $result .= ' '.$real_name.'
1263             } else {
1264 2         4 $result .= ' 
1265             }
1266             } elsif ( ($source->{type} eq 'd') and ($source->{level} eq 1 ) ) {
1267 2         2 $result .= ' 
1268 2         4 $result .= 'base-level-lib
1269             } elsif ( $source->{type} eq 'l' ) {
1270 2         4 $result .= ' => '.$source->{link_target}.' 
1271             } else {
1272 4         5 $result .= ' 
1273             }
1274             # one directory might have multiple rpm-owners like:
1275             # [slava@PBC110 slava]$ rpm -qf /usr/lib/perl5/5.6.1/i386-linux
1276             # perl-5.6.1-34.99.6
1277             # perl-DBI-1.21-1
1278             # perl-DBD-Pg-1.01-8
1279             # perl-DBD-MySQL-1.2219-6
1280             # I care about the rpm-owners of particular files only:
1281 13 50       29 if ( $self->{rpm_active} ){
1282 13         18 my $rpm_package_name = $source->{rpm_package_name};
1283 13 50 33     31 $rpm_package_name = '-'
1284             if defined $rpm_package_name and $rpm_package_name =~ /^file \//; # make short output
1285 13 50       21 if ( $rpm_package_name ){
1286 0         0 $result .= ''.$rpm_package_name.'
1287             } else {
1288 13         17 $result .= ' 
1289             }
1290             }
1291 13         44 $self->{plog}->debug('done'."\n");
1292 13         172 return $result.'
1293             }
1294              
1295             sub _link_icon_overLib {
1296 5     5   7 my $self = shift;
1297 5         28 my $parm = { @_ };
1298 5         7 my $icon_src = $parm->{'icon_src'};
1299 5 50       67 unless ( $icon_src ){
1300 0         0 $self->{plog}->error("has no icon_src\n");
1301 0         0 return undef;
1302             }
1303 5         46 return ' 1304             .$parm->{'on_mouse_over_message'}.'\');" onmouseout="return nd();"> 1305             .$icon_src.'" hspace="'.$parm->{'hspace'}.'" border="'.$parm->{'border'}.'" align="'
1306             .$parm->{'align'}.'">';
1307             }
1308              
1309             sub _link_text_overLib {
1310 5     5   7 my $self = shift;
1311 5         17 my $parm = { @_ };
1312 5         9 my $href = $parm->{'href'};
1313 5 50       11 unless ( $href ){
1314 0         0 $self->{plog}->error("has no href\n");
1315 0         0 return undef;
1316             }
1317 5         24 return ' 1318             .'" onmouseover="return overlib(\''.$parm->{'on_mouse_over_message'}.'\');"'
1319             .' onmouseout="return nd();">'.$parm->{'text'}.'';
1320             }
1321              
1322             1;
1323             __END__