Fixed failing hunks.
[rsync/rsync-patches.git] / verify-patches
index a4cf8c8..64ae506 100755 (executable)
@@ -5,7 +5,8 @@ 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, $require_quit);
+my($no_cvs, $failures_only, $minor_updates, $prepare_source);
+my @auto_cmds;
 
 &Getopt::Long::Configure('bundling');
 GetOptions(
@@ -13,10 +14,13 @@ GetOptions(
     'failures-only|f' => \$failures_only,
     'minor-updates|u' => \$minor_updates,
     'prepare-source|p' => \$prepare_source,
-    'require-quit|Q' => \$require_quit,
+    'auto-cmd|a=s' => sub { push(@auto_cmds, $_[1]) },
 ) or &usage;
 
+$" = '|';
+my $auto_regex = @auto_cmds ? qr/^(@auto_cmds)$/i : qr/^never$/;
 my $interesting_fuzz = $minor_updates ? '\d' : '[2-9]';
+$" = ' ';
 
 chdir('patches') if -d 'patches';
 
@@ -27,14 +31,17 @@ from inside the patches subdir.
 EOT
 }
 
+$ENV{'LC_COLLATE'} = 'C';
 $| = 1;
-my $CONF_OPTS = '-C';
+my $CONF_OPTS = '--cache-file=../config.cache';
 
 my($has_dependencies, @new, @rejects);
 
 END {
     &restore_cvsdir;
     system "rsync -a --del cvsdir/ workdir/" if -d 'cvsdir';
+    my $pid = readlink('lock') || 0;
+    unlink('lock') if $pid == $$;
 };
 
 my $root;
@@ -44,6 +51,9 @@ close IN;
 
 mkdir('tmp', 0777) unless -d 'tmp';
 chdir('tmp') or die "Unable to chdir to 'tmp'";
+symlink('..', 'patches') unless -d 'patches';
+
+symlink($$, 'lock') or die "Unable to create lock file: $!\n";
 
 mkdir('workdir') unless -d 'workdir';
 open(OUT, '>exclude') or die $!;
@@ -53,9 +63,18 @@ 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|;
+    @_ = qw( configure configure.in config.h.in configure.in
+            rsync.1 rsync.yo rsyncd.conf.5 rsyncd.conf.yo );
+    while (@_) {
+       my $gen = 'cvsdir/' . shift(@_);
+       my $src = 'cvsdir/' . shift(@_);
+       if ((-M $gen) > (-M $src)) {
+           system "touch $gen";
+       }
+    }
 }
 
-@ARGV = glob('../*.diff') unless @ARGV;
+@ARGV = glob('patches/*.diff') unless @ARGV;
 
 DIFF:
 foreach my $diff (@ARGV) {
@@ -64,24 +83,24 @@ foreach my $diff (@ARGV) {
     $diff =~ s#^(patches|\.\.)/##;
 
     my $conf_opts;
-    open(IN, "../$diff") or die $!;
+    open(IN, "patches/$diff") or die $!;
     while (<IN>) {
        last if /^--- /;
-       if (/^Depends-On-Patch: (\S+.diff)$/) {
+       if (m#^\s+patch -p1 <patches/(\S+.diff)# && $1 ne $diff) {
            my $dep = $1;
            $has_dependencies = 1;
            print "\nApplying dependency patch $dep...\n";
-           if (system("patch -d cvsdir -p1 -b -Vt <../$dep") != 0) {
+           if (system("patch -f -d cvsdir -p1 -b -Vt <patches/$dep") != 0) {
                print "Unable to cleanly apply dependency patch -- skipping $diff\n";
                system "rm -f cvsdir/*.rej cvsdir/*/*.rej";
                &restore_cvsdir;
                next DIFF;
            }
-           sleep(1) if $prepare_source; # Ensure later diffs get later times.
+           sleep(1); # Ensure later diffs get later times.
        }
-       if (!defined($conf_opts) && m#^\s*\./configure( .+)#) {
-           $conf_opts = $1;
-           $conf_opts =~ s/\s+\(.*?\)//;
+       if (!defined($conf_opts) && s#^\s+\./configure\s+##) {
+           chomp($conf_opts = $_);
+           $conf_opts =~ s/\s*\(.*?\)//;
        }
     }
     close IN;
@@ -102,14 +121,28 @@ foreach my $diff (@ARGV) {
        $default = 'N' if !$minor_updates && $default eq 'U,N';
     }
 
+    my $first_time = 1;
     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 = <STDIN>;
-       chomp $ans;
-       $ans = $default if $ans eq '';
+       my $ans = $default;
+       if ($first_time && $default =~ /$auto_regex/) {
+           print $default, "\n";
+       } else {
+           my $input = <STDIN>;
+           chomp $input;
+           if ($input =~ s/^(-a|--auto-cmd=?)\s*//) {
+               push(@auto_cmds, $input eq '' ? $default : $input);
+               $" = '|';
+               $auto_regex = qr/^(@auto_cmds)$/i;
+               $" = ' ';
+               next;
+           }
+           $ans = $input if $input ne '';
+       }
+       $first_time = 0;
        while ($ans =~ s/^\s*(!|\w)((?<!!)[^;,]*|[^;]*)[;,]?//) {
            my $cmd = "\U$1\E";
            if ($cmd eq '!') {
@@ -126,7 +159,9 @@ foreach my $diff (@ARGV) {
            }
            if ($cmd eq 'B') {
                chdir('workdir') or die $!;
-               system "./prepare-source && ./configure $CONF_OPTS $conf_opts && make";
+               my $cmd = "./prepare-source && ./configure $CONF_OPTS $conf_opts && make";
+               print "Running: $cmd\n";
+               system $cmd;
                chdir('..') or die $!;
                $default = '!make test';
                next;
@@ -137,7 +172,7 @@ foreach my $diff (@ARGV) {
            }
            if ($cmd eq 'E') {
                chdir('workdir') or die $!;
-               system "vim -d ../../$diff ../new.patch";
+               system "vim -d ../patches/$diff ../new.patch";
                chdir('..') or die $!;
                $default = 'U,A,D';
                next;
@@ -156,7 +191,7 @@ foreach my $diff (@ARGV) {
                exit;
            }
            if ($cmd eq 'U') {
-               system "cp -p new.patch ../$diff";
+               system "cp -p new.patch patches/$diff";
                print "\nUpdated $diff from new.patch\n";
                $default = 'A';
                next;
@@ -167,12 +202,6 @@ foreach my $diff (@ARGV) {
     &restore_cvsdir;
 }
 
-while ($require_quit) {
-    print "\nType 'Q' to quit: ";
-    $_ = <STDIN>;
-    exit if /^q/i;
-}
-
 exit;
 
 
@@ -181,11 +210,11 @@ sub apply_patch
     my($diff) = @_;
 
     undef @new;
-    system "rsync -a --delete --exclude='*~' cvsdir/ workdir/";
+    system "rsync -a --del --exclude='*~' cvsdir/ workdir/";
     print "\nApplying patch $diff...\n";
     undef @rejects;
-    my($saw_failure, $saw_offset, $saw_fuzz);
-    open(IN, "patch -d workdir -p1 --no-backup-if-mismatch <../$diff |") or die $!;
+    my($saw_offset, $saw_fuzz);
+    open(IN, "patch -f -d workdir -p1 --no-backup-if-mismatch <patches/$diff |") or die $!;
     while (<IN>) {
        print $_;
        chomp;
@@ -193,15 +222,15 @@ sub apply_patch
            push(@new, $_) unless -f "cvsdir/$_";
        } elsif (s/.* saving rejects to file //) {
            push(@rejects, $_);
-       } elsif (/^Hunk #\d+ FAILED/) {
-           $saw_failure = 1;
+       } elsif (/No file to patch\.\s+Skipping patch/) {
+           push(@rejects, 'skipped.file');
        } 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 'F,D,E' if @rejects;
     return 'D,E' if $saw_fuzz && !$failures_only;
     return 'D,U,N' if $saw_offset && !$failures_only;
     'N';
@@ -227,19 +256,19 @@ sub generate_new_patch
     foreach (@new) {
        system "touch -r workdir/$_ cvsdir/$_";
     }
-    open(IN, "../$diff") or die $!;
+    open(IN, "patches/$diff") or die $!;
     open(OUT, '>new.patch') or die $!;
     while (<IN>) {
        last if /^--- /;
        print OUT $_;
     }
     close IN;
-    &filter_diff('diff --exclude-from=exclude -upr cvsdir workdir');
+    &filter_diff('diff --exclude-from=exclude -dupr cvsdir workdir');
     if ($prepare_source) {
        # These are not included in the diff above so that patch will give
        # generated files a later timestamp than the source files.
        foreach my $fn (@generated_files) {
-           &filter_diff("diff -up cvsdir/$fn workdir");
+           &filter_diff("diff -dup cvsdir/$fn workdir");
        }
     }
     close OUT;
@@ -247,7 +276,7 @@ sub generate_new_patch
        unlink("cvsdir/$_");
     }
     print "\nDiffing... ";
-    if (system("diff ../$diff new.patch >/dev/null") == 0) {
+    if (system("diff patches/$diff new.patch >/dev/null") == 0) {
        print "new patch is identical to old.\n";
        return 'N';
     }
@@ -280,8 +309,8 @@ sub usage
 {
     die <<EOT;
 Usage: $0 [OPTS] [DIFF-FILE...]
+ -a, --auto-cmd=REGEX  If default_cmd =~ /^(REGEX)\$/, enter it automatically
  -f, --failures-only   Suggest skipping patches that don't have failing hunks
- -Q, --require-quit    Don't auto-exit at the end of the list
  -n, --no-cvs          Don't update tmp/cvsdir at the start of the run
  -p, --prepare-source  Run ./prepare-source and include generated files in diff
  -u, --minor-updates   Suggest 'U' for even minor changes in the diff