|
1 @rem = '--*-Perl-*-- |
|
2 @echo off |
|
3 if "%OS%" == "Windows_NT" goto WinNT |
|
4 perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9 |
|
5 goto endofperl |
|
6 :WinNT |
|
7 perl -x -S %0 %* |
|
8 if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endofperl |
|
9 if %errorlevel% == 9009 echo You do not have Perl in your PATH. |
|
10 if errorlevel 1 goto script_failed_so_exit_with_non_zero_val 2>nul |
|
11 goto endofperl |
|
12 @rem '; |
|
13 #!/usr/bin/perl -w |
|
14 #line 15 |
|
15 |
|
16 # Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. |
|
17 # |
|
18 # Redistribution and use in source and binary forms, with or without |
|
19 # modification, are permitted provided that the following conditions |
|
20 # are met: |
|
21 # |
|
22 # 1. Redistributions of source code must retain the above copyright |
|
23 # notice, this list of conditions and the following disclaimer. |
|
24 # 2. Redistributions in binary form must reproduce the above copyright |
|
25 # notice, this list of conditions and the following disclaimer in the |
|
26 # documentation and/or other materials provided with the distribution. |
|
27 # 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
|
28 # its contributors may be used to endorse or promote products derived |
|
29 # from this software without specific prior written permission. |
|
30 # |
|
31 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
|
32 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
33 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
34 # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
|
35 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
36 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
37 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
38 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
39 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
40 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
41 |
|
42 # Extended "svn diff" script for WebKit Open Source Project, used to make patches. |
|
43 |
|
44 # Differences from standard "svn diff": |
|
45 # |
|
46 # Uses the real diff, not svn's built-in diff. |
|
47 # Always passes "-p" to diff so it will try to include function names. |
|
48 # Handles binary files (encoded as a base64 chunk of text). |
|
49 # Sorts the diffs alphabetically by text files, then binary files. |
|
50 # |
|
51 # Missing features: |
|
52 # |
|
53 # Handle moved files. |
|
54 |
|
55 use strict; |
|
56 use warnings; |
|
57 |
|
58 use Config; |
|
59 use Cwd; |
|
60 use File::Basename; |
|
61 use File::Spec; |
|
62 use File::stat; |
|
63 use Getopt::Long; |
|
64 use MIME::Base64; |
|
65 use POSIX qw(:errno_h); |
|
66 use Time::gmtime; |
|
67 |
|
68 sub canonicalizePath($); |
|
69 sub generateDiff($); |
|
70 sub generateFileList($\%\%); |
|
71 sub numericcmp($$); |
|
72 sub outputBinaryContent($); |
|
73 sub pathcmp($$); |
|
74 sub processPaths(\@); |
|
75 sub splitpath($); |
|
76 |
|
77 my $showHelp; |
|
78 if (!GetOptions("help" => \$showHelp) || $showHelp) { |
|
79 print STDERR basename($0) . " [-h|--help] [svndir1 [svndir2 ...]]\n"; |
|
80 exit 1; |
|
81 } |
|
82 |
|
83 my %paths = processPaths(@ARGV); |
|
84 |
|
85 # Generate a list of files requiring diffs |
|
86 my %textFiles; |
|
87 my %binaryFiles; |
|
88 for my $path (keys %paths) { |
|
89 generateFileList($path, %textFiles, %binaryFiles); |
|
90 } |
|
91 |
|
92 # Generate the diff for text files, then binary files, for easy reviewing |
|
93 for my $file (sort pathcmp keys %textFiles) { |
|
94 generateDiff($file); |
|
95 } |
|
96 for my $file (sort pathcmp keys %binaryFiles) { |
|
97 generateDiff($file); |
|
98 } |
|
99 |
|
100 exit 0; |
|
101 |
|
102 |
|
103 sub canonicalizePath($) |
|
104 { |
|
105 my ($file) = @_; |
|
106 |
|
107 # Remove extra slashes and '.' directories in path |
|
108 $file = File::Spec->canonpath($file); |
|
109 |
|
110 # Remove '..' directories in path |
|
111 my @dirs = (); |
|
112 foreach my $dir (File::Spec->splitdir($file)) { |
|
113 if ($dir eq '..' && $#dirs >= 0 && $dirs[$#dirs] ne '..') { |
|
114 pop(@dirs); |
|
115 } else { |
|
116 push(@dirs, $dir); |
|
117 } |
|
118 } |
|
119 return ($#dirs >= 0) ? File::Spec->catdir(@dirs) : "."; |
|
120 } |
|
121 |
|
122 sub generateDiff($) |
|
123 { |
|
124 my ($file) = @_; |
|
125 my $errors = ""; |
|
126 my $isBinary; |
|
127 my $lastLine; |
|
128 open DIFF, "svn diff --diff-cmd diff -x -uNp $file |" or die; |
|
129 while (<DIFF>) { |
|
130 $isBinary = 1 if (/^Cannot display: file marked as a binary type\.$/); |
|
131 print; |
|
132 $lastLine = $_; |
|
133 } |
|
134 close DIFF; |
|
135 print "\n" if ($isBinary && $lastLine =~ m/\S+/); |
|
136 outputBinaryContent($file) if ($isBinary); |
|
137 print STDERR $errors; |
|
138 } |
|
139 |
|
140 sub generateFileList($\%\%) |
|
141 { |
|
142 my ($path, $textFiles, $binaryFiles) = @_; |
|
143 my $indexPath; |
|
144 my $isBinary; |
|
145 open DIFF, "svn diff --diff-cmd diff -x -uNp $path |" or die; |
|
146 while (<DIFF>) { |
|
147 if (/^Index: (.*)/) { |
|
148 my $newIndexPath = $1; |
|
149 if ($indexPath) { |
|
150 if ($isBinary) { |
|
151 $binaryFiles->{$indexPath} = 1; |
|
152 } else { |
|
153 $textFiles->{$indexPath} = 1; |
|
154 } |
|
155 } |
|
156 $indexPath = $newIndexPath; |
|
157 $isBinary = 0; |
|
158 } |
|
159 if (/^Cannot display: file marked as a binary type\.$/) { |
|
160 $isBinary = 1; |
|
161 } |
|
162 } |
|
163 close DIFF; |
|
164 # Handle last patch |
|
165 if ($indexPath) { |
|
166 if ($isBinary) { |
|
167 $binaryFiles->{$indexPath} = 1; |
|
168 } else { |
|
169 $textFiles->{$indexPath} = 1; |
|
170 } |
|
171 } |
|
172 # check files for tabs |
|
173 foreach my $file ( keys %$textFiles ) { |
|
174 next if $file =~ /(\.mmp|\.mk)$/; # makefiles need them |
|
175 open (INPUT, $file) || die "can't open $file: $!"; |
|
176 while (<INPUT>) { |
|
177 /\t/ and die "Error: $file contains tab characters, please remove.\n"; |
|
178 } |
|
179 close(INPUT) || die "can't close $file: $!"; |
|
180 } |
|
181 } |
|
182 |
|
183 # Sort numeric parts of strings as numbers, other parts as strings. |
|
184 # Makes 1.33 come after 1.3, which is cool. |
|
185 sub numericcmp($$) |
|
186 { |
|
187 my ($aa, $bb) = @_; |
|
188 |
|
189 my @a = split /(\d+)/, $aa; |
|
190 my @b = split /(\d+)/, $bb; |
|
191 |
|
192 # Compare one chunk at a time. |
|
193 # Each chunk is either all numeric digits, or all not numeric digits. |
|
194 while (@a && @b) { |
|
195 my $a = shift @a; |
|
196 my $b = shift @b; |
|
197 |
|
198 # Use numeric comparison if chunks are non-equal numbers. |
|
199 return $a <=> $b if $a =~ /^\d/ && $b =~ /^\d/ && $a != $b; |
|
200 |
|
201 # Use string comparison if chunks are any other kind of non-equal string. |
|
202 return $a cmp $b if $a ne $b; |
|
203 } |
|
204 |
|
205 # One of the two is now empty; compare lengths for result in this case. |
|
206 return @a <=> @b; |
|
207 } |
|
208 |
|
209 sub outputBinaryContent($) |
|
210 { |
|
211 my ($path) = @_; |
|
212 # Deletion |
|
213 return if (! -e $path); |
|
214 # Addition or Modification |
|
215 my $buffer; |
|
216 open BINARY, $path or die; |
|
217 while (read(BINARY, $buffer, 60*57)) { |
|
218 print encode_base64($buffer); |
|
219 } |
|
220 close BINARY; |
|
221 print "\n"; |
|
222 } |
|
223 |
|
224 # Sort first by directory, then by file, so all paths in one directory are grouped |
|
225 # rather than being interspersed with items from subdirectories. |
|
226 # Use numericcmp to sort directory and filenames to make order logical. |
|
227 sub pathcmp($$) |
|
228 { |
|
229 my ($patha, $pathb) = @_; |
|
230 |
|
231 my ($dira, $namea) = splitpath($patha); |
|
232 my ($dirb, $nameb) = splitpath($pathb); |
|
233 |
|
234 return numericcmp($dira, $dirb) if $dira ne $dirb; |
|
235 return numericcmp($namea, $nameb); |
|
236 } |
|
237 |
|
238 sub processPaths(\@) |
|
239 { |
|
240 my ($paths) = @_; |
|
241 return ("." => 1) if (!@{$paths}); |
|
242 |
|
243 my %result = (); |
|
244 |
|
245 for my $file (@{$paths}) { |
|
246 die "can't handle absolute paths like \"$file\"\n" if File::Spec->file_name_is_absolute($file); |
|
247 die "can't handle empty string path\n" if $file eq ""; |
|
248 die "can't handle path with single quote in the name like \"$file\"\n" if $file =~ /'/; # ' (keep Xcode syntax highlighting happy) |
|
249 |
|
250 my $untouchedFile = $file; |
|
251 |
|
252 $file = canonicalizePath($file); |
|
253 |
|
254 die "can't handle paths with .. like \"$untouchedFile\"\n" if $file =~ m|/\.\./|; |
|
255 |
|
256 $result{$file} = 1; |
|
257 } |
|
258 |
|
259 return ("." => 1) if ($result{"."}); |
|
260 |
|
261 # Remove any paths that also have a parent listed. |
|
262 for my $path (keys %result) { |
|
263 for (my $parent = dirname($path); $parent ne '.'; $parent = dirname($parent)) { |
|
264 if ($result{$parent}) { |
|
265 delete $result{$path}; |
|
266 last; |
|
267 } |
|
268 } |
|
269 } |
|
270 |
|
271 return %result; |
|
272 } |
|
273 |
|
274 # Break up a path into the directory (with slash) and base name. |
|
275 sub splitpath($) |
|
276 { |
|
277 my ($path) = @_; |
|
278 |
|
279 my $pathSeparator = "/"; |
|
280 my $dirname = dirname($path) . $pathSeparator; |
|
281 $dirname = "" if $dirname eq "." . $pathSeparator; |
|
282 |
|
283 return ($dirname, basename($path)); |
|
284 } |
|
285 |
|
286 __END__ |
|
287 :endofperl |