314 lines
7.9 KiB
Perl
314 lines
7.9 KiB
Perl
|
#!/usr/bin/perl -w
|
||
|
#
|
||
|
# $Id$
|
||
|
# $FreeBSD: commit_prep.pl,v 1.65 2002/08/31 06:07:42 jmallett Exp $
|
||
|
#
|
||
|
#
|
||
|
# Perl filter to handle pre-commit checking of files. This program
|
||
|
# records the last directory where commits will be taking place for
|
||
|
# use by the log_accum.pl script. For new files, it forces the
|
||
|
# existence of a RCS "Id" keyword in the first ten lines of the file.
|
||
|
# For existing files, it checks the version number in the "Id" line to
|
||
|
# prevent losing changes because an old version of a file was copied
|
||
|
# into the direcory.
|
||
|
#
|
||
|
# Possible future enhancements:
|
||
|
#
|
||
|
#
|
||
|
# Check for cruft left by unresolved conflicts. Search for
|
||
|
# "^<<<<<<<$", "^-------$", and "^>>>>>>>$".
|
||
|
#
|
||
|
# Look for a copyright and automagically update it to the
|
||
|
# current year.
|
||
|
#
|
||
|
# Contributed by David Hampton <hampton@cisco.com>
|
||
|
#
|
||
|
|
||
|
require 5.003; # to be sure. log_accum needs perl5
|
||
|
|
||
|
use strict;
|
||
|
use lib $ENV{CVSROOT};
|
||
|
use CVSROOT::cfg;
|
||
|
my $CVSROOT = $ENV{'CVSROOT'} || die "Can't determine \$CVSROOT!";
|
||
|
|
||
|
|
||
|
############################################################
|
||
|
#
|
||
|
# Constants
|
||
|
#
|
||
|
############################################################
|
||
|
my $ENTRIES = "CVS/Entries";
|
||
|
|
||
|
|
||
|
# The "Id" header to check for.
|
||
|
my $HEADER = $cfg::IDHEADER;
|
||
|
|
||
|
############################################################
|
||
|
#
|
||
|
# Error messages
|
||
|
#
|
||
|
############################################################
|
||
|
my $NoId = "%s - \"\$$HEADER\$\" keyword is either missing or corrupt.\n";
|
||
|
|
||
|
# Protect string from substitution by RCS.
|
||
|
my $NoName = "%s - The ID line should contain only \$$HEADER\$ for " .
|
||
|
"a newly created file.\n";
|
||
|
|
||
|
#$DelPath = "
|
||
|
#%s - The old path and version has been deleted from \$$HEADER\$.\n";
|
||
|
|
||
|
my $BadName = "%s - The pathname '%s'
|
||
|
in the \$$HEADER\$ line does not match the actual filename.\n";
|
||
|
|
||
|
my $BadVersion = "%s - GRRR!! You spammed your copy of the file
|
||
|
which was based upon version %s, with a different version based
|
||
|
upon %s. Please move your '%s' out of the way,
|
||
|
perform an update to get the current version, and then
|
||
|
CAREFULLY merge your changes into that file.\n";
|
||
|
|
||
|
my $DOSLineBreak = "%s - Dos/Windows/Mac linebreaks encountered (line %d).\n";
|
||
|
my $DOSLineErr = "PLEASE use only UNIX linebreaks.\n";
|
||
|
|
||
|
|
||
|
############################################################
|
||
|
#
|
||
|
# Subroutines
|
||
|
#
|
||
|
############################################################
|
||
|
|
||
|
# Write a single line to a file.
|
||
|
sub write_line {
|
||
|
my $filename = shift; # File to write to.
|
||
|
my $line = shift; # Line to write to the file.
|
||
|
|
||
|
open FILE, ">$filename" or die "Cannot open $filename, stopped\n";
|
||
|
print FILE "$line\n";
|
||
|
close FILE;
|
||
|
}
|
||
|
|
||
|
# Check to see whether a file is mentioned in the exclusion file.
|
||
|
sub exclude_file {
|
||
|
my $filename = shift;
|
||
|
my $directory = shift;
|
||
|
|
||
|
my $path = "$directory/$filename";
|
||
|
if (open(EX, "< $cfg::EXCLUDE_FILE")) {
|
||
|
while (<EX>) {
|
||
|
chomp;
|
||
|
my $ex_entry = $_;
|
||
|
|
||
|
# Skip comments and blank lines.
|
||
|
next if $ex_entry =~ /^#/;
|
||
|
next if $ex_entry =~ /^$/;
|
||
|
|
||
|
if ($path =~ /$ex_entry/) {
|
||
|
close(EX);
|
||
|
return(1);
|
||
|
}
|
||
|
}
|
||
|
close(EX);
|
||
|
}
|
||
|
|
||
|
# File shouldn't be excluded.
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
sub check_version {
|
||
|
my $filename = shift;
|
||
|
my $directory = shift;
|
||
|
my $hastag = shift;
|
||
|
my $lastversion = shift;
|
||
|
|
||
|
my $found_rcsid; # True if our rcsid was found in the file.
|
||
|
my $rcsid; # The rcsid that was in the file.
|
||
|
my $rcsid_info; # The expanded values of the rcsid.
|
||
|
my $rname; # The file pathname, parsed from the rcsid.
|
||
|
my $version; # The file version, parsed from the rcsid.
|
||
|
my $dos_line_brk_found; # True if we found a DOS line break in the file.
|
||
|
my $line_number; # Keep track of where the line break is.
|
||
|
|
||
|
# not present - either removed or let cvs deal with it.
|
||
|
return 0 unless -f $filename;
|
||
|
|
||
|
# Search the file for our rcsid.
|
||
|
open FILE, $filename or die "Cannot open $filename, stopped\n";
|
||
|
$found_rcsid = 0;
|
||
|
$dos_line_brk_found = 0;
|
||
|
$line_number = 0;
|
||
|
$rcsid_info = "";
|
||
|
while (<FILE>) {
|
||
|
$line_number++;
|
||
|
if ( /^.*(\$$HEADER(: ([^\$]* )?)?\$)/) {
|
||
|
$rcsid = $1;
|
||
|
$rcsid_info = $3 || "";
|
||
|
$found_rcsid = 1;
|
||
|
} elsif ( $cfg::NO_DOS_LINEBREAKS and /\r/ ) {
|
||
|
# Found a DOS linebreak
|
||
|
printf($DOSLineBreak, "$directory/$filename",
|
||
|
$line_number);
|
||
|
$dos_line_brk_found = 1;
|
||
|
}
|
||
|
}
|
||
|
close FILE;
|
||
|
($rname, $version) = split /\s/, $rcsid_info;
|
||
|
|
||
|
# The file must not contain DOS linebreaks.
|
||
|
if ($dos_line_brk_found) {
|
||
|
print $DOSLineErr;
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
# The file should have had an rcsid in it!
|
||
|
unless ($found_rcsid) {
|
||
|
printf($NoId, "$directory/$filename");
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
# Ignore version mismatches (MFC spamming etc) on branches.
|
||
|
if ($hastag) {
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
# A new file should have an unexpanded rcsid.
|
||
|
if ($lastversion eq '0') {
|
||
|
unless ($rcsid_info eq "") {
|
||
|
printf($NoName, "$directory/$filename");
|
||
|
return(1);
|
||
|
}
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
# It's ok for the rcsid to be not expanded.
|
||
|
if ($rcsid_info eq "") {
|
||
|
return (0);
|
||
|
# if ($directory =~ /^ports\//) {
|
||
|
# return (0); # ok for ports
|
||
|
# }
|
||
|
# # Don't know whether to allow or trap this. It means
|
||
|
# # one could bypass the version spam checks by simply
|
||
|
# # using a bare tag.
|
||
|
# printf($DelPath, "$directory/$filename");
|
||
|
# return(1);
|
||
|
}
|
||
|
|
||
|
# Check that the file name in the rcsid matches reality.
|
||
|
if ($rname ne "$directory/$filename,v") {
|
||
|
# If ports and the pathname is just the basename
|
||
|
# (eg: somebody sent in a port with $Id$ and the
|
||
|
# committer changed Id -> $HEADER and the version
|
||
|
# numbers otherwise match.
|
||
|
if (!($directory =~ /^ports\// && $rname eq "$filename,v")) {
|
||
|
printf($BadName, "$directory/$filename,v", $rname);
|
||
|
return(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Check that the version in the rcsid matches reality.
|
||
|
if ($lastversion ne $version) {
|
||
|
printf($BadVersion, $filename, $lastversion,
|
||
|
$version, "$directory/$filename");
|
||
|
return(1);
|
||
|
}
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
# Do file fixups, i.e. replacing $ Id: .* $ with $ Id $.
|
||
|
sub fix_up_file {
|
||
|
my $filename = shift;
|
||
|
|
||
|
# not present - either removed or let cvs deal with it.
|
||
|
return 0 unless -f $filename;
|
||
|
|
||
|
open F, "< $filename" or die "Can't open $filename!\n";
|
||
|
my @file = <F>;
|
||
|
close F;
|
||
|
|
||
|
open F, "> $filename.tmp" or die "Can't create $filename tmpfile!\n";
|
||
|
while (@file) {
|
||
|
my $line = shift @file;
|
||
|
|
||
|
$line =~ s/\$$HEADER:.*?\$/\$$HEADER\$/g;
|
||
|
print F $line or die "Out of disk space?\n";
|
||
|
}
|
||
|
close F;
|
||
|
|
||
|
# overwrite the original file......
|
||
|
system($cfg::PROG_MV, "$filename.tmp", $filename);
|
||
|
die "Can't recreate $filename!\n" if $? >> 8;
|
||
|
}
|
||
|
|
||
|
#############################################################
|
||
|
#
|
||
|
# Main Body
|
||
|
#
|
||
|
############################################################
|
||
|
|
||
|
#print("ARGV - ", join(":", @ARGV), "\n");
|
||
|
#print("id - ", $cfg::PID, "\n");
|
||
|
|
||
|
#
|
||
|
# Suck in the Entries file
|
||
|
#
|
||
|
my %cvsversion;
|
||
|
my %cvstag;
|
||
|
open ENTRIES, $ENTRIES or die "Cannot open $ENTRIES.\n";
|
||
|
while (<ENTRIES>) {
|
||
|
chomp;
|
||
|
next if /^D/;
|
||
|
|
||
|
my ($filename, $ver, $stamp, $opt, $tag) = split '/', substr($_, 1);
|
||
|
$cvsversion{$filename} = $ver;
|
||
|
$cvstag{$filename} = $tag;
|
||
|
$stamp = $opt; #silence -w
|
||
|
}
|
||
|
close ENTRIES;
|
||
|
|
||
|
my $directory = $ARGV[0];
|
||
|
shift @ARGV;
|
||
|
|
||
|
$directory =~ s,^$CVSROOT[/]+,,;
|
||
|
|
||
|
my $check_id = 0;
|
||
|
if ($directory =~ /^src\/contrib/) {
|
||
|
$check_id = 3;
|
||
|
}
|
||
|
if ($directory =~ /^src\/crypto/) {
|
||
|
$check_id = 3;
|
||
|
}
|
||
|
#
|
||
|
# Now check each file name passed in, except those excluded.
|
||
|
#
|
||
|
if ($cfg::CHECK_HEADERS or $cfg::WARN_HEADERS) {
|
||
|
my $failed = 0;
|
||
|
foreach my $arg (@ARGV) {
|
||
|
my $hastag = ($cvstag{$arg} ne '');
|
||
|
next if ($check_id == 3 && $hastag);
|
||
|
|
||
|
# Ignore the file if it's in the exclude list.
|
||
|
next if exclude_file($arg, $directory);
|
||
|
|
||
|
# Check to make sure that the file hasn't had
|
||
|
# it's revision string changed.
|
||
|
$failed += &check_version($arg, $directory, $hastag,
|
||
|
$cvsversion{$arg});
|
||
|
|
||
|
# Unexpand the rcsid if required.
|
||
|
fix_up_file($arg) if $cfg::UNEXPAND_RCSID and !$failed;
|
||
|
}
|
||
|
if ($failed and not $cfg::WARN_HEADERS) {
|
||
|
print "\n";
|
||
|
unlink($cfg::LAST_FILE);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Record this directory as the last one checked. This will be used
|
||
|
# by the log_accumulate script to determine when it is processing
|
||
|
# the final directory of a multi-directory commit.
|
||
|
#
|
||
|
&write_line($cfg::LAST_FILE, $directory);
|
||
|
|
||
|
exit(0);
|