File Coverage

blib/lib/File/System/Real.pm
Criterion Covered Total %
statement 225 251 89.6
branch 77 124 62.1
condition 11 18 61.1
subroutine 38 38 100.0
pod 25 27 92.5
total 376 458 82.1


line stmt bran cond sub pod time code
1             package File::System::Real;
2              
3 7     7   38 use strict;
  7         10  
  7         286  
4 7     7   39 use warnings;
  7         13  
  7         355  
5              
6             our $VERSION = '1.15';
7              
8 7     7   35 use Carp;
  7         10  
  7         533  
9 7     7   7434 use File::Copy ();
  7         31413  
  7         177  
10 7     7   10944 use File::Copy::Recursive;
  7         28107  
  7         410  
11 7     7   68 use File::Glob ();
  7         16  
  7         117  
12 7     7   38 use File::Path ();
  7         13  
  7         119  
13 7     7   38 use File::Spec;
  7         59  
  7         388  
14 7     7   7395 use FileHandle;
  7         62210  
  7         79  
15              
16 7     7   3927 use base 'File::System::Object';
  7         77  
  7         19243  
17              
18             =head1 NAME
19              
20             File::System::Real - A file system module based on the real file system
21              
22             =head1 SYNOPSIS
23              
24             use File::System;
25             $root = File::System->new('Real', root => '/usr/local');
26              
27             =head1 DESCRIPTION
28              
29             This is the most basic file system implementation. It is purely implemented within terms of a real file system.
30              
31             =head1 OPTIONS
32              
33             This file system module accepts only a single object, C. If not given, the current working directory is assumed for the value C. All files returned by the file system will be rooted at the given (or assumed) point.
34              
35             =cut
36              
37             sub new {
38 13     13 0 137 my $class = shift;
39 13         56 my %args = @_;
40              
41 13   50     57 $args{root} ||= '.';
42 13         594 $args{root} = File::Spec->rel2abs($args{root});
43 13         157 $args{root} = $class->normalize_path($args{root});
44 13         70 my $root = File::Spec->canonpath($args{root});
45              
46 13 50       561 -e $root or croak "Sorry, root $root does not exist!";
47 13 50       336 -d $root or croak "Sorry, root $root is not a directory!";
48              
49 13         124 return bless {
50             fs_root => $root,
51             path => '/',
52             fullpath => $root,
53             }, $class;
54             }
55              
56             sub is_valid {
57 983     983 1 2703 my $self = shift;
58 983         29201 return -e $self->{fullpath};
59             }
60              
61             sub root {
62 20     20 1 41 my $self = shift;
63              
64 20         579 return bless {
65             fs_root => $self->{fs_root},
66             path => '/',
67             fullpath => $self->{fs_root},
68             }, ref $self;
69             }
70              
71             sub exists {
72 865     865 1 17238 my $self = shift;
73 865   66     2134 my $path = shift || $self->path;
74              
75 865         3016 return -e $self->normalize_real_path($path);
76             }
77              
78             sub lookup {
79 28236     28236 1 95861 my $self = shift;
80 28236         49746 my $path = shift;
81              
82 28236         81583 my $abspath = $self->normalize_path($path);
83 28236         85256 my $fullpath = $self->normalize_real_path($path);
84              
85             return undef
86 28236 100       846246 unless -e $fullpath;
87              
88 28067         252006 return bless {
89             fs_root => $self->{fs_root},
90             path => $abspath,
91             fullpath => $fullpath,
92             }, ref $self;
93             }
94              
95             sub glob {
96 320     320 1 433 my $self = shift;
97 320         396 my $glob = shift;
98              
99 320         850 my $absglob = $self->normalize_path($glob);
100              
101 320         1113 my $fullglob = $self->normalize_real_path($absglob);
102              
103 228         7673 return sort map {
104 320         29779 s/^$self->{fs_root}//;
105 228         1071 bless {
106             fs_root => $self->{fs_root},
107             path => $self->normalize_path($_),
108             fullpath => $self->normalize_real_path($_),
109             }, ref $self
110             } File::Glob::bsd_glob($fullglob);
111             }
112              
113             sub properties {
114 2569     2569 1 190324 my $self = shift;
115              
116 2569         28655 return qw/
117             basename
118             dirname
119             path
120             object_type
121             dev
122             ino
123             mode
124             nlink
125             uid
126             gid
127             rdev
128             size
129             atime
130             mtime
131             ctime
132             blksize
133             blocks
134             /;
135             }
136              
137             sub settable_properties {
138 364     364 1 2021 my $self = shift;
139              
140 364         2482 return qw/
141             mode
142             uid
143             gid
144             atime
145             mtime
146             /;
147             }
148              
149             sub _stat {
150 1690     1690   2196 my $self = shift;
151              
152 1690         56432 my @stat = stat $self->{fullpath};
153 1690         31030 return \@stat;
154             }
155              
156             sub get_property {
157 92490     92490 1 143648 my $self = shift;
158 92490         155101 local $_ = shift;
159              
160             SWITCH: {
161 92490 100       127588 /^basename$/ && do {
  92490         222724  
162 2281         9326 return $self->basename_of_path($self->{path});
163             };
164 90209 100       221216 /^dirname$/ && do {
165 2647         9284 return $self->dirname_of_path($self->{path});
166             };
167 87562 100       205921 /^path$/ && do {
168 23201         175071 return $self->{path};
169             };
170 64361 100       211908 /^object_type$/ && do {
171 62671         86414 my $result = '';
172 62671 100       1752761 $result .= 'd' if -d $self->{fullpath};
173 62671 100       1409442 $result .= 'f' if -f $self->{fullpath};
174 62671         524498 return $result;
175             };
176 1690 50       3517 /^dev$/ && do {
177 0         0 return $self->_stat->[0];
178             };
179 1690 50       3363 /^ino$/ && do {
180 0         0 return $self->_stat->[1];
181             };
182 1690 100       4135 /^mode$/ && do {
183 338         1186 return $self->_stat->[2];
184             };
185 1352 50       2793 /^nlink$/ && do {
186 0         0 return $self->_stat->[3];
187             };
188 1352 50       17347 /^uid$/ && do {
189 0         0 return $self->_stat->[4];
190             };
191 1352 50       3340 /^gid$/ && do {
192 0         0 return $self->_stat->[5];
193             };
194 1352 50       2943 /^rdev$/ && do {
195 0         0 return $self->_stat->[6];
196             };
197 1352 50       3181 /^size$/ && do {
198 0         0 return $self->_stat->[7];
199             };
200 1352 100       3998 /^atime$/ && do {
201 676         2395 return $self->_stat->[8];
202             };
203 676 50       2938 /^mtime$/ && do {
204 676         2007 return $self->_stat->[9];
205             };
206 0 0       0 /^ctime$/ && do {
207 0         0 return $self->_stat->[10];
208             };
209 0 0       0 /^blksize$/ && do {
210 0         0 return $self->_stat->[11];
211             };
212 0 0       0 /^blocks$/ && do {
213 0         0 return $self->_stat->[12];
214             };
215 0         0 DEFAULT: {
216 0         0 return undef;
217             }
218             }
219             }
220              
221             sub set_property {
222 1014     1014 1 5265 my $self = shift;
223 1014         1745 local $_ = shift;
224 1014         1364 my $value = shift;
225              
226             SWITCH: {
227 1014 100       1421 /^mode$/ && do {
  1014         4044  
228 338         20881 chmod $value, $self->{fullpath};
229 338         1955 last SWITCH;
230             };
231 676 50       1887 /^uid$/ && do {
232 0         0 chown $value, $self->get_property('gid'), $self->{fullpath};
233 0         0 last SWITCH;
234             };
235 676 50       1500 /^gid$/ && do {
236 0         0 chown $self->get_property('uid'), $value, $self->{fullpath};
237 0         0 last SWITCH;
238             };
239 676 100       2056 /^atime$/ && do {
240 338         901 utime $value, $self->get_property('mtime'), $self->{fullpath};
241 338         1741 last SWITCH;
242             };
243 338 50       1519 /^mtime$/ && do {
244 338         910 utime $self->get_property('atime'), $value, $self->{fullpath};
245 338         1562 last SWITCH;
246             };
247 0         0 DEFAULT: {
248 0         0 croak "Cannot set unknown property '$_'";
249             }
250             }
251             }
252              
253             sub is_creatable {
254 10     10 1 17 my $self = shift;
255 10         18 my $path = shift;
256 10         15 my $type = shift;
257              
258 10 50       26 defined $type
259             or croak "No type argument given.";
260              
261 10   33     96 return ($type eq 'f' || $type eq 'd') && !$self->exists($path);
262             }
263              
264             sub create {
265 690     690 1 46389 my $self = shift;
266 690         1024 my $path = shift;
267 690         1005 my $type = shift;
268              
269 690 50       2063 defined $type
270             or croak "Missing required argument 'type'.";
271              
272 690 100       2766 if ($type eq 'f') {
    50          
273 230         558 my $fulldir = $self->dirname_of_path($self->normalize_real_path($path));
274              
275 230         14036 File::Path::mkpath($fulldir, 0);
276              
277 230         890 my $abspath = $self->normalize_path($path);
278 230         565 my $fullpath = $self->normalize_real_path($path);
279              
280 230 50       1582 my $fh = FileHandle->new(">$fullpath")
281             or croak "Cannot create file $abspath: $!";
282 230         65493 close $fh;
283              
284 230         8937 return bless {
285             fs_root => $self->{fs_root},
286             path => $abspath,
287             fullpath => $fullpath,
288             }, ref $self;
289             } elsif ($type eq 'd') {
290 460         1527 my $abspath = $self->normalize_path($path);
291 460         1995 my $fullpath = $self->normalize_real_path($path);
292              
293 460         130772 File::Path::mkpath($fullpath, 0);
294              
295 460 50       14503 -d $fullpath
296             or croak "Failed to create directory '$abspath'";
297              
298 460         6090 return bless {
299             fs_root => $self->{fs_root},
300             path => $abspath,
301             fullpath => $fullpath,
302             }, ref $self;
303             } else {
304 0         0 return undef;
305             }
306             }
307              
308             sub rename {
309 496     496 1 885 my $self = shift;
310 496         1456 my $name = shift;
311              
312 496 50       1552 croak "The 'name' argument must be a plan name, not a path. However, the given value ($name) contains a slash."
313             if $name =~ m#/#;
314              
315 496         1575 my $abspath = $self->normalize_path($self->dirname.'/'.$name);
316 496         1726 my $fullpath = $self->normalize_real_path($self->dirname.'/'.$name);
317              
318 496         47660 rename $self->{fullpath}, $fullpath;
319              
320 496         1424 $self->{path} = $abspath;
321 496         906 $self->{fullpath} = $fullpath;
322              
323 496         1615 return $self;
324             }
325              
326             sub move {
327 496     496 1 1998 my $self = shift;
328 496         738 my $to = shift;
329 496   100     2009 my $force = shift || 0;
330              
331 496 50       2327 UNIVERSAL::isa($to, ref $self)
332             or croak "Move failed; the '$to' object is not a '",ref $self,"'";
333              
334 496 50       1869 $to->{fs_root} eq $self->{fs_root}
335             or croak "Move failed; the '$to' object belongs to a different root.";
336              
337 496 50       1710 $to->is_valid
338             or croak "Move failed; the '$to' object is not valid.";
339            
340 496 50       2086 $to->is_container
341             or croak "Move failed; the '$to' object is not a directory.";
342              
343 496 50       2467 defined $to->child($self->basename)
344             and croak "Move failed; the '$to/",$self->basename,"' object already exists.";
345              
346 496 100       2399 if ($self->is_container) {
347 62 50       225 if ($force) {
348 62         224 $to->create($self->basename, 'd');
349 62 50       462 File::Copy::Recursive::dircopy($self->{fullpath}, $to->{fullpath}.'/'.$self->basename)
350             or croak "Move failed; dircopy failure to '$to'";
351 62         144764 File::Path::rmtree($self->{fullpath});
352             } else {
353 0         0 croak "Move failed; cannot move a directory unless the 'force' argument is true.";
354             }
355             } else {
356 434         2722 File::Copy::move($self->{fullpath}, $to->{fullpath});
357             }
358              
359 496         120362 my $name = $self->basename;
360              
361 496         4940 $self->{path} = $self->normalize_path($to->path.'/'.$name);
362 496         1690 $self->{fullpath} = $self->normalize_real_path($to->path.'/'.$name);
363              
364 496         1697 return $self;
365             }
366              
367             sub copy {
368 248     248 1 389 my $self = shift;
369 248         427 my $to = shift;
370 248   100     1751 my $force = shift || 0;
371              
372 248 50       1122 UNIVERSAL::isa($to, ref $self)
373             or croak "Copy failed; the '$to' object is not a '",ref $self,"'";
374              
375 248 50       898 $to->{fs_root} eq $self->{fs_root}
376             or croak "Copy failed; the '$to' object belongs to a different root.";
377              
378 248 50       734 $to->is_valid
379             or croak "Copy failed; the '$to' object is not valid.";
380            
381 248 50       6115 $to->is_container
382             or croak "Copy failed; the '$to' object is not a directory.";
383              
384 248 50       1035 defined $to->child($self->basename, 'd')
385             and croak "Copy failed; the '$to/",$self->basename,"' object already exists.";
386              
387 248 100       1028 if ($self->is_container) {
388 31 50       86 if ($force) {
389 31         110 $to->create($self->basename, 'd');
390 31 50       377 File::Copy::Recursive::dircopy($self->{fullpath}, $to->{fullpath}.'/'.$self->basename)
391             or croak "Copy failed; dircopy failure to '$to'";
392             } else {
393 0         0 croak "Copy failed; cannot copy a directory unless the 'force' argument is true.";
394             }
395             } else {
396 217         1218 File::Copy::copy($self->{fullpath}, $to->{fullpath});
397             }
398              
399 248         444570 return bless {
400             fs_root => $self->{fs_root},
401             path => $self->normalize_path($to->path.'/'.$self->basename),
402             fullpath => $self->normalize_real_path($to->path.'/'.$self->basename),
403             }, ref $self;
404             }
405              
406             sub remove {
407 496     496 1 183391 my $self = shift;
408 496         786 my $force = shift;
409              
410 496 100 66     32250 if (-d $self->{fullpath} && $force) {
    50 33        
    50          
411 279         153762 File::Path::rmtree($self->{fullpath});
412             } elsif (-d $self->{fullpath} && $self->has_children) {
413 0         0 croak "Cannot delete directory with children unless force is true.";
414             } elsif (-d $self->{fullpath}) {
415 0         0 rmdir $self->{fullpath};
416             } else {
417 217         35591 unlink $self->{fullpath};
418             }
419             }
420              
421             sub is_readable {
422 224     224 1 1268 my $self = shift;
423 224         770 return $self->has_content;
424             }
425              
426             sub is_seekable {
427 224     224 1 1269 my $self = shift;
428             # TODO This is naive. Seekability is a little less available than this
429             # would indicate.
430 224         793 return $self->has_content;
431             }
432              
433             sub is_writable {
434 224     224 1 1820 my $self = shift;
435 224         1039 return $self->has_content;
436             }
437              
438             sub is_appendable {
439 224     224 1 1104 my $self = shift;
440 224         771 return $self->has_content;
441             }
442              
443             sub open {
444 1568     1568 1 4389 my $self = shift;
445 1568         2009 my $access = shift;
446 1568 0       8895 return FileHandle->new($self->{fullpath}, $access)
447             or croak "Cannot open $self with access mode '$access': $!";
448             }
449              
450             sub content {
451 896     896 1 4386 my $self = shift;
452              
453 896         1873 my $fh = $self->open("r");
454 896         102611 my @lines = <$fh>;
455 896         10400 close $fh;
456              
457 896 100       7763 return wantarray ? @lines : join '', @lines;
458             }
459              
460             sub has_children {
461 119     119 1 947 my $self = shift;
462              
463 119 50       4646 opendir DH, $self->{fullpath}
464             or croak "Cannot open directory $self for listing: $!";
465 119         2529 my @dirs = grep !/^\.\.?$/, readdir DH;
466 119         1683 closedir DH;
467              
468 119 100       1390 return @dirs ? 1 : '';
469             }
470              
471             sub children_paths {
472 4367     4367 1 22754 my $self = shift;
473              
474 4367 50       10333481 opendir DH, $self->{fullpath}
475             or croak "Cannot open directory $self for listing: $!";
476 4367         1381109 my @paths = map { s/^$self->{fs_root}//; $_ } readdir DH;
  25938         105132  
  25938         78747  
477 4367         344479 closedir DH;
478              
479 4367         41236 return @paths;
480             }
481              
482             sub children {
483 746     746 1 1296 my $self = shift;
484              
485 746 50       36233 opendir DH, $self->{fullpath}
486             or croak "Cannot open directory $self for listing: $!";
487             my @children = map {
488 746 100       21829 if (/^\.\.?$/) {
  2482         7307  
489             ()
490 1492         2688 } else {
491 990         3615 bless {
492             fs_root => $self->{fs_root},
493             path => $self->normalize_path($_),
494             fullpath => $self->normalize_real_path($_),
495             }, ref $self;
496             }
497             } readdir DH;
498 746         9676 closedir DH;
499              
500 746         5527 return @children;
501             }
502              
503             sub child {
504 788     788 1 1655 my $self = shift;
505 788         2946 my $name = shift;
506              
507 788 50       2330 croak "Name given, '$name', is a path rather than a name (i.e., it contains a slash)." if $name =~ m#/#;
508              
509 788         2637 my $abspath = $self->normalize_path($name);
510 788         2821 my $fullpath = $self->normalize_real_path($name);
511              
512 788 100       25460 if (-e $fullpath) {
513 42         910 return bless {
514             fs_root => $self->{fs_root},
515             path => $abspath,
516             fullpath => $fullpath,
517             }, ref $self;
518             } else {
519 746         2939 return undef;
520             }
521             }
522              
523             # =item $real_path = $obj->normalize_real_path($messy_path)
524             #
525             # Like C, except that it returns a real absolute path.
526             #
527             # =cut
528              
529             sub normalize_real_path {
530 33609     33609 0 51430 my $self = shift;
531 33609         48113 my $path = shift;
532              
533 33609         90260 my $abspath = $self->normalize_path($path);
534 33609         476755 my $fullpath = File::Spec->canonpath(
535             File::Spec->catfile($self->{fs_root}, $abspath)
536             );
537              
538 33609         155368 return $fullpath;
539             }
540              
541             =head1 SEE ALSO
542              
543             L, L
544              
545             =head1 AUTHOR
546              
547             Andrew Sterling Hanenkamp, Ehanenkamp@users.sourceforge.netE
548              
549             =head1 COPYRIGHT AND LICENSE
550              
551             Copyright 2005 Andrew Sterling Hanenkamp. All Rights Reserved.
552              
553             This software is distributed and licensed under the same terms as Perl itself.
554              
555             =cut
556              
557             1