Hello World

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);