17c478bd9Sstevel@tonic-gate#!/usr/bin/perl
27c478bd9Sstevel@tonic-gate#
37c478bd9Sstevel@tonic-gate# CDDL HEADER START
47c478bd9Sstevel@tonic-gate#
57c478bd9Sstevel@tonic-gate# The contents of this file are subject to the terms of the
6*ead1f93eSLiane Praza# Common Development and Distribution License (the "License").
7*ead1f93eSLiane Praza# You may not use this file except in compliance with the License.
87c478bd9Sstevel@tonic-gate#
97c478bd9Sstevel@tonic-gate# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate# or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate# See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate# and limitations under the License.
137c478bd9Sstevel@tonic-gate#
147c478bd9Sstevel@tonic-gate# When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate# If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate# fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate# information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate#
207c478bd9Sstevel@tonic-gate# CDDL HEADER END
217c478bd9Sstevel@tonic-gate#
227c478bd9Sstevel@tonic-gate
23*ead1f93eSLiane Praza#
24*ead1f93eSLiane Praza# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
257c478bd9Sstevel@tonic-gate# Use is subject to license terms.
267c478bd9Sstevel@tonic-gate#
277c478bd9Sstevel@tonic-gate
28*ead1f93eSLiane Praza#
297c478bd9Sstevel@tonic-gate# Given either a list of files containing paths on the command line or
307c478bd9Sstevel@tonic-gate# a set of paths on standard input, validate that the paths actually
317c478bd9Sstevel@tonic-gate# exist, and complain if they do not.  This is invoked by nightly to
327c478bd9Sstevel@tonic-gate# verify the contents of various control files used by the ON build
337c478bd9Sstevel@tonic-gate# process.
347c478bd9Sstevel@tonic-gate#
357c478bd9Sstevel@tonic-gate# Command line options:
367c478bd9Sstevel@tonic-gate#
377c478bd9Sstevel@tonic-gate#	-m	Show the matches (for debug).
387c478bd9Sstevel@tonic-gate#
397c478bd9Sstevel@tonic-gate#	-r	Allow shell globs in the paths.  Unless otherwise
407c478bd9Sstevel@tonic-gate#		flagged by a keyword (see -k) or exclusion (see -e),
417c478bd9Sstevel@tonic-gate#		it is an error if no files match the expression at
427c478bd9Sstevel@tonic-gate#		all.
437c478bd9Sstevel@tonic-gate#
447c478bd9Sstevel@tonic-gate#	-s/from/to/
457c478bd9Sstevel@tonic-gate#		Perform a substitution on all of the paths in the
467c478bd9Sstevel@tonic-gate#		file.  This substitution is performed after stripping
477c478bd9Sstevel@tonic-gate#		any in-line comments but before any exclusion matching
487c478bd9Sstevel@tonic-gate#		is done.  The option may include any legal Perl
497c478bd9Sstevel@tonic-gate#		substitution expression and may be repeated to give
507c478bd9Sstevel@tonic-gate#		multiple expressions.
517c478bd9Sstevel@tonic-gate#
527c478bd9Sstevel@tonic-gate#	-e <pattern>
537c478bd9Sstevel@tonic-gate#		Exclude paths matching the given pattern from the
547c478bd9Sstevel@tonic-gate#		"must exist" rule.  These paths will not be checked.
557c478bd9Sstevel@tonic-gate#		Option may include any legal Perl regular expression,
567c478bd9Sstevel@tonic-gate#		and may be repeated to give multiple patterns.
577c478bd9Sstevel@tonic-gate#
587c478bd9Sstevel@tonic-gate#	-k <keyword>
597c478bd9Sstevel@tonic-gate#		Exclude paths if there is either an in-line comment
607c478bd9Sstevel@tonic-gate#		containing the given keyword, or the preceding line
617c478bd9Sstevel@tonic-gate#		consists of only a comment containing that keyword.
627c478bd9Sstevel@tonic-gate#		Option may be repeated to provide multiple keywords.
637c478bd9Sstevel@tonic-gate#
647c478bd9Sstevel@tonic-gate#	-b <base>
657c478bd9Sstevel@tonic-gate#		Base directory for relative paths tested.
66*ead1f93eSLiane Praza#
67*ead1f93eSLiane Praza#	-n <name>
68*ead1f93eSLiane Praza#		String to use in place of file name when using stdin
697c478bd9Sstevel@tonic-gate
707c478bd9Sstevel@tonic-gateuse strict;
717c478bd9Sstevel@tonic-gate
72*ead1f93eSLiane Prazamy ($opt_r, $opt_m, @opt_s, @opt_e, @opt_k, $opt_b, $opt_n);
737c478bd9Sstevel@tonic-gatemy ($keywords, @exclude);
747c478bd9Sstevel@tonic-gate
757c478bd9Sstevel@tonic-gatesub usage {
767c478bd9Sstevel@tonic-gate    die "usage: $0 [-r] [-m]\n",
777c478bd9Sstevel@tonic-gate    "\t[-s/from/to/] [-e <pattern>] [-k <keyword>] [-b <base>]\n",
78*ead1f93eSLiane Praza    "\t[-n <name> ] [files...]\n";
797c478bd9Sstevel@tonic-gate}
807c478bd9Sstevel@tonic-gate
817c478bd9Sstevel@tonic-gate# process the path list in a given file
827c478bd9Sstevel@tonic-gatesub process_paths {
837c478bd9Sstevel@tonic-gate    my ($FILE, $name) = @_;
847c478bd9Sstevel@tonic-gate    my ($ignore, $file, $line);
857c478bd9Sstevel@tonic-gate    $ignore = 0;
867c478bd9Sstevel@tonic-gate    $line = 0;
877c478bd9Sstevel@tonic-gate    while (<$FILE>) {
887c478bd9Sstevel@tonic-gate	chomp;
897c478bd9Sstevel@tonic-gate	$line++;
907c478bd9Sstevel@tonic-gate	# Ignore comment lines
917c478bd9Sstevel@tonic-gate	if (/^\s*#(.*)$/) {
927c478bd9Sstevel@tonic-gate	    $ignore = ($1 =~ /$keywords/) if defined $keywords;
937c478bd9Sstevel@tonic-gate	    next;
947c478bd9Sstevel@tonic-gate	}
957c478bd9Sstevel@tonic-gate	# Extract path as $1 from line
967c478bd9Sstevel@tonic-gate	if (/^\s*([^#]+)#(.*)$/) {
977c478bd9Sstevel@tonic-gate	    ($ignore = 0, next) if $ignore;
987c478bd9Sstevel@tonic-gate	    $ignore = ($2 =~ /$keywords/) if defined $keywords;
997c478bd9Sstevel@tonic-gate	    ($ignore = 0, next) if $ignore;
1007c478bd9Sstevel@tonic-gate	} elsif (/^\s*([^#]+)$/) {
1017c478bd9Sstevel@tonic-gate	    ($ignore = 0, next) if $ignore;
1027c478bd9Sstevel@tonic-gate	} else {
1037c478bd9Sstevel@tonic-gate	    # Ignore blank lines
1047c478bd9Sstevel@tonic-gate	    $ignore = 0;
1057c478bd9Sstevel@tonic-gate	    next;
1067c478bd9Sstevel@tonic-gate	}
1077c478bd9Sstevel@tonic-gate	# remove any trailing spaces from path
1087c478bd9Sstevel@tonic-gate	($file = $1) =~ s/[	 ]*$//;
1097c478bd9Sstevel@tonic-gate	# perform user-supplied substitutions
1107c478bd9Sstevel@tonic-gate	foreach my $pat (@opt_s) {
1117c478bd9Sstevel@tonic-gate	    eval '$file =~ s' . $pat;
1127c478bd9Sstevel@tonic-gate	}
1137c478bd9Sstevel@tonic-gate	# check if the given path is on the 'exclude' list
1147c478bd9Sstevel@tonic-gate	$ignore = 0;
1157c478bd9Sstevel@tonic-gate	foreach my $pat (@exclude) {
1167c478bd9Sstevel@tonic-gate	    ($ignore = 1, last) if $file =~ /$pat/;
1177c478bd9Sstevel@tonic-gate	}
1187c478bd9Sstevel@tonic-gate	if ($ignore == 0) {
1197c478bd9Sstevel@tonic-gate	    # construct the actual path to the file
1207c478bd9Sstevel@tonic-gate	    my $path = $opt_b . $file;
1217c478bd9Sstevel@tonic-gate	    # Expand any shell globs, if that feature is on.  Since
1227c478bd9Sstevel@tonic-gate	    # Perl's glob() is stateful, we use an array assignment
1237c478bd9Sstevel@tonic-gate	    # to get the first match and discard the others.
1247c478bd9Sstevel@tonic-gate	    ($path) = glob($path) if $opt_r;
1257c478bd9Sstevel@tonic-gate	    print "$name:$line: $file\n" unless !$opt_m && -e $path;
1267c478bd9Sstevel@tonic-gate	    print "  $path\n" if $opt_m;
1277c478bd9Sstevel@tonic-gate	}
1287c478bd9Sstevel@tonic-gate	$ignore = 0;
1297c478bd9Sstevel@tonic-gate    }
1307c478bd9Sstevel@tonic-gate}
1317c478bd9Sstevel@tonic-gate
1327c478bd9Sstevel@tonic-gatesub next_arg {
1337c478bd9Sstevel@tonic-gate    my ($arg) = @_;
1347c478bd9Sstevel@tonic-gate    if ($arg eq "") {
1357c478bd9Sstevel@tonic-gate	die "$0: missing argument for $_\n" if $#ARGV == -1;
1367c478bd9Sstevel@tonic-gate	$arg = shift @ARGV;
1377c478bd9Sstevel@tonic-gate    }
1387c478bd9Sstevel@tonic-gate    $arg;
1397c478bd9Sstevel@tonic-gate}
1407c478bd9Sstevel@tonic-gate
1417c478bd9Sstevel@tonic-gate# I'd like to use Perl's getopts here, but it doesn't handle repeated
1427c478bd9Sstevel@tonic-gate# options, and using comma separators is just too ugly.
1437c478bd9Sstevel@tonic-gate# This doesn't handle combined options (as in '-rm'), but I don't care.
144*ead1f93eSLiane Prazamy $arg, $opt_r, $opt_m, @opt_s, @opt_e, @opt_k, $opt_b, $opt_n;
1457c478bd9Sstevel@tonic-gatewhile ($#ARGV >= 0) {
1467c478bd9Sstevel@tonic-gate    $_ = $ARGV[0];
1477c478bd9Sstevel@tonic-gate    last if /^[^-]/;
1487c478bd9Sstevel@tonic-gate    shift @ARGV;
149*ead1f93eSLiane Praza    $opt_n = "standard input";
1507c478bd9Sstevel@tonic-gate    last if /^--$/;
1517c478bd9Sstevel@tonic-gate    SWITCH: {
1527c478bd9Sstevel@tonic-gate	  /^-r/ && do { $opt_r = 1; last SWITCH; };
1537c478bd9Sstevel@tonic-gate	  /^-m/ && do { $opt_m = 1; last SWITCH; };
1547c478bd9Sstevel@tonic-gate	  if (/^-s(.*)$/) {
1557c478bd9Sstevel@tonic-gate	      $arg = next_arg($1);
1567c478bd9Sstevel@tonic-gate	      push @opt_s, $arg;
1577c478bd9Sstevel@tonic-gate	      last SWITCH;
1587c478bd9Sstevel@tonic-gate	  }
1597c478bd9Sstevel@tonic-gate	  if (/^-e(.*)$/) {
1607c478bd9Sstevel@tonic-gate	      $arg = next_arg($1);
1617c478bd9Sstevel@tonic-gate	      push @opt_e, $arg;
1627c478bd9Sstevel@tonic-gate	      last SWITCH;
1637c478bd9Sstevel@tonic-gate	  }
1647c478bd9Sstevel@tonic-gate	  if (/^-k(.*)$/) {
1657c478bd9Sstevel@tonic-gate	      $arg = next_arg($1);
1667c478bd9Sstevel@tonic-gate	      push @opt_k, $arg;
1677c478bd9Sstevel@tonic-gate	      last SWITCH;
1687c478bd9Sstevel@tonic-gate	  }
1697c478bd9Sstevel@tonic-gate	  if (/^-b(.*)$/) {
1707c478bd9Sstevel@tonic-gate	      $opt_b = next_arg($1);
1717c478bd9Sstevel@tonic-gate	      last SWITCH;
1727c478bd9Sstevel@tonic-gate	  }
173*ead1f93eSLiane Praza	  if (/^-n(.*)$/) {
174*ead1f93eSLiane Praza	      $opt_n = next_arg($1);
175*ead1f93eSLiane Praza	      last SWITCH;
176*ead1f93eSLiane Praza	  }
1777c478bd9Sstevel@tonic-gate	  print "$0: unknown option $_\n";
1787c478bd9Sstevel@tonic-gate	  usage();
1797c478bd9Sstevel@tonic-gate    }
1807c478bd9Sstevel@tonic-gate}
1817c478bd9Sstevel@tonic-gate
1827c478bd9Sstevel@tonic-gate# compile the 'exclude' regexps
1837c478bd9Sstevel@tonic-gate@exclude = map qr/$_/x, @opt_e;
1847c478bd9Sstevel@tonic-gate# if no keywords are given, then leave $keywords undefined
1857c478bd9Sstevel@tonic-gateif (@opt_k) {
1867c478bd9Sstevel@tonic-gate    # construct a regexp that matches the keywords specified
1877c478bd9Sstevel@tonic-gate    my $opt_k = join("|", @opt_k);
1887c478bd9Sstevel@tonic-gate    $keywords = qr/($opt_k)/xo;
1897c478bd9Sstevel@tonic-gate}
1907c478bd9Sstevel@tonic-gate$opt_b .= "/" if $opt_b =~ /[^\/]$/;
1917c478bd9Sstevel@tonic-gate
1927c478bd9Sstevel@tonic-gatemy $file;
1937c478bd9Sstevel@tonic-gate
1947c478bd9Sstevel@tonic-gateif ($#ARGV < 0) {
195*ead1f93eSLiane Praza    process_paths(\*STDIN, $opt_n);
1967c478bd9Sstevel@tonic-gate} else {
1977c478bd9Sstevel@tonic-gate    foreach $file (@ARGV) {
1987c478bd9Sstevel@tonic-gate	if (! -e $file) {
1997c478bd9Sstevel@tonic-gate	    warn "$0: $file doesn't exist\n";
2007c478bd9Sstevel@tonic-gate	} elsif (! -f $file) {
2017c478bd9Sstevel@tonic-gate	    warn "$0: $file isn't a regular file\n";
2027c478bd9Sstevel@tonic-gate	} elsif (! -T $file) {
2037c478bd9Sstevel@tonic-gate	    warn "$0: $file isn't a text file\n";
2047c478bd9Sstevel@tonic-gate	} elsif (open FILE, "<$file") {
2057c478bd9Sstevel@tonic-gate	    process_paths(\*FILE, $file);
2067c478bd9Sstevel@tonic-gate	} else {
2077c478bd9Sstevel@tonic-gate	    warn "$0: $file: $!\n";
2087c478bd9Sstevel@tonic-gate	}
2097c478bd9Sstevel@tonic-gate    }
2107c478bd9Sstevel@tonic-gate}
2117c478bd9Sstevel@tonic-gate
2127c478bd9Sstevel@tonic-gateexit 0
213