File Coverage

blib/lib/File/stat/Extra.pm
Criterion Covered Total %
statement 85 90 94.4
branch 17 26 65.3
condition n/a
subroutine 31 31 100.0
pod 13 13 100.0
total 146 160 91.2


line stmt bran cond sub pod time code
1             package File::stat::Extra;
2 1     1   13877 use strict;
  1         2  
  1         23  
3 1     1   3 use warnings;
  1         1  
  1         22  
4 1     1   4 use warnings::register;
  1         1  
  1         92  
5              
6 1     1   13 use 5.006;
  1         2  
7              
8             # ABSTRACT: An extension of the File::stat module, provides additional methods.
9             our $VERSION = '0.008'; # VERSION
10              
11             #pod =begin :prelude
12             #pod
13             #pod =for test_synopsis
14             #pod my ($st, $file);
15             #pod
16             #pod =end :prelude
17             #pod
18             #pod =head1 SYNOPSIS
19             #pod
20             #pod use File::stat::Extra;
21             #pod
22             #pod $st = lstat($file) or die "No $file: $!";
23             #pod
24             #pod if ($st->isLink) {
25             #pod print "$file is a symbolic link";
26             #pod }
27             #pod
28             #pod if (-x $st) {
29             #pod print "$file is executable";
30             #pod }
31             #pod
32             #pod use Fcntl 'S_IRUSR';
33             #pod if ( $st->cando(S_IRUSR, 1) ) {
34             #pod print "My effective uid can read $file";
35             #pod }
36             #pod
37             #pod if ($st == stat($file)) {
38             #pod printf "%s and $file are the same", $st->file;
39             #pod }
40             #pod
41             #pod =head1 DESCRIPTION
42             #pod
43             #pod This module's default exports override the core stat() and lstat()
44             #pod functions, replacing them with versions that return
45             #pod C objects when called in scalar context. In list
46             #pod context the same 13 item list is returned as with the original C
47             #pod and C functions.
48             #pod
49             #pod C is an extension of the L
50             #pod module.
51             #pod
52             #pod =for :list
53             #pod * Returns non-object result in list context.
54             #pod * You can now pass in bare file handles to C and C under C.
55             #pod * File tests C<-t> C<-T>, and C<-B> have been implemented too.
56             #pod * Convenience functions C and C for direct access to filetype and permission parts of the mode field.
57             #pod * Named access to common file tests (C / C, C, C, C, C, C / C, C).
58             #pod * Access to the name of the file / file handle used for the stat (C, C / C).
59             #pod
60             #pod =head1 SEE ALSO
61             #pod
62             #pod =for :list
63             #pod * L for the module for which C is the extension.
64             #pod * L and L for the original C and C functions.
65             #pod
66             #pod =head1 COMPATIBILITY
67             #pod
68             #pod As with L, you can no longer use the implicit C<$_> or the
69             #pod special filehandle C<_> with this module's versions of C and
70             #pod C.
71             #pod
72             #pod Currently C only provides an object interface, the
73             #pod L C<$st_*> variables and C funtion are not
74             #pod available. This may change in a future version of this module.
75             #pod
76             #pod =head1 WARNINGS
77             #pod
78             #pod When a file (handle) can not be (l)stat-ed, a warning C
79             #pod stat: %s>. To disable this warning, specify
80             #pod
81             #pod no warnings "File::stat::Extra";
82             #pod
83             #pod The following warnings are inhereted from C, these can all
84             #pod be disabled with
85             #pod
86             #pod no warnings "File::stat";
87             #pod
88             #pod =over 4
89             #pod
90             #pod =item File::stat ignores use filetest 'access'
91             #pod
92             #pod You have tried to use one of the C<-rwxRWX> filetests with C
93             #pod filetest 'access'> in effect. C will ignore the pragma, and
94             #pod just use the information in the C member as usual.
95             #pod
96             #pod =item File::stat ignores VMS ACLs
97             #pod
98             #pod VMS systems have a permissions structure that cannot be completely
99             #pod represented in a stat buffer, and unlike on other systems the builtin
100             #pod filetest operators respect this. The C overloads, however,
101             #pod do not, since the information required is not available.
102             #pod
103             #pod =back
104             #pod
105             #pod =cut
106              
107             # Note: we are not defining File::stat::Extra as a subclass of File::stat
108             # as we need to add an additional field and can not rely on the fact that
109             # File::stat will always be implemented as an array (struct).
110              
111             our @ISA = qw(Exporter);
112             our @EXPORT = qw(stat lstat);
113              
114 1     1   476 use File::stat ();
  1         6338  
  1         19  
115 1     1   6 use File::Spec ();
  1         1  
  1         10  
116 1     1   4 use Cwd ();
  1         1  
  1         8  
117 1     1   3 use Fcntl ();
  1         1  
  1         87  
118              
119             require Carp;
120             $Carp::Internal{ (__PACKAGE__) }++; # To get warnings reported at correct caller level
121              
122             #pod =func stat( I )
123             #pod
124             #pod =func stat( I )
125             #pod
126             #pod =func stat( I )
127             #pod
128             #pod =func lstat( I )
129             #pod
130             #pod =func lstat( I )
131             #pod
132             #pod =func lstat( I )
133             #pod
134             #pod When called in list context, these functions behave as the original
135             #pod C and C functions, returning the 13 element C list.
136             #pod When called in scalar context, a C object is
137             #pod returned with the methods as outlined below.
138             #pod
139             #pod =cut
140              
141             # Runs stat or lstat on "file"
142             sub __stat_lstat {
143 29     29   34 my $func = shift;
144 29         21 my $file = shift;
145              
146 29 100       282 return $func eq 'lstat' ? CORE::lstat($file) : CORE::stat($file);
147             }
148              
149             # Wrapper around stat/lstat, handles passing of file as a bare handle too
150             sub _stat_lstat {
151 25     25   24 my $func = shift;
152 25         23 my $file = shift;
153              
154 25         30 my @stat = __stat_lstat($func, $file);
155              
156 25 100       40 if (@stat) {
157             # We have a file, so make it absolute (NOT resolving the symlinks)
158 21 100       259 $file = File::Spec->rel2abs($file) if !ref $file;
159             } else {
160             # Try again, interpretting $file as handle
161 1     1   4 no strict 'refs'; ## no critic (TestingAndDebugging::ProhibitNoStrict)
  1         1  
  1         176  
162 4         32 local $! = undef;
163 4         516 require Symbol;
164 4         672 my $fh = \*{ Symbol::qualify($file, caller(1)) };
  4         23  
165 4 50       62 if (defined fileno $fh) {
166 4         5 @stat = __stat_lstat($func, $fh);
167             }
168 4 50       12 if (!@stat) {
169 0         0 warnings::warnif("Unable to stat: $file");
170 0         0 return;
171             }
172             # We have a (valid) file handle, so we make file point to it
173 4         9 $file = $fh;
174             }
175              
176 25 100       34 if (wantarray) {
177 8         25 return @stat;
178             } else {
179 17         36 return bless [ File::stat::populate(@stat), $file ], 'File::stat::Extra';
180             }
181             }
182              
183             sub stat(*) { ## no critic (Subroutines::ProhibitSubroutinePrototypes)
184 17     17 1 4308 return _stat_lstat('stat', shift);
185             }
186              
187             sub lstat(*) { ## no critic (Subroutines::ProhibitSubroutinePrototypes)
188 8     8 1 433 return _stat_lstat('lstat', shift);
189             }
190              
191             #pod =method dev
192             #pod
193             #pod =method ino
194             #pod
195             #pod =method mode
196             #pod
197             #pod =method nlink
198             #pod
199             #pod =method uid
200             #pod
201             #pod =method gid
202             #pod
203             #pod =method rdev
204             #pod
205             #pod =method size
206             #pod
207             #pod =method atime
208             #pod
209             #pod =method mtime
210             #pod
211             #pod =method ctime
212             #pod
213             #pod =method blksize
214             #pod
215             #pod =method blocks
216             #pod
217             #pod These methods provide named acced to the same fields in the original
218             #pod C result. Just like the original L.
219             #pod
220             #pod =method cando( I, I )
221             #pod
222             #pod Interprets the C, C and C fields, and returns whether
223             #pod or not the current process would be allowed the specified access.
224             #pod
225             #pod I is one of C, C or C from the
226             #pod L module, and I indicates whether to use
227             #pod effective (true) or real (false) ids.
228             #pod
229             #pod =cut
230              
231             BEGIN {
232 1     1   10 no strict 'refs'; ## no critic (TestingAndDebugging::ProhibitNoStrict)
  1         1  
  1         157  
233              
234             # Define the main field accessors and the cando method using the File::stat version
235 1     1   2 for my $f (qw(dev ino mode nlink uid gid rdev size atime mtime ctime blksize blocks cando)) {
236 14     93   31 *{$f} = sub { $_[0][0]->$f; }
  93         5835  
237 14         21 }
238              
239             #pod =for Pod::Coverage S_ISBLK S_ISCHR S_ISDIR S_ISFIFO S_ISLNK S_ISREG S_ISSOCK
240             #pod
241             #pod =cut
242              
243             # Create own versions of these functions as they will croak on use
244             # if the platform doesn't define them. It's important to avoid
245             # inflicting that on the user.
246             # Note: to stay (more) version independent, we do not rely on the
247             # implementation in File::stat, but rather recreate here.
248 1         1 for (qw(BLK CHR DIR LNK REG SOCK)) {
249 6 50       6 *{"S_IS$_"} = defined eval { &{"Fcntl::S_IF$_"} } ? \&{"Fcntl::S_IS$_"} : sub { '' };
  6         11  
  6         4  
  6         14  
  6         7  
  0         0  
250             }
251             # FIFO flag and macro don't quite follow the S_IF/S_IS pattern above
252 1 50       3 *{'S_ISFIFO'} = defined &Fcntl::S_IFIFO ? \&Fcntl::S_ISFIFO : sub { '' };
  1         728  
  0         0  
253             }
254              
255             #pod =method file
256             #pod
257             #pod Returns the full path to the original file (or the filehandle) on which
258             #pod C or C was called.
259             #pod
260             #pod Note: Symlinks are not resolved. And, like C, neither are
261             #pod C constructs. Use the C / C methods to
262             #pod resolve these too.
263             #pod
264             #pod =cut
265              
266             sub file {
267 9     9 1 826 return $_[0][1];
268             }
269              
270             #pod =method abs_file
271             #pod
272             #pod =method target
273             #pod
274             #pod Returns the absolute path of the file. In case of a file handle, this is returned unaltered.
275             #pod
276             #pod =cut
277              
278             sub abs_file {
279 2 50   2 1 5 return ref $_[0]->file ? $_[0]->file : Cwd::abs_path($_[0]->file);
280             }
281              
282             *target = *abs_file;
283              
284             #pod =method permissions
285             #pod
286             #pod Returns just the permissions (including setuid/setgid/sticky bits) of the C stat field.
287             #pod
288             #pod =cut
289              
290             sub permissions {
291 2     2 1 810 return Fcntl::S_IMODE($_[0]->mode);
292             }
293              
294             #pod =method filetype
295             #pod
296             #pod Returns just the filetype of the C stat field.
297             #pod
298             #pod =cut
299              
300             sub filetype {
301 2     2 1 565 return Fcntl::S_IFMT($_[0]->mode);
302             }
303              
304             #pod =method isFile
305             #pod
306             #pod =method isRegular
307             #pod
308             #pod Returns true if the file is a regular file (same as -f file test).
309             #pod
310             #pod =cut
311              
312             sub isFile {
313 2     2 1 298 return S_ISREG($_[0]->mode);
314             }
315              
316             *isRegular = *isFile;
317              
318             #pod =method isDir
319             #pod
320             #pod Returns true if the file is a directory (same as -d file test).
321             #pod
322             #pod =cut
323              
324             sub isDir {
325 2     2 1 395 return S_ISDIR($_[0]->mode);
326             }
327              
328             #pod =method isLink
329             #pod
330             #pod Returns true if the file is a symbolic link (same as -l file test).
331             #pod
332             #pod Note: Only relevant when C was used!
333             #pod
334             #pod =cut
335              
336             sub isLink {
337 5     5 1 812 return S_ISLNK($_[0]->mode);
338             }
339              
340             #pod =method isBlock
341             #pod
342             #pod Returns true if the file is a block special file (same as -b file test).
343             #pod
344             #pod =cut
345              
346             sub isBlock {
347 1     1 1 168 return S_ISBLK($_[0]->mode);
348             }
349              
350             #pod =method isChar
351             #pod
352             #pod Returns true if the file is a character special file (same as -c file test).
353             #pod
354             #pod =cut
355              
356             sub isChar {
357 1     1 1 173 return S_ISCHR($_[0]->mode);
358             }
359              
360             #pod =method isFIFO
361             #pod
362             #pod =method isPipe
363             #pod
364             #pod Returns true if the file is a FIFO file or, in case of a file handle, a pipe (same as -p file test).
365             #pod
366             #pod =cut
367              
368             sub isFIFO {
369 1     1 1 181 return S_ISFIFO($_[0]->mode);
370             }
371              
372             *isPipe = *isFIFO;
373              
374             #pod =method isSocket
375             #pod
376             #pod Returns true if the file is a socket file (same as -S file test).
377             #pod
378             #pod =cut
379              
380             sub isSocket {
381 1     1 1 170 return S_ISSOCK($_[0]->mode);
382             }
383              
384             #pod =method -X operator
385             #pod
386             #pod You can use the file test operators on the C object
387             #pod just as you would on a file (handle). However, instead of querying the
388             #pod file system, these operators will use the information from the
389             #pod object itself.
390             #pod
391             #pod The overloaded filetests are only supported from Perl version 5.12 and
392             #pod higer. The named access to these tests can still be used though.
393             #pod
394             #pod Note: in case of the special file tests C<-t>, C<-T>, and C<-B>, the
395             #pod file (handle) I tested the I time the operator is
396             #pod used. After the first time, the initial result is re-used and no
397             #pod further testing of the file (handle) is performed.
398             #pod
399             #pod =method Unary C<""> (stringification) operator
400             #pod
401             #pod The unary C<""> (stringification) operator is overloaded to return the the device and inode
402             #pod numbers separated by a C<.> (C.I>). This yields a uniqe file identifier (as string).
403             #pod
404             #pod =method Comparison operators C<< <=> >>, C, and C<~~>
405             #pod
406             #pod The comparison operators use the string representation of the
407             #pod C object. So, to see if two C
408             #pod object point to the same (hardlinked) file, you can simply say
409             #pod something like this:
410             #pod
411             #pod print 'Same file' if $obj1 == $obj2;
412             #pod
413             #pod For objects created from an C of a symbolic link, the actual
414             #pod I of the link is used in the comparison! If you want to
415             #pod compare the actual symlink file, use C instead.
416             #pod
417             #pod Note: All comparisons (also the numeric versions) are performed on the
418             #pod full stringified versions of the object. This to prevent files on the
419             #pod same device, but with an inode number ending in a zero to compare
420             #pod equally while they aren't (e.g., 5.10 and 5.100 compare equal
421             #pod numerically but denote a different file).
422             #pod
423             #pod Note: the smartmatch C<~~> operator is only overloaded on Perl version
424             #pod 5.10 and above.
425             #pod
426             #pod =method Other operators
427             #pod
428             #pod As the other operators (C<+>, C<->, C<*>, etc.) are meaningless, they
429             #pod have not been overloaded and will cause a run-time error.
430             #pod
431             #pod =cut
432              
433             my %op = (
434             # Use the named version of these tests
435             f => sub { $_[0]->isRegular },
436             d => sub { $_[0]->isDir },
437             l => sub { $_[0]->isLink },
438             p => sub { $_[0]->isFIFO },
439             S => sub { $_[0]->isSocket },
440             b => sub { $_[0]->isBlock },
441             c => sub { $_[0]->isChar },
442              
443             # Defer implementation of rest to File::stat
444             r => sub { -r $_[0][0] },
445             w => sub { -w $_[0][0] },
446             x => sub { -x $_[0][0] },
447             o => sub { -o $_[0][0] },
448              
449             R => sub { -R $_[0][0] },
450             W => sub { -W $_[0][0] },
451             X => sub { -X $_[0][0] },
452             O => sub { -O $_[0][0] },
453              
454             e => sub { -e $_[0][0] },
455             z => sub { -z $_[0][0] },
456             s => sub { -s $_[0][0] },
457              
458             u => sub { -u $_[0][0] },
459             g => sub { -g $_[0][0] },
460             k => sub { -k $_[0][0] },
461              
462             M => sub { -M $_[0][0] },
463             C => sub { -C $_[0][0] },
464             A => sub { -A $_[0][0] },
465              
466             # Implement these operators by testing the underlying file, caching the result
467             t => sub { defined $_[0][2] ? $_[0][2] : $_[0][2] = (-t $_[0]->file) || 0 }, ## no critic (InputOutput::ProhibitInteractiveTest)
468             T => sub { defined $_[0][3] ? $_[0][3] : $_[0][3] = (-T $_[0]->file) || 0 },
469             B => sub { defined $_[0][4] ? $_[0][4] : $_[0][4] = (-B $_[0]->file) || 0 },
470             );
471              
472             sub _filetest {
473 6     6   745 my ($s, $op) = @_;
474 6 50       15 if ($op{$op}) {
475 6         15 return $op{$op}->($s);
476             } else {
477             # We should have everything covered so this is just a safegauard
478 0         0 Carp::croak "-$op is not implemented on a File::stat::Extra object";
479             }
480             }
481              
482             sub _dev_ino {
483 12     12   42 return $_[0]->dev . "." . $_[0]->ino;
484             }
485              
486             sub _compare {
487 6     6   1271 my $va = shift;
488 6         7 my $vb = shift;
489 6         5 my $swapped = shift;
490 6 50       13 ($vb, $va) = ($va, $vb) if $swapped;
491              
492 6         8 return "$va" cmp "$vb"; # Force stringification when comparing
493             }
494              
495             use overload
496             # File test operators (as of Perl v5.12)
497 1 50       23 $^V >= 5.012 ? (-X => \&_filetest) : (),
    50          
498              
499             # Unary "" returns the object as "dev.ino", this should be a
500             # unique string for each file.
501             '""' => \&_dev_ino,
502              
503             # Comparison is done based on the unique string created with the stringification
504             '<=>' => \&_compare,
505             'cmp' => \&_compare,
506              
507             # Smartmatch as of Perl v5.10
508             $^V >= 5.010 ? ('~~' => \&_compare) : (),
509              
510 1     1   4 ;
  1         1  
511              
512             1;
513              
514             __END__