#!/usr/bin/perl
#############################################################################
# Copyright (c) 2015 Micro Focus
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public License 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; if not, contact Novell, Inc.
#
# To contact Novell about this file by physical or electronic mail,
# you may find current contact information at www.novell.com
#############################################################################
# Check that the a patch is in the format produced by scripts/refresh_patch.sh
use strict;
use warnings;
my $errors = 0;
my $git_renames = 0;
my %reported;
our $file;
sub err {
return if ($reported{"$file:$_[0]"}++);
print STDERR "$file:$.: error: @_\n";
$errors = 1;
}
sub check_filename {
my $filename = shift;
if ($filename !~ m:^(/dev/null\b|[ab]/):) {
err("Patch not in -p ab format");
}
if ($filename =~ /\s\S/) {
err("Timestamp after filename");
}
if ($filename =~ /\/\//) {
err("Double slash in filename");
}
}
sub do_patch {
my $fh = shift;
my $fn = shift;
my ($in_payload, $in_hunk, $maybe_eof, $last_noncontext);
my $xen_prefix = "patches.xen/";
my $is_xen = $xen_prefix eq substr($fn,0,length($xen_prefix));
my @lines = <$fh>;
chomp(@lines);
for (my $i = 1; $i < scalar(@lines); $i++) {
my $cur = $lines[$i];
$. = $i + 1;
if ($cur =~ /^similarity index \d+%/ &&
$lines[$i-1] =~ /^diff --git/) {
$in_payload = 1;
$in_hunk = 0;
$maybe_eof = 0;
err("git diff -M patches fail with sequences-patch.sh --fast");
$git_renames = 1;
}
if ($cur =~ /^\+\+\+ / && $lines[$i-1] =~ /^--- /) {
$in_payload = 1;
$in_hunk = 0;
$maybe_eof = 0;
(my $new = $cur) =~ s/^\+\+\+ //;
(my $old = $lines[$i-1]) =~ s/^--- //;
if (!$is_xen) { check_filename($new); }
if (!$is_xen) { check_filename($old); }
if (!$is_xen && $i > 2 && $lines[$i-2] =~ /^={20}/ &&
$lines[$i-3] =~ /^Index: /) {
err("Superfluous Index: line in patch");
}
next;
}
next unless $in_payload;
if ($cur =~ /^(diff |Index: |--- |\\ No newline at end of file)/) {
$in_hunk = 0;
$maybe_eof = 0;
next;
} elsif ($cur =~ /^@@ /) {
$in_hunk = 1;
$last_noncontext = $i;
$maybe_eof = 0;
next;
}
next unless $in_hunk;
# "-- " can be used as signature delimiter and is sometimes
# written as "--"
if ($cur =~ /^-- ?$/) {
$maybe_eof = 1;
next;
}
if ($cur =~ /^[-+]/) {
$last_noncontext = $i;
next;
}
# Blank lines seem to be OK for git am
if ($cur =~ /^$|^ /) {
next;
}
# Try to ignore junk at the end of patch files
if ($i - $last_noncontext <= 3 && !$maybe_eof) {
err("Malformed patch (missing leading space in context line?)");
}
}
}
if (!@ARGV) {
die "Usage: $0 <patch>...\n";
}
if ($ARGV[0] eq "--stdin") {
shift;
$file = "<stdin>";
if (scalar(@ARGV)) {
$file = $ARGV[0];
}
open(my $fh, '-');
do_patch($fh, $file);
close($fh);
} else {
for $file (@ARGV) {
open(my $fh, '<', $file) or die "$file: $!\n";
do_patch($fh, $file);
close($fh);
}
}
if ($errors) {
printf STDERR ("Please refresh the patch%s using scripts/refresh_patch.sh\n", (scalar(@ARGV) > 1 ? "es" : ""));
if ($git_renames) {
print STDERR "Use --no-renames when generating patches from git\n";
}
exit(1);
}
exit(0);