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