blob: af119168998f0bac8f4216973e8fb30252865d57 [file] [log] [blame]
#!/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);
}