1#!/usr/bin/perl 2# 3# CDDL HEADER START 4# 5# The contents of this file are subject to the terms of the 6# Common Development and Distribution License (the "License"). 7# You may not use this file except in compliance with the License. 8# 9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10# or http://www.opensolaris.org/os/licensing. 11# See the License for the specific language governing permissions 12# and limitations under the License. 13# 14# When distributing Covered Code, include this CDDL HEADER in each 15# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16# If applicable, add the following below this CDDL HEADER, with the 17# fields enclosed by brackets "[]" replaced with your own identifying 18# information: Portions Copyright [yyyy] [name of copyright owner] 19# 20# CDDL HEADER END 21# 22 23# 24# Copyright 2009 Sun Microsystems, Inc. All rights reserved. 25# Use is subject to license terms. 26# 27 28# 29# Given either a list of files containing paths on the command line or 30# a set of paths on standard input, validate that the paths actually 31# exist, and complain if they do not. This is invoked by nightly to 32# verify the contents of various control files used by the ON build 33# process. 34# 35# Command line options: 36# 37# -m Show the matches (for debug). 38# 39# -r Allow shell globs in the paths. Unless otherwise 40# flagged by a keyword (see -k) or exclusion (see -e), 41# it is an error if no files match the expression at 42# all. 43# 44# -s/from/to/ 45# Perform a substitution on all of the paths in the 46# file. This substitution is performed after stripping 47# any in-line comments but before any exclusion matching 48# is done. The option may include any legal Perl 49# substitution expression and may be repeated to give 50# multiple expressions. 51# 52# -e <pattern> 53# Exclude paths matching the given pattern from the 54# "must exist" rule. These paths will not be checked. 55# Option may include any legal Perl regular expression, 56# and may be repeated to give multiple patterns. 57# 58# -k <keyword> 59# Exclude paths if there is either an in-line comment 60# containing the given keyword, or the preceding line 61# consists of only a comment containing that keyword. 62# Option may be repeated to provide multiple keywords. 63# 64# -b <base> 65# Base directory for relative paths tested. 66# 67# -n <name> 68# String to use in place of file name when using stdin 69 70use strict; 71 72my ($opt_r, $opt_m, @opt_s, @opt_e, @opt_k, $opt_b, $opt_n); 73my ($keywords, @exclude); 74 75sub usage { 76 die "usage: $0 [-r] [-m]\n", 77 "\t[-s/from/to/] [-e <pattern>] [-k <keyword>] [-b <base>]\n", 78 "\t[-n <name> ] [files...]\n"; 79} 80 81# process the path list in a given file 82sub process_paths { 83 my ($FILE, $name) = @_; 84 my ($ignore, $file, $line); 85 $ignore = 0; 86 $line = 0; 87 while (<$FILE>) { 88 chomp; 89 $line++; 90 # Ignore comment lines 91 if (/^\s*#(.*)$/) { 92 $ignore = ($1 =~ /$keywords/) if defined $keywords; 93 next; 94 } 95 # Extract path as $1 from line 96 if (/^\s*([^#]+)#(.*)$/) { 97 ($ignore = 0, next) if $ignore; 98 $ignore = ($2 =~ /$keywords/) if defined $keywords; 99 ($ignore = 0, next) if $ignore; 100 } elsif (/^\s*([^#]+)$/) { 101 ($ignore = 0, next) if $ignore; 102 } else { 103 # Ignore blank lines 104 $ignore = 0; 105 next; 106 } 107 # remove any trailing spaces from path 108 ($file = $1) =~ s/[ ]*$//; 109 # perform user-supplied substitutions 110 foreach my $pat (@opt_s) { 111 eval '$file =~ s' . $pat; 112 } 113 # check if the given path is on the 'exclude' list 114 $ignore = 0; 115 foreach my $pat (@exclude) { 116 ($ignore = 1, last) if $file =~ /$pat/; 117 } 118 if ($ignore == 0) { 119 # construct the actual path to the file 120 my $path = $opt_b . $file; 121 # Expand any shell globs, if that feature is on. Since 122 # Perl's glob() is stateful, we use an array assignment 123 # to get the first match and discard the others. 124 ($path) = glob($path) if $opt_r; 125 print "$name:$line: $file\n" unless !$opt_m && -e $path; 126 print " $path\n" if $opt_m; 127 } 128 $ignore = 0; 129 } 130} 131 132sub next_arg { 133 my ($arg) = @_; 134 if ($arg eq "") { 135 die "$0: missing argument for $_\n" if $#ARGV == -1; 136 $arg = shift @ARGV; 137 } 138 $arg; 139} 140 141# I'd like to use Perl's getopts here, but it doesn't handle repeated 142# options, and using comma separators is just too ugly. 143# This doesn't handle combined options (as in '-rm'), but I don't care. 144my $arg, $opt_r, $opt_m, @opt_s, @opt_e, @opt_k, $opt_b, $opt_n; 145while ($#ARGV >= 0) { 146 $_ = $ARGV[0]; 147 last if /^[^-]/; 148 shift @ARGV; 149 $opt_n = "standard input"; 150 last if /^--$/; 151 SWITCH: { 152 /^-r/ && do { $opt_r = 1; last SWITCH; }; 153 /^-m/ && do { $opt_m = 1; last SWITCH; }; 154 if (/^-s(.*)$/) { 155 $arg = next_arg($1); 156 push @opt_s, $arg; 157 last SWITCH; 158 } 159 if (/^-e(.*)$/) { 160 $arg = next_arg($1); 161 push @opt_e, $arg; 162 last SWITCH; 163 } 164 if (/^-k(.*)$/) { 165 $arg = next_arg($1); 166 push @opt_k, $arg; 167 last SWITCH; 168 } 169 if (/^-b(.*)$/) { 170 $opt_b = next_arg($1); 171 last SWITCH; 172 } 173 if (/^-n(.*)$/) { 174 $opt_n = next_arg($1); 175 last SWITCH; 176 } 177 print "$0: unknown option $_\n"; 178 usage(); 179 } 180} 181 182# compile the 'exclude' regexps 183@exclude = map qr/$_/x, @opt_e; 184# if no keywords are given, then leave $keywords undefined 185if (@opt_k) { 186 # construct a regexp that matches the keywords specified 187 my $opt_k = join("|", @opt_k); 188 $keywords = qr/($opt_k)/xo; 189} 190$opt_b .= "/" if $opt_b =~ /[^\/]$/; 191 192my $file; 193 194if ($#ARGV < 0) { 195 process_paths(\*STDIN, $opt_n); 196} else { 197 foreach $file (@ARGV) { 198 if (! -e $file) { 199 warn "$0: $file doesn't exist\n"; 200 } elsif (! -f $file) { 201 warn "$0: $file isn't a regular file\n"; 202 } elsif (! -T $file) { 203 warn "$0: $file isn't a text file\n"; 204 } elsif (open FILE, "<$file") { 205 process_paths(\*FILE, $file); 206 } else { 207 warn "$0: $file: $!\n"; 208 } 209 } 210} 211 212exit 0 213