Fixed failing hunks.
[rsync/rsync-patches.git] / verify-patches
CommitLineData
3a849c85
WD
1#!/usr/bin/perl
2
3use strict;
78b5ec5e
WD
4use Getopt::Long;
5
6my($no_cvs, $minor_updates);
7
8&Getopt::Long::Configure('bundling');
9GetOptions(
10 'no-cvs|n' => \$no_cvs,
11 'minor-updates|u' => \$minor_updates,
12) or &usage;
3a849c85
WD
13
14chdir('patches') if -d 'patches';
15
16if (!-f 'verify-patches') {
17 die <<EOT;
18Please run this script from the root of the rsync dir or
19from inside the patches subdir.
20EOT
21}
22
a978ee02 23$| = 1;
b1a4cc32
WD
24$ENV{'TZ'} = 'UTC';
25my $CONF_OPTS = '-C';
3a849c85 26
b1a4cc32 27my($has_dependencies, @new, @rejects);
3a849c85
WD
28
29END {
30 &restore_cvsdir;
b1a4cc32 31 system "rsync -a --delete cvsdir/ workdir/" if -d 'cvsdir';
3a849c85
WD
32};
33
34my $root;
35open(IN, '../CVS/Root') or die $!;
36chomp($root = <IN>);
37close IN;
38
b1a4cc32
WD
39mkdir('tmp', 0777) unless -d 'tmp';
40chdir('tmp') or die "Unable to chdir to 'tmp'";
3a849c85
WD
41
42mkdir('workdir') unless -d 'workdir';
43open(OUT, '>exclude') or die $!;
44print OUT <<EOT;
b1a4cc32 45CVS
3a849c85
WD
46proto.h
47configure
48config.h.in
49rsync.1
50rsyncd.conf.5
51EOT
52close OUT;
53
78b5ec5e
WD
54unless ($no_cvs) {
55 print "Using CVS to update the tmp/cvsdir copy of the source.\n";
56 system qq|cvs -d "$root" co -d cvsdir rsync|;
57}
3a849c85
WD
58
59@ARGV = glob('../*.diff') unless @ARGV;
60
61DIFF:
62foreach my $diff (@ARGV) {
63 next unless $diff =~ /\.diff$/;
64 next if $diff =~ /gzip-rsyncable\.diff$/;
65 $diff =~ s#^(patches|\.\.)/##;
66
67 open(IN, "../$diff") or die $!;
68 while (<IN>) {
69 last if /^--- /;
70 if (/^Depends-On-Patch: (\S+.diff)$/) {
71 my $dep = $1;
72 $has_dependencies = 1;
a978ee02 73 print "\nApplying dependency patch $dep...\n";
b1a4cc32 74 if (system("patch -d cvsdir -p0 -b -Vt -Zf <../$dep") != 0) {
14191ec6 75 print "Unable to cleanly apply dependency patch -- skipping $diff\n";
7c88f9bf 76 system "rm -f cvsdir/*.rej cvsdir/*/*.rej";
3a849c85
WD
77 &restore_cvsdir;
78 next DIFF;
79 }
80 }
81 }
82 close IN;
83
b1a4cc32 84 my $default = apply_patch($diff);
fcbecd85 85 if ($default =~ s/^D,// || $default eq 'N') {
14191ec6
WD
86 my $def = generate_new_patch($diff);
87 $default = 'U,N' if $default eq 'N' && $def eq 'E';
78b5ec5e 88 $default = 'N' if !$minor_updates && $default eq 'U,N';
a978ee02 89 }
b1a4cc32 90
a978ee02 91 PROMPT:
3a849c85 92 while (1) {
b1a4cc32
WD
93 print "\n----------- $diff ------------\n",
94 "\nFix rejects, Diff create, Edit both diffs, Update patch,\n",
95 "Apply patch again, !(CMD), Build rsync, Next, Quit: [$default] ";
3a849c85
WD
96 my $ans = <STDIN>;
97 chomp $ans;
b1a4cc32 98 $ans = $default if $ans eq '';
a978ee02
WD
99 while ($ans =~ s/^\s*(!|\w)((?<!!)[^;,]*|[^;]*)[;,]?//) {
100 my $cmd = "\U$1\E";
101 if ($cmd eq '!') {
102 $cmd = $2 || $ENV{'SHELL'};
103 chdir('workdir') or die $!;
104 system $cmd;
105 chdir('..') or die $!;
7c88f9bf 106 $default = 'D';
a978ee02
WD
107 next;
108 }
109 if ($cmd eq 'A') {
110 $default = apply_patch($diff);
111 next;
112 }
113 if ($cmd eq 'B') {
114 if (!-f 'workdir/Makefile') {
115 open(IN, '../../Makefile') or die $!;
116 open(OUT, '>workdir/Makefile') or die $!;
117 print OUT "srcdir=.\n\n";
118 while (<IN>) {
119 last if /^gen:/;
120 }
b1a4cc32 121 print OUT $_;
a978ee02
WD
122 while (<IN>) {
123 last if /^clean:/;
124 print OUT $_;
125 }
126 close IN;
127 close OUT;
b1a4cc32 128 }
11360491
WD
129 my $need_autoconf;
130 my $conf_opts;
131 open(IN, "../$diff") or die $!;
132 while (<IN>) {
133 if (!defined $conf_opts) {
134 $conf_opts = '' if /^---/;
135 if (m#^\s*\./configure( .+)#) {
136 $conf_opts = $1;
137 }
138 }
139 if (m#^--- orig/(configure\.in|/aclocal\.m4)#) {
140 $need_autoconf = 1;
141 last;
142 }
143 }
144 close IN;
a978ee02 145 chdir('workdir') or die $!;
11360491
WD
146 system "autoconf; autoheader" if $need_autoconf;
147 system "make proto; ./configure $CONF_OPTS $conf_opts; make";
a978ee02
WD
148 chdir('..') or die $!;
149 $default = '!make test';
150 next;
151 }
152 if ($cmd eq 'D') {
153 $default = generate_new_patch($diff);
154 next;
155 }
156 if ($cmd eq 'E') {
157 chdir('workdir') or die $!;
158 system "vim -d ../../$diff ../new.patch";
159 chdir('..') or die $!;
160 $default = 'U,A,D';
161 next;
162 }
163 if ($cmd eq 'F') {
164 chdir('workdir') or die $!;
165 system "vim @rejects";
166 chdir('..') or die $!;
167 $default = 'D,E';
168 next;
169 }
170 if ($cmd eq 'N') {
171 last PROMPT;
172 }
173 if ($cmd eq 'Q') {
174 exit;
175 }
176 if ($cmd eq 'U') {
177 system "cp -p new.patch ../$diff";
178 print "\nUpdated $diff from new.patch\n";
179 $default = 'A';
180 next;
b1a4cc32 181 }
b1a4cc32 182 }
3a849c85
WD
183 }
184
185 &restore_cvsdir;
186}
187
188exit;
189
190
b1a4cc32
WD
191sub apply_patch
192{
193 my($diff) = @_;
194
195 undef @new;
a978ee02
WD
196 system "rsync -a --delete --exclude='*~' cvsdir/ workdir/";
197 print "\nApplying patch $diff...\n";
fcbecd85 198 undef @rejects;
14191ec6 199 my($saw_failure, $saw_offset, $saw_fuzz);
b1a4cc32
WD
200 open(IN, "patch -d workdir -p0 --no-backup-if-mismatch -Zf <../$diff |") or die $!;
201 while (<IN>) {
202 print $_;
203 chomp;
204 if (s/^patching file //) {
205 push(@new, $_) unless -f "cvsdir/$_";
206 } elsif (s/.* saving rejects to file //) {
207 push(@rejects, $_);
208 } elsif (/^Hunk #\d+ FAILED/) {
14191ec6
WD
209 $saw_failure = 1;
210 } elsif (/^Hunk #\d+ succeeded at \d+( with fuzz )?/) {
211 $saw_fuzz ||= defined $1;
212 $saw_offset = 1;
b1a4cc32
WD
213 }
214 }
215 close IN;
14191ec6
WD
216 return 'F,D,E' if $saw_failure;
217 return 'D,E' if $saw_fuzz;
218 return 'D,U,N' if $saw_offset;
219 'N';
b1a4cc32
WD
220}
221
3a849c85
WD
222sub generate_new_patch
223{
224 my($diff) = @_;
225
226 foreach (@new) {
227 system "touch -r workdir/$_ cvsdir/$_";
228 }
229 open(IN, "../$diff") or die $!;
230 open(OUT, '>new.patch') or die $!;
231 while (<IN>) {
232 last if /^--- /;
233 print OUT $_;
234 }
235 close IN;
236 open(IN, 'diff --exclude-from=exclude -upr cvsdir workdir |') or die $!;
237 while (<IN>) {
238 next if /^(diff -|Index: |Only in )/;
239 s#^\Q--- cvsdir/\E#--- orig/#;
240 s#^\Q+++ workdir/\E#+++ #;
241 s#(\.000000000)? \+0000$##;
242 print OUT $_;
243 }
244 close IN;
245 close OUT;
246 foreach (@new) {
247 unlink("cvsdir/$_");
248 }
a978ee02 249 print "\nDiffing... ";
b1a4cc32 250 if (system("diff ../$diff new.patch >/dev/null") == 0) {
a978ee02 251 print "new patch is identical to old.\n";
b1a4cc32
WD
252 return 'N';
253 }
a978ee02 254 print "New patch DIFFERS from old.\n";
b1a4cc32 255 'E';
3a849c85
WD
256}
257
258sub restore_cvsdir
259{
260 return unless $has_dependencies;
261 $has_dependencies = 0;
262
263 foreach (glob('*.~[1-9]~'), glob('*/*.~[1-9]~')) {
264 my $fn;
265 ($fn = $_) =~ s/\.~1~$//;
266 if ($fn eq $_) {
267 unlink($_);
268 } elsif (-r $fn) {
269 rename($_, $fn);
270 } else {
271 unlink($_);
272 unlink($fn);
273 }
274 }
275}
78b5ec5e
WD
276
277sub usage
278{
279 die <<EOT;
280Usage: $0 [OPTS] [DIFF-FILE...]
281 -n, --no-cvs Don't update tmp/cvsdir at the start of the run
282 -u, --minor-updates Suggest 'U' for even minor changes in the diff
283EOT
284}