mirror of
git://git.gnupg.org/gnupg.git
synced 2024-10-31 20:08:43 +01:00
596 lines
15 KiB
Perl
Executable File
596 lines
15 KiB
Perl
Executable File
#! /usr/bin/perl
|
|
# -*-Perl-*-
|
|
#
|
|
# Perl filter to handle the log messages from the checkin of files in
|
|
# a directory. This script will group the lists of files by log
|
|
# message, and mail a single consolidated log message at the end of
|
|
# the commit.
|
|
#
|
|
# This file assumes a pre-commit checking program that leaves the
|
|
# names of the first and last commit directories in a temporary file.
|
|
#
|
|
# Contributed by David Hampton <hampton@cisco.com>
|
|
#
|
|
# hacked greatly by Greg A. Woods <woods@planix.com>
|
|
#
|
|
# Modified by werner.koch@guug.de to add support for
|
|
# automagically extraction of ChangeLog entries 1998-12-29
|
|
|
|
# Usage: log_accum.pl [-d] [-s] [-M module] [[-m mailto] ...] [[-R replyto] ...] [-f logfile]
|
|
# -d - turn on debugging
|
|
# -m mailto - send mail to "mailto" (multiple)
|
|
# -R replyto - set the "Reply-To:" to "replyto" (multiple)
|
|
# -M modulename - set module name to "modulename"
|
|
# -f logfile - write commit messages to logfile too
|
|
# -s - *don't* run "cvs status -v" for each file
|
|
# -w - show working directory with log message
|
|
|
|
#
|
|
# Configurable options
|
|
#
|
|
|
|
# set this to something that takes a whole message on stdin
|
|
$MAILER = "/usr/lib/sendmail -t";
|
|
|
|
#
|
|
# End user configurable options.
|
|
#
|
|
|
|
# Constants (don't change these!)
|
|
#
|
|
$STATE_NONE = 0;
|
|
$STATE_CHANGED = 1;
|
|
$STATE_ADDED = 2;
|
|
$STATE_REMOVED = 3;
|
|
$STATE_LOG = 4;
|
|
|
|
$LAST_FILE = "/tmp/#cvs.lastdir";
|
|
|
|
$CHANGED_FILE = "/tmp/#cvs.files.changed";
|
|
$ADDED_FILE = "/tmp/#cvs.files.added";
|
|
$REMOVED_FILE = "/tmp/#cvs.files.removed";
|
|
$LOG_FILE = "/tmp/#cvs.files.log";
|
|
|
|
$FILE_PREFIX = "#cvs.files";
|
|
|
|
#
|
|
# Subroutines
|
|
#
|
|
|
|
sub cleanup_tmpfiles {
|
|
local($wd, @files);
|
|
|
|
$wd = `pwd`;
|
|
chdir("/tmp") || die("Can't chdir('/tmp')\n");
|
|
opendir(DIR, ".");
|
|
push(@files, grep(/^$FILE_PREFIX\..*\.$id$/, readdir(DIR)));
|
|
closedir(DIR);
|
|
foreach (@files) {
|
|
unlink $_;
|
|
}
|
|
unlink $LAST_FILE . "." . $id;
|
|
|
|
chdir($wd);
|
|
}
|
|
|
|
sub write_logfile {
|
|
local($filename, @lines) = @_;
|
|
|
|
open(FILE, ">$filename") || die("Cannot open log file $filename.\n");
|
|
print FILE join("\n", @lines), "\n";
|
|
close(FILE);
|
|
}
|
|
|
|
sub append_to_logfile {
|
|
local($filename, @lines) = @_;
|
|
|
|
open(FILE, ">$filename") || die("Cannot open log file $filename.\n");
|
|
print FILE join("\n", @lines), "\n";
|
|
close(FILE);
|
|
}
|
|
|
|
sub format_names {
|
|
local($dir, @files) = @_;
|
|
local(@lines);
|
|
|
|
$format = "\t%-" . sprintf("%d", length($dir)) . "s%s ";
|
|
|
|
$lines[0] = sprintf($format, $dir, ":");
|
|
|
|
if ($debug) {
|
|
print STDERR "format_names(): dir = ", $dir, "; files = ", join(":", @files), ".\n";
|
|
}
|
|
foreach $file (@files) {
|
|
if (length($lines[$#lines]) + length($file) > 65) {
|
|
$lines[++$#lines] = sprintf($format, " ", " ");
|
|
}
|
|
$lines[$#lines] .= $file . " ";
|
|
}
|
|
|
|
@lines;
|
|
}
|
|
|
|
sub format_lists {
|
|
local(@lines) = @_;
|
|
local(@text, @files, $lastdir);
|
|
|
|
if ($debug) {
|
|
print STDERR "format_lists(): ", join(":", @lines), "\n";
|
|
}
|
|
@text = ();
|
|
@files = ();
|
|
$lastdir = shift @lines; # first thing is always a directory
|
|
if ($lastdir !~ /.*\/$/) {
|
|
die("Damn, $lastdir doesn't look like a directory!\n");
|
|
}
|
|
foreach $line (@lines) {
|
|
if ($line =~ /.*\/$/) {
|
|
push(@text, &format_names($lastdir, @files));
|
|
$lastdir = $line;
|
|
@files = ();
|
|
} else {
|
|
push(@files, $line);
|
|
}
|
|
}
|
|
push(@text, &format_names($lastdir, @files));
|
|
|
|
@text;
|
|
}
|
|
|
|
sub append_names_to_file {
|
|
local($filename, $dir, @files) = @_;
|
|
|
|
if (@files) {
|
|
open(FILE, ">>$filename") || die("Cannot open file $filename.\n");
|
|
print FILE $dir, "\n";
|
|
print FILE join("\n", @files), "\n";
|
|
close(FILE);
|
|
}
|
|
}
|
|
|
|
sub read_line {
|
|
local($line);
|
|
local($filename) = @_;
|
|
|
|
open(FILE, "<$filename") || die("Cannot open file $filename.\n");
|
|
$line = <FILE>;
|
|
close(FILE);
|
|
chop($line);
|
|
$line;
|
|
}
|
|
|
|
sub read_logfile {
|
|
local(@text);
|
|
local($filename, $leader) = @_;
|
|
|
|
open(FILE, "<$filename");
|
|
while (<FILE>) {
|
|
chop;
|
|
push(@text, $leader.$_);
|
|
}
|
|
close(FILE);
|
|
@text;
|
|
}
|
|
|
|
sub build_header {
|
|
local($header);
|
|
local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
|
|
$header = sprintf("CVSROOT:\t%s\nModule name:\t%s\nRepository:\t%s\nChanges by:\t%s@%s\t%02d/%02d/%02d %02d:%02d:%02d",
|
|
$cvsroot,
|
|
$modulename,
|
|
$dir,
|
|
$login, $hostdomain,
|
|
$year%100, $mon+1, $mday,
|
|
$hour, $min, $sec);
|
|
}
|
|
|
|
sub mail_notification {
|
|
local(@text) = @_;
|
|
|
|
# if only we had strftime()... stuff stolen from perl's ctime.pl:
|
|
local($[) = 0;
|
|
|
|
@DoW = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
|
|
@MoY = ('Jan','Feb','Mar','Apr','May','Jun',
|
|
'Jul','Aug','Sep','Oct','Nov','Dec');
|
|
|
|
# Determine what time zone is in effect.
|
|
# Use GMT if TZ is defined as null, local time if TZ undefined.
|
|
# There's no portable way to find the system default timezone.
|
|
#
|
|
$TZ = defined($ENV{'TZ'}) ? ( $ENV{'TZ'} ? $ENV{'TZ'} : 'GMT' ) : '';
|
|
|
|
# Hack to deal with 'PST8PDT' format of TZ
|
|
# Note that this can't deal with all the esoteric forms, but it
|
|
# does recognize the most common: [:]STDoff[DST[off][,rule]]
|
|
#
|
|
if ($TZ =~ /^([^:\d+\-,]{3,})([+-]?\d{1,2}(:\d{1,2}){0,2})([^\d+\-,]{3,})?/) {
|
|
$TZ = $isdst ? $4 : $1;
|
|
$tzoff = sprintf("%05d", -($2) * 100);
|
|
}
|
|
|
|
# perl-4.036 doesn't have the $zone or $gmtoff...
|
|
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst, $zone, $gmtoff) =
|
|
($TZ eq 'GMT') ? gmtime(time) : localtime(time);
|
|
|
|
$year += ($year < 70) ? 2000 : 1900;
|
|
|
|
if ($gmtoff != 0) {
|
|
$tzoff = sprintf("%05d", ($gmtoff / 60) * 100);
|
|
}
|
|
if ($zone ne '') {
|
|
$TZ = $zone;
|
|
}
|
|
|
|
# ok, let's try....
|
|
$rfc822date = sprintf("%s, %2d %s %4d %2d:%02d:%02d %s (%s)",
|
|
$DoW[$wday], $mday, $MoY[$mon], $year,
|
|
$hour, $min, $sec, $tzoff, $TZ);
|
|
|
|
open(MAIL, "| $MAILER");
|
|
print MAIL "Date: " . $rfc822date . "\n";
|
|
print MAIL "Subject: CVS Update: " . $modulename . "\n";
|
|
print MAIL "To: " . $mailto . "\n";
|
|
print MAIL "Reply-To: " . $replyto . "\n";
|
|
print MAIL "\n";
|
|
print MAIL join("\n", @text), "\n";
|
|
close(MAIL);
|
|
}
|
|
|
|
sub write_commitlog {
|
|
local($logfile, @text) = @_;
|
|
|
|
open(FILE, ">>$logfile");
|
|
print FILE join("\n", @text), "\n";
|
|
close(FILE);
|
|
}
|
|
|
|
#
|
|
# Main Body
|
|
#
|
|
|
|
# Initialize basic variables
|
|
#
|
|
$debug = 0;
|
|
$id = getpgrp(); # note, you *must* use a shell which does setpgrp()
|
|
$state = $STATE_NONE;
|
|
$login = getlogin || (getpwuid($<))[0] || "nobody";
|
|
chop($hostname = `hostname`);
|
|
chop($domainname = `domainname`);
|
|
if ($domainname !~ '^\..*') {
|
|
$domainname = '.' . $domainname;
|
|
}
|
|
$hostdomain = $hostname . $domainname;
|
|
$cvsroot = $ENV{'CVSROOT'};
|
|
$do_status = 1; # moderately useful
|
|
$show_wd = 0; # useless in client/server
|
|
$modulename = "";
|
|
|
|
# parse command line arguments (file list is seen as one arg)
|
|
#
|
|
while (@ARGV) {
|
|
$arg = shift @ARGV;
|
|
|
|
if ($arg eq '-d') {
|
|
$debug = 1;
|
|
print STDERR "Debug turned on...\n";
|
|
} elsif ($arg eq '-m') {
|
|
if ($mailto eq '') {
|
|
$mailto = shift @ARGV;
|
|
} else {
|
|
$mailto = $mailto . ", " . shift @ARGV;
|
|
}
|
|
} elsif ($arg eq '-R') {
|
|
if ($replyto eq '') {
|
|
$replyto = shift @ARGV;
|
|
} else {
|
|
$replyto = $replyto . ", " . shift @ARGV;
|
|
}
|
|
} elsif ($arg eq '-M') {
|
|
$modulename = shift @ARGV;
|
|
} elsif ($arg eq '-s') {
|
|
$do_status = 0;
|
|
} elsif ($arg eq '-w') {
|
|
$show_wd = 1;
|
|
} elsif ($arg eq '-f') {
|
|
($commitlog) && die("Too many '-f' args\n");
|
|
$commitlog = shift @ARGV;
|
|
} else {
|
|
($donefiles) && die("Too many arguments! Check usage.\n");
|
|
$donefiles = 1;
|
|
@files = split(/ /, $arg);
|
|
}
|
|
}
|
|
($mailto) || die("No mail recipient specified (use -m)\n");
|
|
if ($replyto eq '') {
|
|
$replyto = $login;
|
|
}
|
|
|
|
# for now, the first "file" is the repository directory being committed,
|
|
# relative to the $CVSROOT location
|
|
#
|
|
@path = split('/', $files[0]);
|
|
|
|
# XXX There are some ugly assumptions in here about module names and
|
|
# XXX directories relative to the $CVSROOT location -- really should
|
|
# XXX read $CVSROOT/CVSROOT/modules, but that's not so easy to do, since
|
|
# XXX we have to parse it backwards.
|
|
# XXX
|
|
# XXX Fortunately it's relatively easy for the user to specify the
|
|
# XXX module name as appropriate with a '-M' via the directory
|
|
# XXX matching in loginfo.
|
|
#
|
|
if ($modulename eq "") {
|
|
$modulename = $path[0]; # I.e. the module name == top-level dir
|
|
}
|
|
if ($#path == 0) {
|
|
$dir = ".";
|
|
} else {
|
|
$dir = join('/', @path);
|
|
}
|
|
$dir = $dir . "/";
|
|
|
|
if ($debug) {
|
|
print STDERR "module - ", $modulename, "\n";
|
|
print STDERR "dir - ", $dir, "\n";
|
|
print STDERR "path - ", join(":", @path), "\n";
|
|
print STDERR "files - ", join(":", @files), "\n";
|
|
print STDERR "id - ", $id, "\n";
|
|
}
|
|
|
|
# Check for a new directory first. This appears with files set as follows:
|
|
#
|
|
# files[0] - "path/name/newdir"
|
|
# files[1] - "-"
|
|
# files[2] - "New"
|
|
# files[3] - "directory"
|
|
#
|
|
if ($files[2] =~ /New/ && $files[3] =~ /directory/) {
|
|
local(@text);
|
|
|
|
@text = ();
|
|
push(@text, &build_header());
|
|
push(@text, "");
|
|
push(@text, $files[0]);
|
|
push(@text, "");
|
|
|
|
while (<STDIN>) {
|
|
chop; # Drop the newline
|
|
push(@text, $_);
|
|
}
|
|
|
|
&mail_notification($mailto, @text);
|
|
|
|
exit 0;
|
|
}
|
|
|
|
# Check for an import command. This appears with files set as follows:
|
|
#
|
|
# files[0] - "path/name"
|
|
# files[1] - "-"
|
|
# files[2] - "Imported"
|
|
# files[3] - "sources"
|
|
#
|
|
if ($files[2] =~ /Imported/ && $files[3] =~ /sources/) {
|
|
local(@text);
|
|
|
|
@text = ();
|
|
push(@text, &build_header());
|
|
push(@text, "");
|
|
push(@text, $files[0]);
|
|
push(@text, "");
|
|
|
|
while (<STDIN>) {
|
|
chop; # Drop the newline
|
|
push(@text, $_);
|
|
}
|
|
|
|
&mail_notification(@text);
|
|
|
|
exit 0;
|
|
}
|
|
|
|
# Iterate over the body of the message collecting information.
|
|
#
|
|
while (<STDIN>) {
|
|
chop; # Drop the newline
|
|
|
|
if (/^In directory/) {
|
|
if ($show_wd) { # useless in client/server mode
|
|
push(@log_lines, $_);
|
|
push(@log_lines, "");
|
|
}
|
|
next;
|
|
}
|
|
|
|
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; }
|
|
|
|
s/^[ \t\n]+//; # delete leading whitespace
|
|
s/[ \t\n]+$//; # delete trailing whitespace
|
|
|
|
if ($state == $STATE_CHANGED) { push(@changed_files, split); }
|
|
if ($state == $STATE_ADDED) { push(@added_files, split); }
|
|
if ($state == $STATE_REMOVED) { push(@removed_files, split); }
|
|
if ($state == $STATE_LOG) {
|
|
if( /^See[ ]ChangeLog:[ ](.*)/ ) {
|
|
$changelog = $1;
|
|
$okay = false;
|
|
open(RCS, "-|") || exec 'cvs', '-Qn', 'update', '-p', 'ChangeLog';
|
|
while (<RCS>) {
|
|
if( /^$changelog .*/ ) {
|
|
$okay = true;
|
|
push(@log_lines, $_);
|
|
}
|
|
elsif( $okay ) {
|
|
last if( /^[A-Z]+.*/ );
|
|
push(@log_lines, $_);
|
|
}
|
|
}
|
|
while (<RCS>) { ; }
|
|
close(RCS);
|
|
}
|
|
else {
|
|
push(@log_lines, $_);
|
|
}
|
|
}
|
|
}
|
|
|
|
# Strip leading and trailing blank lines from the log message. Also
|
|
# compress multiple blank lines in the body of the message down to a
|
|
# single blank line.
|
|
#
|
|
while ($#log_lines > -1) {
|
|
last if ($log_lines[0] ne "");
|
|
shift(@log_lines);
|
|
}
|
|
while ($#log_lines > -1) {
|
|
last if ($log_lines[$#log_lines] ne "");
|
|
pop(@log_lines);
|
|
}
|
|
for ($i = $#log_lines; $i > 0; $i--) {
|
|
if (($log_lines[$i - 1] eq "") && ($log_lines[$i] eq "")) {
|
|
splice(@log_lines, $i, 1);
|
|
}
|
|
}
|
|
|
|
if ($debug) {
|
|
print STDERR "Searching for log file index...";
|
|
}
|
|
# Find an index to a log file that matches this log message
|
|
#
|
|
for ($i = 0; ; $i++) {
|
|
local(@text);
|
|
|
|
last if (! -e "$LOG_FILE.$i.$id"); # the next available one
|
|
@text = &read_logfile("$LOG_FILE.$i.$id", "");
|
|
last if ($#text == -1); # nothing in this file, use it
|
|
last if (join(" ", @log_lines) eq join(" ", @text)); # it's the same log message as another
|
|
}
|
|
if ($debug) {
|
|
print STDERR " found log file at $i.$id, now writing tmp files.\n";
|
|
}
|
|
|
|
# Spit out the information gathered in this pass.
|
|
#
|
|
&append_names_to_file("$CHANGED_FILE.$i.$id", $dir, @changed_files);
|
|
&append_names_to_file("$ADDED_FILE.$i.$id", $dir, @added_files);
|
|
&append_names_to_file("$REMOVED_FILE.$i.$id", $dir, @removed_files);
|
|
&write_logfile("$LOG_FILE.$i.$id", @log_lines);
|
|
|
|
# Check whether this is the last directory. If not, quit.
|
|
#
|
|
if ($debug) {
|
|
print STDERR "Checking current dir against last dir.\n";
|
|
}
|
|
$_ = &read_line("$LAST_FILE.$id");
|
|
|
|
if ($_ ne $cvsroot . "/" . $files[0]) {
|
|
if ($debug) {
|
|
print STDERR sprintf("Current directory %s is not last directory %s.\n", $cvsroot . "/" .$files[0], $_);
|
|
}
|
|
exit 0;
|
|
}
|
|
if ($debug) {
|
|
print STDERR sprintf("Current directory %s is last directory %s -- all commits done.\n", $files[0], $_);
|
|
}
|
|
|
|
#
|
|
# End Of Commits!
|
|
#
|
|
|
|
# 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
|
|
#
|
|
@text = ();
|
|
@status_txt = ();
|
|
push(@text, &build_header());
|
|
push(@text, "");
|
|
|
|
for ($i = 0; ; $i++) {
|
|
last if (! -e "$LOG_FILE.$i.$id"); # we're done them all!
|
|
@lines = &read_logfile("$CHANGED_FILE.$i.$id", "");
|
|
if ($#lines >= 0) {
|
|
push(@text, "Modified files:");
|
|
push(@text, &format_lists(@lines));
|
|
}
|
|
@lines = &read_logfile("$ADDED_FILE.$i.$id", "");
|
|
if ($#lines >= 0) {
|
|
push(@text, "Added files:");
|
|
push(@text, &format_lists(@lines));
|
|
}
|
|
@lines = &read_logfile("$REMOVED_FILE.$i.$id", "");
|
|
if ($#lines >= 0) {
|
|
push(@text, "Removed files:");
|
|
push(@text, &format_lists(@lines));
|
|
}
|
|
if ($#text >= 0) {
|
|
push(@text, "");
|
|
}
|
|
@lines = &read_logfile("$LOG_FILE.$i.$id", "\t");
|
|
if ($#lines >= 0) {
|
|
push(@text, "Log message:");
|
|
push(@text, @lines);
|
|
push(@text, "");
|
|
}
|
|
if ($do_status) {
|
|
local(@changed_files);
|
|
|
|
@changed_files = ();
|
|
push(@changed_files, &read_logfile("$CHANGED_FILE.$i.$id", ""));
|
|
push(@changed_files, &read_logfile("$ADDED_FILE.$i.$id", ""));
|
|
push(@changed_files, &read_logfile("$REMOVED_FILE.$i.$id", ""));
|
|
|
|
if ($debug) {
|
|
print STDERR "main: pre-sort changed_files = ", join(":", @changed_files), ".\n";
|
|
}
|
|
sort(@changed_files);
|
|
if ($debug) {
|
|
print STDERR "main: post-sort changed_files = ", join(":", @changed_files), ".\n";
|
|
}
|
|
|
|
foreach $dofile (@changed_files) {
|
|
if ($dofile =~ /\/$/) {
|
|
next; # ignore the silly "dir" entries
|
|
}
|
|
if ($debug) {
|
|
print STDERR "main(): doing 'cvs -nQq status -v $dofile'\n";
|
|
}
|
|
open(STATUS, "-|") || exec 'cvs', '-nQq', 'status', '-v', $dofile;
|
|
while (<STATUS>) {
|
|
chop;
|
|
push(@status_txt, $_);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Write to the commitlog file
|
|
#
|
|
if ($commitlog) {
|
|
&write_commitlog($commitlog, @text);
|
|
}
|
|
|
|
if ($#status_txt >= 0) {
|
|
push(@text, @status_txt);
|
|
}
|
|
|
|
# Mailout the notification.
|
|
#
|
|
&mail_notification(@text);
|
|
|
|
# cleanup
|
|
#
|
|
if (! $debug) {
|
|
&cleanup_tmpfiles();
|
|
}
|
|
|
|
exit 0;
|