]> git.pld-linux.org Git - projects/distfiles.git/blame - specparser.pl
Avoid shell expansions of wget arguments
[projects/distfiles.git] / specparser.pl
CommitLineData
0db58532
MM
1#!/usr/bin/perl
2#
3# USAGE: specparser.pl file.spec
4#
5# Output:
6# stdout:
7# list of:
8# <md5><space><url>\n
9# for given specfile.
1b5ce8c2 10# except for lines prefixed with: ERROR:
0db58532
MM
11# exit status:
12# 0 - normal
13# 2 - cannot open spec file
14#
8079fcc5 15use strict;
16use warnings;
17
18my %no_source;
19my $spec;
20my $base_spec;
21my @spec;
22my @sources;
1db641df 23my $sources_file;
8b8c50a0 24my %patchset;
0db58532 25
ca6092e6
ER
26sub print_source($$$);
27
0dd6294b
MM
28sub next_spec($)
29{
e3413dab
ER
30 $spec = shift;
31 @spec = ();
32 $base_spec = $spec;
33 $base_spec =~ s|.*/||;
0dd6294b 34}
0db58532 35
0dd6294b 36sub error($)
0db58532 37{
e3413dab 38 print_once( "ERROR: $base_spec: $_[0]" );
0dd6294b
MM
39}
40
41sub warning($)
42{
e3413dab 43 print "ERROR: $base_spec: $_[0]\n";
0dd6294b 44}
51a652ef 45
0dd6294b
MM
46sub trim_spaces($)
47{
e3413dab 48 my $v = shift;
62c5617b 49
e3413dab
ER
50 $v =~ s/\s+$//;
51 $v =~ s/^\s+//;
62c5617b 52
e3413dab 53 return $v;
0dd6294b
MM
54}
55
56# expand macros in string
8079fcc5 57sub expand($$) # {{{
0dd6294b 58{
e3413dab
ER
59 my $v = trim_spaces(shift);
60 my $macrotree = shift;
61 my $cnt = 20;
62
63 while ($v =~ /\%\{([^\}]+)\}/) {
64 my $value;
65 if (defined $macrotree->{$1}) {
66 $value = $macrotree->{$1};
67 } else {
68 error("undefined macro $1");
69 $value = "UNDEFINED";
70 return undef;
71 }
72 $v =~ s/\%\{([^\}]+)\}/$value/;
73
74 return $v if (length $v > 1000 or $cnt-- <= 0)
75 }
76
77 while ($v =~ s/\%\(\s*echo\s+([^\|]+?)\s*\|\s*tr\s*(-d|)\s+([^\)]+?)\s*\)/\@\@tr-me\@\@/) {
78 my ($what, $d_opt, $how) = ($1, $2, $3);
79 my ($from, $to) = ($how, "");
80 ($from, $to) = ($1, $2)
81 if $how =~ /^([^\s]+)\s+([^\s]+)$/;
82 if ($d_opt and $to ne "") {
83 error("tr -d with second string)");
84 } elsif (($from . $to) =~ /^[a-zA-Z0-9\+_\-\.]+$/) {
85 if ($d_opt) {
86 eval "\$what =~ tr/$from//d;";
87 } else {
88 eval "\$what =~ tr/$from/$to/;";
89 }
90 } else {
91 error("illegal characters in tr string(s) '$from' '$to'");
92 }
93 $v =~ s/\@\@tr-me\@\@/$what/;
94
95 return $v if (length $v > 1000 or $cnt-- <= 0)
96 }
97
98 error("unexpanded macros in $v")
99 if ($v =~ /\%[^0-9]/);
100
101 return $v;
8079fcc5 102} # }}}
103
104sub preparse_spec($) # {{{
0db58532 105{
e3413dab
ER
106 @spec = ("");
107
108 open(F, "< $_[0]") or die("failed opening: " . $_[0]);
109 while (<F>) {
110 chomp;
111 if (/^\s*(\%(description|package|prep|install|pre|post|files)|BuildRoot|URL)/) {
112 last;
113 } elsif (/^\s*(\%if.*|\%else|\%endif|\%define\s+.*|Version.*|Name.*)\s*$/) {
114 $_ = $1;
115 if ($spec[$#spec] =~ /\%if/) {
116 if (/\%else/) {
117 next; # don't include empty %if-%else
118 } elsif (/\%endif/) {
119 # remove empty %if-%endif
120 pop @spec;
121 next;
122 }
123 }
124 push @spec, $_;
125 } elsif (/^NoSource\s*:\s*(\d+)\s*$/i) {
126 $no_source{ "source" . $1 } = 1;
127 } elsif (my ($patchset_pattern, $patchset_start, $patchset_end) = /^%patchset_source.+-f\s+(\S+)\s+(\S+)\s+(\S+)/i) {
128 %patchset = (
129 pattern => $patchset_pattern,
130 start => $patchset_start,
131 end => $patchset_end
132 );
133 }
134 }
135 close(F);
136
137 shift @spec;
8079fcc5 138} # }}}
139
1db641df
ER
140# read in 'sources' file
141sub read_sources_file() {
142 return () unless $sources_file and -e $sources_file;
e3413dab 143
1db641df
ER
144 our %files;
145 return \%files if %files;
146
147 open(my $fh, '<', $sources_file) or die $!;
e3413dab
ER
148 while (<$fh>) {
149 chomp;
150 next unless my ($hash, $filename) = /^([a-f0-9]{32})\s+\*?(.+)$/;
151 $files{$filename} = $hash;
152 }
1db641df
ER
153 return \%files;
154}
155
156sub process_patchset($) {
157 my $macros = shift;
158 return unless %patchset;
159
160 # parse sources file
161 my $checksums = read_sources_file() or return;
e3413dab
ER
162
163 # print out patchset entries which source md5 is present in source file
164 my $start = expand($patchset{start}, $macros);
165 my $end = expand($patchset{end}, $macros);
166 my $pattern = expand($patchset{pattern}, $macros);
167 for (my $i = $start; $i <= $end; $i++) {
168 my $url = sprintf($pattern, $i);
169 my ($basename) = $url =~ m{/([^/]+$)};
1db641df 170 my $hash = $checksums->{$basename} or next;
e3413dab
ER
171 print_source "patchset $i", $hash, $url;
172 }
8b8c50a0
ER
173}
174
8079fcc5 175
176my $total = 0;
177
178sub cont($$);
179sub cont($$) # {{{
180{
e3413dab
ER
181 my ($spec, $macros) = @_;
182 local $_;
183 while ($_ = shift @{$spec}) {
184 if (0 <= index $_, '%if') { # if, ifarch, ifos
185
186 # split spec parsing
187 my @speccopy = @{$spec};
188 my %macroscopy = %{$macros};
189 cont(\@speccopy, \%macroscopy);
190
191 my $level = 0;
192 while ($_ = shift @{$spec}) {
193 if ($level <= 0 and ($_ eq '%else' or $_ eq '%endif')) {
194 last;
195 } elsif (0 <= index $_, '%if') {
196 $level++;
197 } elsif ($_ eq '%endif') {
198 $level--;
199 }
200 }
201
202 # continue parsing
203 } elsif ($_ eq '%else') {
204
205 # %else happens only when %if was interpreted
206 # so skip until %endif
207
208 my $level = 0;
209 while ($_ = shift @{$spec}) {
210 if ($level <= 0 and $_ eq '%endif') {
211 last;
212 } elsif (0 <= index $_, '%if') {
213 $level++;
214 } elsif ($_ eq '%endif') {
215 $level--;
216 }
217 }
218
219 } elsif (/^\s*\%(define|global)\s+([^\s]+)\s+([^\s].*?)\s*$/) {
220 $macros->{$2} = $3;
221
222 } elsif (/^Version\s*:\s*(.*?)\s*$/i) {
223 $macros->{"version"} = $1;
224 } elsif (/^Name\s*:\s*(.*?)\s*$/i) {
225 $macros->{"name"} = $1;
226 } elsif (!/\%endif/) {
227 warn "unrecognised line: $_\n";
228 }
229 }
230
231 # so we have macros now, can parse patchset source
232 process_patchset($macros);
233
234 # the end, yuppie !
235 foreach my $s (@sources) {
236 my $src = expand( $s->[2], $macros );
237 if (defined $src) {
238 our %tried;
239 unless (exists $tried{$src}) {
240 print_source( $s->[0], $s->[1], $src );
241 $tried{$src} = 1;
242 }
243 }
244 }
245
246 if (++$total > 10000) {
247 error("maximum number of bcond posibilities exceeded");
248 exit 0;
249 }
8079fcc5 250} # }}}
251
252sub print_once($)
0db58532 253{
e3413dab
ER
254 our %printed;
255 my $l = shift;
256 unless (exists $printed{$l}) {
257 print $l . "\n";
258 $printed{$l} = 1;
259 }
0db58532
MM
260}
261
8079fcc5 262sub print_source($$$) # {{{
0db58532 263{
e3413dab
ER
264 my ($no, $md5, $s) = @_;
265
266 if ($s =~ /^([a-z0-9A-Z;:\=\?&\@\+\~\.,\-\/_]|\%[0-9])+(#\/[a-zA-Z0-9\._-]+)?$/) {
267 if ($s =~ /^(ftp|http|https):\/\//) {
268 if ($s =~ /\/$/) {
269 error("source $no ($s) is directory");
270 } else {
271 if ($s =~ /:\/\/distfiles\.pld-linux\.org\/src/) {
272 $s =~ s|.*/||;
273 print_once( "$md5 no-url-copy://$s" );
274 } else {
275 print_once( "$md5 $s" );
276 }
277 }
278 } else {
279 $s =~ s|.*/||;
280 print_once( "$md5 no-url://$s");
281 }
282 } else {
283 error("source $no url $s is ill-formatted");
284 }
8079fcc5 285} # }}}
286
287sub add_md5_to_print($) # {{{
288{
e3413dab
ER
289 open(F, "< $_[0]") or die("failed opening: " . $_[0]);
290 my %sourcemap = ();
291 while (<F>) {
292 chomp;
293 if (/^((?:Source|Patch)\d+)\s*:\s*(.*)/i) {
294 my $sourceno = lc $1;
295 my $source = $2;
296 # master.dl is outdated currently
297 # $source =~ s/dl.sourceforge.net/master.dl.sourceforge.net/;
298 $sourcemap{ $sourceno } = $source;
299 } elsif (/^\s*#\s*((?:source|patch)\d+)-md5\s*:\s*([a-f0-9]{32})/i) {
300 my $no = lc $1;
301 my $md5 = $2;
302 if (defined $no_source{$no}) {
303 error("both NoSource: $no and md5 given");
304 } elsif (defined $sourcemap{ $no} ) {
305 my $source = $sourcemap{ $no };
306 push @sources, [$no, $md5, $source];
307 } else {
308 error("source $no not defined (# SourceN-md5: has to be placed just after SourceN:)");
309 }
310 } elsif (/^\s*BuildRequires:\s*digest\(%((?:SOURCE|PATCH)\d+)\)\s*=\s*([a-f0-9]{32})/i) {
311 my $no = lc $1;
312 my $md5 = $2;
313 if (defined $no_source{ $no }) {
314 error("both NoSource: $no and md5 given");
315 } elsif (defined $sourcemap{ $no }) {
316 my $source = $sourcemap{ $no };
317 push @sources, [$no, $md5, $source];
318 } else {
319 error("source $no not defined (# Source digest has to be placed after SourceN:)");
320 }
321 }
322 }
323 close(F);
0de41088
ER
324
325 # find extra sources from detached checksum file
326 my $checksums = read_sources_file();
327 my @mapped = map { $_->[0] } @sources;
328 delete @sourcemap{@mapped};
329 while (my($source, $url) = each %sourcemap) {
330 my ($basename) = $url =~ m{/([^/]+$)};
164cf5c1 331 next unless defined $basename;
0de41088
ER
332 my $hash = $checksums->{$basename} or next;
333 push @sources, [$source, $hash, $url];
334 }
8079fcc5 335} # }}}
0db58532 336
0dd6294b 337next_spec(shift);
1db641df 338$sources_file = shift if @ARGV;
8079fcc5 339preparse_spec($spec);
340add_md5_to_print($spec);
341cont( \@spec, { "nil" => "" } );
0db58532
MM
342
343exit(0);
8079fcc5 344
345# vim: ts=4:sw=4:fdm=marker
This page took 0.186731 seconds and 4 git commands to generate.