File Coverage

blib/lib/File/Properties/Generic.pm
Criterion Covered Total %
statement 90 112 80.3
branch 30 52 57.6
condition 2 3 66.6
subroutine 21 23 91.3
pod 11 11 100.0
total 154 201 76.6


line stmt bran cond sub pod time code
1             # ----------------------------------------------------------------------------
2             #
3             # This module provides a class representing properties of a generic
4             # disk file.
5             #
6             # Copyright © 2010,2011 Brendt Wohlberg
7             # See distribution LICENSE file for license details.
8             #
9             # Most recent modification: 5 November 2011
10             #
11             # ----------------------------------------------------------------------------
12              
13             package File::Properties::Generic;
14             our $VERSION = 0.01;
15              
16 5     5   34493 use File::Properties::Error;
  5         11  
  5         36  
17              
18             require 5.005;
19 5     5   275 use strict;
  5         11  
  5         162  
20 5     5   27 use warnings;
  5         10  
  5         201  
21 5     5   25 use Error qw(:try);
  5         12  
  5         34  
22 5     5   805 use Cwd;
  5         10  
  5         6591  
23 5     5   5401 use File::stat;
  5         95610  
  5         56  
24 5     5   407 use Fcntl ':mode';
  5         12  
  5         1948  
25 5     5   36 use File::Spec;
  5         12  
  5         114  
26 5     5   5529 use DirHandle;
  5         12715  
  5         7203  
27              
28             # This approach copied from File::Spec
29             my $OSTypes = {MacOS => 'Mac', MSWin32 => 'Win32',
30             os2 => 'OS2', VMS => 'VMS',
31             epoc => 'Epoc', NetWare => 'Win32',
32             symbian => 'Win32', dos => 'OS2',
33             cygwin => 'Cygwin'};
34             my $OSType = $OSTypes->{$^O} || 'Unix';
35             # Flag indicating whether stat is fully supported. Currently only set
36             # true for Unix, since other operating systems not available for
37             # testing.
38             our $FullStatSupport = ($OSType eq 'Unix')?1:0;
39              
40              
41             # ----------------------------------------------------------------------------
42             # Constructor
43             # ----------------------------------------------------------------------------
44             sub new {
45 13     13 1 38002696 my $this = shift;
46 13   66     110 my $clss = ref($this) || $this;
47 13         36 my $self = {};
48 13         44 bless $self, $clss;
49 13         73 $self->_init(@_);
50 13         150 return $self;
51             }
52              
53              
54             # ----------------------------------------------------------------------------
55             # Initialiser
56             # ----------------------------------------------------------------------------
57             sub _init {
58 13     13   24 my $self = shift;
59 13         23798 my $path = shift; # File path
60 13         29 my $fpcr = shift; # File::Properties::Cache reference
61              
62             # Ensure path specified
63 13 50       53 throw File::Properties::Error("Path is not defined")
64             if not defined $path;
65             # Ensure specified path is readable
66 13 50       391 throw File::Properties::Error("Path $path is not readable")
67             if not -r $path;
68             # Convert specified path to canonical, absolute path
69 13         583 $self->path(Cwd::realpath($path));
70             ## Get file properties via stat call
71 13         77 my $fstt = stat($path);
72 13 50       2574 throw File::Properties::Error("Stat failed on $path") if not defined $fstt;
73 13 50       410 $self->device($fstt->dev) if ($FullStatSupport);
74 13 50       310 $self->inode($fstt->ino) if ($FullStatSupport);
75 13         273 $self->size($fstt->size);
76 13         271 $self->mtime($fstt->mtime);
77 13         266 $self->mode($fstt->mode);
78             ## If specified path is a directory, construct child properties
79             ## objects for each directory entry
80 13 100       55 if ($self->isdir) {
81 1         8 $self->children($self->_scandir($path, $fpcr));
82             } else {
83 12         52 $self->children(undef);
84             }
85             }
86              
87              
88             # ----------------------------------------------------------------------------
89             # Get file path
90             # ----------------------------------------------------------------------------
91             sub path {
92 29     29 1 154 my $self = shift;
93              
94 29 100       1332 $self->{'path'} = shift if (@_);
95 29         107 return $self->{'path'};
96             }
97              
98              
99             # ----------------------------------------------------------------------------
100             # Get file device number
101             # ----------------------------------------------------------------------------
102             sub device {
103 22     22 1 133 my $self = shift;
104              
105 22 50       57 if ($FullStatSupport) {
106 22 100       73 $self->{'devc'} = shift if (@_);
107 22         69 return $self->{'devc'};
108             } else {
109 0         0 throw File::Properties::Error("Stat not fully supported");
110             }
111             }
112              
113              
114             # ----------------------------------------------------------------------------
115             # Get file inode number
116             # ----------------------------------------------------------------------------
117             sub inode {
118 22     22 1 122 my $self = shift;
119              
120 22 50       53 if ($FullStatSupport) {
121 22 100       78 $self->{'inod'} = shift if (@_);
122 22         72 return $self->{'inod'};
123             } else {
124 0         0 throw File::Properties::Error("Stat not fully supported");
125             }
126             }
127              
128              
129             # ----------------------------------------------------------------------------
130             # Get file size
131             # ----------------------------------------------------------------------------
132             sub size {
133 28     28 1 348 my $self = shift;
134              
135 28 100       86 $self->{'size'} = shift if (@_);
136 28         86 return $self->{'size'};
137             }
138              
139              
140             # ----------------------------------------------------------------------------
141             # Get file modification time
142             # ----------------------------------------------------------------------------
143             sub mtime {
144 22     22 1 111 my $self = shift;
145              
146 22 100       67 $self->{'mtim'} = shift if (@_);
147 22         86 return $self->{'mtim'};
148             }
149              
150              
151             # ----------------------------------------------------------------------------
152             # Get file mode
153             # ----------------------------------------------------------------------------
154             sub mode {
155 34     34 1 130 my $self = shift;
156              
157 34 100       87 $self->{'mode'} = shift if (@_);
158 34         190 return $self->{'mode'};
159             }
160              
161              
162             # ----------------------------------------------------------------------------
163             # Get directory content
164             # ----------------------------------------------------------------------------
165             sub children {
166 14     14 1 113 my $self = shift;
167              
168 14 100       54 $self->{'chld'} = shift if (@_);
169 14         85 return $self->{'chld'};
170             }
171              
172              
173              
174             # ----------------------------------------------------------------------------
175             # Test whether file is a regular (plain) file
176             # ----------------------------------------------------------------------------
177             sub isreg {
178 8     8 1 19 my $self = shift;
179              
180 8 50       39 return $FullStatSupport?S_ISREG($self->mode):(-f $self->path);
181             }
182              
183              
184             # ----------------------------------------------------------------------------
185             # Test whether file is a directory
186             # ----------------------------------------------------------------------------
187             sub isdir {
188 13     13 1 23 my $self = shift;
189              
190 13 50       85 return $FullStatSupport?S_ISDIR($self->mode):(-d $self->path);
191             }
192              
193              
194             # ----------------------------------------------------------------------------
195             # Construct string description of object
196             # ----------------------------------------------------------------------------
197             sub string {
198 0     0 1 0 my $self = shift;
199 0         0 my $levl = shift;
200              
201 0 0       0 $levl = 0 if (!defined $levl);
202 0         0 my $lpfx = ' ' x (2*$levl);
203 0         0 my $s;
204 0         0 $s = $lpfx . "Path: ".$self->path."\n";
205 0 0       0 $s .= $lpfx . "Device: ".$self->device." Inode: ".$self->inode." "
206             if $FullStatSupport;
207 0         0 $s .= "Size: ".$self->size." MTime: ".$self->mtime."\n";
208 0 0       0 $s .= $self->_dirstring($levl) if ($self->isdir);
209              
210 0         0 return $s;
211             }
212              
213              
214             # ----------------------------------------------------------------------------
215             # Scan a directory, constructing a hash mapping file basenames to
216             # File::Properties::Generic objects
217             # ----------------------------------------------------------------------------
218             sub _scandir {
219 1     1   2 my $self = shift;
220 1         3 my $path = shift; # Directory path
221 1         2 my $fpcr = shift; # File::Properties::Cache reference
222              
223 1 50       5 throw File::Properties::Error("Path $path is not readable")
224             if (not -r $path);
225 1 50       37 throw File::Properties::Error("Path $path is not a directory")
226             if (not -d $path);
227 1         92439 my $dh = new DirHandle $path;
228 1 50       6711 throw File::Properties::Error("Error constructing DirHandle for $path")
229             if (!defined $dh);
230 1         5 my $dhsh = {};
231 1         3 my ($dp, $fp);
232             ## Create File::Properties::Generic object for each directory entry
233 1         8 while (defined($dp = $dh->read)) {
234             # Skip . and .. directory entries
235 7 100       135 next if ($dp =~ /^\.{1,2}$/);
236             # Total path of current directory entry
237 5         35 $fp = File::Spec->catdir($path, $dp);
238             # Add hash entry for current directory entry
239 5         68 $dhsh->{$dp} = $self->new($fp, $fpcr);
240             }
241 1         16 return $dhsh;
242             }
243              
244              
245             # ----------------------------------------------------------------------------
246             # Construct a string description of an object representing a directory file
247             # ----------------------------------------------------------------------------
248             sub _dirstring {
249 0     0     my $self = shift;
250 0           my $levl = shift;
251              
252 0 0         $levl = 0 if (!defined $levl);
253 0           my $s = '';
254 0           my $chsh = $self->children;
255 0 0         if (defined $chsh) {
256 0           my $chld;
257 0           foreach $chld ( sort keys %$chsh ) {
258 0           $s .= $chsh->{$chld}->string($levl + 1);
259             }
260             }
261 0           return $s;
262             }
263              
264              
265             # ----------------------------------------------------------------------------
266             # End of method definitions
267             # ----------------------------------------------------------------------------
268              
269              
270             1;
271             __END__