|
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 # "patch" script for WebKit Open Source Project, used to apply patches. |
|
43 |
|
44 # Differences from invoking "patch -p0": |
|
45 # |
|
46 # Handles added files (does a svn add). |
|
47 # Handles added directories (does a svn add). |
|
48 # Handles removed files (does a svn rm). |
|
49 # Handles removed directories--those with no more files or directories left in them |
|
50 # (does a svn rm). |
|
51 # Has mode where it will roll back to svn version numbers in the patch file so svn |
|
52 # can do a 3-way merge. |
|
53 # Paths from Index: lines are used rather than the paths on the patch lines, which |
|
54 # makes patches generated by "cvs diff" work (increasingly unimportant since we |
|
55 # use Subversion now). |
|
56 # ChangeLog patches use --fuzz=3 to prevent rejects. |
|
57 # Handles binary files (requires patches made by svn-create-patch). |
|
58 # |
|
59 # Missing features: |
|
60 # |
|
61 # Handle property changes. |
|
62 # Handle file moves (would require patches made by svn-create-patch). |
|
63 # When doing a removal, check that old file matches what's being removed. |
|
64 # Notice a patch that's being applied at the "wrong level" and make it work anyway. |
|
65 # Do a dry run on the whole patch and don't do anything if part of the patch is |
|
66 # going to fail (probably too strict unless we exclude ChangeLog). |
|
67 |
|
68 use strict; |
|
69 use warnings; |
|
70 |
|
71 use Cwd; |
|
72 use File::Basename; |
|
73 use File::Spec; |
|
74 use Getopt::Long; |
|
75 use MIME::Base64; |
|
76 use FindBin qw($Bin); |
|
77 |
|
78 sub addDirectoriesIfNeeded($); |
|
79 sub applyPatch($$;$); |
|
80 sub handleBinaryChange($$); |
|
81 sub isDirectoryEmptyForRemoval($); |
|
82 sub patch($); |
|
83 sub removeDirectoriesIfNeeded(); |
|
84 sub svnStatus($); |
|
85 |
|
86 my $patch_cmd = "$Bin\\patch.exe"; |
|
87 my $merge = 0; |
|
88 my $showHelp = 0; |
|
89 if (!GetOptions("merge!" => \$merge, "help!" => \$showHelp) || $showHelp) { |
|
90 print STDERR basename($0) . " [-h|--help] [-m|--merge] patch1 [patch2 ...]\n"; |
|
91 exit 1; |
|
92 } |
|
93 |
|
94 my %removeDirectoryIgnoreList = ( |
|
95 '.' => 1, |
|
96 '..' => 1, |
|
97 '.svn' => 1, |
|
98 '_svn' => 1, |
|
99 ); |
|
100 |
|
101 my %checkedDirectories; |
|
102 my @patches; |
|
103 my %versions; |
|
104 |
|
105 my $indexPath; |
|
106 my $patch; |
|
107 while (<>) { |
|
108 s/\r//g; |
|
109 chomp; |
|
110 if (/^Index: (.+)/) { |
|
111 $indexPath = $1; |
|
112 if ($patch) { |
|
113 push @patches, $patch; |
|
114 $patch = ""; |
|
115 } |
|
116 } |
|
117 if ($indexPath) { |
|
118 # Fix paths on diff, ---, and +++ lines to match preceding Index: line. |
|
119 s/\S+$/$indexPath/ if /^diff/; |
|
120 s/^--- \S+/--- $indexPath/; |
|
121 if (s/^\+\+\+ \S+/+++ $indexPath/) { |
|
122 $indexPath = ""; |
|
123 } |
|
124 } |
|
125 if (/^--- .+\(revision (\d+)\)$/) { |
|
126 $versions{$indexPath} = $1 if( $1 != 0 ); |
|
127 } |
|
128 $patch .= $_; |
|
129 $patch .= "\n"; |
|
130 } |
|
131 |
|
132 push @patches, $patch if $patch; |
|
133 |
|
134 if ($merge) { |
|
135 for my $file (sort keys %versions) { |
|
136 print "Getting version $versions{$file} of $file\n"; |
|
137 system "svn", "update", "-r", $versions{$file}, $file; |
|
138 } |
|
139 } |
|
140 |
|
141 for $patch (@patches) { |
|
142 patch($patch); |
|
143 } |
|
144 |
|
145 removeDirectoriesIfNeeded(); |
|
146 |
|
147 exit 0; |
|
148 |
|
149 sub addDirectoriesIfNeeded($) |
|
150 { |
|
151 my ($path) = @_; |
|
152 my @dirs = File::Spec->splitdir($path); |
|
153 my $dir = "."; |
|
154 while (scalar @dirs) { |
|
155 $dir = File::Spec->catdir($dir, shift @dirs); |
|
156 next if exists $checkedDirectories{$dir}; |
|
157 if (! -e $dir) { |
|
158 mkdir $dir or die "Failed to create required directory '$dir' for path '$path'\n"; |
|
159 system "svn", "add", $dir; |
|
160 $checkedDirectories{$dir} = 1; |
|
161 } |
|
162 elsif (-d $dir) { |
|
163 my $svnOutput = svnStatus($dir); |
|
164 if ($svnOutput && substr($svnOutput, 0, 1) eq "?") { |
|
165 system "svn", "add", $dir; |
|
166 } |
|
167 $checkedDirectories{$dir} = 1; |
|
168 } |
|
169 else { |
|
170 die "'$dir' is not a directory"; |
|
171 } |
|
172 } |
|
173 } |
|
174 |
|
175 sub applyPatch($$;$) |
|
176 { |
|
177 my ($patch, $fullPath, $options) = @_; |
|
178 $options = [] if (! $options); |
|
179 my $command = "$patch_cmd " . join(" ", "-p0", @{$options}); |
|
180 open PATCH, "| $command" or die "Failed to patch $fullPath\n"; |
|
181 print PATCH $patch; |
|
182 close PATCH; |
|
183 } |
|
184 |
|
185 sub handleBinaryChange($$) |
|
186 { |
|
187 my ($fullPath, $contents) = @_; |
|
188 if ($contents =~ m#((\n[A-Za-z0-9+/]{76})+\n[A-Za-z0-9+/=]{4,76}\n)\n#) { |
|
189 # Addition or Modification |
|
190 open FILE, ">", $fullPath or die; |
|
191 print FILE decode_base64($1); |
|
192 close FILE; |
|
193 my $svnOutput = svnStatus($fullPath); |
|
194 if ($svnOutput && substr($svnOutput, 0, 1) eq "?") { |
|
195 # Addition |
|
196 system "svn", "add", $fullPath; |
|
197 } else { |
|
198 # Modification |
|
199 print $svnOutput if $svnOutput; |
|
200 } |
|
201 } else { |
|
202 # Deletion |
|
203 system "svn", "rm", $fullPath; |
|
204 } |
|
205 } |
|
206 |
|
207 sub isDirectoryEmptyForRemoval($) |
|
208 { |
|
209 my ($dir) = @_; |
|
210 my $directoryIsEmpty = 1; |
|
211 opendir DIR, $dir or die "Could not open '$dir' to list files: $?"; |
|
212 for (my $item = readdir DIR; $item && $directoryIsEmpty; $item = readdir DIR) { |
|
213 next if exists $removeDirectoryIgnoreList{$item}; |
|
214 if (! -d File::Spec->catdir($dir, $item)) { |
|
215 $directoryIsEmpty = 0; |
|
216 } else { |
|
217 my $svnOutput = svnStatus(File::Spec->catdir($dir, $item)); |
|
218 next if $svnOutput && substr($svnOutput, 0, 1) eq "D"; |
|
219 $directoryIsEmpty = 0; |
|
220 } |
|
221 } |
|
222 closedir DIR; |
|
223 return $directoryIsEmpty; |
|
224 } |
|
225 |
|
226 sub patch($) |
|
227 { |
|
228 my ($patch) = @_; |
|
229 return if !$patch; |
|
230 |
|
231 $patch =~ m|^Index: ([^\n]+)| or die "Failed to find 'Index:' in \"$patch\"\n"; |
|
232 my $fullPath = $1; |
|
233 |
|
234 my $deletion = 0; |
|
235 my $addition = 0; |
|
236 my $isBinary = 0; |
|
237 |
|
238 $addition = 1 if $patch =~ /\n--- .+\(revision 0\)\n/; |
|
239 $deletion = 1 if $patch =~ /\n@@ .* \+0,0 @@/; |
|
240 $isBinary = 1 if $patch =~ /\nCannot display: file marked as a binary type\./; |
|
241 |
|
242 if (!$addition && !$deletion && !$isBinary) { |
|
243 # Standard patch, patch tool can handle this. |
|
244 if (basename($fullPath) eq "ChangeLog") { |
|
245 my $changeLogDotOrigExisted = -f "${fullPath}.orig"; |
|
246 applyPatch($patch, $fullPath, ["--fuzz=3"]); |
|
247 unlink("${fullPath}.orig") if (! $changeLogDotOrigExisted); |
|
248 } else { |
|
249 applyPatch($patch, $fullPath); |
|
250 } |
|
251 } else { |
|
252 # Either a deletion, an addition or a binary change. |
|
253 |
|
254 addDirectoriesIfNeeded(dirname($fullPath)); |
|
255 |
|
256 if ($isBinary) { |
|
257 # Binary change |
|
258 handleBinaryChange($fullPath, $patch); |
|
259 } elsif ($deletion) { |
|
260 # Deletion. |
|
261 system "svn", "rm", $fullPath; |
|
262 } else { |
|
263 # Addition. |
|
264 my $contents = $patch; |
|
265 if ($contents !~ s/^(.*\n)*@@[^\n]+@@\n//) { |
|
266 # Empty contents. |
|
267 $contents = ""; |
|
268 } else { |
|
269 # Non-empty contents: Remove leading + signs. |
|
270 $contents =~ s/^\+//; |
|
271 $contents =~ s/\n\+/\n/g; |
|
272 } |
|
273 open FILE, ">", $fullPath or die; |
|
274 print FILE $contents; |
|
275 close FILE; |
|
276 system "svn", "add", $fullPath; |
|
277 } |
|
278 } |
|
279 } |
|
280 |
|
281 sub removeDirectoriesIfNeeded() |
|
282 { |
|
283 foreach my $dir (reverse sort keys %checkedDirectories) { |
|
284 if (isDirectoryEmptyForRemoval($dir)) { |
|
285 my $svnOutput; |
|
286 open SVN, "svn rm $dir |" or die; |
|
287 # Only save the last line since Subversion lists all changed statuses below $dir |
|
288 while (<SVN>) { |
|
289 $svnOutput = $_; |
|
290 } |
|
291 close SVN; |
|
292 print $svnOutput if $svnOutput; |
|
293 } |
|
294 } |
|
295 } |
|
296 |
|
297 sub svnStatus($) |
|
298 { |
|
299 my ($fullPath) = @_; |
|
300 open SVN, "svn status --non-interactive --non-recursive $fullPath |" or die; |
|
301 my $svnStatus = <SVN>; |
|
302 close SVN; |
|
303 return $svnStatus; |
|
304 } |
|
305 |
|
306 __END__ |
|
307 :endofperl |