close_or_die($fh);
}
+# AIX supplies a pre-historic patch program, which certainly predates Linux
+# and is probably older than NT. It can't cope with unified diffs. Meanwhile,
+# it's hard enough to get git diff to output context diffs, let alone git show,
+# and nearly all the patches embedded here are unified. So it seems that the
+# path of least resistance is to convert unified diffs to context diffs:
+
+sub process_hunk {
+ my ($from_out, $to_out, $has_from, $has_to, $delete, $add) = @_;
+ ++$$has_from if $delete;
+ ++$$has_to if $add;
+
+ if ($delete && $add) {
+ $$from_out .= "! $_\n" foreach @$delete;
+ $$to_out .= "! $_\n" foreach @$add;
+ } elsif ($delete) {
+ $$from_out .= "- $_\n" foreach @$delete;
+ } elsif ($add) {
+ $$to_out .= "+ $_\n" foreach @$add;
+ }
+}
+
+# This isn't quite general purpose, as it can't cope with
+# '\ No newline at end of file'
+sub ud2cd {
+ my $diff_in = shift;
+ my $diff_out = '';
+
+ # Stuff before the diff
+ while ($diff_in =~ s/\A(?!\*\*\* )(?!--- )([^\n]*\n?)//ms && length $1) {
+ $diff_out .= $1;
+ }
+
+ if (!length $diff_in) {
+ die "That didn't seem to be a diff";
+ }
+
+ if ($diff_in =~ /\A\*\*\* /ms) {
+ warn "Seems to be a context diff already\n";
+ return $diff_out . $diff_in;
+ }
+
+ # Loop for files
+ FILE: while (1) {
+ if ($diff_in =~ s/\A((?:diff |index )[^\n]+\n)//ms) {
+ $diff_out .= $1;
+ next;
+ }
+ if ($diff_in !~ /\A--- /ms) {
+ # Stuff after the diff;
+ return $diff_out . $diff_in;
+ }
+ $diff_in =~ s/\A([^\n]+\n?)//ms;
+ my $line = $1;
+ die "Can't parse '$line'" unless $line =~ s/\A--- /*** /ms;
+ $diff_out .= $line;
+ $diff_in =~ s/\A([^\n]+\n?)//ms;
+ $line = $1;
+ die "Can't parse '$line'" unless $line =~ s/\A\+\+\+ /--- /ms;
+ $diff_out .= $line;
+
+ # Loop for hunks
+ while (1) {
+ next FILE
+ unless $diff_in =~ s/\A\@\@ (-([0-9]+),([0-9]+) \+([0-9]+),([0-9]+)) \@\@[^\n]*\n?//;
+ my ($hunk, $from_start, $from_count, $to_start, $to_count)
+ = ($1, $2, $3, $4, $5);
+ my $from_end = $from_start + $from_count - 1;
+ my $to_end = $to_start + $to_count - 1;
+ my ($from_out, $to_out, $has_from, $has_to, $add, $delete);
+ while (length $diff_in && ($from_count || $to_count)) {
+ die "Confused in $hunk" unless $diff_in =~ s/\A([^\n]*)\n//ms;
+ my $line = $1;
+ $line = ' ' unless length $line;
+ if ($line =~ /^ .*/) {
+ process_hunk(\$from_out, \$to_out, \$has_from, \$has_to,
+ $delete, $add);
+ undef $delete;
+ undef $add;
+ $from_out .= " $line\n";
+ $to_out .= " $line\n";
+ --$from_count;
+ --$to_count;
+ } elsif ($line =~ /^-(.*)/) {
+ push @$delete, $1;
+ --$from_count;
+ } elsif ($line =~ /^\+(.*)/) {
+ push @$add, $1;
+ --$to_count;
+ } else {
+ die "Can't parse '$line' as part of hunk $hunk";
+ }
+ }
+ process_hunk(\$from_out, \$to_out, \$has_from, \$has_to,
+ $delete, $add);
+ die "No lines in hunk $hunk"
+ unless length $from_out || length $to_out;
+ die "No changes in hunk $hunk"
+ unless $has_from || $has_to;
+ $diff_out .= "***************\n";
+ $diff_out .= "*** $from_start,$from_end ****\n";
+ $diff_out .= $from_out if $has_from;
+ $diff_out .= "--- $to_start,$to_end ----\n";
+ $diff_out .= $to_out if $has_to;
+ }
+ }
+}
+
+{
+ my $use_context;
+
+ sub placate_patch_prog {
+ my $patch = shift;
+
+ if (!defined $use_context) {
+ my $version = `patch -v 2>&1`;
+ die "Can't run `patch -v`, \$?=$?, bailing out"
+ unless defined $version;
+ if ($version =~ /Free Software Foundation/) {
+ $use_context = 0;
+ } elsif ($version =~ /Header: patch\.c,v.*\blwall\b/) {
+ # The system patch is older than Linux, and probably older than
+ # Windows NT.
+ $use_context = 1;
+ } else {
+ # Don't know.
+ $use_context = 0;
+ }
+ }
+
+ return $use_context ? ud2cd($patch) : $patch;
+ }
+}
+
sub apply_patch {
my ($patch, $what, $files) = @_;
$what = 'patch' unless defined $what;
$patch =~ m!^--- a/(\S+)\n\+\+\+ b/\1!sm;
$files = " $1";
}
+ my $patch_to_use = placate_patch_prog($patch);
open my $fh, '|-', 'patch', '-p1' or die "Can't run patch: $!";
- print $fh $patch;
+ print $fh $patch_to_use;
return if close $fh;
print STDERR "Patch is <<'EOPATCH'\n${patch}EOPATCH\n";
+ print STDERR "\nConverted to a context diff <<'EOCONTEXT'\n${patch_to_use}EOCONTEXT\n";
die "Can't $what$files: $?, $!";
}