A couple parsing improvements and a new test case added to
[rsync/rsync-patches.git] / verify-patches
index ea72381..28d37b9 100755 (executable)
@@ -1,6 +1,18 @@
 #!/usr/bin/perl
 
 use strict;
+use Getopt::Long;
+
+my($no_cvs, $failures_only, $minor_updates);
+
+&Getopt::Long::Configure('bundling');
+GetOptions(
+    'no-cvs|n' => \$no_cvs,
+    'failures-only|f' => \$failures_only,
+    'minor-updates|u' => \$minor_updates,
+) or &usage;
+
+my $interesting_fuzz = $minor_updates ? '\d' : '[2-9]';
 
 chdir('patches') if -d 'patches';
 
@@ -11,6 +23,7 @@ from inside the patches subdir.
 EOT
 }
 
+$| = 1;
 $ENV{'TZ'} = 'UTC';
 my $CONF_OPTS = '-C';
 
@@ -41,15 +54,17 @@ rsyncd.conf.5
 EOT
 close OUT;
 
-print "Using CVS to update the tmp/cvsdir copy of the source.\n";
-system qq|cvs -d "$root" co -d cvsdir rsync|;
+unless ($no_cvs) {
+    print "Using CVS to update the tmp/cvsdir copy of the source.\n";
+    system qq|cvs -d "$root" co -d cvsdir rsync|;
+}
 
 @ARGV = glob('../*.diff') unless @ARGV;
 
 DIFF:
 foreach my $diff (@ARGV) {
     next unless $diff =~ /\.diff$/;
-    next if $diff =~ /gzip-rsyncable\.diff$/;
+    next if $diff =~ /gzip-rsyncable[-_a-z]*\.diff$/;
     $diff =~ s#^(patches|\.\.)/##;
 
     open(IN, "../$diff") or die $!;
@@ -58,9 +73,10 @@ foreach my $diff (@ARGV) {
        if (/^Depends-On-Patch: (\S+.diff)$/) {
            my $dep = $1;
            $has_dependencies = 1;
-           print "\nApplying dependency patch $dep\n";
+           print "\nApplying dependency patch $dep...\n";
            if (system("patch -d cvsdir -p0 -b -Vt -Zf <../$dep") != 0) {
-               print "Unable to cleanly apply depenency patch -- skipping $diff\n";
+               print "Unable to cleanly apply dependency patch -- skipping $diff\n";
+               system "rm -f cvsdir/*.rej cvsdir/*/*.rej";
                &restore_cvsdir;
                next DIFF;
            }
@@ -69,7 +85,13 @@ foreach my $diff (@ARGV) {
     close IN;
 
     my $default = apply_patch($diff);
+    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",
@@ -77,63 +99,89 @@ foreach my $diff (@ARGV) {
        my $ans = <STDIN>;
        chomp $ans;
        $ans = $default if $ans eq '';
-       if ($ans =~ /^!(.*)/) {
-           my $cmd = $1 || $ENV{'SHELL'};
-           chdir('workdir') or die $!;
-           system $cmd;
-           chdir('..') or die $!;
-           $default = 'D,E';
-           next;
-       }
-       if ($ans =~ /^\S*F/i) {
-           chdir('workdir') or die $!;
-           system "vim @rejects";
-           chdir('..') or die $!;
-           $default = 'D,E';
-       }
-       if ($ans =~ /^\S*D/i) {
-           $default = generate_new_patch($diff);
-       }
-       if ($ans =~ /^\S*E/i) {
-           chdir('workdir') or die $!;
-           system "vim -d ../../$diff ../new.patch";
-           chdir('..') or die $!;
-           $default = 'U,A';
-       }
-       if ($ans =~ /^\S*U/i) {
-           system "cp -p new.patch ../$diff";
-           print "\nUpdated $diff from new.patch\n";
-           $default = 'A';
-       }
-       if ($ans =~ /^\S*A/i) {
-           $default = apply_patch($diff);
-       }
-       if ($ans =~ /^\S*B/i) {
-           if (!-f 'workdir/Makefile') {
-               open(IN, '../../Makefile') or die $!;
-               open(OUT, '>workdir/Makefile') or die $!;
-               print OUT "srcdir=.\n\n";
-               while (<IN>) {
-                   last if /^gen:/;
+       while ($ans =~ s/^\s*(!|\w)((?<!!)[^;,]*|[^;]*)[;,]?//) {
+           my $cmd = "\U$1\E";
+           if ($cmd eq '!') {
+               $cmd = $2 || $ENV{'SHELL'};
+               chdir('workdir') or die $!;
+               system $cmd;
+               chdir('..') or die $!;
+               $default = 'D';
+               next;
+           }
+           if ($cmd eq 'A') {
+               $default = apply_patch($diff);
+               next;
+           }
+           if ($cmd eq 'B') {
+               if (!-f 'workdir/Makefile') {
+                   open(IN, '../../Makefile') or die $!;
+                   open(OUT, '>workdir/Makefile') or die $!;
+                   print OUT "srcdir=.\n\n";
+                   while (<IN>) {
+                       last if /^gen:/;
+                   }
+                   print OUT $_;
+                   while (<IN>) {
+                       last if /^clean:/;
+                       print OUT $_;
+                   }
+                   close IN;
+                   close OUT;
                }
-               print OUT $_;
+               my $need_autoconf;
+               my $conf_opts;
+               open(IN, "../$diff") or die $!;
                while (<IN>) {
-                   last if /^clean:/;
-                   print OUT $_;
+                   if (!defined $conf_opts) {
+                       $conf_opts = '' if /^---/;
+                       if (m#^\s*\./configure( .+)#) {
+                           $conf_opts = $1;
+                       }
+                   }
+                   if (m#^--- orig/(configure\.in|/aclocal\.m4)#) {
+                       $need_autoconf = 1;
+                       last;
+                   }
                }
                close IN;
-               close OUT;
+               chdir('workdir') or die $!;
+               system "autoconf; autoheader" if $need_autoconf;
+               system "make proto; ./configure $CONF_OPTS $conf_opts; make";
+               chdir('..') or die $!;
+               $default = '!make test';
+               next;
+           }
+           if ($cmd eq 'D') {
+               $default = generate_new_patch($diff);
+               next;
+           }
+           if ($cmd eq 'E') {
+               chdir('workdir') or die $!;
+               system "vim -d ../../$diff ../new.patch";
+               chdir('..') or die $!;
+               $default = 'U,A,D';
+               next;
+           }
+           if ($cmd eq 'F') {
+               chdir('workdir') or die $!;
+               system "vim @rejects";
+               chdir('..') or die $!;
+               $default = 'D,E';
+               next;
+           }
+           if ($cmd eq 'N') {
+               last PROMPT;
+           }
+           if ($cmd eq 'Q') {
+               exit;
+           }
+           if ($cmd eq 'U') {
+               system "cp -p new.patch ../$diff";
+               print "\nUpdated $diff from new.patch\n";
+               $default = 'A';
+               next;
            }
-           chdir('workdir') or die $!;
-           system "make gen; ./configure $CONF_OPTS; make";
-           chdir('..') or die $!;
-           $default = '!make test';
-       }
-       if ($ans =~ /^\S*Q/i) {
-           exit;
-       }
-       if ($ans =~ /^\S*N/i) {
-           last;
        }
     }
 
@@ -148,9 +196,10 @@ sub apply_patch
     my($diff) = @_;
 
     undef @new;
-    my $def = 'N';
-    system "rsync -a --delete cvsdir/ workdir/";
-    print "\n";
+    system "rsync -a --delete --exclude='*~' cvsdir/ workdir/";
+    print "\nApplying patch $diff...\n";
+    undef @rejects;
+    my($saw_failure, $saw_offset, $saw_fuzz);
     open(IN, "patch -d workdir -p0 --no-backup-if-mismatch -Zf <../$diff |") or die $!;
     while (<IN>) {
        print $_;
@@ -160,16 +209,17 @@ sub apply_patch
        } elsif (s/.* saving rejects to file //) {
            push(@rejects, $_);
        } elsif (/^Hunk #\d+ FAILED/) {
-           $def = 'F';
-       } elsif (/^Hunk #\d+ succeeded/) {
-           $def = 'E' unless $def eq 'F,D,E';
+           $saw_failure = 1;
+       } elsif (/^Hunk #\d+ succeeded at \d+( with fuzz $interesting_fuzz)?/o) {
+           $saw_fuzz ||= defined $1;
+           $saw_offset = 1;
        }
     }
     close IN;
-    if ($def eq 'N') {
-       generate_new_patch($diff);
-    }
-    $def;
+    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
@@ -191,7 +241,7 @@ sub generate_new_patch
        next if /^(diff -|Index: |Only in )/;
        s#^\Q--- cvsdir/\E#--- orig/#;
        s#^\Q+++ workdir/\E#+++ #;
-       s#(\.000000000)? \+0000$##;
+       s#(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d)(\.\d\d\d\d\d\d\d\d\d)? \+0000$#$1#;
        print OUT $_;
     }
     close IN;
@@ -199,10 +249,12 @@ sub generate_new_patch
     foreach (@new) {
        unlink("cvsdir/$_");
     }
+    print "\nDiffing... ";
     if (system("diff ../$diff new.patch >/dev/null") == 0) {
-       print "\n(New patch is identical to old.)\n";
+       print "new patch is identical to old.\n";
        return 'N';
     }
+    print "New patch DIFFERS from old.\n";
     'E';
 }
 
@@ -211,16 +263,27 @@ 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 $fn) {
-           rename($_,  $fn);
+       } elsif (-r $_) {
+           rename($_, $fn);
        } else {
            unlink($_);
            unlink($fn);
        }
     }
+    chdir('..') or die $!;
+}
+
+sub usage
+{
+    die <<EOT;
+Usage: $0 [OPTS] [DIFF-FILE...]
+ -n, --no-cvs          Don't update tmp/cvsdir at the start of the run
+ -u, --minor-updates   Suggest 'U' for even minor changes in the diff
+EOT
 }