1*43c1707eStholo#! @PERL@ 2*43c1707eStholo# -*-Perl-*- 3*43c1707eStholo# 4*43c1707eStholo# 5*43c1707eStholo# Perl filter to handle pre-commit checking of files. This program 6*43c1707eStholo# records the last directory where commits will be taking place for 7*43c1707eStholo# use by the log_accum.pl script. For new files, it forces the 8*43c1707eStholo# existence of a RCS "Id" keyword in the first ten lines of the file. 9*43c1707eStholo# For existing files, it checks version number in the "Id" line to 10*43c1707eStholo# prevent losing changes because an old version of a file was copied 11*43c1707eStholo# into the direcory. 12*43c1707eStholo# 13*43c1707eStholo# Possible future enhancements: 14*43c1707eStholo# 15*43c1707eStholo# Check for cruft left by unresolved conflicts. Search for 16*43c1707eStholo# "^<<<<<<<$", "^-------$", and "^>>>>>>>$". 17*43c1707eStholo# 18*43c1707eStholo# Look for a copyright and automagically update it to the 19*43c1707eStholo# current year. [[ bad idea! -- woods ]] 20*43c1707eStholo# 21*43c1707eStholo# 22*43c1707eStholo# Contributed by David Hampton <hampton@cisco.com> 23*43c1707eStholo# 24*43c1707eStholo# Hacked on lots by Greg A. Woods <woods@web.net> 25*43c1707eStholo 26*43c1707eStholo# 27*43c1707eStholo# Configurable options 28*43c1707eStholo# 29*43c1707eStholo 30*43c1707eStholo# Constants (remember to protect strings from RCS keyword substitution) 31*43c1707eStholo# 32*43c1707eStholo$LAST_FILE = "/tmp/#cvs.lastdir"; # must match name in log_accum.pl 33*43c1707eStholo$ENTRIES = "CVS/Entries"; 34*43c1707eStholo 35*43c1707eStholo# Patterns to find $Log keywords in files 36*43c1707eStholo# 37*43c1707eStholo$LogString1 = "\\\$\\Log: .* \\\$"; 38*43c1707eStholo$LogString2 = "\\\$\\Log\\\$"; 39*43c1707eStholo$NoLog = "%s - contains an RCS \$Log keyword. It must not!\n"; 40*43c1707eStholo 41*43c1707eStholo# pattern to match an RCS Id keyword line with an existing ID 42*43c1707eStholo# 43*43c1707eStholo$IDstring = "\"@\\(#\\)[^:]*:.*\\\$\Id: .*\\\$\""; 44*43c1707eStholo$NoId = " 45*43c1707eStholo%s - Does not contain a properly formatted line with the keyword \"Id:\". 46*43c1707eStholo I.e. no lines match \"" . $IDstring . "\". 47*43c1707eStholo Please see the template files for an example.\n"; 48*43c1707eStholo 49*43c1707eStholo# pattern to match an RCS Id keyword line for a new file (i.e. un-expanded) 50*43c1707eStholo# 51*43c1707eStholo$NewId = "\"@(#)[^:]*:.*\\$\Id\\$\""; 52*43c1707eStholo 53*43c1707eStholo$NoName = " 54*43c1707eStholo%s - The ID line should contain only \"@(#)module/path:\$Name\$:\$\Id\$\" 55*43c1707eStholo for a newly created file.\n"; 56*43c1707eStholo 57*43c1707eStholo$BadName = " 58*43c1707eStholo%s - The file name '%s' in the ID line does not match 59*43c1707eStholo the actual filename.\n"; 60*43c1707eStholo 61*43c1707eStholo$BadVersion = " 62*43c1707eStholo%s - How dare you!!! You replaced your copy of the file '%s', 63*43c1707eStholo which was based upon version %s, with an %s version based 64*43c1707eStholo upon %s. Please move your '%s' out of the way, perform an 65*43c1707eStholo update to get the current version, and them merge your changes 66*43c1707eStholo into that file, then try the commit again.\n"; 67*43c1707eStholo 68*43c1707eStholo# 69*43c1707eStholo# Subroutines 70*43c1707eStholo# 71*43c1707eStholo 72*43c1707eStholosub write_line { 73*43c1707eStholo local($filename, $line) = @_; 74*43c1707eStholo open(FILE, ">$filename") || die("Cannot open $filename, stopped"); 75*43c1707eStholo print(FILE $line, "\n"); 76*43c1707eStholo close(FILE); 77*43c1707eStholo} 78*43c1707eStholo 79*43c1707eStholosub check_version { 80*43c1707eStholo local($i, $id, $rname, $version); 81*43c1707eStholo local($filename, $cvsversion) = @_; 82*43c1707eStholo 83*43c1707eStholo open(FILE, "<$filename") || return(0); 84*43c1707eStholo 85*43c1707eStholo @all_lines = (); 86*43c1707eStholo $idpos = -1; 87*43c1707eStholo $newidpos = -1; 88*43c1707eStholo for ($i = 0; <FILE>; $i++) { 89*43c1707eStholo chop; 90*43c1707eStholo push(@all_lines, $_); 91*43c1707eStholo if ($_ =~ /$IDstring/) { 92*43c1707eStholo $idpos = $i; 93*43c1707eStholo } 94*43c1707eStholo if ($_ =~ /$NewId/) { 95*43c1707eStholo $newidpos = $i; 96*43c1707eStholo } 97*43c1707eStholo } 98*43c1707eStholo 99*43c1707eStholo if (grep(/$LogString1/, @all_lines) || grep(/$LogString2/, @all_lines)) { 100*43c1707eStholo print STDERR sprintf($NoLog, $filename); 101*43c1707eStholo return(1); 102*43c1707eStholo } 103*43c1707eStholo 104*43c1707eStholo if ($debug != 0) { 105*43c1707eStholo print STDERR sprintf("file = %s, version = %d.\n", $filename, $cvsversion{$filename}); 106*43c1707eStholo } 107*43c1707eStholo 108*43c1707eStholo if ($cvsversion{$filename} == 0) { 109*43c1707eStholo if ($newidpos != -1 && $all_lines[$newidpos] !~ /$NewId/) { 110*43c1707eStholo print STDERR sprintf($NoName, $filename); 111*43c1707eStholo return(1); 112*43c1707eStholo } 113*43c1707eStholo return(0); 114*43c1707eStholo } 115*43c1707eStholo 116*43c1707eStholo if ($idpos == -1) { 117*43c1707eStholo print STDERR sprintf($NoId, $filename); 118*43c1707eStholo return(1); 119*43c1707eStholo } 120*43c1707eStholo 121*43c1707eStholo $line = $all_lines[$idpos]; 122*43c1707eStholo $pos = index($line, "Id: "); 123*43c1707eStholo if ($debug != 0) { 124*43c1707eStholo print STDERR sprintf("%d in '%s'.\n", $pos, $line); 125*43c1707eStholo } 126*43c1707eStholo ($id, $rname, $version) = split(' ', substr($line, $pos)); 127*43c1707eStholo if ($rname ne "$filename,v") { 128*43c1707eStholo print STDERR sprintf($BadName, $filename, substr($rname, 0, length($rname)-2)); 129*43c1707eStholo return(1); 130*43c1707eStholo } 131*43c1707eStholo if ($cvsversion{$filename} < $version) { 132*43c1707eStholo print STDERR sprintf($BadVersion, $filename, $filename, $cvsversion{$filename}, 133*43c1707eStholo "newer", $version, $filename); 134*43c1707eStholo return(1); 135*43c1707eStholo } 136*43c1707eStholo if ($cvsversion{$filename} > $version) { 137*43c1707eStholo print STDERR sprintf($BadVersion, $filename, $filename, $cvsversion{$filename}, 138*43c1707eStholo "older", $version, $filename); 139*43c1707eStholo return(1); 140*43c1707eStholo } 141*43c1707eStholo return(0); 142*43c1707eStholo} 143*43c1707eStholo 144*43c1707eStholo# 145*43c1707eStholo# Main Body 146*43c1707eStholo# 147*43c1707eStholo 148*43c1707eStholo$id = getpgrp(); # You *must* use a shell that does setpgrp()! 149*43c1707eStholo 150*43c1707eStholo# Check each file (except dot files) for an RCS "Id" keyword. 151*43c1707eStholo# 152*43c1707eStholo$check_id = 0; 153*43c1707eStholo 154*43c1707eStholo# Record the directory for later use by the log_accumulate stript. 155*43c1707eStholo# 156*43c1707eStholo$record_directory = 0; 157*43c1707eStholo 158*43c1707eStholo# parse command line arguments 159*43c1707eStholo# 160*43c1707eStholowhile (@ARGV) { 161*43c1707eStholo $arg = shift @ARGV; 162*43c1707eStholo 163*43c1707eStholo if ($arg eq '-d') { 164*43c1707eStholo $debug = 1; 165*43c1707eStholo print STDERR "Debug turned on...\n"; 166*43c1707eStholo } elsif ($arg eq '-c') { 167*43c1707eStholo $check_id = 1; 168*43c1707eStholo } elsif ($arg eq '-r') { 169*43c1707eStholo $record_directory = 1; 170*43c1707eStholo } else { 171*43c1707eStholo push(@files, $arg); 172*43c1707eStholo } 173*43c1707eStholo} 174*43c1707eStholo 175*43c1707eStholo$directory = shift @files; 176*43c1707eStholo 177*43c1707eStholoif ($debug != 0) { 178*43c1707eStholo print STDERR "dir - ", $directory, "\n"; 179*43c1707eStholo print STDERR "files - ", join(":", @files), "\n"; 180*43c1707eStholo print STDERR "id - ", $id, "\n"; 181*43c1707eStholo} 182*43c1707eStholo 183*43c1707eStholo# Suck in the CVS/Entries file 184*43c1707eStholo# 185*43c1707eStholoopen(ENTRIES, $ENTRIES) || die("Cannot open $ENTRIES.\n"); 186*43c1707eStholowhile (<ENTRIES>) { 187*43c1707eStholo local($filename, $version) = split('/', substr($_, 1)); 188*43c1707eStholo $cvsversion{$filename} = $version; 189*43c1707eStholo} 190*43c1707eStholo 191*43c1707eStholo# Now check each file name passed in, except for dot files. Dot files 192*43c1707eStholo# are considered to be administrative files by this script. 193*43c1707eStholo# 194*43c1707eStholoif ($check_id != 0) { 195*43c1707eStholo $failed = 0; 196*43c1707eStholo foreach $arg (@files) { 197*43c1707eStholo if (index($arg, ".") == 0) { 198*43c1707eStholo next; 199*43c1707eStholo } 200*43c1707eStholo $failed += &check_version($arg); 201*43c1707eStholo } 202*43c1707eStholo if ($failed) { 203*43c1707eStholo print STDERR "\n"; 204*43c1707eStholo exit(1); 205*43c1707eStholo } 206*43c1707eStholo} 207*43c1707eStholo 208*43c1707eStholo# Record this directory as the last one checked. This will be used 209*43c1707eStholo# by the log_accumulate script to determine when it is processing 210*43c1707eStholo# the final directory of a multi-directory commit. 211*43c1707eStholo# 212*43c1707eStholoif ($record_directory != 0) { 213*43c1707eStholo &write_line("$LAST_FILE.$id", $directory); 214*43c1707eStholo} 215*43c1707eStholoexit(0); 216