File spec_add_patch of Package build
#!/usr/bin/perl -w
# vim:sw=4:et
# Author: Dirk Mueller
################################################################
#
# Copyright (c) 1995-2014 SUSE Linux Products GmbH
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program (see the file COPYING); if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#
################################################################
use strict;
sub helpexit {
print "$0: <patches...> [file.spec]\n";
exit 1;
}
my $specname;
my %diffs;
for my $arg (@ARGV) {
if ($arg =~ /\.spec$/) {
helpexit() if $specname;
$specname = $arg;
next;
}
$diffs{$arg} = 1;
}
sub find_specfile()
{
opendir(D, ".");
my @specs = grep { /\.spec$/ } readdir(D);
closedir(D);
# choose the one with the shortest name (heuristic)
$specname = ( sort { length($a) - length($b) } @specs)[0];
}
if (!defined($specname) || ! -f $specname) {
&find_specfile();
}
open(S, '<', $specname) or die;
my $ifdef_level = 0;
my $in_prep = 0;
my $in_global = 1;
my $autopatch = 0;
my $last_patch_in_prep_index = 0;
my $last_patch_in_global_index = 0;
my $last_source_in_global_index = 0;
my @c = ();
my $index = 0;
# first read the specfile, parse useful information
while(<S>)
{
if(/^\s*%\s*endif/) {
$ifdef_level--;
$last_patch_in_prep_index = $index if ($in_prep && $ifdef_level == 0);
}
die if ($ifdef_level < 0);
$ifdef_level++ if(/^\s*%\s*if/);
if ($ifdef_level == 0 && !$in_prep && $in_global
&& /^\%(?:prep|build|install|package|description|doc)/) {
$in_global = 0;
}
if (!$in_prep && /^%prep/i) {
$in_prep = 1;
die if ($in_global);
}
if ($in_prep
&& /^%auto(patch|setup)\b/) {
$autopatch = 1;
}
if ($in_prep
&& /^%setup\b/) {
$last_patch_in_prep_index = $index;
}
if ($in_prep
&& /^\%(?:build|install|package|description|doc)/) {
$in_prep = 0;
}
die if (($in_prep + $in_global) > 1);
if ($in_global && /^Patch(?:\d+)?:\s+(.+)/) {
$last_patch_in_global_index = $index;
if ($diffs{$1}) {
print "$1 already in, skipped.";
delete $diffs{$1};
}
}
if ($in_global && $ifdef_level == 0 && /^Source(?:\d+)?:/) {
$last_source_in_global_index = $index;
}
if ($in_prep && $ifdef_level == 0 && /^\%patch/) {
$last_patch_in_prep_index = $index;
}
push(@c, $_);
$index++;
}
close(S);
# append after last Source if this spec doesn't have any Patches
if ($last_patch_in_global_index == 0) {
$last_patch_in_global_index = $last_source_in_global_index;
}
die if ($ifdef_level > 0);
die if ($in_global || $in_prep);
die if ($last_patch_in_prep_index == 0 && !$autopatch);
die if ($last_patch_in_global_index == 0);
#print "adding Patch: $diffname to line $last_patch_in_global_index\n";
#print "adding %patch to line $last_patch_in_prep_index\n";
# determine patch number
my $patchnum = 0;
$patchnum = $1+1 if ($c[$last_patch_in_global_index] =~ /Patch(\d+):/);
$patchnum = 1 if ($c[$last_patch_in_global_index] =~ /Patch:/);
for my $diffname (keys %diffs) {
# determine strip level
my $striplevel = "";
open(P, '<', $diffname) or die "$diffname: $!\n";
while(<P>) {
# Check if either the --- filename starts with 'a/' or the +++
# filename starts with 'b/', or either starts with a package
# name/version prefix. We have to check for either, because either
# of them could be /dev/null if a file is being added or
# deleted.
$striplevel = " -p1"
if m,^--- a/, or
m,^\+\+\+ b/, or
m,^(---|\+\+\+) [^/]+-\d+\.,;
last if (/^@@ -\d/);
}
close(P);
print "Adding patch$striplevel $diffname to $specname\n";
splice @c, $last_patch_in_prep_index+1, 0, ("\%patch$patchnum$striplevel\n") unless $autopatch;
splice @c, $last_patch_in_global_index+1, 0,
(sprintf "Patch%s:%s%s\n", $patchnum, ' ' x (10-length($patchnum)), $diffname);
++$last_patch_in_global_index;
$last_patch_in_prep_index+=2; # actually line number
++$patchnum;
}
open(O, '>', "$specname.new") or die;
print O @c;
close(O);
system("diff", "-u", $specname, "$specname.new");
rename("$specname.new", $specname);