Blame guards

Bernhard M. Wiedemann 6ebc4a
#!/usr/bin/perl -w
5c34fe
##############################################################################
Bernhard M. Wiedemann 6ebc4a
# Copyright (c) 2003-2007,2009 Novell, Inc.
5c34fe
# Copyright (c) 2010-2022 SUSE LLC
Bernhard M. Wiedemann 6ebc4a
#
Bernhard M. Wiedemann 6ebc4a
# This program is free software; you can redistribute it and/or
Bernhard M. Wiedemann 6ebc4a
# modify it under the terms of version 2 of the GNU General Public License as
Bernhard M. Wiedemann 6ebc4a
# published by the Free Software Foundation.
Bernhard M. Wiedemann 6ebc4a
#
Bernhard M. Wiedemann 6ebc4a
# This program is distributed in the hope that it will be useful,
Bernhard M. Wiedemann 6ebc4a
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Bernhard M. Wiedemann 6ebc4a
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
Bernhard M. Wiedemann 6ebc4a
# GNU General Public License for more details.
Bernhard M. Wiedemann 6ebc4a
#
Bernhard M. Wiedemann 6ebc4a
# You should have received a copy of the GNU General Public License
5c34fe
# along with this program; if not, contact https://www.suse.com/source-code/
Bernhard M. Wiedemann 6ebc4a
#
Bernhard M. Wiedemann 6ebc4a
#############################################################################
Bernhard M. Wiedemann 6ebc4a
#
Bernhard M. Wiedemann 6ebc4a
# Guards:
Bernhard M. Wiedemann 6ebc4a
#
Bernhard M. Wiedemann 6ebc4a
# +xxx   include if xxx is defined
Bernhard M. Wiedemann 6ebc4a
# -xxx   exclude if xxx is defined
Bernhard M. Wiedemann 6ebc4a
# +!xxx  include if xxx is not defined
Bernhard M. Wiedemann 6ebc4a
# -!xxx  exclude if xxx is not defined
Bernhard M. Wiedemann 6ebc4a
#
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
use FileHandle;
Bernhard M. Wiedemann 6ebc4a
use Getopt::Long;
Bernhard M. Wiedemann 6ebc4a
use strict;
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
# Prototypes
Bernhard M. Wiedemann 6ebc4a
sub files_in($$);
Bernhard M. Wiedemann 6ebc4a
sub parse($$);
Bernhard M. Wiedemann 6ebc4a
sub help();
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
sub slashme($) {
Bernhard M. Wiedemann 6ebc4a
    my ($dir) = @_;
Bernhard M. Wiedemann 6ebc4a
    $dir =~ s#([^/])$#$&/;; # append a slash if necessary
Bernhard M. Wiedemann 6ebc4a
    if ($dir eq './') {
Bernhard M. Wiedemann 6ebc4a
	return '';
Bernhard M. Wiedemann 6ebc4a
    } else {
Bernhard M. Wiedemann 6ebc4a
	return $dir;
Bernhard M. Wiedemann 6ebc4a
    }
Bernhard M. Wiedemann 6ebc4a
}
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
# Generate a list of files in a directory
Bernhard M. Wiedemann 6ebc4a
#
Bernhard M. Wiedemann 6ebc4a
sub files_in($$) {
Bernhard M. Wiedemann 6ebc4a
    my ($dir, $path) = @_;
Bernhard M. Wiedemann 6ebc4a
    my $dh = new FileHandle;
Bernhard M. Wiedemann 6ebc4a
    my (@files, $file);
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
    # @<file> syntax
Bernhard M. Wiedemann 6ebc4a
    if ($path =~ s/^@//) {
Bernhard M. Wiedemann 6ebc4a
	my $fh;
Bernhard M. Wiedemann 6ebc4a
	open($fh, '<', $path) or die "$path: $!\n";
Bernhard M. Wiedemann 6ebc4a
	@files = <$fh>;
Bernhard M. Wiedemann 6ebc4a
	close($fh);
Bernhard M. Wiedemann 6ebc4a
	chomp(@files);
Bernhard M. Wiedemann 6ebc4a
	s:^$dir:: for @files;
Bernhard M. Wiedemann 6ebc4a
	return @files;
Bernhard M. Wiedemann 6ebc4a
    }
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
    $path = slashme($path);
Bernhard M. Wiedemann 6ebc4a
    opendir $dh, length("$dir$path") ? "$dir$path" : '.'
Bernhard M. Wiedemann 6ebc4a
	or die "$dir$path: $!\n";
Bernhard M. Wiedemann 6ebc4a
    while ($file = readdir($dh)) {
Bernhard M. Wiedemann 6ebc4a
	next if $file =~ /^(\.|\.\.|\.#.*|CVS|.*~)$/;
Bernhard M. Wiedemann 6ebc4a
	if (-d "$dir$path$file") {
Bernhard M. Wiedemann 6ebc4a
		@files = (@files, files_in($dir, "$path$file/"));
Bernhard M. Wiedemann 6ebc4a
	} else {
Bernhard M. Wiedemann 6ebc4a
		#print "[$path$file]\n";
Bernhard M. Wiedemann 6ebc4a
		push @files, "$path$file";
Bernhard M. Wiedemann 6ebc4a
	}
Bernhard M. Wiedemann 6ebc4a
    }
Bernhard M. Wiedemann 6ebc4a
    closedir $dh;
Bernhard M. Wiedemann 6ebc4a
    return @files;
Bernhard M. Wiedemann 6ebc4a
}
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
# Parse a configuration file
Bernhard M. Wiedemann 6ebc4a
# Callback called with ($patch, @guards) arguments
Bernhard M. Wiedemann 6ebc4a
#
Bernhard M. Wiedemann 6ebc4a
sub parse($$) {
Bernhard M. Wiedemann 6ebc4a
    my ($fh, $callback) = @_;
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
    my $line = "";
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
    while (<$fh>) {
Bernhard M. Wiedemann 6ebc4a
	chomp;
Bernhard M. Wiedemann 6ebc4a
	s/(^|\s+)#.*//;
Bernhard M. Wiedemann 6ebc4a
	if (s/\\$/ /) {
Bernhard M. Wiedemann 6ebc4a
		$line .= $_;
Bernhard M. Wiedemann 6ebc4a
		next;
Bernhard M. Wiedemann 6ebc4a
	}
Bernhard M. Wiedemann 6ebc4a
	$line .= $_;
Bernhard M. Wiedemann 6ebc4a
	my @guards = ();
Bernhard M. Wiedemann 6ebc4a
	foreach my $token (split /[\s\t\n]+/, $line) {
Bernhard M. Wiedemann 6ebc4a
	    next if $token eq "";
Bernhard M. Wiedemann 6ebc4a
	    if ($token =~ /^[-+]/) {
Bernhard M. Wiedemann 6ebc4a
		push @guards, $token;
Bernhard M. Wiedemann 6ebc4a
	    } else {
Bernhard M. Wiedemann 6ebc4a
		#print "[" . join(",", @guards) . "] $token\n";
Bernhard M. Wiedemann 6ebc4a
		&$callback($token, @guards);
Bernhard M. Wiedemann 6ebc4a
	    }
Bernhard M. Wiedemann 6ebc4a
	}
Bernhard M. Wiedemann 6ebc4a
	$line = "";
Bernhard M. Wiedemann 6ebc4a
    }
Bernhard M. Wiedemann 6ebc4a
}
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
# Command line options
Bernhard M. Wiedemann 6ebc4a
#
Bernhard M. Wiedemann 6ebc4a
my ($dir, $config, $default, $check, $list, $invert_match, $with_guards) =
Bernhard M. Wiedemann 6ebc4a
   (  '',     '-',        1,      0,     0,             0,            0);
Bernhard M. Wiedemann 6ebc4a
my @path;
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
# Help text
Bernhard M. Wiedemann 6ebc4a
#
Bernhard M. Wiedemann 6ebc4a
sub help() {
Bernhard M. Wiedemann 6ebc4a
    print "$0 - select from a list of files guarded by conditions\n";
Bernhard M. Wiedemann 6ebc4a
    print "SYNOPSIS: $0 [--prefix=dir] [--path=dir1:dir2:...]\n" .
Bernhard M. Wiedemann 6ebc4a
	"	[--default=0|1] [--check|--list] [--invert-match]\n" .
Bernhard M. Wiedemann 6ebc4a
	"	[--with-guards] [--config=file] symbol ...\n\n" .
Bernhard M. Wiedemann 6ebc4a
	"	Defaults: --default=$default\n" .
Bernhard M. Wiedemann 6ebc4a
	"	Use --path=\@<file> to read the list of entries from <file>\n";
Bernhard M. Wiedemann 6ebc4a
    exit 0;
Bernhard M. Wiedemann 6ebc4a
}
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
# Parse command line options
Bernhard M. Wiedemann 6ebc4a
#
Bernhard M. Wiedemann 6ebc4a
Getopt::Long::Configure ("bundling");
Bernhard M. Wiedemann 6ebc4a
eval {
Bernhard M. Wiedemann 6ebc4a
    unless (GetOptions (
Bernhard M. Wiedemann 6ebc4a
	'd|prefix=s' => \$dir,
Bernhard M. Wiedemann 6ebc4a
	'c|config=s' => \$config,
Bernhard M. Wiedemann 6ebc4a
	'C|check' => \$check,
Bernhard M. Wiedemann 6ebc4a
	'l|list' => \$list,
Bernhard M. Wiedemann 6ebc4a
	'w|with-guards' => \$with_guards,
Bernhard M. Wiedemann 6ebc4a
	'p|path=s' => \@path,
Bernhard M. Wiedemann 6ebc4a
	'D|default=i' => \$default,
Bernhard M. Wiedemann 6ebc4a
	'v|invert-match' => \$invert_match,
Bernhard M. Wiedemann 6ebc4a
	'h|help' => sub { help(); exit 0; })) {
Bernhard M. Wiedemann 6ebc4a
	help();
Bernhard M. Wiedemann 6ebc4a
	exit 1;
Bernhard M. Wiedemann 6ebc4a
    }
Bernhard M. Wiedemann 6ebc4a
};
Bernhard M. Wiedemann 6ebc4a
if ($@) {
Bernhard M. Wiedemann 6ebc4a
    print "$@";
Bernhard M. Wiedemann 6ebc4a
    help();
Bernhard M. Wiedemann 6ebc4a
    exit 1;
Bernhard M. Wiedemann 6ebc4a
}
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
@path = ('.')
Bernhard M. Wiedemann 6ebc4a
    unless (@path);
Bernhard M. Wiedemann 6ebc4a
@path = split(/:/, join(':', @path));
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
my $fh = ($config eq '-') ? \*STDIN : new FileHandle($config)
Bernhard M. Wiedemann 6ebc4a
    or die "$config: $!\n";
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
$dir = slashme($dir);
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
if ($check) {
Bernhard M. Wiedemann 6ebc4a
    # Check for duplicate files, or for files that are not referenced by
Bernhard M. Wiedemann 6ebc4a
    # the specification.
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
    my $problems = 0;
Bernhard M. Wiedemann 6ebc4a
    my @files;
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
    foreach (@path) {
Bernhard M. Wiedemann 6ebc4a
	@files = (@files, files_in($dir, $_));
Bernhard M. Wiedemann 6ebc4a
    }
Bernhard M. Wiedemann 6ebc4a
    my %files = map { $_ => 0 } @files;
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
    parse($fh, sub {
Bernhard M. Wiedemann 6ebc4a
	my ($patch, @guards) = @_;
Bernhard M. Wiedemann 6ebc4a
	if (exists $files{$patch}) {
Bernhard M. Wiedemann 6ebc4a
	    $files{$patch}++;
Bernhard M. Wiedemann 6ebc4a
	} else {
Bernhard M. Wiedemann 6ebc4a
	    if ($config eq '-') {
Bernhard M. Wiedemann 6ebc4a
		print "Not found: $dir$patch\n";
Bernhard M. Wiedemann 6ebc4a
	    } else {
Bernhard M. Wiedemann 6ebc4a
		print "In $config but not found: $dir$patch\n";
Bernhard M. Wiedemann 6ebc4a
	    }
Bernhard M. Wiedemann 6ebc4a
	    $problems++;
Bernhard M. Wiedemann 6ebc4a
	}});
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
    $fh->close();
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
    my ($file, $ref);
Bernhard M. Wiedemann 6ebc4a
    while (($file, $ref) = each %files) {
Bernhard M. Wiedemann 6ebc4a
	next if $ref == 1;
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
	if ($ref == 0) {
Bernhard M. Wiedemann 6ebc4a
	    if ($config eq '-') {
Bernhard M. Wiedemann 6ebc4a
		print "Unused: $file\n";
Bernhard M. Wiedemann 6ebc4a
	    } else {
Bernhard M. Wiedemann 6ebc4a
		print "Not in $config: $file\n";
Bernhard M. Wiedemann 6ebc4a
	    }
Bernhard M. Wiedemann 6ebc4a
	    $problems++;
Bernhard M. Wiedemann 6ebc4a
	}
Bernhard M. Wiedemann 6ebc4a
	if ($ref > 1) {
Bernhard M. Wiedemann 6ebc4a
	    print "Warning: multiple uses";
Bernhard M. Wiedemann 6ebc4a
	    print " in $config" if $config ne '-';
Bernhard M. Wiedemann 6ebc4a
	    print ": $file\n";
Bernhard M. Wiedemann 6ebc4a
	    # This is not an error if the entries are mutually exclusive...
Bernhard M. Wiedemann 6ebc4a
	}
Bernhard M. Wiedemann 6ebc4a
    }
Bernhard M. Wiedemann 6ebc4a
    exit $problems ? 1 : 0;
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
} elsif ($list) {
Bernhard M. Wiedemann 6ebc4a
    parse($fh, sub {
Bernhard M. Wiedemann 6ebc4a
	my ($patch, @guards) = @_;
Bernhard M. Wiedemann 6ebc4a
	print join(' ', @guards), ' '
Bernhard M. Wiedemann 6ebc4a
		if (@guards && $with_guards);
Bernhard M. Wiedemann 6ebc4a
	print "$dir$patch\n";
Bernhard M. Wiedemann 6ebc4a
	});
Bernhard M. Wiedemann 6ebc4a
} else {
Bernhard M. Wiedemann 6ebc4a
    # Generate a list of patches to apply.
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
    my %symbols = map { $_ => 1 } @ARGV;
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
    parse($fh, sub {
Bernhard M. Wiedemann 6ebc4a
	my ($patch, @guards) = @_;
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
	my $selected;
Bernhard M. Wiedemann 6ebc4a
	if (@guards) {
Bernhard M. Wiedemann 6ebc4a
	    # If the first guard is -xxx, the patch is included by default;
Bernhard M. Wiedemann 6ebc4a
	    # if it is +xxx, the patch is excluded by default.
Bernhard M. Wiedemann 6ebc4a
	    $selected = ($guards[0] =~ /^-/);
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
	    foreach (@guards) {
Bernhard M. Wiedemann 6ebc4a
		/^([-+])(!?)(.*)?/
Bernhard M. Wiedemann 6ebc4a
		    or die "Bad guard '$_'\n";
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
		# Check if the guard matches
Bernhard M. Wiedemann 6ebc4a
		if (($2 eq '!' && !exists $symbols{$3}) ||
Bernhard M. Wiedemann 6ebc4a
		    ($2 eq ''  && ( $3 eq '' || exists $symbols{$3}))) {
Bernhard M. Wiedemann 6ebc4a
		    # Include or exclude
Bernhard M. Wiedemann 6ebc4a
		    $selected = ($1 eq '+');
Bernhard M. Wiedemann 6ebc4a
		}
Bernhard M. Wiedemann 6ebc4a
	    }
Bernhard M. Wiedemann 6ebc4a
	} else {
Bernhard M. Wiedemann 6ebc4a
	    # If there are no guards, use the specified default result.
Bernhard M. Wiedemann 6ebc4a
	    $selected = $default;
Bernhard M. Wiedemann 6ebc4a
	}
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
	print "$dir$patch\n"
Bernhard M. Wiedemann 6ebc4a
	    if $selected ^ $invert_match;
Bernhard M. Wiedemann 6ebc4a
	});
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
    $fh->close();
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
    exit 0;
Bernhard M. Wiedemann 6ebc4a
}
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
__END__
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
=head1 NAME
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
guards - select from a list of files guarded by conditions
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
=head1 SYNOPSIS
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
F<guards> [--prefix=F<dir>] [--path=F<dir1:dir2:...>] [--default=<0|1>]
Bernhard M. Wiedemann 6ebc4a
[--check|--list] [--invert-match] [--with-guards] [--config=<file>]
Bernhard M. Wiedemann 6ebc4a
I<symbol> ...
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
=head1 DESCRIPTION
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
The script reads a configuration file that may contain so-called guards, file
Bernhard M. Wiedemann 6ebc4a
names, and comments, and writes those file names that satisfy all guards to
Bernhard M. Wiedemann 6ebc4a
standard output. The script takes a list of symbols as its arguments. Each line
Bernhard M. Wiedemann 6ebc4a
in the configuration file is processed separately. Lines may start with a
Bernhard M. Wiedemann 6ebc4a
number of guards. The following guards are defined:
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
=over
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
+I<xxx> Include the file(s) on this line if the symbol I<xxx> is defined.
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
-I<xxx> Exclude the file(s) on this line if the symbol I<xxx> is defined.
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
+!I<xxx> Include the file(s) on this line if the symbol I<xxx> is not defined.
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
-!I<xxx> Exclude the file(s) on this line if the symbol I<xxx> is not defined.
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
- Exclude this file. Used to avoid spurious I<--check> messages.
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
=back
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
The guards are processed left to right. The last guard that matches determines
Bernhard M. Wiedemann 6ebc4a
if the file is included. If no guard is specified, the I<--default>
Bernhard M. Wiedemann 6ebc4a
setting determines if the file is included.
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
If no configuration file is specified, the script reads from standard input.
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
The I<--check> option is used to compare the specification file against the
Bernhard M. Wiedemann 6ebc4a
file system. If files are referenced in the specification that do not exist, or
Bernhard M. Wiedemann 6ebc4a
if files are not enlisted in the specification file warnings are printed. The
Bernhard M. Wiedemann 6ebc4a
I<--path> option can be used to specify which directory or directories to scan.
Bernhard M. Wiedemann 6ebc4a
Multiple directories are separated by a colon (C<:>) character. The
Bernhard M. Wiedemann 6ebc4a
I<--prefix> option specifies the location of the files. Alternatively, the
Bernhard M. Wiedemann 6ebc4a
I<--path=@E<lt>fileE<gt>> syntax can be used to specify a file from which the
Bernhard M. Wiedemann 6ebc4a
file names will be read.
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
Use I<--list> to list all files independent of any rules. Use I<--invert-match>
Bernhard M. Wiedemann 6ebc4a
to list only the excluded patches. Use I<--with-guards> to also include all
Bernhard M. Wiedemann 6ebc4a
inclusion and exclusion rules.
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
=head1 AUTHOR
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
Andreas Gruenbacher <agruen@suse.de>, SUSE Labs