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