File Coverage

blib/lib/File/CheckTree.pm
Criterion Covered Total %
statement 66 72 91.6
branch 22 34 64.7
condition 11 15 73.3
subroutine 10 10 100.0
pod 0 2 0.0
total 109 133 81.9


line stmt bran cond sub pod time code
1             package File::CheckTree;
2              
3 1     1   24230 use 5.006;
  1         5  
  1         37  
4 1     1   6 use Cwd;
  1         2  
  1         82  
5 1     1   5 use Exporter;
  1         86  
  1         36  
6 1     1   5 use File::Spec;
  1         1  
  1         31  
7 1     1   5 use warnings;
  1         2  
  1         43  
8 1     1   4 use strict;
  1         3  
  1         36  
9              
10 1     1   1054 use if $] > 5.017, 'deprecate';
  1         8  
  1         5  
11              
12             our $VERSION = '4.42';
13             our @ISA = qw(Exporter);
14             our @EXPORT = qw(validate);
15              
16             =head1 NAME
17              
18             File::CheckTree - run many filetest checks on a tree
19              
20             =head1 SYNOPSIS
21              
22             use File::CheckTree;
23              
24             $num_warnings = validate( q{
25             /vmunix -e || die
26             /boot -e || die
27             /bin cd
28             csh -ex
29             csh !-ug
30             sh -ex
31             sh !-ug
32             /usr -d || warn "What happened to $file?\n"
33             });
34              
35             =head1 DESCRIPTION
36              
37             The validate() routine takes a single multiline string consisting of
38             directives, each containing a filename plus a file test to try on it.
39             (The file test may also be a "cd", causing subsequent relative filenames
40             to be interpreted relative to that directory.) After the file test
41             you may put C<|| die> to make it a fatal error if the file test fails.
42             The default is C<|| warn>. The file test may optionally have a "!' prepended
43             to test for the opposite condition. If you do a cd and then list some
44             relative filenames, you may want to indent them slightly for readability.
45             If you supply your own die() or warn() message, you can use $file to
46             interpolate the filename.
47              
48             Filetests may be bunched: "-rwx" tests for all of C<-r>, C<-w>, and C<-x>.
49             Only the first failed test of the bunch will produce a warning.
50              
51             The routine returns the number of warnings issued.
52              
53             =head1 AUTHOR
54              
55             File::CheckTree was derived from lib/validate.pl which was
56             written by Larry Wall.
57             Revised by Paul Grassie > in 2002.
58              
59             =head1 HISTORY
60              
61             File::CheckTree used to not display fatal error messages.
62             It used to count only those warnings produced by a generic C<|| warn>
63             (and not those in which the user supplied the message). In addition,
64             the validate() routine would leave the user program in whatever
65             directory was last entered through the use of "cd" directives.
66             These bugs were fixed during the development of perl 5.8.
67             The first fixed version of File::CheckTree was 4.2.
68              
69             =cut
70              
71             my $Warnings;
72              
73             sub validate {
74 8     8 0 22058 my ($starting_dir, $file, $test, $cwd, $oldwarnings);
75              
76 8         64117 $starting_dir = cwd;
77              
78 8         128 $cwd = "";
79 8         34 $Warnings = 0;
80              
81 8         192 foreach my $check (split /\n/, $_[0]) {
82 39         54 my ($testlist, @testlist);
83              
84             # skip blanks/comments
85 39 100 100     670 next if $check =~ /^\s*#/ || $check =~ /^\s*$/;
86              
87             # Todo:
88             # should probably check for invalid directives and die
89             # but earlier versions of File::CheckTree did not do this either
90              
91             # split a line like "/foo -r || die"
92             # so that $file is "/foo", $test is "-r || die"
93             # (making special allowance for quoted filenames).
94 22 50 100     533 if ($check =~ m/^\s*"([^"]+)"\s+(.*?)\s*$/ or
      66        
95             $check =~ m/^\s*'([^']+)'\s+(.*?)\s*$/ or
96             $check =~ m/^\s*(\S+?)\s+(\S.*?)\s*$/)
97             {
98 22         228 ($file, $test) = ($1,$2);
99             }
100             else {
101 0         0 die "Malformed line: '$check'";
102             };
103              
104             # change a $test like "!-ug || die" to "!-Z || die",
105             # capturing the bundled tests (e.g. "ug") in $2
106 22 100       215 if ($test =~ s/ ^ (!?-) (\w{2,}) \b /$1Z/x) {
107 6         26 $testlist = $2;
108             # split bundled tests, e.g. "ug" to 'u', 'g'
109 6         60 @testlist = split(//, $testlist);
110             }
111             else {
112             # put in placeholder Z for stand-alone test
113 16         55 @testlist = ('Z');
114             }
115              
116             # will compare these two later to stop on 1st warning w/in a bundle
117 22         34 $oldwarnings = $Warnings;
118              
119 22         60 foreach my $one (@testlist) {
120             # examples of $test: "!-Z || die" or "-w || warn"
121 28         56 my $this = $test;
122              
123             # expand relative $file to full pathname if preceded by cd directive
124 28 100 100     178 $file = File::Spec->catfile($cwd, $file)
125             if $cwd && !File::Spec->file_name_is_absolute($file);
126              
127             # put filename in after the test operator
128 28         190 $this =~ s/(-\w\b)/$1 "\$file"/g;
129              
130             # change the "-Z" representing a bundle with the $one test
131 28         92 $this =~ s/-Z/-$one/;
132              
133             # if it's a "cd" directive...
134 28 100       71 if ($this =~ /^cd\b/) {
135             # add "|| die ..."
136 2         4 $this .= ' || die "cannot cd to $file\n"';
137             # expand "cd" directive with directory name
138 2         14 $this =~ s/\bcd\b/chdir(\$cwd = '$file')/;
139             }
140             else {
141             # add "|| warn" as a default disposition
142 26 100       129 $this .= ' || warn' unless $this =~ /\|\|/;
143              
144             # change a generic ".. || die" or ".. || warn"
145             # to call valmess instead of die/warn directly
146             # valmess will look up the error message from %Val_Message
147 26         279 $this =~ s/ ^ ( (\S+) \s+ \S+ ) \s* \|\| \s* (die|warn) \s* $
148             /$1 || valmess('$3', '$2', \$file)/x;
149             }
150              
151             {
152             # count warnings, either from valmess or '-r || warn "my msg"'
153             # also, call any pre-existing signal handler for __WARN__
154 28         578 my $orig_sigwarn = $SIG{__WARN__};
  28         93  
155             local $SIG{__WARN__} = sub {
156 6     6   7 ++$Warnings;
157 6 50       14 if ( $orig_sigwarn ) {
158 6         24 $orig_sigwarn->(@_);
159             }
160             else {
161 0         0 warn "@_";
162             }
163 28         507 };
164              
165             # do the test
166 28         6528 eval $this;
167              
168             # re-raise an exception caused by a "... || die" test
169 28 100       699 if (my $err = $@) {
170             # in case of any cd directives, return from whence we came
171 3 50       22038 if ($starting_dir ne cwd) {
172 0 0       0 chdir($starting_dir) || die "$starting_dir: $!";
173             }
174 3         252 die $err;
175             }
176             }
177              
178             # stop on 1st warning within a bundle of tests
179 25 100       103 last if $Warnings > $oldwarnings;
180             }
181             }
182              
183             # in case of any cd directives, return from whence we came
184 5 50       28217 if ($starting_dir ne cwd) {
185 0 0       0 chdir($starting_dir) || die "chdir $starting_dir: $!";
186             }
187              
188 5         214 return $Warnings;
189             }
190              
191             my %Val_Message = (
192             'r' => "is not readable by uid $>.",
193             'w' => "is not writable by uid $>.",
194             'x' => "is not executable by uid $>.",
195             'o' => "is not owned by uid $>.",
196             'R' => "is not readable by you.",
197             'W' => "is not writable by you.",
198             'X' => "is not executable by you.",
199             'O' => "is not owned by you.",
200             'e' => "does not exist.",
201             'z' => "does not have zero size.",
202             's' => "does not have non-zero size.",
203             'f' => "is not a plain file.",
204             'd' => "is not a directory.",
205             'l' => "is not a symbolic link.",
206             'p' => "is not a named pipe (FIFO).",
207             'S' => "is not a socket.",
208             'b' => "is not a block special file.",
209             'c' => "is not a character special file.",
210             'u' => "does not have the setuid bit set.",
211             'g' => "does not have the setgid bit set.",
212             'k' => "does not have the sticky bit set.",
213             'T' => "is not a text file.",
214             'B' => "is not a binary file."
215             );
216              
217             sub valmess {
218 6     6 0 20 my ($disposition, $test, $file) = @_;
219 6         10 my $ferror;
220              
221 6 50       51 if ($test =~ / ^ (!?) -(\w) \s* $ /x) {
222 6         17 my ($neg, $ftype) = ($1, $2);
223              
224 6         38 $ferror = "$file $Val_Message{$ftype}";
225              
226 6 50       18 if ($neg eq '!') {
227 0 0 0     0 $ferror =~ s/ is not / should not be / ||
228             $ferror =~ s/ does not / should not / ||
229             $ferror =~ s/ not / /;
230             }
231             }
232             else {
233 0         0 $ferror = "Can't do $test $file.\n";
234             }
235              
236 6 100       27 die "$ferror\n" if $disposition eq 'die';
237 5         103 warn "$ferror\n";
238             }
239              
240             1;