4 # USAGE: specparser.pl file.spec
11 # except for lines prefixed with: ERROR:
14 # 2 - cannot open spec file
32 $base_spec =~ s|.*/||;
37 print_once( "ERROR: $base_spec: $_[0]" );
42 print "ERROR: $base_spec: $_[0]\n";
55 # expand macros in string
58 my $v = trim_spaces(shift);
59 my $macrotree = shift;
62 while ($v =~ /\%\{([^\}]+)\}/) {
64 if (defined $macrotree->{$1}) {
65 $value = $macrotree->{$1};
67 error("undefined macro $1");
71 $v =~ s/\%\{([^\}]+)\}/$value/;
73 return $v if (length $v > 1000 or $cnt-- <= 0)
76 while ($v =~ s/\%\(\s*echo\s+([^\|]+?)\s*\|\s*tr\s*(-d|)\s+([^\)]+?)\s*\)/\@\@tr-me\@\@/) {
77 my ($what, $d_opt, $how) = ($1, $2, $3);
78 my ($from, $to) = ($how, "");
79 ($from, $to) = ($1, $2)
80 if $how =~ /^([^\s]+)\s+([^\s]+)$/;
81 if ($d_opt and $to ne "") {
82 error("tr -d with second string)");
83 } elsif (($from . $to) =~ /^[a-zA-Z0-9\+_\-\.]+$/) {
85 eval "\$what =~ tr/$from//d;";
87 eval "\$what =~ tr/$from/$to/;";
90 error("illegal characters in tr string(s) '$from' '$to'");
92 $v =~ s/\@\@tr-me\@\@/$what/;
94 return $v if (length $v > 1000 or $cnt-- <= 0)
97 error("unexpanded macros in $v")
98 if ($v =~ /\%[^0-9]/);
103 sub preparse_spec($) # {{{
107 open(F, "< $_[0]") or die("failed opening: " . $_[0]);
110 if (/^\s*(\%(description|package|prep|install|pre|post|files)|BuildRoot|URL)/) {
112 } elsif (/^\s*(\%if.*|\%else|\%endif|\%define\s+.*|Version.*|Name.*)\s*$/) {
114 if ($spec[$#spec] =~ /\%if/) {
116 next; # don't include empty %if-%else
117 } elsif (/\%endif/) {
118 # remove empty %if-%endif
124 } elsif (/^NoSource\s*:\s*(\d+)\s*$/i) {
125 $no_source{ "source" . $1 } = 1;
126 } elsif (my ($patchset_pattern, $patchset_start, $patchset_end) = /^%patchset_source.+-f\s+(\S+)\s+(\S+)\s+(\S+)/i) {
128 pattern => $patchset_pattern,
129 start => $patchset_start,
139 sub process_patchset($) {
141 return unless %patchset or $sources;
145 open(my $fh, '<', $sources) or die $!;
148 next unless my ($hash, $filename) = /^([a-f0-9]{32})\s+\*?(.+)$/;
149 $files{$filename} = $hash;
152 # print out patchset entries which source md5 is present in source file
153 my $start = expand($patchset{start}, $macros);
154 my $end = expand($patchset{end}, $macros);
155 for (my $i = $start; $i < $end; $i++) {
156 my $url = sprintf($patchset{pattern}, $i);
157 my ($basename) = $url =~ m{/([^/]+$)};
158 my $hash = $files{$basename} or next;
159 printf "%s %s\n", $hash, $url;
169 my ($spec, $macros) = @_;
171 while ($_ = shift @{$spec}) {
172 if (0 <= index $_, '%if') { # if, ifarch, ifos
175 my @speccopy = @{$spec};
176 my %macroscopy = %{$macros};
177 cont(\@speccopy, \%macroscopy);
180 while ($_ = shift @{$spec}) {
181 if ($level <= 0 and ($_ eq '%else' or $_ eq '%endif')) {
183 } elsif (0 <= index $_, '%if') {
185 } elsif ($_ eq '%endif') {
191 } elsif ($_ eq '%else') {
193 # %else happens only when %if was interpreted
194 # so skip until %endif
197 while ($_ = shift @{$spec}) {
198 if ($level <= 0 and $_ eq '%endif') {
200 } elsif (0 <= index $_, '%if') {
202 } elsif ($_ eq '%endif') {
207 } elsif (/^\s*\%(define|global)\s+([^\s]+)\s+([^\s].*?)\s*$/) {
210 } elsif (/^Version\s*:\s*(.*?)\s*$/i) {
211 $macros->{"version"} = $1;
212 } elsif (/^Name\s*:\s*(.*?)\s*$/i) {
213 $macros->{"name"} = $1;
214 } elsif (!/\%endif/) {
215 warn "unrecognised line: $_\n";
219 # so we have macros now, can parse patchset source
220 process_patchset($macros);
223 foreach my $s (@sources) {
224 my $src = expand( $s->[2], $macros );
227 unless (exists $tried{$src}) {
228 print_source( $s->[0], $s->[1], $src );
234 if (++$total > 10000) {
235 error("maximum number of bcond posibilities exceeded");
244 unless (exists $printed{$l}) {
250 sub print_source($$$) # {{{
252 my ($no, $md5, $s) = @_;
254 if ($s =~ /^([a-z0-9A-Z;:\=\?&\@\+\~\.,\-\/_]|\%[0-9])+(#\/[a-zA-Z0-9\._-]+)?$/) {
255 if ($s =~ /^(ftp|http|https):\/\//) {
257 error("source $no ($s) is directory");
259 if ($s =~ /:\/\/distfiles\.pld-linux\.org\/src/) {
261 print_once( "$md5 no-url-copy://$s" );
263 print_once( "$md5 $s" );
268 print_once( "$md5 no-url://$s");
271 error("source $no url $s is ill-formatted");
275 sub add_md5_to_print($) # {{{
277 open(F, "< $_[0]") or die("failed opening: " . $_[0]);
281 if (/^((?:Source|Patch)\d+)\s*:\s*(.*)/i) {
282 my $sourceno = lc $1;
284 # master.dl is outdated currently
285 # $source =~ s/dl.sourceforge.net/master.dl.sourceforge.net/;
286 $sourcemap{ $sourceno } = $source;
287 } elsif (/^\s*#\s*((?:source|patch)\d+)-md5\s*:\s*([a-f0-9]{32})/i) {
290 if (defined $no_source{$no}) {
291 error("both NoSource: $no and md5 given");
292 } elsif (defined $sourcemap{ $no} ) {
293 my $source = $sourcemap{ $no };
294 push @sources, [$no, $md5, $source];
296 error("source $no not defined (# SourceN-md5: has to be placed just after SourceN:)");
298 } elsif (/^\s*BuildRequires:\s*digest\(%((?:SOURCE|PATCH)\d+)\)\s*=\s*([a-f0-9]{32})/i) {
301 if (defined $no_source{ $no }) {
302 error("both NoSource: $no and md5 given");
303 } elsif (defined $sourcemap{ $no }) {
304 my $source = $sourcemap{ $no };
305 push @sources, [$no, $md5, $source];
307 error("source $no not defined (# Source digest has to be placed after SourceN:)");
315 $sources = shift if @ARGV;
316 undef $sources if $sources and not -e $sources;
317 preparse_spec($spec);
318 add_md5_to_print($spec);
319 cont( \@spec, { "nil" => "" } );
323 # vim: ts=4:sw=4:fdm=marker