]> git.pld-linux.org Git - projects/distfiles.git/blob - specparser.pl
Merge changes for git to master
[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("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) {
123             $no_source{ "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         } elsif ($_ eq '%else') {
160
161             # %else happens only when %if was interpreted
162             # so skip until %endif
163
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             }
174
175         } elsif (/^\s*\%(define|global)\s+([^\s]+)\s+([^\s].*?)\s*$/) {
176             $macros->{$2} = $3;
177
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     }
186
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     }
198
199     if (++$total > 10000) {
200         error("maximum number of bcond posibilities exceeded");
201         exit 0;
202     }
203 } # }}}
204
205 sub print_once($)
206 {
207     our %printed;
208     my $l = shift;
209     unless (exists $printed{$l}) {
210         print $l . "\n";
211         $printed{$l} = 1;
212     }
213 }
214
215 sub print_source($$$) # {{{
216 {
217     my ($no, $md5, $s) = @_;
218
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     }
238 } # }}}
239
240 sub add_md5_to_print($) # {{{
241 {
242     open(F, "< $_[0]") or die("failed opening: " . $_[0]);
243     my %sourcemap = ();
244     while (<F>) {
245         chomp;
246         if (/^((?:Source|Patch)\d+)\s*:\s*(.*)/i) {
247             my $sourceno = lc $1;
248             my $source = $2;
249             # master.dl is outdated currently
250             # $source =~ s/dl.sourceforge.net/master.dl.sourceforge.net/;
251             $sourcemap{ $sourceno } = $source;
252         } elsif (/^\s*#\s*((?:source|patch)\d+)-md5\s*:\s*([a-f0-9]{32})/i) {
253             my $no = lc $1;
254             my $md5 = $2;
255             if (defined $no_source{$no}) {
256                 error("both NoSource: $no and md5 given");
257             } elsif (defined $sourcemap{ $no} ) {
258                 my $source = $sourcemap{ $no };
259                 push @sources, [$no, $md5, $source];
260             } else {
261                 error("source $no not defined (# SourceN-md5: has to be placed just after SourceN:)");
262             }
263         } elsif (/^\s*BuildRequires:\s*digest\(%((?:SOURCE|PATCH)\d+)\)\s*=\s*([a-f0-9]{32})/i) {
264             my $no = lc $1;
265             my $md5 = $2;
266             if (defined $no_source{ $no }) {
267                 error("both NoSource: $no and md5 given");
268             } elsif (defined $sourcemap{ $no }) {
269                 my $source = $sourcemap{ $no };
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);
277 } # }}}
278
279 next_spec(shift);
280 preparse_spec($spec);
281 add_md5_to_print($spec);
282 cont( \@spec, { "nil" => "" } );
283
284 exit(0);
285
286 # vim: ts=4:sw=4:fdm=marker
This page took 0.080629 seconds and 4 git commands to generate.