#!/usr/bin/perl # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # Given either a list of files containing paths on the command line or # a set of paths on standard input, validate that the paths actually # exist, and complain if they do not. This is invoked by nightly to # verify the contents of various control files used by the ON build # process. # # Command line options: # # -m Show the matches (for debug). # # -r Allow shell globs in the paths. Unless otherwise # flagged by a keyword (see -k) or exclusion (see -e), # it is an error if no files match the expression at # all. # # -s/from/to/ # Perform a substitution on all of the paths in the # file. This substitution is performed after stripping # any in-line comments but before any exclusion matching # is done. The option may include any legal Perl # substitution expression and may be repeated to give # multiple expressions. # # -e # Exclude paths matching the given pattern from the # "must exist" rule. These paths will not be checked. # Option may include any legal Perl regular expression, # and may be repeated to give multiple patterns. # # -k # Exclude paths if there is either an in-line comment # containing the given keyword, or the preceding line # consists of only a comment containing that keyword. # Option may be repeated to provide multiple keywords. # # -b # Base directory for relative paths tested. # # -n # String to use in place of file name when using stdin use strict; my ($opt_r, $opt_m, @opt_s, @opt_e, @opt_k, $opt_b, $opt_n); my ($keywords, @exclude); sub usage { die "usage: $0 [-r] [-m]\n", "\t[-s/from/to/] [-e ] [-k ] [-b ]\n", "\t[-n ] [files...]\n"; } # process the path list in a given file sub process_paths { my ($FILE, $name) = @_; my ($ignore, $file, $line); $ignore = 0; $line = 0; while (<$FILE>) { chomp; $line++; # Ignore comment lines if (/^\s*#(.*)$/) { $ignore = ($1 =~ /$keywords/) if defined $keywords; next; } # Extract path as $1 from line if (/^\s*([^#]+)#(.*)$/) { ($ignore = 0, next) if $ignore; $ignore = ($2 =~ /$keywords/) if defined $keywords; ($ignore = 0, next) if $ignore; } elsif (/^\s*([^#]+)$/) { ($ignore = 0, next) if $ignore; } else { # Ignore blank lines $ignore = 0; next; } # remove any trailing spaces from path ($file = $1) =~ s/[ ]*$//; # perform user-supplied substitutions foreach my $pat (@opt_s) { eval '$file =~ s' . $pat; } # check if the given path is on the 'exclude' list $ignore = 0; foreach my $pat (@exclude) { ($ignore = 1, last) if $file =~ /$pat/; } if ($ignore == 0) { # construct the actual path to the file my $path = $opt_b . $file; # Expand any shell globs, if that feature is on. Since # Perl's glob() is stateful, we use an array assignment # to get the first match and discard the others. ($path) = glob($path) if $opt_r; print "$name:$line: $file\n" unless !$opt_m && -e $path; print " $path\n" if $opt_m; } $ignore = 0; } } sub next_arg { my ($arg) = @_; if ($arg eq "") { die "$0: missing argument for $_\n" if $#ARGV == -1; $arg = shift @ARGV; } $arg; } # I'd like to use Perl's getopts here, but it doesn't handle repeated # options, and using comma separators is just too ugly. # This doesn't handle combined options (as in '-rm'), but I don't care. my $arg, $opt_r, $opt_m, @opt_s, @opt_e, @opt_k, $opt_b, $opt_n; while ($#ARGV >= 0) { $_ = $ARGV[0]; last if /^[^-]/; shift @ARGV; $opt_n = "standard input"; last if /^--$/; SWITCH: { /^-r/ && do { $opt_r = 1; last SWITCH; }; /^-m/ && do { $opt_m = 1; last SWITCH; }; if (/^-s(.*)$/) { $arg = next_arg($1); push @opt_s, $arg; last SWITCH; } if (/^-e(.*)$/) { $arg = next_arg($1); push @opt_e, $arg; last SWITCH; } if (/^-k(.*)$/) { $arg = next_arg($1); push @opt_k, $arg; last SWITCH; } if (/^-b(.*)$/) { $opt_b = next_arg($1); last SWITCH; } if (/^-n(.*)$/) { $opt_n = next_arg($1); last SWITCH; } print "$0: unknown option $_\n"; usage(); } } # compile the 'exclude' regexps @exclude = map qr/$_/x, @opt_e; # if no keywords are given, then leave $keywords undefined if (@opt_k) { # construct a regexp that matches the keywords specified my $opt_k = join("|", @opt_k); $keywords = qr/($opt_k)/xo; } $opt_b .= "/" if $opt_b =~ /[^\/]$/; my $file; if ($#ARGV < 0) { process_paths(\*STDIN, $opt_n); } else { foreach $file (@ARGV) { if (! -e $file) { warn "$0: $file doesn't exist\n"; } elsif (! -f $file) { warn "$0: $file isn't a regular file\n"; } elsif (! -T $file) { warn "$0: $file isn't a text file\n"; } elsif (open FILE, "<$file") { process_paths(\*FILE, $file); } else { warn "$0: $file: $!\n"; } } } exit 0