#!/usr/bin/perl
-# $Id$
#
# USAGE: specparser.pl file.spec
#
my $base_spec;
my @spec;
my @sources;
+my $sources_file;
+my %patchset;
+
+sub print_source($$$);
sub next_spec($)
{
sub trim_spaces($)
{
my $v = shift;
-
+
$v =~ s/\s+$//;
$v =~ s/^\s+//;
-
+
return $v;
}
while ($v =~ s/\%\(\s*echo\s+([^\|]+?)\s*\|\s*tr\s*(-d|)\s+([^\)]+?)\s*\)/\@\@tr-me\@\@/) {
my ($what, $d_opt, $how) = ($1, $2, $3);
my ($from, $to) = ($how, "");
- ($from, $to) = ($1, $2)
+ ($from, $to) = ($1, $2)
if $how =~ /^([^\s]+)\s+([^\s]+)$/;
if ($d_opt and $to ne "") {
error("tr -d with second string)");
error("illegal characters in tr string(s) '$from' '$to'");
}
$v =~ s/\@\@tr-me\@\@/$what/;
-
+
return $v if (length $v > 1000 or $cnt-- <= 0)
}
error("unexpanded macros in $v")
if ($v =~ /\%[^0-9]/);
-
+
return $v;
} # }}}
{
@spec = ("");
- open(F, "< $_[0]") or die;
+ open(F, "< $_[0]") or die("failed opening: " . $_[0]);
while (<F>) {
chomp;
if (/^\s*(\%(description|package|prep|install|pre|post|files)|BuildRoot|URL)/) {
}
push @spec, $_;
} elsif (/^NoSource\s*:\s*(\d+)\s*$/i) {
- $no_source{$1} = 1;
+ $no_source{ "source" . $1 } = 1;
+ } elsif (my ($patchset) = /^%patchset_source\s+(.+)$/) {
+ use Getopt::Long qw(GetOptionsFromString);
+ my ($f, $s);
+ my ($ret, $args) = GetOptionsFromString($patchset, 's=s' => \$s, 'f=s' => \$f);
+ %patchset = (
+ pattern => $f,
+ filelist => $s,
+ start => $args->[0],
+ end => $args->[1],
+ );
}
}
close(F);
shift @spec;
} # }}}
+# read in 'sources' file
+sub read_sources_file {
+ my $filename = $_[0] || $sources_file;
+ return () unless $filename and -e $filename;
+
+ my %files;
+
+ open(my $fh, '<', $filename) or die $!;
+ while (<$fh>) {
+ chomp;
+ next unless my ($hash, $filename) = /^([a-f0-9]{32})\s+\*?(.+)$/;
+ $files{$filename} = $hash;
+ }
+ return \%files;
+}
+
+sub process_patchset($) {
+ my $macros = shift;
+ my $checksums;
+
+ return unless %patchset;
+
+ # print all files from sources file
+ if ($patchset{filelist}) {
+ my $prefix = expand($patchset{pattern}, $macros);
+ my $filelist = expand($patchset{filelist}, $macros);
+ $checksums = read_sources_file($filelist);
+ while (my($file, $hash) = each %$checksums) {
+ my $url = $prefix . $file;
+ print_source "patchset $file", $hash, $url;
+ }
+ return;
+ }
+
+ # parse sources file sequences
+ $checksums = read_sources_file() or return;
+
+ # print out patchset entries which source md5 is present in source file
+ my $start = expand($patchset{start}, $macros);
+ my $end = expand($patchset{end}, $macros);
+ my $pattern = expand($patchset{pattern}, $macros);
+ for (my $i = $start; $i <= $end; $i++) {
+ my $url = sprintf($pattern, $i);
+ my ($basename) = $url =~ m{/([^/]+$)};
+ my $hash = $checksums->{$basename} or next;
+ print_source "patchset $i", $hash, $url;
+ }
+}
+
my $total = 0;
}
# continue parsing
-
} elsif ($_ eq '%else') {
# %else happens only when %if was interpreted
# so skip until %endif
-
+
my $level = 0;
while ($_ = shift @{$spec}) {
if ($level <= 0 and $_ eq '%endif') {
}
}
+ # so we have macros now, can parse patchset source
+ process_patchset($macros);
+
# the end, yuppie !
foreach my $s (@sources) {
my $src = expand( $s->[2], $macros );
}
}
}
-
+
if (++$total > 10000) {
error("maximum number of bcond posibilities exceeded");
exit 0;
{
my ($no, $md5, $s) = @_;
- if ($s =~ /^([a-z0-9A-Z:\=\?\@\+\~\.\-\/_]|\%[0-9])+$/) {
+ if ($s =~ /^([a-z0-9A-Z;:\=\?&\@\+\~\.,\-\/\#_]|\%[0-9])+(#\/[a-zA-Z0-9\._-]+)?$/) {
if ($s =~ /^(ftp|http|https):\/\//) {
if ($s =~ /\/$/) {
error("source $no ($s) is directory");
sub add_md5_to_print($) # {{{
{
- open(F, "< $_[0]") or die;
- my $sourceno = undef;
- my $source = undef;
+ open(F, "< $_[0]") or die("failed opening: " . $_[0]);
+ my %sourcemap = ();
while (<F>) {
chomp;
- if (/^Source(\d+)\s*:\s*(.*)/i) {
- $sourceno = $1;
- $source = $2;
- } elsif (/^\s*#\s*source(\d+)-md5\s*:\s*([a-f0-9]{32})/i) {
- my $no = $1;
+ if (/^((?:Source|Patch)\d+)\s*:\s*(.*)/i) {
+ my $sourceno = lc $1;
+ my $source = $2;
+ # master.dl is outdated currently
+ # $source =~ s/dl.sourceforge.net/master.dl.sourceforge.net/;
+ $sourcemap{ $sourceno } = $source;
+ } elsif (/^\s*#\s*((?:source|patch)\d+)-md5\s*:\s*([a-f0-9]{32})/i) {
+ my $no = lc $1;
my $md5 = $2;
if (defined $no_source{$no}) {
error("both NoSource: $no and md5 given");
- } if (defined $sourceno) {
- if ($sourceno == $no) {
- push @sources, [$no, $md5, $source];
- } else {
- error("found md5 for source $no, but last defined source is $sourceno (# SourceN-md5: has to be placed just after SourceN:)");
- }
+ } elsif (defined $sourcemap{ $no} ) {
+ my $source = $sourcemap{ $no };
+ push @sources, [$no, $md5, $source];
} else {
error("source $no not defined (# SourceN-md5: has to be placed just after SourceN:)");
}
-
- $sourceno = undef;
- $source = undef;
+ } elsif (/^\s*BuildRequires:\s*digest\(%((?:SOURCE|PATCH)\d+)\)\s*=\s*([a-f0-9]{32})/i) {
+ my $no = lc $1;
+ my $md5 = $2;
+ if (defined $no_source{ $no }) {
+ error("both NoSource: $no and md5 given");
+ } elsif (defined $sourcemap{ $no }) {
+ my $source = $sourcemap{ $no };
+ push @sources, [$no, $md5, $source];
+ } else {
+ error("source $no not defined (# Source digest has to be placed after SourceN:)");
+ }
}
}
close(F);
+
+ # find extra sources from detached checksum file
+ my $checksums = read_sources_file();
+ my @mapped = map { $_->[0] } @sources;
+ delete @sourcemap{@mapped};
+ while (my($source, $url) = each %sourcemap) {
+ my ($basename) = $url =~ m{/([^/]+$)};
+ next unless defined $basename;
+ my $hash = $checksums->{$basename} or next;
+ push @sources, [$source, $hash, $url];
+ }
} # }}}
next_spec(shift);
+$sources_file = shift if @ARGV;
preparse_spec($spec);
add_md5_to_print($spec);
cont( \@spec, { "nil" => "" } );