From 074f2b7e481e8e4879aa6cb6c04c93538b477952 Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Fri, 20 Jan 2006 12:16:39 +0000 Subject: [PATCH] FreeBSD-comittmails dazu --- CVSROOT/access | 8 + CVSROOT/avail | 17 + CVSROOT/cfg.pm | 284 +++++++++++++ CVSROOT/cfg_local.pm | 70 ++++ CVSROOT/checkoutlist | 17 +- CVSROOT/commit_prep.pl | 313 ++++++++++++++ CVSROOT/commitcheck | 74 ++++ CVSROOT/commitinfo | 1 + CVSROOT/config | 4 +- CVSROOT/cvs_acls.pl | 246 +++++++++++ CVSROOT/exclude | 75 ++++ CVSROOT/log_accum.pl | 903 +++++++++++++++++++++++++++++++++++++++++ CVSROOT/logcheck | 137 +++++++ CVSROOT/loginfo | 1 + CVSROOT/options | 2 + CVSROOT/rcstemplate | 15 + CVSROOT/tagcheck | 31 ++ CVSROOT/unwrap | 22 + CVSROOT/wrap | 22 + 19 files changed, 2239 insertions(+), 3 deletions(-) create mode 100644 CVSROOT/access create mode 100644 CVSROOT/avail create mode 100755 CVSROOT/cfg.pm create mode 100644 CVSROOT/cfg_local.pm create mode 100755 CVSROOT/commit_prep.pl create mode 100755 CVSROOT/commitcheck create mode 100755 CVSROOT/cvs_acls.pl create mode 100644 CVSROOT/exclude create mode 100755 CVSROOT/log_accum.pl create mode 100755 CVSROOT/logcheck create mode 100644 CVSROOT/options create mode 100644 CVSROOT/rcstemplate create mode 100755 CVSROOT/tagcheck create mode 100755 CVSROOT/unwrap create mode 100755 CVSROOT/wrap diff --git a/CVSROOT/access b/CVSROOT/access new file mode 100644 index 0000000..409208c --- /dev/null +++ b/CVSROOT/access @@ -0,0 +1,8 @@ +# +# $Id$ +# +# Wer committen darf +lea10010 +bes10033 +wiw10050 +jgz10052 \ No newline at end of file diff --git a/CVSROOT/avail b/CVSROOT/avail new file mode 100644 index 0000000..1c05817 --- /dev/null +++ b/CVSROOT/avail @@ -0,0 +1,17 @@ +# +# $Id$ +# +# Wer welche Teile bearbeiten darf +# +# Beispiele: +# Gruppendefinition +#group|meisters|peter,jdp,markm,joe +# Gruppendefinition aus Datei +#group|penaltybox|!badcommitters +# Verboten fuer alle +#unavail||src/contrib/binutils,src/contrib/file +# Erlaubt fuer einige +#avail|:meisters|CVSROOT +# +# Alle duerfen ueberall +avail diff --git a/CVSROOT/cfg.pm b/CVSROOT/cfg.pm new file mode 100755 index 0000000..fd8ffe6 --- /dev/null +++ b/CVSROOT/cfg.pm @@ -0,0 +1,284 @@ +# $Id$ +# $FreeBSD: cfg.pm,v 1.50 2003/02/28 18:28:11 peter Exp $ + +#################################################################### +#################################################################### +# This file contains the default configuration for the CVSROOT +# perl scripts. You are advised to override the configuration +# in the cfg_local.pm file instead of here. +# +# WARNING: You are strongly advised to check for syntax errors +# in this file before committing it. Use: perl -cw cfg.pm +#################################################################### +#################################################################### + +package cfg; +use strict; +use vars qw( + $ADD_TO_LINE $AVAIL_FILE $CHECK_HEADERS $COMMITCHECK_EXTRA + @COMMIT_HOSTS $COMMITTER $DEBUG $DIFF_BLOCK_TOTAL_LINES $EXCLUDE_FILE + $FILE_PREFIX $IDHEADER $LAST_FILE @LOG_FILE_MAP $MAILADDRS $MAILBANNER + $MAILCMD $MAIL_BRANCH_HDR $MAIL_ON_DIR_CREATION $MAIL_TRANSFORM + $MINCVSVERSION $MAX_DIFF_SIZE $NO_DOS_LINEBREAKS $PID $PROG_CVS + $PROG_MV %TEMPLATE_HEADERS $TMPDIR $UNEXPAND_RCSID $WARN_HEADERS +); + +my $CVSROOT = $ENV{'CVSROOT'} || die "Can't determine \$CVSROOT!"; + + + +###################### +### global options ### +###################### +### WARNING: these aren't global across all the scripts yet. +### This is work in progress. + +# Process group id; used as a unique number in temporary file names. +$PID = getpgrp(); + +# Debug level, 0 = off, 1 = on. +$DEBUG = 0; + +# Location of temporary directory. +$TMPDIR = "/tmp/"; + +# The filename prefix used for temporary files. +$FILE_PREFIX = "#cvs.files.$PID"; + +# The file used to store the name of the last directory examined +# when processing a multi-directory commit. +$LAST_FILE = "$TMPDIR/$FILE_PREFIX.lastdir"; +# System tools. +$PROG_CVS = '/usr/bin/cvs'; # cvs(1) +$PROG_MV = '/bin/mv'; # mv(1) + +# The username of the committer. +$COMMITTER = $ENV{"LOGNAME"} || $ENV{'USER'} || getlogin + || (getpwuid($<))[0] || sprintf("uid#%d",$<); + + +################### +### commitcheck ### +################### + +# A list of hosts the it's ok to commit on. Useful if your committers +# take local copies of the repository to work off-line. +# (Empty if you don't want checks.) +@COMMIT_HOSTS = (); + +# The minimum version of cvs that we will work with. +$MINCVSVERSION = "1090900"; # 1.9.9p0 + +# Additional commit time checks. This is an anonymous subroutine +# that gets called early on in the validation process to see whether +# the committer is allowed to commit. It should return true if +# everything is ok, otherwise the commit will be terminated. +$COMMITCHECK_EXTRA = ""; + + +################### +### cvs_acls.pl ### +################### + +# The name of the avail file that defines who's allow to +# commit to what. +$AVAIL_FILE = "$CVSROOT/CVSROOT/avail"; + + +################ +### logcheck ### +################ + +# These are the optional headers that can be filled at the end of +# each commit message. The associated value is a regular-expression +# that is used to type-check the entered value. If a match fails +# then the commit is rejected. (See rcstemplate). +# +# Make sure that these are also described in the rcstemplate to +# make their usage clear to committers. +# +# In addition any of these entries that are left blank are removed +# from the log at commit time. [Please note that for them to be +# removed from the rcslog in the repository you need to be running +# the version of cvs in the FreeBSD source tree. We've got a local +# patch that causes cvs to read back the commit message after the +# commit_prep.pl script has had a chance to modify it (via the +# 'commitinfo' hook). ] +%TEMPLATE_HEADERS = ( +# "Reviewed by" => '.*', +# "Submitted by" => '.*', +# "Obtained from" => '.*', +# "Approved by" => '.*', +# "PR" => '.*', +# "MFC after" => '\d+(\s+(days?|weeks?|months?))?' +); + + +###################### +### commit_prep.pl ### +###################### + +# Check for instances of $IDHEADER in committed files, and +# bomb out if they're not present, or corrupted. +# Exclusions can be specified in the exclude file. +# Currently $IDHEADER must be an instance of '$ CVSHeader $', or an alias +# defined in CVSROOT/options. +$CHECK_HEADERS = 0; +$EXCLUDE_FILE = "$CVSROOT/CVSROOT/exclude"; + +# Make a header check a non-fatal error - just warn, don't exit. +$WARN_HEADERS = 0; + +# WARNING: You will also need to be running the version of cvs that +# the FreeBSD project is using; I believe that we have some local patches +# that aren't in the main 'cvs' source. +# Additionally you'll need to tweak CVSROOT/options if you wish to use your +# own ident header. +$IDHEADER = 'CVSHeader'; + +# Contract any instances of $IDHEADER in the source file before committing. +# This is useful because it means that expanded headers aren't stored in +# the repository as part of the delta. +$UNEXPAND_RCSID = 0; + +# Check for DOS/WINDOWS/MAC linebreaks in the file, bomb out if present. +# Exclusions can be specified in the exclude file. +$NO_DOS_LINEBREAKS = 0; + + +#################### +### log_accum.pl ### +#################### + +# The command used to mail the log messages. +# Usually something like '/usr/sbin/sendmail'. +$MAILCMD = "/usr/sbin/sendmail"; + +# Email addresses of recipients of commit mail. +$MAILADDRS = 'nobody'; + +# Extra banner added to the top of commit email. +# Use "" if you don't want one. +# i.e. $MAILBANNER = "Project X CVS Repository"; +$MAILBANNER = ""; + +# Send mail when directories are created in the repository. +# 0 = off, 1 = on. +$MAIL_ON_DIR_CREATION = 0; + +# Include the names of the branches committed to in the commit email, +# using this header (leave off the trailing ':'). +# Use "" if you don't want one. +$MAIL_BRANCH_HDR = "X-CVS-Branch"; + +# Include a 'To:' header in the generated commit mail? +$ADD_TO_LINE = 1; + +# This is a way to post-process the log email before it is mailed. +# Some people find it useful to use this to create URLs in their +# commit mails to show the patch in a web page (using cvsweb) for +# instance. +# +# The $MAIL_TRANSFORM variable should be "" if you don't want to +# use this feature. Otherwise it should be a reference to a +# subroutine that is passed the email message as a list, and returns +# the modified list to the log_accum.pl script. The list has one +# element per email line, with no trailing line feeds. This function +# shouldn't add them. If $DEBUG is switched on the log_accum.pl +# script will show the before and after on stdout at commit time. +# +# The example below shows a way of inserting links to cvsweb. +$MAIL_TRANSFORM = ""; +#$MAIL_TRANSFORM = sub { +# add_cvsweb_entry("http://www.example.org/cgi-bin/cvsweb.cgi", @_); +#}; + +# A copy of the commit summary is saved locally as well as being +# emailed to the committers. The name of the local log is obtained +# by performing a pattern match on the directory that the files are +# in. The following map defines the file names and their associated +# pattern match. They are checked in order. The name 'other' is +# used if none of the patterns match. +# +# XXX The directory that the logs are placed in should be a +# configuration option too. +@LOG_FILE_MAP = ( + 'CVSROOT' => '^CVSROOT/', + 'doc' => '^doc/', + 'user' => '^src/', + 'other' => '.*' +); + +# Include diffs of not greater than this size in kbytes in the +# commit mail for each file modified. (0 = off). +$MAX_DIFF_SIZE = 0; + +# Maximum size of in lines of the diff block. +$DIFF_BLOCK_TOTAL_LINES = 200; + + +###################### +# EXAMPLES +###################### +# A function for post-processing a log message +# and outputing it with URLs to a cvsweb.cgi in. +sub add_cvsweb_entry { + my $url_to_cvsweb = shift; + my @input = @_; + my @output = (); + + # Skip down to the revision summary. + while (1) { + my $line = shift @input; + last unless defined($line); + + push @output, $line; + last if $line =~ /^\s*Revision\s*Changes\s*Path\s*$/; + } + + # Add the url links + my $skip = 0; + foreach (@input) { + # The revision block is terminated with an empty line. + $skip = 1 if $_ =~ /^\s*$/; + + push @output, $_; + next if $skip; + + my ($rev, $add, $sub, $file, $status) = split; + + $rev =~ /(?:(.*)\.)?([^\.]+)\.([^\.]+)$/; + my ($base, $r1, $r2) = ($1, $2, $3); + my $prevrev = ""; + if ($r2 == 1) { + $prevrev = $base; + } else { + $prevrev = "$base." if $base; + $prevrev .= "$r1." . ($r2 - 1); + } + + my $baseurl = "$url_to_cvsweb/$file"; + my $extra; + if (defined($status)) { + $rev = $prevrev if $status =~ /dead/; + $extra = "?rev=$rev&content-type=text/plain"; + } else { + $extra = ".diff?r1=$prevrev&r2=$rev&f=h"; + } + push @output, "$baseurl$extra"; + } + + return @output; +}; + + +###################################################################### +# Load the local configuration file, that allows the entries in this +# file to be overridden. +###################################################################### +eval { require "$CVSROOT/CVSROOT/cfg_local.pm" } + if -e "$CVSROOT/CVSROOT/cfg_local.pm"; +warn $@ if $@; + +1; # Perl requires all modules to return true. Don't delete!!!! +#end diff --git a/CVSROOT/cfg_local.pm b/CVSROOT/cfg_local.pm new file mode 100644 index 0000000..baee3d4 --- /dev/null +++ b/CVSROOT/cfg_local.pm @@ -0,0 +1,70 @@ +# $Id$ +# $FreeBSD: cfg_local.pm,v 1.26 2003/02/28 21:17:06 peter Exp $ + +#################################################################### +#################################################################### +# This file contains local configuration for the CVSROOT perl +# scripts. It is loaded by cfg.pm and overrides the default +# configuration in that file. +# +# It is advised that you test it with +# 'env CVSROOT=/path/to/cvsroot perl -cw cfg.pm' +# before you commit any changes. The check is to cfg.pm which +# loads this file. +#################################################################### +#################################################################### + +# Noch nicht +#$CHECK_HEADERS = 1; +$IDHEADER = 'Id'; +$UNEXPAND_RCSID = 1; + +%TEMPLATE_HEADERS = ( +# "Reviewed by" => '.*', +# "Submitted by" => '.*', +# "Obtained from" => '.*', +# "Approved by" => '.*', +# "PR" => '.*', +# "MFC after" => '\d+(\s+(days?|weeks?|months?))?' +); + +$MAILADDRS = 'schlepperbande-commit@zs64.net'; +#$MAILCMD = "/usr/local/bin/mailsend -H"; +$MAIL_BRANCH_HDR = "X-SCHLEPPERBANDE"; +$MAILBANNER = "Schlepperbande repository"; +if (defined $ENV{'CVS_COMMIT_ATTRIB'}) { + my $attrib = $ENV{'CVS_COMMIT_ATTRIB'}; + $MAILBANNER .= " ($attrib committer)"; +} +$MAIL_TRANSFORM = sub { + add_cvsweb_entry("https://koef.zs64.net/schlepperbande/cvs/cvsweb.cgi", @_); +}; + +$MAX_DIFF_SIZE = 4096; + + +# Sanity check to make sure we've been run through the wrapper and are +# now primary group 'ncvs'. +# +#$COMMITCHECK_EXTRA = sub { +# my $GRP=`/usr/bin/id -gn`; +# chomp $GRP; +# unless ( $GRP =~ /^ncvs$/ ) { +# print "You do not have group ncvs (commitcheck)!\n"; +# exit 1; # We could return false here. But there's +# # nothing to stop us taking action here instead. +# } +# return 1; +#}; + +@LOG_FILE_MAP = ( + 'CVSROOT' => '^CVSROOT/', + 'distrib' => '^distrib/', + + 'test' => '^test/', + + 'other' => '.*' +); + +1; # Perl requires all modules to return true. Don't delete!!!! +#end diff --git a/CVSROOT/checkoutlist b/CVSROOT/checkoutlist index 2921bff..e786b10 100644 --- a/CVSROOT/checkoutlist +++ b/CVSROOT/checkoutlist @@ -8,6 +8,21 @@ # # File format: # -# [][] +# [] # # comment lines begin with '#' +access +avail +cfg.pm +cfg_local.pm +commit_prep.pl +commitcheck +cvs_acls.pl +exclude +log_accum.pl +logcheck +options +rcstemplate +tagcheck +unwrap +wrap diff --git a/CVSROOT/commit_prep.pl b/CVSROOT/commit_prep.pl new file mode 100755 index 0000000..281eab7 --- /dev/null +++ b/CVSROOT/commit_prep.pl @@ -0,0 +1,313 @@ +#!/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 +# + +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 () { + 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 () { + $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 = ; + 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 () { + 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); diff --git a/CVSROOT/commitcheck b/CVSROOT/commitcheck new file mode 100755 index 0000000..d13e536 --- /dev/null +++ b/CVSROOT/commitcheck @@ -0,0 +1,74 @@ +#!/usr/bin/perl -w +# +# $Id$ +# $FreeBSD: commitcheck,v 1.26 2002/10/13 23:25:06 peter Exp $ + +# +# This script is the first thing that is run at commit time +# to determine whether the committer has commit priviledges. +# (See CVSROOT/commitinfo). +# + +use strict; +use lib $ENV{CVSROOT}; +use CVSROOT::cfg; +my $CVSROOT = $ENV{CVSROOT} || die "Can't determine CVSROOT (commitcheck)!\n"; + +# Check that the user is committing on the right machine. +# +use Sys::Hostname; +my $hostname = hostname(); +if (@cfg::COMMIT_HOSTS && !grep(/^\Q$hostname\E$/i, @cfg::COMMIT_HOSTS)) { + print "Please don't commit on this host!\n"; + print "Please commit on ", + join(" or ", @cfg::COMMIT_HOSTS), + " instead.\n"; + exit 1; +} + + +# Run locally defined extra commitchecks. +if (defined($cfg::COMMITCHECK_EXTRA) && $cfg::COMMITCHECK_EXTRA) { + die 'commitcheck: $cfg::COMMITCHECK_EXTRA isn\'t a sub!' + unless ref($cfg::COMMITCHECK_EXTRA) eq "CODE"; + + die "Failed commitcheck_extra\n" unless &$cfg::COMMITCHECK_EXTRA(); +} + + +# +# Ensure the minimum version of cvs is installed. +# +#my $VERSTR = `$cfg::PROG_CVS -v`; +#$VERSTR =~ s/.*Concurrent\D*(\S*).*/$1/s; +#$VERSTR =~ s/\D+/./g; +#my $VERSION = sprintf "%d%02d%02d%02d\n", split /\./, $VERSTR; +#unless ($VERSION && $VERSION >= $cfg::MINCVSVERSION) { +# print "The wrong version of CVS is installed (commitcheck)!\n"; +# exit 1; +#} + + +# +# Does the access control list allow them commit access? +# +system("$CVSROOT/CVSROOT/cvs_acls.pl", @ARGV); +if ($? >> 8) { + print "Access control checks failed! (cvs_acls.pl)\n"; + exit 1; +} + +# +# Last minute checks and preparations for log_accum.pl later. This +# records the last directory in this commit so that log_accum knows when +# to finish coalescing commit messages and mail it. +# +system("$CVSROOT/CVSROOT/commit_prep.pl", @ARGV); +if ($? >> 8) { + print "commit_prep.pl failed!\n"; + exit 1; +} + +exit 0; # Lets do it! + +#end diff --git a/CVSROOT/commitinfo b/CVSROOT/commitinfo index b19e7b7..d41e783 100644 --- a/CVSROOT/commitinfo +++ b/CVSROOT/commitinfo @@ -13,3 +13,4 @@ # # If the name "ALL" appears as a regular expression it is always used # in addition to the first matching regex or "DEFAULT". +ALL $CVSROOT/CVSROOT/commitcheck diff --git a/CVSROOT/config b/CVSROOT/config index 92c150b..56ea7b5 100644 --- a/CVSROOT/config +++ b/CVSROOT/config @@ -9,9 +9,9 @@ # command. #TopLevelAdmin=no -# Set `LogHistory' to `all' or `TOEFWUPCGMAR' to log all transactions to the +# Set `LogHistory' to `all' or `TOFEWGCMAR' to log all transactions to the # history file, or a subset as needed (ie `TMAR' logs all write operations) -#LogHistory=TOEFWUPCGMAR +#LogHistory=TOFEWGCMAR # Set `RereadLogAfterVerify' to `always' (the default) to allow the verifymsg # script to change the log message. Set it to `stat' to force CVS to verify# that the file has changed before reading it (this can take up to an extra diff --git a/CVSROOT/cvs_acls.pl b/CVSROOT/cvs_acls.pl new file mode 100755 index 0000000..eb17d01 --- /dev/null +++ b/CVSROOT/cvs_acls.pl @@ -0,0 +1,246 @@ +#!/usr/bin/perl -w +# +# $Id$ +# $FreeBSD: cvs_acls.pl,v 1.24 2002/07/22 17:24:29 joe Exp $ +# +# Access control lists for CVS. dgg@ksr.com (David G. Grubbs) +# +# ==== FORMAT OF THE avail FILE: +# +# The avail file determines whether you may commit files. It contains lines +# read from top to bottom, keeping track of a single "bit". The "bit" +# defaults to "on". It can be turned "off" by "unavail" lines and "on" by +# "avail" lines. ==> Last one counts. +# +# Any line not beginning with "avail" or "unavail" is ignored. +# +# Lines beginning with "avail" or "unavail" are assumed to be '|'-separated +# triples: (All spaces and tabs are ignored in a line.) +# +# {avail.*,unavail.*} [| user,user,... [| repos,repos,...]] +# +# 1. String starting with "avail" or "unavail". +# 2. Optional, comma-separated list of usernames. +# 3. Optional, comma-separated list of repository pathnames. +# These are pathnames relative to $CVSROOT. They can be directories or +# filenames. A directory name allows access to all files and +# directories below it. +# +# Example: (Text from the ';;' rightward may not appear in the file.) +# +# unavail ;; Make whole repository unavailable. +# avail|dgg ;; Except for user "dgg". +# avail|fred, john|bin/ls ;; Except when "fred" or "john" commit to +# ;; the module whose repository is "bin/ls" +# +# PROGRAM LOGIC: +# +# CVS passes to @ARGV an absolute directory pathname (the repository +# appended to your $CVSROOT variable), followed by a list of filenames +# within that directory. +# +# We walk through the avail file looking for a line that matches both +# the username and repository. +# +# A username match is simply the user's name appearing in the second +# column of the avail line in a space-or-comma separate list. +# +# A repository match is either: +# - One element of the third column matches $ARGV[0], or some +# parent directory of $ARGV[0]. +# - Otherwise *all* file arguments ($ARGV[1..$#ARGV]) must be +# in the file list in one avail line. +# - In other words, using directory names in the third column of +# the avail file allows committing of any file (or group of +# files in a single commit) in the tree below that directory. +# - If individual file names are used in the third column of +# the avail file, then files must be committed individually or +# all files specified in a single commit must all appear in +# third column of a single avail line. +# +# Additional (2001/11/16): I've added a group function for labelling +# groups of users. To define a group add a line in the avail file of +# the form: +# group|grpname1|joe,fred,bob +# group|grpname2|pete,:grpname1,simon +# group|grpname2|sid,:grpname2,mike +# group|anothergroup|!filename/containing/listofusers +# +# The group name can be used in any place a user name could be used in +# an avail or unavail line. Just precede the group name with a ':' +# character. In the example above you'll note that you can define a +# group more than once. Each definition overrides the previous one, +# but can include itself to add to it. +# +# In place of a username in any of the above rules, you can specify +# a group name preceeded with a ':' character, or a filename preceeded +# with a '!' character. In the case of a file it is assumed relative to +# $CVSROOT/CVSROOT/ unless it started witha leading '/'. All blank lines +# and comments are ignored, and the remaining lines are treated as one +# username per line. + +use strict; + +use lib $ENV{CVSROOT}; +use CVSROOT::cfg; +my $CVSROOT = $ENV{'CVSROOT'} || die "Can't determine \$CVSROOT!"; + +my $debug = $cfg::DEBUG; + +my %GROUPS; # List of committer groups +my $exit_val = 0; # Good Exit value +my $universal_off = 0; + + +####################################### +# process any variable=value switches +####################################### +my $die = ''; +eval "print STDERR \$die='Unknown parameter $1\n' if !defined \$$1; \$$1=\$';" + while ($ARGV[0] =~ /^(\w+)=/ && shift(@ARGV)); +exit 255 if $die; + + +####################################### +# Work out where in the repo we're at. +####################################### +my $repos = shift; +$repos =~ s:^$CVSROOT/::; +grep($_ = $repos . '/' . $_, @ARGV); + +print "$$ Repos: $repos\n","$$ ==== ",join("\n$$ ==== ",@ARGV),"\n" if $debug; + + +####################################### +# Check that the user has permission. +####################################### + +# It is ok for the avail file not to exist. +exit 0 unless -e $cfg::AVAIL_FILE; + +# Suck in a list of committer groups from the avail file. +open (AVAIL, $cfg::AVAIL_FILE) || die "open $cfg::AVAIL_FILE: $!\n"; +while () { + next unless /^group\|/; + chomp; + + my ($keywrd, $gname, $members) = split /\|/, $_; + $GROUPS{$gname} = expand_users($members); +} +close(AVAIL); + + +open (AVAIL, $cfg::AVAIL_FILE) || die "open $cfg::AVAIL_FILE: $!\n"; +while () { + chomp; + next if /^\s*\#/; + next if /^\s*$/; + next if /^group\|/; + + print "--------------------\n" if $debug; + + my $rule = $_; + my ($flagstr, $u, $m) = split(/[\s,]*\|[\s,]*/, $rule); + + # Skip anything not starting with "avail" or "unavail" and complain. + if ($flagstr !~ /^avail/ && $flagstr !~ /^unavail/) { + print "Bad avail line: $rule\n"; + next; + } + + # Set which bit we are playing with. ('0' is OK == Available). + my $flag = (($& eq "avail") ? 0 : 1); + + # If we find a "universal off" flag (i.e. a simple "unavail") + # remember it + my $universal_off = 1 if ($flag && !$u && !$m); + + # Expand any group names into a full user list. + my $users = expand_users($u); + + # $cfg::COMMITTER considered "in user list" if actually in list + # or is NULL + my $in_user = (!$u || grep ($_ eq $cfg::COMMITTER, + split(/[\s,]+/, $users))); + print "$$ \$cfg::COMMITTER ($cfg::COMMITTER) in user list: $rule\n" + if $debug && $in_user; + + # Module matches if it is a NULL module list in the avail line. + # If module list is not null, we check every argument combination. + my $in_repo = (!$m || 0); + unless ($in_repo) { + my @tmp = split(/[\s,]+/, $m); + for my $j (@tmp) { + # If the repos from avail is a parent(or equal) + # dir of $repos, OK + if ($repos eq $j || $repos =~ /^$j\//) { + $in_repo = 1; + last; + } + } + unless ($in_repo) { + #$in_repo = 1; + for my $j (@ARGV) { + last unless $in_repo = grep ($_ eq $j, @tmp); + } + } + } + print "$$ \$repos($repos) in repository list: $rule\n" + if $debug && $in_repo; + + print "$$ Expanded user list: $users\n" if $debug; + + $exit_val = $flag if ($in_user && $in_repo); + print "$$ ==== \$exit_val = $exit_val\n$$ ==== \$flag = $flag\n" + if $debug; +} +close(AVAIL); +print "$$ ==== \$exit_val = $exit_val\n" if $debug; +print "**** Access denied: Insufficient Karma ($cfg::COMMITTER|$repos)\n" + if $exit_val; +print "**** Access allowed: Personal Karma exceeds Environmental Karma.\n" + if $debug && $universal_off && !$exit_val; +exit($exit_val); + + +# Expand a user specification containing group names and deltas into +# a definitive list of users. +sub expand_users { + my $user_list = shift || ""; + + # Parse the members. + my @members = split /,/, $user_list; + my %members; + foreach my $m (@members) { + if ($m =~ s/^://) { + if (!defined($GROUPS{$m})) { + warn "Group '$m' not defined before use in " . + "$cfg::AVAIL_FILE.\n"; + next; + } + # Add the specified group to the membership. + foreach (split /,/, $GROUPS{$m}) { + $members{$_} = 1; + } + } elsif ($m =~ s/\!//) { + $m = "$CVSROOT/CVSROOT/$m" if $m !~ /^\//; + if (open USERLIST, $m) { + while () { + chomp; + s/\s*(#.*)?$//; + next if /^$/; + + $members{$_} = 1; + } + close USERLIST; + } else { + warn "Can't open user file $m " . + "defined in $cfg::AVAIL_FILE.\n"; + } + } else { + $members{$m} = 1; + } + } + + return join("," , sort keys %members); +} diff --git a/CVSROOT/exclude b/CVSROOT/exclude new file mode 100644 index 0000000..e277f90 --- /dev/null +++ b/CVSROOT/exclude @@ -0,0 +1,75 @@ +# Exclusions list for header checking. +# $Id$ +# $FreeBSD: exclude,v 1.105 2003/11/07 04:27:59 kan Exp $ +# + +# Skip dot files. +^(.*/)?\. + +# A number of files in CVSROOT don't need to be checked. +^CVSROOT/cvsignore +^CVSROOT/options +^CVSROOT/rcstemplate + +^distrib/ +^www/ + +^doc/.*\.eps +^doc/.*\.png +^doc/.*\.scr + +# Skip all ports files except for Makefiles. +^ports/.*(? +# +# Extensively hacked for FreeBSD by Peter Wemm , +# with parts stolen from Greg Woods version. +# +# Extensively cleaned up and re-worked to use an external configuration +# file by Josef Karthauser . + +require 5.003; # might work with older perl5 + +use strict; +use Text::Tabs; + +use lib $ENV{CVSROOT}; +use CVSROOT::cfg; +my $CVSROOT = $ENV{'CVSROOT'} || die "Can't determine \$CVSROOT!"; + + +############################################################ +# +# Constants +# +############################################################ +my $STATE_NONE = 0; +my $STATE_CHANGED = 1; +my $STATE_ADDED = 2; +my $STATE_REMOVED = 3; +my $STATE_LOG = 4; + +my $BASE_FN = "$cfg::TMPDIR/$cfg::FILE_PREFIX"; +my $LAST_FILE = $cfg::LAST_FILE; +my $CHANGED_FILE = "$BASE_FN.changed"; +my $ADDED_FILE = "$BASE_FN.added"; +my $REMOVED_FILE = "$BASE_FN.removed"; +my $LOG_FILE = "$BASE_FN.log"; +my $SUMMARY_FILE = "$BASE_FN.summary"; +my $LOGNAMES_FILE = "$BASE_FN.lognames"; +my $SUBJ_FILE = "$BASE_FN.subj"; +my $TAGS_FILE = "$BASE_FN.tags"; +my $DIFF_FILE = "$BASE_FN.diff"; + + +############################################################ +# +# Subroutines +# +############################################################ + +# Remove the temporary files. +sub cleanup_tmpfiles { + my @files; # The list of temporary files. + + # Don't clean up afterwards if in debug mode. + return if $cfg::DEBUG; + + opendir DIR, $cfg::TMPDIR or die "Cannot open directory: $cfg::TMPDIR!"; + push @files, grep /^$cfg::FILE_PREFIX\..*$/, readdir(DIR); + closedir DIR; + + foreach (@files) { + unlink "$cfg::TMPDIR/$_"; + } +} + + +# Append a line to a named file. +sub append_line { + my $filename = shift; # File to append to. + my $line = shift; # Line to append. + + open FILE, ">>$filename" or + die "Cannot open for append file $filename.\n"; + print FILE "$line\n"; + close FILE; +} + + +# Read the first line from a named file. +sub read_line { + my $filename = shift; # The file to read the line from. + + my $line; # The line read from the file. + + open FILE, "<$filename" or die "Cannot read file $filename!"; + $line = ; + close FILE; + chomp $line; + + return $line; +} + + +# Return the contents of a file as a list of strings, +# with trailing line feeds removed. +# Return an empty list of the file couldn't be opened for some reason. +sub read_logfile { + my $filename = shift; # The file to read from. + + my @text = (); # The contents of the file. + + if (open FILE, "<$filename") { + while () { + chomp; + push @text, $_; + } + close FILE; + } + + return @text; +} + + +# Write a list to a file. +sub write_logfile { + my $filename = shift; # File to write to. + my @lines = @_; # Contents to write to file. + + open FILE, ">$filename" or + die "Cannot open for write log file $filename."; + print FILE map { "$_\n" } @lines; + close FILE; +} + + +sub format_names { + my $dir = shift; + my @files = @_; + + my $indent = length($dir); + $indent = 20 if $indent < 20; + + my $format = " %-" . sprintf("%d", $indent) . "s "; + + my @lines = (sprintf($format, $dir)); + + if ($cfg::DEBUG) { + print STDERR "format_names(): dir = ", $dir; + #print STDERR "; tag = ", $tag; + print STDERR "; files = ", join(":", @files), ".\n"; + } + + foreach my $file (@files) { + if (length($lines[$#lines]) + length($file) > 66) { + $lines[++$#lines] = sprintf($format, "", ""); + } + $lines[$#lines] .= $file . " "; + } + + return @lines; +} + + +sub format_lists { + my $header = shift; + my @lines = @_; + + print STDERR "format_lists(): ", join(":", @lines), "\n" if $cfg::DEBUG; + + my $lastdir = ''; + my $lastsep = ''; + my $tag = ''; + my @files = (); + my @text = (); + foreach my $line (@lines) { + if ($line =~ /.*\/$/) { + push @text, &format_names($lastdir, @files) if $lastdir; + @files = (); + + $lastdir = $line; + $lastdir =~ s,/$,,; + + $tag = ""; # next thing is a tag + } elsif (!$tag) { + $tag = $line; + next if "$header$tag" eq $lastsep; + + $lastsep = $header . $tag; + if ($tag eq 'HEAD') { + push @text, " $header files:"; + } else { + push @text, sprintf(" %-22s (Branch: %s)", + "$header files:", $tag); + } + } else { + push @files, $line; + } + } + push @text, &format_names($lastdir, sort @files); + + return @text; +} + + +sub append_names_to_file { + my $filename = shift; + my $dir = shift; + my $tag = shift; + my @files = @_; + + return unless @files; + + open FILE, ">>$filename" or die "Cannot append to file $filename."; + + print FILE $dir, "\n"; + print FILE $tag, "\n"; + print FILE map { "$_\n" } @files; + close FILE; +} + + +# +# Use cvs status to obtain the current revision number of a given file. +# +sub get_revision_number { + my $file = shift; + + my $rcsfile = ""; + my $revision = ""; + + open(RCS, "-|") || exec $cfg::PROG_CVS, '-Qn', 'status', $file; + while () { + if (/^[ \t]*Repository revision/) { + chomp; + my @revline = split; + $revision = $revline[2]; + $revline[3] =~ m|^$CVSROOT/+(.*),v$|; + $rcsfile = $1; + last; + } + } + close RCS; + + $rcsfile =~ s|/Attic/|/|; # Remove 'Attic/' if present. + return($revision, $rcsfile); +} + + +# +# Return the previous revision number. +# +sub previous_revision { + my $rev = shift; + + $rev =~ /(?:(.*)\.)?([^\.]+)\.([^\.]+)$/; + my ($base, $r1, $r2) = ($1, $2, $3); + + my $prevrev = ""; + if ($r2 == 1) { + $prevrev = $base; + } else { + $prevrev = "$base." if $base; + $prevrev .= "$r1." . ($r2 - 1); + } + return $prevrev; +} + + +# +# Count the number of lines in a given revision of a file. +# +sub count_lines_in_revision { + my $file = shift; # File in repository. + my $rev = shift; # Revision number. + + my $lines = 0; + open(RCS, "-|") || + exec $cfg::PROG_CVS, '-Qn', 'update', '-p', "-r$rev", $file; + while () { + ++$lines; + } + close RCS; + + return $lines; +} + + +# +# Summarise details of the file modifications. +# +sub change_summary_changed { + my $outfile = shift; # File name of output file. + my @filenames = @_; # List of files to check. + + foreach my $file (@filenames) { + next unless $file; + + my $delta = ""; + my ($rev, $rcsfile) = get_revision_number($file); + + if ($rev and $rcsfile) { + open(RCS, "-|") || + exec $cfg::PROG_CVS, '-Qn', 'log', "-r$rev", $file; + while () { + if (/^date:.*lines:\s(.*)$/) { + $delta = $1; + last; + } + } + close RCS; + } + + &append_line($outfile, "$rev,$delta,,$rcsfile"); + } +} + + +# +# Summarise details of added files. +# +sub change_summary_added { + my $outfile = shift; # File name of output file. + my @filenames = @_; # List of files to check. + + foreach my $file (@filenames) { + next unless $file; + + my $delta = ""; + my ($rev, $rcsfile) = get_revision_number($file); + + if ($rev and $rcsfile) { + my $lines = count_lines_in_revision($file, $rev); + $delta = "+$lines -0"; + } + + &append_line($outfile, "$rev,$delta,new,$rcsfile"); + } +} + + +# +# Summarise details of removed files. +# +sub change_summary_removed { + my $outfile = shift; # File name of output file. + my @filenames = @_; # List of files to check. + + foreach my $file (@filenames) { + next unless $file; + + my $delta = ""; + my ($rev, $rcsfile) = get_revision_number($file); + + if ($rev and $rcsfile) { + my $prevrev = previous_revision($rev); + my $lines = count_lines_in_revision($file, $prevrev); + $delta = "+0 -$lines"; + } + + &append_line($outfile, "$rev,$delta,dead,$rcsfile"); + } +} + + +sub build_header { + delete $ENV{'TZ'}; + + my $datestr = `/bin/date +"%Y/%m/%d %H:%M:%S %Z"`; + chomp $datestr; + + my $header = sprintf("%-8s %s", $cfg::COMMITTER, $datestr); + + my @text; + push @text, $header; + push @text, ""; + push @text, " $cfg::MAILBANNER", "" if $cfg::MAILBANNER; + + return @text; +} + + +# !!! Mailing-list and commitlog history file mappings here !!! +# This needs pulling out as a configuration block somewhere so +# that others can easily change it. +sub get_log_name { + my $dir = shift; # Directory name + + + for my $i (0 .. ($#cfg::LOG_FILE_MAP - 1) / 2) { + my $log = $cfg::LOG_FILE_MAP[$i * 2]; + my $pattern = $cfg::LOG_FILE_MAP[$i * 2 + 1]; + + return $log if $dir =~ /$pattern/; + } + + return 'other'; +} + + +sub do_changes_file { + my @text = @_; + + my %unique = (); + my @mailaddrs = &read_logfile($LOGNAMES_FILE); + foreach my $category (@mailaddrs) { + next if ($unique{$category}); + $unique{$category} = 1; + + my $changes = "$CVSROOT/CVSROOT/commitlogs/$category"; + open CHANGES, ">>$changes" + or die "Cannot open $changes.\n"; + print CHANGES map { "$_\n" } @text; + print CHANGES "\n\n\n"; + close CHANGES; + } +} + + +sub mail_notification { + my @text = @_; + +# This is turned off since the To: lines go overboard. +# Also it has bit-rotted since, and can't just be switched on again. +# - but keep it for the time being in case we do something like cvs-stable +# my @mailaddrs = &read_logfile($LOGNAMES_FILE); +# print(MAIL 'To: cvs-committers' . $dom . ", cvs-all" . $dom); +# foreach $line (@mailaddrs) { +# next if ($unique{$line}); +# $unique{$line} = 1; +# next if /^cvs-/; +# print(MAIL ", " . $line . $dom); +# } +# print(MAIL "\n"); + + my @email = (); + + my $to = $cfg::MAILADDRS; + print "Mailing the commit message to '$to'.\n"; + + push @email, "To: $to" if $cfg::ADD_TO_LINE; + + my $subject = 'Subject: Schlepperbande commit:'; + my @subj = &read_logfile($SUBJ_FILE); + my $subjlines = 0; + my $subjwords = 0; # minimum of two "words" per line + LINE: foreach my $line (@subj) { + foreach my $word (split(/ /, $line)) { + if ($subjwords > 2 && + length("$subject $word") > 75) { + if ($subjlines > 2) { + $subject .= " ..."; + } + push @email, $subject; + if ($subjlines > 2) { + $subject = ""; + last LINE; + } + + # rfc822 continuation line + $subject = " "; + $subjwords = 0; + $subjlines++; + } + $subject .= " " . $word; + $subjwords++; + } + } + push @email, $subject if $subject; + + # If required add a header to the mail msg showing + # which branches were modified during the commit. + if ($cfg::MAIL_BRANCH_HDR) { + my %tags = map { $_ => 1 } &read_logfile($TAGS_FILE); + if (keys %tags) { + push @email, $cfg::MAIL_BRANCH_HDR . ": " . + join(",", sort keys %tags); + } + } + + push @email, ""; + push @email, @text; + + # Transform the email message? + if (defined($cfg::MAIL_TRANSFORM) && $cfg::MAIL_TRANSFORM) { + die 'log_accum.pl: $cfg::MAIL_TRANSFORM isn\'t a sub!' + unless ref($cfg::MAIL_TRANSFORM) eq "CODE"; + + if ($cfg::DEBUG) { + print "Email transform.\n"; + print map { "Before: $_\n" } @email; + } + + @email = &$cfg::MAIL_TRANSFORM(@email); + + print map { "After: $_\n" } @email if $cfg::DEBUG; + } + + # Send the email. + open MAIL, "| $cfg::MAILCMD $to" + or die "Please check $cfg::MAILCMD."; + print MAIL map { "$_\n" } @email; + close MAIL; +} + + +# Return the length of the longest value in the list. +sub longest_value { + my @values = @_; + + my @sorted = sort { $b <=> $a } map { length $_ } @values; + return $sorted[0]; +} + + +sub format_summaries { + my @filenames = @_; + + my @revs; + my @deltas; + my @files; + my @statuses; + + # Parse the summary file. + foreach my $filename (@filenames) { + open FILE, $filename or next; + while () { + chomp; + my ($r, $d, $s, $f) = split(/,/, $_, 4); + push @revs, $r; + push @deltas, $d; + push @statuses, $s; + push @files, $f; + } + close FILE; + } + + # Format the output, extra spaces after "Changes" + # to match historic formatting. + my $r_max = longest_value("Revision", @revs) + 2; + my $d_max = longest_value("Changes ", @deltas) + 2; + + my @text; + my $fmt = "%-" . $r_max . "s%-" . $d_max . "s%s"; + push @text, sprintf $fmt, "Revision", "Changes", "Path"; + + my @order = sort { $files[$a] cmp $files[$b] } (0 .. $#revs); + foreach (@order) { + my $file = $files[$_]; + my $status = $statuses[$_]; + $file .= " ($status)" if $status; + push @text, sprintf $fmt, $revs[$_], $deltas[$_], $file; + } + + return @text; +} + + +# +# Make a diff of the changes. +# +sub do_diff { + my $outfile = shift; + my @filenames = @_; # List of files to check. + + foreach my $file (@filenames) { + next unless $file; + + my $diff; + + my ($rev, $rcsfile) = get_revision_number($file); + + # + # If this is a binary file, don't try to report a diff; + # not only is it meaningless, but it also screws up some + # mailers. We rely on Perl's 'is this binary' algorithm; + # it's pretty good. But not perfect. + # + if (($file =~ /\.(?:pdf|gif|jpg|tar|tgz|gz)$/i) or (-B $file)) { + $diff .= "Index: $file\n"; + $diff .= "=" x 67 . "\n"; + $diff .= "\t<>\n"; + } else { + # + # Get the differences between this and the previous + # revision, being aware that new files always have + # revision '1.1' and new branches always end in '.n.1'. + # + if ($rev =~ /^(.*)\.([0-9]+)$/) { + my $prev_rev = previous_revision($rev); + + my @args = (); + if ($rev eq '1.1') { + $diff .= "Index: $file\n" + . "=" x 68 . "\n"; + @args = ('-Qn', 'update', '-p', + '-r1.1', $file); + } else { + @args = ('-Qn', 'diff', '-u', + "-r$prev_rev", "-r$rev", $file); + } + + print "Generating diff: $cfg::PROG_CVS @args\n" + if $cfg::DEBUG; + open(DIFF, "-|") || exec $cfg::PROG_CVS, @args; + while() { + $diff .= $_; + } + close DIFF; + } + } + + my $diff_length = length($diff); + if ($diff_length > $cfg::MAX_DIFF_SIZE * 1024) { + $diff = "File/diff for $file is too large (" . + $diff_length . " bytes > " . + $cfg::MAX_DIFF_SIZE * 1024 . " bytes)!\n"; + } + #&append_line($outfile, "$diff"); + } +} + +############################################################# +# +# Main Body +# +############################################################ + +# +# Setup environment +# +umask (002); + +# +# Initialize basic variables +# +my $input_params = $ARGV[0]; +my ($directory, @filenames) = split " ", $input_params; +#@files = split(' ', $input_params); + +my @path = split('/', $directory); +my $dir; +if ($#path == 0) { + $dir = "."; +} else { + $dir = join('/', @path[1..$#path]); +} +$dir = $dir . "/"; + +# +# Throw some values at the developer if in debug mode +# +if ($cfg::DEBUG) { + print "ARGV - ", join(":", @ARGV), "\n"; + print "directory - ", $directory, "\n"; + print "filenames - ", join(":", @filenames), "\n"; + print "path - ", join(":", @path), "\n"; + print "dir - ", $dir, "\n"; + print "pid - ", $cfg::PID, "\n"; +} + +# Was used for To: lines, still used for commitlogs naming. +&append_line($LOGNAMES_FILE, &get_log_name("$directory/")); +&append_line($SUBJ_FILE, "$directory " . join(" ", sort @filenames)); + +# +# Check for a new directory first. This will always appear as a +# single item in the argument list, and an empty log message. +# +if ($input_params =~ /New directory/) { + my @text = &build_header(); + + push @text, " $input_params"; + &do_changes_file(@text); + &mail_notification(@text) if $cfg::MAIL_ON_DIR_CREATION; + &cleanup_tmpfiles(); + exit 0; +} + +# +# Check for an import command. This will always appear as a +# single item in the argument list, and a log message. +# +if ($input_params =~ /Imported sources/) { + my @text = &build_header(); + my $vendor_tag; + + push @text, " $input_params"; + + while () { + chomp; + push @text, " $_"; + + $vendor_tag = $1 if /Vendor Tag:\s*(\S*)/; + } + &append_line($TAGS_FILE, $vendor_tag) if $vendor_tag; + + &do_changes_file(@text); + &mail_notification(@text); + #system("/usr/local/bin/awake", $directory); + &cleanup_tmpfiles(); + exit 0; +} + +# +# Iterate over the body of the message collecting information. +# +my %added_files; # Hashes containing lists of files +my %changed_files; # that have been changed, keyed +my %removed_files; # by branch tag. + +my @log_lines; # The lines of the log message. + +my $tag = "HEAD"; # Default branch is HEAD. +my $state = $STATE_NONE; # Initially in no state. + +while () { + s/[ \t\n]+$//; # delete trailing space + + # parse the revision tag if it exists. + if (/^Revision\/Branch:(.*)/) { $tag = $1; next; } + if (/^[ \t]+Tag: (.*)/) { $tag = $1; next; } + if (/^[ \t]+No tag$/) { $tag = "HEAD"; next; } + + # check for a state change, guarding against similar markers + # in the log message itself. + unless ($state == $STATE_LOG) { + if (/^Modified Files/) { $state = $STATE_CHANGED; next; } + if (/^Added Files/) { $state = $STATE_ADDED; next; } + if (/^Removed Files/) { $state = $STATE_REMOVED; next; } + if (/^Log Message/) { $state = $STATE_LOG; next; } + } + + # don't so anything if we're not in a state. + next if $state == $STATE_NONE; + + # collect the log line (ignoring empty template entries)? + if ($state == $STATE_LOG) { + next if /^(.*):$/ and $cfg::TEMPLATE_HEADERS{$1}; + + push @log_lines, $_; + } + + # otherwise collect information about which files changed. + my @files = split; + push @{ $changed_files{$tag} }, @files if $state == $STATE_CHANGED; + push @{ $added_files{$tag} }, @files if $state == $STATE_ADDED; + push @{ $removed_files{$tag} }, @files if $state == $STATE_REMOVED; +} +&append_line($TAGS_FILE, $tag); + +# +# Strip leading and trailing blank lines from the log message. +# Compress multiple blank lines in the body of the message down to a +# single blank line. +# Convert tabs to spaces, so that when we indent the email message and +# log file everything still lines up. +# (Note, this only does the mail and changes log, not the rcs log). +# +my $log_message = join "\n", @log_lines; +$log_message =~ s/\n{3,}/\n\n/g; +$log_message =~ s/^\n+//; +$log_message =~ s/\n+$//; +@log_lines = expand(split /\n/, $log_message); + + +# +# Find the log file that matches this log message +# +my $message_index; # The index of this log message +for ($message_index = 0; ; $message_index++) { + last unless -e "$LOG_FILE.$message_index"; + + my @text = &read_logfile("$LOG_FILE.$message_index"); + last unless @text; + last if "@log_lines" eq "@text"; +} + +# +# Spit out the information gathered in this pass. +# +foreach my $tag ( keys %added_files ) { + &append_names_to_file("$ADDED_FILE.$message_index", $dir, $tag, + @{ $added_files{$tag} }); +} +foreach my $tag ( keys %changed_files ) { + &append_names_to_file("$CHANGED_FILE.$message_index", $dir, $tag, + @{ $changed_files{$tag} }); +} +foreach my $tag ( keys %removed_files ) { + &append_names_to_file("$REMOVED_FILE.$message_index", $dir, $tag, + @{ $removed_files{$tag} }); +} +&write_logfile("$LOG_FILE.$message_index", @log_lines); + +# +# Save the info for the commit summary. +# +foreach my $tag ( keys %added_files ) { + &change_summary_added("$SUMMARY_FILE.$message_index", + @{ $added_files{$tag} }); + &do_diff("$DIFF_FILE.$message_index", @{ $added_files{$tag} }) + if ( $cfg::MAX_DIFF_SIZE > 0 ); +} +foreach my $tag ( keys %changed_files ) { + &change_summary_changed("$SUMMARY_FILE.$message_index", + @{ $changed_files{$tag} }); + &do_diff("$DIFF_FILE.$message_index", @{ $changed_files{$tag} }) + if ( $cfg::MAX_DIFF_SIZE > 0 ); +} +foreach my $tag ( keys %removed_files ) { + &change_summary_removed("$SUMMARY_FILE.$message_index", + @{ $removed_files{$tag} }); +} + +# +# Check whether this is the last directory. If not, quit. +# The last directory name was written by commit_prep.pl on +# the way in. +# +if (-e $LAST_FILE) { + $_ = &read_line($LAST_FILE); + my $tmpfiles = $directory; + $tmpfiles =~ s,([^a-zA-Z0-9_/]),\\$1,g; + unless (grep(/$tmpfiles$/, $_)) { + print "More commits to come...\n"; + exit 0 + } +} + +# +# This is it. The commits are all finished. Lump everything together +# into a single message, fire a copy off to the mailing list, and drop +# it on the end of the Changes file. +# + +# +# Produce the final compilation of the log messages +# +my $diff_num_lines = $cfg::DIFF_BLOCK_TOTAL_LINES; +for (my $i = 0; ; $i++) { + last unless -e "$LOG_FILE.$i"; + + my @log_msg = &build_header(); + + my @mod_lines = &read_logfile("$CHANGED_FILE.$i"); + push @log_msg, &format_lists("Modified", @mod_lines) if @mod_lines; + + my @add_lines = &read_logfile("$ADDED_FILE.$i"); + push @log_msg, &format_lists("Added", @add_lines) if @add_lines; + + my @rem_lines = &read_logfile("$REMOVED_FILE.$i"); + push @log_msg, &format_lists("Removed", @rem_lines) if @rem_lines; + + my @msg_lines = &read_logfile("$LOG_FILE.$i"); + push @log_msg, " Log:", (map { " $_" } @msg_lines) if @msg_lines; + + + if (-e "$SUMMARY_FILE.$i") { + push @log_msg, " ", map {" $_"} + format_summaries("$SUMMARY_FILE.$i"); + } + + # + # Add a copy of the message in the relevant log files. + # + &do_changes_file(@log_msg); + + # + # Add the diff after writing the log files. + # + if (-e "$DIFF_FILE.$i" and $diff_num_lines > 0) { + my @diff_block = read_logfile("$DIFF_FILE.$i"); + + my $lines_to_use = scalar @diff_block; + $lines_to_use = $diff_num_lines + if $lines_to_use > $diff_num_lines; + + push @log_msg, " ", + map {" $_"} @diff_block[0 .. $lines_to_use - 1]; + + $diff_num_lines -= $lines_to_use; + if ($diff_num_lines <= 0) { + push @log_msg, "", + "----------------------------------------------", + "Diff block truncated. (Max lines = " . + $cfg::DIFF_BLOCK_TOTAL_LINES . ")", + "----------------------------------------------", + ""; + } + } + + # + # Mail out the notification. + # + &mail_notification(@log_msg); +} + +#system("/usr/local/bin/awake", $directory); +&cleanup_tmpfiles(); +exit 0; +# EOF diff --git a/CVSROOT/logcheck b/CVSROOT/logcheck new file mode 100755 index 0000000..9ddad06 --- /dev/null +++ b/CVSROOT/logcheck @@ -0,0 +1,137 @@ +#! /usr/bin/perl -w +# +# $FreeBSD: logcheck,v 1.24 2001/12/24 15:00:18 joe Exp $ +# +# This hack is to sanitise the results of what the user may have +# "done" while editing the commit log message.. :-) Peter Wemm. +# +# Note: this uses an enhancement to cvs's verifymsg functionality. +# Normally, the check is advisory only, the FreeBSD version reads +# back the file after the verifymsg file so that this script can +# make changes. +# + +use strict; +use lib $ENV{CVSROOT}; +use CVSROOT::cfg; + + +############################################################# +# +# Main Body +# +############################################################ + +my $filename = shift; +die "Usage: logcheck filename\n" unless $filename; + + + + +# Read the log file in, stripping 'CVS:' lines and removing trailing +# white spaces. +open IN, "< $filename" or + die "logcheck: Cannot open for reading: $filename: $!\n"; +my @log_in = map { s/^(.*?)\s*$/$1/; $1 } grep { !/^CVS:/ } ; +close IN; + +# Remove duplicate blank lines. +my $i = 0; +while ($i < scalar(@log_in) - 1) { + if ($log_in[$i] eq "" && $log_in[$i + 1] eq "") { + splice(@log_in, $i, 1); + next; + } + ++$i; +} + +# Remove leading and trailing blank lines. (There will be at most +# one because of the duplicate removal above). +shift @log_in if $log_in[0] eq ""; +pop @log_in if $log_in[-1] eq ""; + +# Scan through the commit message looking for templated headers +# as defined in the configuration file, and rcstemplate. +# Assume that these only exist in the last paragraph. +# Filter out blank entries, and type check if necessary. +my $j = $#log_in; # The index of the last entry in the commit msg. +my $error = 0; +while ($j >= 0) { + my $logline = $log_in[$j]; + + --$j; + + # Hitting a blank line means that we've seen all of the last paragraph. + last if $logline eq ""; + + unless ($logline =~ /^(.*?):\s*(.*)$/) { + ### XXX + # We're here because we saw a line that didn't match + # a template header (no ':'). This could be a continuation + # line from the previous header, or the log message proper. + # We don't know, so run the risk of checking the last paragraph + # of the log message for headers. + next; + } + + my $header = $1; + my $value = $2; + my $pattern = $cfg::TEMPLATE_HEADERS{$header}; + + # Ignore unrecognised headers. + unless (defined($pattern)) { + ### print "Warning: unknown template header: $header\n"; + next; + } + + # Filter out the template header if it's blank. + if ($value eq "") { + splice(@log_in, $j + 1, 1); + next; + } + + # Type check the header + unless ($value =~ /^$pattern$/) { + print "Error: $header: should match '$pattern'.\n"; + ++$error; + next; + } +} + + +# Make sure that there is some content in the log message. +# XXX Note that logcheck isn't evoked if the log message is +# completely empty. This is a bug in cvs. +my $log = "@log_in"; +die "Log message contains no content!\n" if $log =~ /^\s*$/; + + +# Write the modified log file back out. +my $tmpfile = $filename . "tmp"; +open OUT, "> $tmpfile" or + die "logcheck: Cannot open for writing: $tmpfile: $!\n"; +print OUT map { "$_\n" } @log_in; +close OUT; + + +# Stop the commit if there was a problem with the template headers. +if ($error) { + print "There were $error errors in the template headers.\n"; + print "Please fix the log message and commit again.\n"; + print "A copy of your log message was saved in $tmpfile.\n"; + exit 1; +} + + + +# Nuke likely editor backups. +unlink "$filename.~"; +unlink "$filename.bak"; + + +# Overwrite the log message with our sanitised one. (See the comment +# block at the top of this script for an explaination of why.) +rename($tmpfile, $filename) or + die "logcheck: Could not rename $tmpfile to $filename: $!"; + +exit 0; diff --git a/CVSROOT/loginfo b/CVSROOT/loginfo index 5a59f0a..ba05922 100644 --- a/CVSROOT/loginfo +++ b/CVSROOT/loginfo @@ -24,3 +24,4 @@ #DEFAULT (echo ""; id; echo %s; date; cat) >> $CVSROOT/CVSROOT/commitlog # or #DEFAULT (echo ""; id; echo %{sVv}; date; cat) >> $CVSROOT/CVSROOT/commitlog +DEFAULT $CVSROOT/CVSROOT/log_accum.pl %s diff --git a/CVSROOT/options b/CVSROOT/options new file mode 100644 index 0000000..b893d2c --- /dev/null +++ b/CVSROOT/options @@ -0,0 +1,2 @@ +tag=Schlepperbande=CVSHeader +tagexpand=iSchlepperbande diff --git a/CVSROOT/rcstemplate b/CVSROOT/rcstemplate new file mode 100644 index 0000000..50abb1f --- /dev/null +++ b/CVSROOT/rcstemplate @@ -0,0 +1,15 @@ + +PR: +Submitted by: +Reviewed by: +Approved by: +Obtained from: +MFC after: +CVS: ---------------------------------------------------------------------- +CVS: PR: Fill this in if a GNATS PR is affected by the change. +CVS: Submitted by: Fill this in if someone else sent in the change. +CVS: Reviewed by: Fill this in if someone else reviewed your modification. +CVS: Approved by: Fill this in if you needed approval for this commit. +CVS: Obtained from: Fill this in if the change is from third party software. +CVS: MFC after: N [day[s]|week[s]|month[s]] +CVS: Fill in to get MFC notification later. (days assumed unless specified) diff --git a/CVSROOT/tagcheck b/CVSROOT/tagcheck new file mode 100755 index 0000000..bba101c --- /dev/null +++ b/CVSROOT/tagcheck @@ -0,0 +1,31 @@ +#! /bin/sh +# +# $FreeBSD: tagcheck,v 1.10 2003/01/10 03:05:43 kris Exp $ + +# TAG add/mov/del repo files... +# $1 $2 $3 $4 ... + +case "$1" in + RELENG*) + ;; + RELEASE*) + ;; + *) + exit 0 # not reserved, ok. + ;; +esac + +USER=`/usr/bin/id -un` +case "$USER" in + peter | jdp | markm | obrien | murray | jhb | bmah | scottl | kris | nik) + exit 0 # ok + ;; + *) + echo "$USER does not have permission to perform this tag operation!" 1>&2 + echo "RELENG* tag operations are reserved for release engineering!" 1>&2 + echo "RELEASE* tag operations are reserved for portmgr and doceng!" 1>&2 + echo "Use 'cvs add' or 'cvs rm' to add/remove files from branches!" 1>&2 + echo "$*" | /usr/bin/mail -s "NCVS TAG" cvs + exit 1 + ;; +esac diff --git a/CVSROOT/unwrap b/CVSROOT/unwrap new file mode 100755 index 0000000..d71d08c --- /dev/null +++ b/CVSROOT/unwrap @@ -0,0 +1,22 @@ +#! /bin/sh +# +# $Id$ +# $FreeBSD: unwrap,v 1.3 1999/08/27 22:46:57 peter Exp $ +# +# unwrap - extract the combined package (created with wrap) + +# move the file to a new name with an extension +rm -rf $1.cvswrap +mv $1 $1.cvswrap + +# untar the file + +if `gzip -t $1.cvswrap > /dev/null 2>&1` +then + zcat -d $1.cvswrap | tar --preserve --sparse -x -f - +else + tar --preserve -x -f $1.cvswrap +fi + +# remove the original +rm -rf $1.cvswrap diff --git a/CVSROOT/wrap b/CVSROOT/wrap new file mode 100755 index 0000000..6a28399 --- /dev/null +++ b/CVSROOT/wrap @@ -0,0 +1,22 @@ +#! /bin/sh +# +# $Id$ +# $FreeBSD: wrap,v 1.3 1999/08/27 22:46:57 peter Exp $ +# +# wrap - Combine a directory into a single tar package. + +# This script is always called with the current directory set to +# where the file to be combined exists. but i may get called with a +# path to where cvs first started executing. (this probably should be +# fixed in cvs) so strip out all of the directory information. The +# first sed expression will only work if the path has a leading / +# if it doesn't the one in the if statement will work. +DIRNAME=`echo $1 | sed -e "s|/.*/||g"` +if [ ! -d $DIRNAME ] ; then + DIRNAME=`echo $1 | sed -e "s|.*/||g"` +fi +# +# Now tar up the directory but we now will only get a relative path +# even if the user did a cvs commit . at the top. +# +tar --preserve -cf - $DIRNAME | gzip --no-name --best -c > $2