File Coverage

perllib/Arch/Inventory.pm
Criterion Covered Total %
statement 61 136 44.8
branch 0 36 0.0
condition 0 17 0.0
subroutine 21 33 63.6
pod 9 9 100.0
total 91 231 39.3


line stmt bran cond sub pod time code
1             # Arch Perl library, Copyright (C) 2004 Mikhael Goikhman, Enno Cramer
2             #
3             # This program is free software; you can redistribute it and/or modify
4             # it under the terms of the GNU General Public License as published by
5             # the Free Software Foundation; either version 2 of the License, or
6             # (at your option) any later version.
7             #
8             # This program is distributed in the hope that it will be useful,
9             # but WITHOUT ANY WARRANTY; without even the implied warranty of
10             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11             # GNU General Public License for more details.
12             #
13             # You should have received a copy of the GNU General Public License
14             # along with this program; if not, write to the Free Software
15             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16              
17 3     3   65 use 5.005;
  3         11  
  3         125  
18 3     3   16 use strict;
  3         7  
  3         121  
19              
20             package Arch::Inventory;
21              
22 3     3   16 use Exporter;
  3         6  
  3         158  
23 3     3   63 BEGIN { *Arch::Inventory::import = *Exporter::import; }
24 3     3   17 use vars qw(@EXPORT_OK %EXPORT_TAGS);
  3         5  
  3         301  
25              
26             @EXPORT_OK = qw(
27             TREE SOURCE PRECIOUS BACKUP JUNK UNRECOGNIZED
28             FILE DIRECTORY SYMLINK
29             TAGLINE EXPLICIT NAME
30             );
31             %EXPORT_TAGS = (
32             category => [ qw(TREE SOURCE PRECIOUS BACKUP JUNK UNRECOGNIZED) ],
33             type => [ qw(FILE DIRECTORY SYMLINK) ],
34             id_type => [ qw(TAGLINE EXPLICIT NAME) ],
35             );
36              
37              
38 3     3   15 use Arch::Util qw(run_tla);
  3         5  
  3         140  
39              
40 3     3   15 use constant TREE => 'T';
  3         7  
  3         190  
41 3     3   17 use constant SOURCE => 'S';
  3         4  
  3         133  
42 3     3   15 use constant PRECIOUS => 'P';
  3         4  
  3         131  
43 3     3   14 use constant BACKUP => 'B';
  3         5  
  3         118  
44 3     3   13 use constant JUNK => 'J';
  3         6  
  3         114  
45 3     3   18 use constant UNRECOGNIZED => 'U';
  3         5  
  3         113  
46              
47 3     3   14 use constant FILE => 'r';
  3         6  
  3         118  
48 3     3   16 use constant DIRECTORY => 'd';
  3         5  
  3         112  
49 3     3   14 use constant SYMLINK => '>';
  3         6  
  3         114  
50              
51 3     3   22 use constant TAGLINE => 'i';
  3         5  
  3         113  
52 3     3   14 use constant EXPLICIT => 'x';
  3         6  
  3         117  
53 3     3   15 use constant NAMES => '?';
  3         4  
  3         110  
54 3     3   14 use constant ARCH_CONTROL => 'A';
  3         9  
  3         135  
55 3     3   14 use constant ARCH_ID_DIR => 'D';
  3         6  
  3         117  
56 3     3   13 use constant ARCH_ID_FILE => 'E';
  3         5  
  3         4782  
57              
58             sub new ($$) {
59 0     0 1   my $class = shift;
60 0   0       my $dir = shift || ".";
61              
62 0           $dir =~ s!/$!!;
63              
64 0 0         die(__PACKAGE__ . ": directory $dir does not exist\n") unless -d $dir;
65              
66 0           my $prefix = $dir . '/';
67 0           my $plen = length($prefix);
68              
69             # parse inventory output
70 0           my @inv_temp = run_tla(qw{inventory -spbju -B --kind --ids}, $dir);
71 0           my @inv_entries = ();
72 0           foreach my $line (@inv_temp) {
73 0 0         $line =~ /^([TSPBJU])([? ]) ([rd>]) ([^\t]+)\t(.+)$/
74             or die "Unrecognized inventory line: $line\n";
75              
76 0 0 0       my $path = (length($4) > $plen) && (substr($4, 0, $plen) eq $prefix) ? substr($4, $plen) : $4;
77              
78 0 0         push @inv_entries, {
    0          
79             category => $1,
80             untagged => $2 eq '?',
81             type => $3,
82             path => $path,
83             id => $5 eq '???' ? undef : $5,
84             id_type => $5 eq '???' ? undef : substr($5, 0, 1),
85             };
86             }
87              
88 0 0         my $root = {
89             category => -d "$dir/{arch}" ? TREE : SOURCE,
90             untagged => 0,
91             type => DIRECTORY,
92             path => '',
93             id => undef,
94             id_type => undef,
95             children => _build_inv_tree(0, @inv_entries),
96             };
97              
98 0           my $self = {
99             directory => $dir,
100             root => $root,
101             };
102              
103 0           return bless $self, $class;
104             }
105              
106             sub directory ($) {
107 0     0 1   my $self = shift;
108              
109 0           return $self->{directory};
110             }
111              
112             sub get_root_entry ($) {
113 0     0 1   my $self = shift;
114              
115 0           return $self->{root};
116             }
117              
118             sub get_entry ($@) {
119 0     0 1   my $self = shift;
120 0           my @path = @_;
121              
122 0 0         @path = split /\//, $path[0]
123             if @path == 1;
124              
125 0           my $entry = $self->get_root_entry;
126 0   0       while (@path && defined $entry && ($entry->{type} eq DIRECTORY)) {
      0        
127 0           $entry = $entry->{children}->{shift @path};
128             }
129              
130 0 0         return @path ? undef : $entry;
131             }
132              
133             sub get_listing ($) {
134 0     0 1   my $self = shift;
135              
136 0           my $str;
137             $self->foreach(sub {
138 0 0   0     return unless $_[0]->{path};
139              
140 0           $str .= Arch::Inventory->to_string($_[0]);
141 0           $str .= "\n";
142 0           });
143              
144 0           return $str;
145             }
146              
147             sub annotate_fs ($;$) {
148 0     0 1   my $self = shift;
149              
150 0 0         if (@_) {
151 0           $_[0]->{stat} = [ lstat("$self->{directory}/$_[0]->{path}") ];
152 0 0         $_[0]->{symlink} = readlink("$self->{directory}/$_[0]->{path}")
153             if $_[0]->{type} eq SYMLINK;
154             } else {
155 0     0     $self->foreach(sub { $self->annotate_fs($_[0]) });
  0            
156             }
157             }
158              
159             *annotate_stat = *annotate_fs; *annotate_fs = *annotate_fs;
160              
161             sub foreach ($$) {
162 0     0 1   my $self = shift;
163 0           my $sub = shift;
164 0   0       my $root = shift || $self->get_root_entry;
165              
166 0           $sub->($root);
167              
168 0 0         if ($root->{type} eq DIRECTORY) {
169 0           foreach my $child (sort keys %{$root->{children}}) {
  0            
170 0           $self->foreach($sub, $root->{children}->{$child});
171             }
172             }
173             }
174              
175             sub dump ($) {
176 0     0 1   my $self = shift;
177              
178 0           require Data::Dumper;
179 0           my $dumper = Data::Dumper->new([$self->get_root_entry]);
180 0 0         $dumper->Sortkeys(1) if $dumper->can('Sortkeys');
181 0           $dumper->Quotekeys(0);
182 0           $dumper->Indent(1);
183 0           $dumper->Terse(1);
184              
185 0           return $dumper->Dump;
186             }
187              
188             sub to_string ($$) {
189 0     0 1   my $class = shift;
190 0           my $entry = shift;
191              
192 0 0         return sprintf("%s%s %s %s\t%s",
    0          
193             $entry->{category},
194             $entry->{untagged} ? '?' : ' ',
195             $entry->{type},
196             $entry->{path},
197             $entry->{id} ? $entry->{id} : '???',
198             );
199             }
200              
201             # this assumes depth first ordering of @items
202             sub _build_inv_tree ($@) {
203 0     0     my ($cut, @entries) = @_;
204              
205 0           my %toplevel = ();
206 0           while (@entries) {
207 0           my $child = shift @entries;
208 0           my $name = substr($child->{path}, $cut);
209              
210 0 0         die("invalid name $name; input not in correct order\n")
211             if $name =~ m!/!;
212              
213 0           $toplevel{$name} = $child;
214 0 0         next unless $child->{type} eq DIRECTORY;
215              
216 0           my $prefix = $child->{path} . '/';
217 0           my $plen = length($prefix);
218              
219 0           my @children = ();
220 0           for (my $i = 0; $i < @entries;) {
221 0 0 0       if ((length($entries[$i]->{path}) > $plen) &&
222             (substr($entries[$i]->{path}, 0, $plen) eq $prefix)) {
223 0           push @children, splice @entries, $i, 1;
224             } else {
225 0           ++$i;
226             }
227             }
228              
229 0           $child->{children} = &_build_inv_tree($plen, @children);
230             }
231              
232 0           return \%toplevel;
233             }
234              
235             1;
236              
237             __END__