A few more improvements including an ability to build the patched
[rsync/rsync-patches.git] / verify-patches
1 #!/usr/bin/perl
2
3 use strict;
4
5 chdir('patches') if -d 'patches';
6
7 if (!-f 'verify-patches') {
8     die <<EOT;
9 Please run this script from the root of the rsync dir or
10 from inside the patches subdir.
11 EOT
12 }
13
14 $ENV{'TZ'} = 'UTC';
15 my $CONF_OPTS = '-C';
16
17 my($has_dependencies, @new, @rejects);
18
19 END {
20     &restore_cvsdir;
21     system "rsync -a --delete cvsdir/ workdir/" if -d 'cvsdir';
22 };
23
24 my $root;
25 open(IN, '../CVS/Root') or die $!;
26 chomp($root = <IN>);
27 close IN;
28
29 mkdir('tmp', 0777) unless -d 'tmp';
30 chdir('tmp') or die "Unable to chdir to 'tmp'";
31
32 mkdir('workdir') unless -d 'workdir';
33 open(OUT, '>exclude') or die $!;
34 print OUT <<EOT;
35 CVS
36 proto.h
37 configure
38 config.h.in
39 rsync.1
40 rsyncd.conf.5
41 EOT
42 close OUT;
43
44 print "Using CVS to update the tmp/cvsdir copy of the source.\n";
45 system qq|cvs -d "$root" co -d cvsdir rsync|;
46
47 @ARGV = glob('../*.diff') unless @ARGV;
48
49 DIFF:
50 foreach my $diff (@ARGV) {
51     next unless $diff =~ /\.diff$/;
52     next if $diff =~ /gzip-rsyncable\.diff$/;
53     $diff =~ s#^(patches|\.\.)/##;
54
55     open(IN, "../$diff") or die $!;
56     while (<IN>) {
57         last if /^--- /;
58         if (/^Depends-On-Patch: (\S+.diff)$/) {
59             my $dep = $1;
60             $has_dependencies = 1;
61             print "\nApplying dependency patch $dep\n";
62             if (system("patch -d cvsdir -p0 -b -Vt -Zf <../$dep") != 0) {
63                 print "Unable to cleanly apply depenency patch -- skipping $diff\n";
64                 &restore_cvsdir;
65                 next DIFF;
66             }
67         }
68     }
69     close IN;
70
71     my $default = apply_patch($diff);
72
73     while (1) {
74         print "\n----------- $diff ------------\n",
75             "\nFix rejects, Diff create, Edit both diffs, Update patch,\n",
76             "Apply patch again, !(CMD), Build rsync, Next, Quit: [$default] ";
77         my $ans = <STDIN>;
78         chomp $ans;
79         $ans = $default if $ans eq '';
80         if ($ans =~ /^!(.*)/) {
81             my $cmd = $1 || $ENV{'SHELL'};
82             chdir('workdir') or die $!;
83             system $cmd;
84             chdir('..') or die $!;
85             $default = 'D,E';
86             next;
87         }
88         if ($ans =~ /^\S*F/i) {
89             chdir('workdir') or die $!;
90             system "vim @rejects";
91             chdir('..') or die $!;
92             $default = 'D,E';
93         }
94         if ($ans =~ /^\S*D/i) {
95             $default = generate_new_patch($diff);
96         }
97         if ($ans =~ /^\S*E/i) {
98             chdir('workdir') or die $!;
99             system "vim -d ../../$diff ../new.patch";
100             chdir('..') or die $!;
101             $default = 'U,A';
102         }
103         if ($ans =~ /^\S*U/i) {
104             system "cp -p new.patch ../$diff";
105             print "\nUpdated $diff from new.patch\n";
106             $default = 'A';
107         }
108         if ($ans =~ /^\S*A/i) {
109             $default = apply_patch($diff);
110         }
111         if ($ans =~ /^\S*B/i) {
112             if (!-f 'workdir/Makefile') {
113                 open(IN, '../../Makefile') or die $!;
114                 open(OUT, '>workdir/Makefile') or die $!;
115                 print OUT "srcdir=.\n\n";
116                 while (<IN>) {
117                     last if /^gen:/;
118                 }
119                 print OUT $_;
120                 while (<IN>) {
121                     last if /^clean:/;
122                     print OUT $_;
123                 }
124                 close IN;
125                 close OUT;
126             }
127             chdir('workdir') or die $!;
128             system "make gen; ./configure $CONF_OPTS; make";
129             chdir('..') or die $!;
130             $default = '!make test';
131         }
132         if ($ans =~ /^\S*Q/i) {
133             exit;
134         }
135         if ($ans =~ /^\S*N/i) {
136             last;
137         }
138     }
139
140     &restore_cvsdir;
141 }
142
143 exit;
144
145
146 sub apply_patch
147 {
148     my($diff) = @_;
149
150     undef @new;
151     my $def = 'N';
152     system "rsync -a --delete cvsdir/ workdir/";
153     print "\n";
154     open(IN, "patch -d workdir -p0 --no-backup-if-mismatch -Zf <../$diff |") or die $!;
155     while (<IN>) {
156         print $_;
157         chomp;
158         if (s/^patching file //) {
159             push(@new, $_) unless -f "cvsdir/$_";
160         } elsif (s/.* saving rejects to file //) {
161             push(@rejects, $_);
162         } elsif (/^Hunk #\d+ FAILED/) {
163             $def = 'F';
164         } elsif (/^Hunk #\d+ succeeded/) {
165             $def = 'E' unless $def eq 'F,D,E';
166         }
167     }
168     close IN;
169     if ($def eq 'N') {
170         generate_new_patch($diff);
171     }
172     $def;
173 }
174
175 sub generate_new_patch
176 {
177     my($diff) = @_;
178
179     foreach (@new) {
180         system "touch -r workdir/$_ cvsdir/$_";
181     }
182     open(IN, "../$diff") or die $!;
183     open(OUT, '>new.patch') or die $!;
184     while (<IN>) {
185         last if /^--- /;
186         print OUT $_;
187     }
188     close IN;
189     open(IN, 'diff --exclude-from=exclude -upr cvsdir workdir |') or die $!;
190     while (<IN>) {
191         next if /^(diff -|Index: |Only in )/;
192         s#^\Q--- cvsdir/\E#--- orig/#;
193         s#^\Q+++ workdir/\E#+++ #;
194         s#(\.000000000)? \+0000$##;
195         print OUT $_;
196     }
197     close IN;
198     close OUT;
199     foreach (@new) {
200         unlink("cvsdir/$_");
201     }
202     if (system("diff ../$diff new.patch >/dev/null") == 0) {
203         print "\n(New patch is identical to old.)\n";
204         return 'N';
205     }
206     'E';
207 }
208
209 sub restore_cvsdir
210 {
211     return unless $has_dependencies;
212     $has_dependencies = 0;
213
214     foreach (glob('*.~[1-9]~'), glob('*/*.~[1-9]~')) {
215         my $fn;
216         ($fn = $_) =~ s/\.~1~$//;
217         if ($fn eq $_) {
218             unlink($_);
219         } elsif (-r $fn) {
220             rename($_,  $fn);
221         } else {
222             unlink($_);
223             unlink($fn);
224         }
225     }
226 }