@@ -0,0 +1,287 @@
+#!/usr/bin/perl -w
+
+#
+# Guards:
+#
+# +xxx include if xxx is defined
+# -xxx exclude if xxx is defined
+# +!xxx include if xxx is not defined
+# -!xxx exclude if xxx is not defined
+#
+
+use FileHandle;
+use Getopt::Long;
+use strict;
+
+# Prototypes
+sub files_in($$);
+sub parse($$);
+sub help();
+
+#sub strip_ext($) {
+# local ($_) = @_;
+# s/\.(diff?|patch)$//;
+#}
+
+#sub try_ext($) {
+# my ($path) = @_;
+# for my $p in (($path, "$path.diff", "$path.dif", "$path.patch")) {
+# return $p
+# if (-f $p);
+# }
+# return undef;
+#}
+
+sub slashme($) {
+ my ($dir) = @_;
+ $dir =~ s#([^/])$#$&/#; # append a slash if necessary
+ if ($dir eq './') {
+ return '';
+ } else {
+ return $dir;
+ }
+}
+
+# Generate a list of files in a directory
+#
+sub files_in($$) {
+ my ($dir, $path) = @_;
+ my $dh = new FileHandle;
+ my (@files, $file);
+
+
+ opendir $dh, length("$dir$path") ? "$dir$path" : '.'
+ or die "$dir$path: $!\n";
+ while ($file = readdir($dh)) {
+ next if $file =~ /^(\.|\.\.|\.#.*|CVS|.*~)$/;
+ if (-d "$dir$path$file") {
+ @files = (@files, files_in($dir, "$path$file/"));
+ } else {
+ #print "[$path$file]\n";
+ push @files, "$path$file";
+ }
+ }
+ closedir $dh;
+ return @files;
+}
+
+# Parse a configuration file
+# Callback called with ($patch, @guards) arguments
+#
+sub parse($$) {
+ my ($fh, $callback) = @_;
+
+ my $line = "";
+
+ while (<$fh>) {
+ chomp;
+ s/(^|\s+)#.*//;
+ if (s/\\$/ /) {
+ $line .= $_;
+ next;
+ }
+ $line .= $_;
+ my @guards = ();
+ foreach my $token (split /[\s\t\n]+/, $line) {
+ next if $token eq "";
+ if ($token =~ /^[-+]/) {
+ push @guards, $token;
+ } else {
+ #print "[" . join(",", @guards) . "] $token\n";
+ &$callback($token, @guards);
+ }
+ }
+ $line = "";
+ }
+}
+
+# Command line options
+#
+my ($dir, $config, $default, $check, $list, $invert_match, $with_guards) =
+ ( '', '-', 1, 0, 0, 0, 0);
+my @path;
+
+# Help text
+#
+sub help() {
+ print "$0 - select from a list of files guarded by conditions\n";
+ print "SYNOPSIS: $0 [--prefix=dir] [--path=dir1:dir2:...]\n" .
+ " [--default=0|1] [--check|--list] [--invert-match]\n" .
+ " [--with-guards] [--config=file] symbol ...\n\n" .
+ " (Default values: --path='" . join(':', @path) . "', " .
+ "--default=$default)\n";
+ exit 0;
+}
+
+# Parse command line options
+#
+Getopt::Long::Configure ("bundling");
+eval {
+ unless (GetOptions (
+ 'd|prefix=s' => \$dir,
+ 'c|config=s' => \$config,
+ 'C|check' => \$check,
+ 'l|list' => \$list,
+ 'w|with-guards' => \$with_guards,
+ 'p|path=s' => \@path,
+ 'D|default=i' => \$default,
+ 'v|invert-match' => \$invert_match,
+ 'h|help' => sub { help(); exit 0; })) {
+ help();
+ exit 1;
+ }
+};
+if ($@) {
+ print "$@";
+ help();
+ exit 1;
+}
+
+@path = ('.')
+ unless (@path);
+@path = split(/:/, join(':', @path));
+
+my $fh = ($config eq '-') ? \*STDIN : new FileHandle($config)
+ or die "$config: $!\n";
+
+$dir = slashme($dir);
+
+if ($check) {
+ # Check for duplicate files, or for files that are not referenced by
+ # the specification.
+
+ my $problems = 0;
+ my @files;
+
+ foreach (@path) {
+ @files = (@files, files_in($dir, slashme($_)));
+ }
+ my %files = map { $_ => 0 } @files;
+
+ parse($fh, sub {
+ my ($patch, @guards) = @_;
+ if (exists $files{$patch}) {
+ $files{$patch}++;
+ } else {
+ print "Not found: $dir$patch\n";
+ $problems++;
+ }});
+
+ $fh->close();
+
+ my ($file, $ref);
+ while (($file, $ref) = each %files) {
+ next if $ref == 1;
+
+ if ($ref == 0) {
+ print "Unused: $file\n" if $ref == 0;
+ $problems++;
+ }
+ if ($ref > 1) {
+ print "Warning: multiple uses: $file\n" if $ref > 1;
+ # This is not an error if the entries are mutually exclusive...
+ }
+ }
+ exit $problems ? 1 : 0;
+
+} elsif ($list) {
+ parse($fh, sub {
+ my ($patch, @guards) = @_;
+ print join(' ', @guards), ' '
+ if (@guards && $with_guards);
+ print "$dir$patch\n";
+ });
+} else {
+ # Generate a list of patches to apply.
+
+ my %symbols = map { $_ => 1 } @ARGV;
+
+ parse($fh, sub {
|