#!/usr/bin/perl use strict; use Getopt::Long; my @generated_files = qw( proto.h configure config.h.in rsync.1 rsyncd.conf.5 ); my($no_cvs, $failures_only, $minor_updates, $prepare_source); &Getopt::Long::Configure('bundling'); GetOptions( 'no-cvs|n' => \$no_cvs, 'failures-only|f' => \$failures_only, 'minor-updates|u' => \$minor_updates, 'prepare-source|p' => \$prepare_source, ) or &usage; my $interesting_fuzz = $minor_updates ? '\d' : '[2-9]'; chdir('patches') if -d 'patches'; if (!-f 'verify-patches') { die <); close IN; mkdir('tmp', 0777) unless -d 'tmp'; chdir('tmp') or die "Unable to chdir to 'tmp'"; mkdir('workdir') unless -d 'workdir'; open(OUT, '>exclude') or die $!; print OUT "CVS\n"; if (!$prepare_source) { print OUT join("\n", @generated_files), "\n"; } close OUT; unless ($no_cvs) { print "Using CVS to update the tmp/cvsdir copy of the source.\n"; system qq|cvs -qd "$root" co -P -d cvsdir rsync|; } @ARGV = glob('../*.diff') unless @ARGV; DIFF: foreach my $diff (@ARGV) { next unless $diff =~ /\.diff$/; next if $diff =~ /gzip-rsyncable[-_a-z]*\.diff$/; $diff =~ s#^(patches|\.\.)/##; my $conf_opts; open(IN, "../$diff") or die $!; while () { last if /^--- /; if (/^Depends-On-Patch: (\S+.diff)$/) { my $dep = $1; $has_dependencies = 1; print "\nApplying dependency patch $dep...\n"; if (system("patch -d cvsdir -p1 -b -Vt <../$dep") != 0) { print "Unable to cleanly apply dependency patch -- skipping $diff\n"; system "rm -f cvsdir/*.rej cvsdir/*/*.rej"; &restore_cvsdir; next DIFF; } } if (!defined($conf_opts) && m#^\s*\./configure( .+)#) { $conf_opts = $1; $conf_opts =~ s/\s+\(.*?\)//; } } close IN; $conf_opts = '' unless defined $conf_opts; my $default = apply_patch($diff); if ($prepare_source) { print "\nPreparing the source...\n"; chdir('workdir') or die $!; system "./prepare-source"; chdir('..') or die $!; } if ($default =~ s/^D,// || $default eq 'N') { my $def = generate_new_patch($diff); $default = 'U,N' if $default eq 'N' && $def eq 'E'; $default = 'N' if !$minor_updates && $default eq 'U,N'; } PROMPT: while (1) { print "\n----------- $diff ------------\n", "\nFix rejects, Diff create, Edit both diffs, Update patch,\n", "Apply patch again, !(CMD), Build rsync, Next, Quit: [$default] "; my $ans = ; chomp $ans; $ans = $default if $ans eq ''; while ($ans =~ s/^\s*(!|\w)((?) { print $_; chomp; if (s/^patching file //) { push(@new, $_) unless -f "cvsdir/$_"; } elsif (s/.* saving rejects to file //) { push(@rejects, $_); } elsif (/^Hunk #\d+ FAILED/) { $saw_failure = 1; } elsif (/^Hunk #\d+ succeeded at \d+( with fuzz $interesting_fuzz)?/o) { $saw_fuzz ||= defined $1; $saw_offset = 1; } } close IN; return 'F,D,E' if $saw_failure; return 'D,E' if $saw_fuzz && !$failures_only; return 'D,U,N' if $saw_offset && !$failures_only; 'N'; } sub generate_new_patch { my($diff) = @_; foreach (@new) { system "touch -r workdir/$_ cvsdir/$_"; } open(IN, "../$diff") or die $!; open(OUT, '>new.patch') or die $!; while () { last if /^--- /; print OUT $_; } close IN; open(IN, 'diff --exclude-from=exclude -upr cvsdir workdir |') or die $!; while () { next if /^(diff -|Index: |Only in )/; s#^\Q--- cvsdir/\E([^\t]+).*#--- old/$1#; s#^\Q+++ workdir/\E([^\t]+).*#+++ new/$1#; print OUT $_; } close IN; close OUT; foreach (@new) { unlink("cvsdir/$_"); } print "\nDiffing... "; if (system("diff ../$diff new.patch >/dev/null") == 0) { print "new patch is identical to old.\n"; return 'N'; } print "New patch DIFFERS from old.\n"; 'E'; } sub restore_cvsdir { return unless $has_dependencies; $has_dependencies = 0; chdir('cvsdir') or die $!; foreach (glob('*.~[1-9]~'), glob('*/*.~[1-9]~')) { my $fn; ($fn = $_) =~ s/\.~1~$//; if ($fn eq $_) { unlink($_); } elsif (-r $_) { rename($_, $fn); } else { unlink($_); unlink($fn); } } chdir('..') or die $!; } sub usage { die <