| #!/usr/bin/perl -w |
| |
| # Copyright (C) 2005 Apple Computer, Inc. All rights reserved. |
| # Copyright (C) 2005 Ben La Monica <ben.lamonica@gmail.com> All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions |
| # are met: |
| # |
| # 1. Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| # its contributors may be used to endorse or promote products derived |
| # from this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| # Simplified but improved "cvs diff" script for Web Kit Open Source Project, used to make patches. |
| |
| # Differences from standard "cvs diff": |
| # |
| # Always passes "-N" to diff so it will include deleted and added files. |
| # Always passes "-u" to diff so it will use unified diff format. |
| # Always passes "-p" to diff so it will try to include function names. |
| # Other command line options are not supported. |
| # Works in mixed CVS root situations like the mix of internal and open source CVS repositories |
| # for Apple's Safari and WebKit team, by doing a separate cvs invocation for each directory. |
| # Fixes diff output paths so they will work with the "patch" tool. |
| # |
| # Missing feature: |
| # |
| # Handle binary files (some text form of the binary file). |
| |
| use strict; |
| use Cwd; |
| use Getopt::Long; |
| use Time::gmtime; |
| use File::stat; |
| use POSIX qw(:errno_h); |
| use Config; |
| |
| my $startDir = getcwd(); |
| my $includeUnknowns = ""; |
| my %paths; |
| |
| GetOptions("include-unknowns", \$includeUnknowns); |
| |
| # Create list of paths to diff. |
| if (!@ARGV) { |
| $paths{"."} = 1; |
| } else { |
| for my $file (@ARGV) { |
| die "can't handle absolute paths like \"$file\"\n" if $file =~ m|^/|; |
| die "can't handle empty string path\n" if $file eq ""; |
| die "can't handle path with ' in the name like \"$file\"\n" if $file =~ /'/; # ' (keep Xcode syntax highlighting happy) |
| |
| my $untouchedFile = $file; |
| |
| # Add a leading and trailing slash to simplify logic below. |
| $file = "/$file/"; |
| |
| # Remove repeated slashes. |
| $file =~ s|//+|/|g; |
| |
| # Remove meaningless sequences involving ".". |
| $file =~ s|/\./|/|g; |
| |
| # Remove meaningless sequences involving "..". |
| $file =~ s|/[^./]/\.\./|/|g; |
| $file =~ s|/[^/]+[^./]/\.\./|/|g; |
| $file =~ s|/[^./][^/]+/\.\./|/|g; |
| die "can't handle paths with .. like \"$untouchedFile\"\n" if $file =~ m|/\.\./|; |
| |
| # Remove the leading and trailing slash. |
| $file =~ s|^/(.*)/$|$1|; |
| |
| $paths{$file} = 1; |
| } |
| } |
| |
| # Remove any paths that also have a parent listed. |
| for my $path (keys %paths) { |
| my $parent = $path; |
| while ($parent =~ s|/+[^/]+$||) { |
| if ($paths{$parent}) { |
| delete $paths{$path}; |
| last; |
| } |
| } |
| } |
| |
| sub getDirAndBase |
| { |
| my ($path) = @_; |
| if (-d $path) { |
| $path =~ s|/+$||; |
| return ($path, "."); |
| } |
| return ($1, $2) if $path =~ m|^(.+)/([^/]+)$|; |
| $path !~ m|/| or die "Could not parse path name $path.\n"; |
| return (".", $path); |
| } |
| |
| # Function to generate a diff. |
| sub diff |
| { |
| my ($path) = @_; |
| my ($dir, $base) = getDirAndBase($path); |
| my $errors = ""; |
| chdir $dir or die; |
| open DIFF, "cvs -f diff -Npu '$base' |" or die; |
| my $indexPath; |
| while (<DIFF>) { |
| if (/^Index: (.*)/) { |
| $indexPath = $1; |
| if ($dir ne ".") { |
| $indexPath = "$dir/$indexPath"; |
| s/Index: .*/Index: $indexPath/; |
| } |
| } |
| if ($indexPath) { |
| # Fix paths on diff, ---, and +++ lines to match preceding Index: line. |
| s/\S+$/$indexPath/ if /^diff/; |
| s/^--- \S+/--- $indexPath/; |
| s/^\+\+\+ \S+/+++ $indexPath/; |
| } |
| if (/^\? (.+)$/) { |
| $errors .= $_; |
| my $unknown = $1; |
| if ($includeUnknowns && $unknown !~ /^\./) { |
| if (-d $unknown) { |
| addNewDirectory($indexPath, $unknown); |
| } else { |
| addNewFile($indexPath, $unknown); |
| } |
| } |
| } else { |
| print; |
| } |
| } |
| close DIFF; |
| chdir $startDir or die; |
| print STDERR $errors; |
| } |
| |
| sub addNewDirectory |
| { |
| my $indexPath = ""; |
| my $dir = ""; |
| my @files; |
| |
| $indexPath = shift; |
| $dir = shift; |
| |
| opendir NEWDIR, $dir; |
| @files = readdir(NEWDIR); |
| closedir NEWDIR; |
| |
| if (!defined $indexPath || $indexPath eq "") { |
| $indexPath = $dir; |
| } else { |
| $indexPath .= "/$dir"; |
| } |
| |
| for my $file (@files) { |
| next if $file eq "." || $file eq ".."; |
| if (-d "$dir/$file") { |
| addNewDirectory($indexPath, "$dir/$file"); |
| } else { |
| addNewFile("$indexPath", "$dir/$file"); |
| } |
| } |
| } |
| |
| sub addNewFile |
| { |
| my $indexPath = ""; |
| my $newFile = ""; |
| my @fileContents; |
| my $fileDate = ""; |
| my @months = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"); |
| |
| $indexPath = shift; |
| $newFile = shift; |
| |
| $fileDate = gmtime(stat($newFile)->mtime); |
| |
| @fileContents = `diff -Nup /dev/null $newFile`; |
| |
| if ($#fileContents == 0) { |
| return; |
| } |
| |
| $indexPath =~ s|[^/]+$|| if defined $indexPath; |
| $indexPath .= $newFile; |
| |
| #get rid of the first 2 lines |
| shift @fileContents; |
| shift @fileContents; |
| |
| print "Index: $indexPath\n"; |
| print "===================================================================\n"; |
| print "diff -Npu $indexPath\n"; |
| print "--- $indexPath\t1 Jan 1970 00:00:00 -0000\n"; |
| print "+++ $indexPath\t" . $fileDate->mday . " " . $months[$fileDate->mon] . " " . ($fileDate->year + 1900) . " "; |
| printf "%02d:%02d:%02d -0000\n", ($fileDate->hour, $fileDate->min,$fileDate->sec); |
| print @fileContents; |
| } |
| |
| # Generate the diff for each passed file or directory. |
| for my $path (sort keys %paths) { |
| diff($path); |
| } |