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