]> git.pld-linux.org Git - projects/distfiles.git/blob - specparser.pl
- simplify parsing by storing mapping between numbers and URIs
[projects/distfiles.git] / specparser.pl
1 #!/usr/bin/perl
2 # $Id$
3 #
4 # USAGE: specparser.pl file.spec
5 #
6 # Output:
7 #   stdout:
8 #     list of:
9 #       <md5><space><url>\n
10 #     for given specfile.
11 #     except for lines prefixed with: ERROR:
12 #   exit status:
13 #     0 - normal
14 #     2 - cannot open spec file
15 #
16 use strict;
17 use warnings;
18
19 my %no_source;
20 my $spec;
21 my $base_spec;
22 my @spec;
23 my @sources;
24
25 sub next_spec($)
26 {
27         $spec = shift;
28         @spec = ();
29         $base_spec = $spec;
30         $base_spec =~ s|.*/||;
31 }
32
33 sub error($)
34 {
35         print_once( "ERROR: $base_spec: $_[0]" );
36 }
37
38 sub warning($)
39 {
40         print "ERROR: $base_spec: $_[0]\n";
41 }
42
43 sub trim_spaces($)
44 {
45         my $v = shift;
46         
47         $v =~ s/\s+$//;
48         $v =~ s/^\s+//;
49         
50         return $v;
51 }
52
53 # expand macros in string
54 sub expand($$) # {{{
55 {
56         my $v = trim_spaces(shift);
57         my $macrotree = shift;
58         my $cnt = 20;
59
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/;
70
71                 return $v if (length $v > 1000 or $cnt-- <= 0)
72         }
73
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, "");
77                 ($from, $to) = ($1, $2) 
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/;
91                 
92                 return $v if (length $v > 1000 or $cnt-- <= 0)
93         }
94
95         error("unexpanded macros in $v")
96                 if ($v =~ /\%[^0-9]/);
97         
98         return $v;
99 } # }}}
100
101 sub preparse_spec($) # {{{
102 {
103         @spec = ("");
104
105         open(F, "< $_[0]") or die;
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) {
123                         $no_source{$1} = 1;
124                 }
125         }
126         close(F);
127
128         shift @spec;
129 } # }}}
130
131
132 my $total = 0;
133
134 sub cont($$);
135 sub cont($$) # {{{
136 {
137         my ($spec, $macros) = @_;
138         local $_;
139         while ($_ = shift @{$spec}) {
140                 if (0 <= index $_, '%if') { # if, ifarch, ifos
141
142                         # split spec parsing
143                         my @speccopy = @{$spec};
144                         my %macroscopy = %{$macros};
145                         cont(\@speccopy, \%macroscopy);
146
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                         }
157
158                         # continue parsing
159                         
160                 } elsif ($_ eq '%else') {
161
162                         # %else happens only when %if was interpreted
163                         # so skip until %endif
164                         
165                         my $level = 0;
166                         while ($_ = shift @{$spec}) {
167                                 if ($level <= 0 and $_ eq '%endif') {
168                                         last;
169                                 } elsif (0 <= index $_, '%if') {
170                                         $level++;
171                                 } elsif ($_ eq '%endif') {
172                                         $level--;
173                                 }
174                         }
175
176                 } elsif (/^\s*\%(define|global)\s+([^\s]+)\s+([^\s].*?)\s*$/) {
177                         $macros->{$2} = $3;
178
179                 } elsif (/^Version\s*:\s*(.*?)\s*$/i) {
180                         $macros->{"version"} = $1;
181                 } elsif (/^Name\s*:\s*(.*?)\s*$/i) {
182                         $macros->{"name"} = $1;
183                 } elsif (!/\%endif/) {
184                         warn "unrecognised line: $_\n";
185                 }
186         }
187
188         # the end, yuppie !
189         foreach my $s (@sources) {
190                 my $src = expand( $s->[2], $macros );
191                 if (defined $src) {
192                         our %tried;
193                         unless (exists $tried{$src}) {
194                                 print_source( $s->[0], $s->[1], $src );
195                                 $tried{$src} = 1;
196                         }
197                 }
198         }
199         
200         if (++$total > 10000) {
201                 error("maximum number of bcond posibilities exceeded");
202                 exit 0;
203         }
204 } # }}}
205
206 sub print_once($)
207 {
208         our %printed;
209         my $l = shift;
210         unless (exists $printed{$l}) {
211                 print $l . "\n";
212                 $printed{$l} = 1;
213         }
214 }
215
216 sub print_source($$$) # {{{
217 {
218         my ($no, $md5, $s) = @_;
219
220         if ($s =~ /^([a-z0-9A-Z:\=\?\@\+\~\.,\-\/_]|\%[0-9])+$/) {
221                 if ($s =~ /^(ftp|http|https):\/\//) {
222                         if ($s =~ /\/$/) {
223                                 error("source $no ($s) is directory");
224                         } else {
225                                 if ($s =~ /:\/\/distfiles\.pld-linux\.org\/src/) {
226                                         $s =~ s|.*/||;
227                                         print_once( "$md5 no-url-copy://$s" );
228                                 } else {
229                                         print_once( "$md5 $s" );
230                                 }
231                         }
232                 } else {
233                         $s =~ s|.*/||;
234                         print_once( "$md5 no-url://$s");
235                 }
236         } else {
237                 error("source $no url $s is ill-formatted");
238         }
239 } # }}}
240
241 sub add_md5_to_print($) # {{{
242 {
243         open(F, "< $_[0]") or die;
244         my @sourcemap = ();
245         while (<F>) {
246                 chomp;
247                 if (/^Source(\d+)\s*:\s*(.*)/i) {
248                         my $sourceno = $1;
249                         my $source = $2;
250                         $sourcemap[$sourceno] = $source;
251                 } elsif (/^\s*#\s*source(\d+)-md5\s*:\s*([a-f0-9]{32})/i) {
252                         my $no = $1;
253                         my $md5 = $2;
254                         if (defined $no_source{$no}) {
255                                 error("both NoSource: $no and md5 given");
256                         } if (defined $sourcemap[$no]) {
257                                 my $source = $sourcemap[$no];
258                                 push @sources, [$no, $md5, $source];
259                         } else {
260                                 error("source $no not defined (# SourceN-md5: has to be placed just after SourceN:)");
261                         }
262                 } elsif (/^\s*BuildRequires:\s*digest\(%SOURCE(\d+)\)\s*=\s*([a-f0-9]{32})/i) {
263                         my $no = $1;
264                         my $md5 = $2;
265                         if (defined $no_source{$no}) {
266                                 error("both NoSource: $no and md5 given");
267                         } if (defined $sourcemap[$no]) {
268                                 my $source = $sourcemap[$no];
269                                 push @sources, [$no, $md5, $source];
270                         } else {
271                                 error("source $no not defined (# Source digest has to be placed after SourceN:)");
272                         }
273                 }
274         }
275         close(F);
276 } # }}}
277
278 next_spec(shift);
279 preparse_spec($spec);
280 add_md5_to_print($spec);
281 cont( \@spec, { "nil" => "" } );
282
283 exit(0);
284
285 # vim: ts=4:sw=4:fdm=marker
This page took 0.096052 seconds and 4 git commands to generate.