]>
Commit | Line | Data |
---|---|---|
00e5a55c BS |
1 | #!/usr/bin/perl -w |
2 | ||
3 | # | |
4 | # Guards: | |
5 | # | |
6 | # +xxx include if xxx is defined | |
7 | # -xxx exclude if xxx is defined | |
8 | # +!xxx include if xxx is not defined | |
9 | # -!xxx exclude if xxx is not defined | |
10 | # | |
11 | ||
12 | use FileHandle; | |
13 | use Getopt::Long; | |
14 | use strict; | |
15 | ||
16 | # Prototypes | |
17 | sub files_in($$); | |
18 | sub parse($$); | |
19 | sub help(); | |
20 | ||
21 | #sub strip_ext($) { | |
22 | # local ($_) = @_; | |
23 | # s/\.(diff?|patch)$//; | |
24 | #} | |
25 | ||
26 | #sub try_ext($) { | |
27 | # my ($path) = @_; | |
28 | # for my $p in (($path, "$path.diff", "$path.dif", "$path.patch")) { | |
29 | # return $p | |
30 | # if (-f $p); | |
31 | # } | |
32 | # return undef; | |
33 | #} | |
34 | ||
35 | sub slashme($) { | |
36 | my ($dir) = @_; | |
37 | $dir =~ s#([^/])$#$&/#; # append a slash if necessary | |
38 | if ($dir eq './') { | |
39 | return ''; | |
40 | } else { | |
41 | return $dir; | |
42 | } | |
43 | } | |
44 | ||
45 | # Generate a list of files in a directory | |
46 | # | |
47 | sub files_in($$) { | |
48 | my ($dir, $path) = @_; | |
49 | my $dh = new FileHandle; | |
50 | my (@files, $file); | |
51 | ||
52 | ||
53 | opendir $dh, length("$dir$path") ? "$dir$path" : '.' | |
54 | or die "$dir$path: $!\n"; | |
55 | while ($file = readdir($dh)) { | |
56 | next if $file =~ /^(\.|\.\.|\.#.*|CVS|.*~)$/; | |
57 | if (-d "$dir$path$file") { | |
58 | @files = (@files, files_in($dir, "$path$file/")); | |
59 | } else { | |
60 | #print "[$path$file]\n"; | |
61 | push @files, "$path$file"; | |
62 | } | |
63 | } | |
64 | closedir $dh; | |
65 | return @files; | |
66 | } | |
67 | ||
68 | # Parse a configuration file | |
69 | # Callback called with ($patch, @guards) arguments | |
70 | # | |
71 | sub parse($$) { | |
72 | my ($fh, $callback) = @_; | |
73 | ||
74 | my $line = ""; | |
75 | ||
76 | while (<$fh>) { | |
77 | chomp; | |
78 | s/(^|\s+)#.*//; | |
79 | if (s/\\$/ /) { | |
80 | $line .= $_; | |
81 | next; | |
82 | } | |
83 | $line .= $_; | |
84 | my @guards = (); | |
85 | foreach my $token (split /[\s\t\n]+/, $line) { | |
86 | next if $token eq ""; | |
87 | if ($token =~ /^[-+]/) { | |
88 | push @guards, $token; | |
89 | } else { | |
90 | #print "[" . join(",", @guards) . "] $token\n"; | |
91 | &$callback($token, @guards); | |
92 | } | |
93 | } | |
94 | $line = ""; | |
95 | } | |
96 | } | |
97 | ||
98 | # Command line options | |
99 | # | |
100 | my ($dir, $config, $default, $check, $list, $invert_match, $with_guards) = | |
101 | ( '', '-', 1, 0, 0, 0, 0); | |
102 | my @path; | |
103 | ||
104 | # Help text | |
105 | # | |
106 | sub help() { | |
107 | print "$0 - select from a list of files guarded by conditions\n"; | |
108 | print "SYNOPSIS: $0 [--prefix=dir] [--path=dir1:dir2:...]\n" . | |
109 | " [--default=0|1] [--check|--list] [--invert-match]\n" . | |
110 | " [--with-guards] [--config=file] symbol ...\n\n" . | |
111 | " (Default values: --path='" . join(':', @path) . "', " . | |
112 | "--default=$default)\n"; | |
113 | exit 0; | |
114 | } | |
115 | ||
116 | # Parse command line options | |
117 | # | |
118 | Getopt::Long::Configure ("bundling"); | |
119 | eval { | |
120 | unless (GetOptions ( | |
121 | 'd|prefix=s' => \$dir, | |
122 | 'c|config=s' => \$config, | |
123 | 'C|check' => \$check, | |
124 | 'l|list' => \$list, | |
125 | 'w|with-guards' => \$with_guards, | |
126 | 'p|path=s' => \@path, | |
127 | 'D|default=i' => \$default, | |
128 | 'v|invert-match' => \$invert_match, | |
129 | 'h|help' => sub { help(); exit 0; })) { | |
130 | help(); | |
131 | exit 1; | |
132 | } | |
133 | }; | |
134 | if ($@) { | |
135 | print "$@"; | |
136 | help(); | |
137 | exit 1; | |
138 | } | |
139 | ||
140 | @path = ('.') | |
141 | unless (@path); | |
142 | @path = split(/:/, join(':', @path)); | |
143 | ||
144 | my $fh = ($config eq '-') ? \*STDIN : new FileHandle($config) | |
145 | or die "$config: $!\n"; | |
146 | ||
147 | $dir = slashme($dir); | |
148 | ||
149 | if ($check) { | |
150 | # Check for duplicate files, or for files that are not referenced by | |
151 | # the specification. | |
152 | ||
153 | my $problems = 0; | |
154 | my @files; | |
155 | ||
156 | foreach (@path) { | |
157 | @files = (@files, files_in($dir, slashme($_))); | |
158 | } | |
159 | my %files = map { $_ => 0 } @files; | |
160 | ||
161 | parse($fh, sub { | |
162 | my ($patch, @guards) = @_; | |
163 | if (exists $files{$patch}) { | |
164 | $files{$patch}++; | |
165 | } else { | |
166 | print "Not found: $dir$patch\n"; | |
167 | $problems++; | |
168 | }}); | |
169 | ||
170 | $fh->close(); | |
171 | ||
172 | my ($file, $ref); | |
173 | while (($file, $ref) = each %files) { | |
174 | next if $ref == 1; | |
175 | ||
176 | if ($ref == 0) { | |
177 | print "Unused: $file\n" if $ref == 0; | |
178 | $problems++; | |
179 | } | |
180 | if ($ref > 1) { | |
181 | print "Warning: multiple uses: $file\n" if $ref > 1; | |
182 | # This is not an error if the entries are mutually exclusive... | |
183 | } | |
184 | } | |
185 | exit $problems ? 1 : 0; | |
186 | ||
187 | } elsif ($list) { | |
188 | parse($fh, sub { | |
189 | my ($patch, @guards) = @_; | |
190 | print join(' ', @guards), ' ' | |
191 | if (@guards && $with_guards); | |
192 | print "$dir$patch\n"; | |
193 | }); | |
194 | } else { | |
195 | # Generate a list of patches to apply. | |
196 | ||
197 | my %symbols = map { $_ => 1 } @ARGV; | |
198 | ||
199 | parse($fh, sub { | |
200 | my ($patch, @guards) = @_; | |
201 | ||
202 | my $selected; | |
203 | if (@guards) { | |
204 | # If the first guard is -xxx, the patch is included by default; | |
205 | # if it is +xxx, the patch is excluded by default. | |
206 | $selected = ($guards[0] =~ /^-/); | |
207 | ||
208 | foreach (@guards) { | |
209 | /^([-+])(!?)(.*)?/ | |
210 | or die "Bad guard '$_'\n"; | |
211 | ||
212 | # Check if the guard matches | |
213 | if (($2 eq '!' && !exists $symbols{$3}) || | |
214 | ($2 eq '' && ( $3 eq '' || exists $symbols{$3}))) { | |
215 | # Include or exclude | |
216 | $selected = ($1 eq '+'); | |
217 | } | |
218 | } | |
219 | } else { | |
220 | # If there are no guards, use the specified default result. | |
221 | $selected = $default; | |
222 | } | |
223 | ||
224 | print "$dir$patch\n" | |
225 | if $selected ^ $invert_match; | |
226 | }); | |
227 | ||
228 | $fh->close(); | |
229 | ||
230 | exit 0; | |
231 | } | |
232 | ||
233 | __END__ | |
234 | ||
235 | =head1 NAME | |
236 | ||
237 | guards - select from a list of files guarded by conditions | |
238 | ||
239 | =head1 SYNOPSIS | |
240 | ||
241 | F<guards> [--prefix=F<dir>] [--path=F<dir1:dir2:...>] [--default=<0|1>] | |
242 | [--check|--list] [--invert-match] [--with-guards] [--config=<file>] | |
243 | I<symbol> ... | |
244 | ||
245 | ||
246 | =head1 DESCRIPTION | |
247 | ||
248 | The script reads a configuration file that may contain so-called guards, file | |
249 | names, and comments, and writes those file names that satisfy all guards to | |
250 | standard output. The script takes a list of symbols as its arguments. Each line | |
251 | in the configuration file is processed separately. Lines may start with a | |
252 | number of guards. The following guards are defined: | |
253 | ||
254 | =over | |
255 | ||
256 | +I<xxx> Include the file(s) on this line if the symbol I<xxx> is defined. | |
257 | ||
258 | -I<xxx> Exclude the file(s) on this line if the symbol I<xxx> is defined. | |
259 | ||
260 | +!I<xxx> Include the file(s) on this line if the symbol I<xxx> is not defined. | |
261 | ||
262 | -!I<xxx> Exclude the file(s) on this line if the symbol I<xxx> is not defined. | |
263 | ||
264 | - Exclude this file. Used to avoid spurious I<--check> messages. | |
265 | ||
266 | =back | |
267 | ||
268 | The guards are processed left to right. The last guard that matches determines | |
269 | if the file is included. If no guard is specified, the I<--default> | |
270 | setting determines if the file is included. | |
271 | ||
272 | If no configuration file is specified, the script reads from standard input. | |
273 | ||
274 | The I<--check> option is used to compare the specification file against the | |
275 | file system. If files are referenced in the specification that do not exist, or | |
276 | if files are not enlisted in the specification file warnings are printed. The | |
277 | I<--path> option can be used to specify which directory or directories to scan. | |
278 | Multiple directories are eparated by a colon (C<:>) character. The | |
279 | I<--prefix> option specifies the location of the files. | |
280 | ||
281 | Use I<--list> to list all files independend of any rules. Use I<--invert-match> | |
282 | to list only the excluded patches. Use I<--with-guards> to also include all | |
283 | inclusion and exclusion rules. | |
284 | ||
285 | =head1 AUTHOR | |
286 | ||
287 | Andreas Gruenbacher <agruen@suse.de>, SUSE Labs |