head	1.3;
access;
symbols;
locks; strict;
comment	@# @;


1.3
date	99.10.09.20.05.59;	author jsm;	state dead;
branches;
next	1.2;

1.2
date	99.10.07.22.27.17;	author tromey;	state Exp;
branches;
next	1.1;

1.1
date	99.02.08.15.09.25;	author jsm;	state Exp;
branches;
next	;


desc
@@


1.3
log
@
1999-10-09  Jason Molenda  (jsm@@bugshack.cygnus.com)

	* CVSROOT/auto_checkout, CVSROOT/commit_prep, CVSROOT/log_accum:
	Deleted; generic versions now used for all repositories.

	* CVSROOT/commitinfo, CVSROOT/loginfo:  Change pathnames to
	generic versions.

        * CVSROOT/checkoutlist:  Don't try to check out the removed
        files any longer.
@
text
@#!/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@@web.net>

# Usage: log_accum.pl [-d] [-s] [-M module] [[-m mailto] ...] [-f logfile]
#	-d		- turn on debugging
#       -G database     - interface to Gnats
#	-m mailto	- send mail to "mailto" (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
#       -T text         - use TEXT in temp file names.

#
#	Configurable options
#

# Set this to something that takes "-s"
$MAILER	       = "/usr/unsupported/bin/ucbMail";

# Used with sprintf to form name of Gnats notification mailing list.
# %s argument comse from -G option.
$GNATS_MAIL_FORMAT = "%s-gnats\@@sourceware.cygnus.com";

# Used with sprintf to form name of Gnats root directory.  Potential
# PR info is appended to see if PR actually exists.  %s argument comes
# from -G option.
$GNATS_ROOT_FORMAT = "/egcs/carton/gnats/%s-db";

# Constants (don't change these!)
#
$STATE_NONE    = 0;
$STATE_CHANGED = 1;
$STATE_ADDED   = 2;
$STATE_REMOVED = 3;
$STATE_LOG     = 4;

#
#	Subroutines
#

sub set_temp_vars {
    local ($name) = @@_;

    $LAST_FILE     = sprintf ("/tmp/#%s.lastdir", $name);

    $CHANGED_FILE  = sprintf ("/tmp/#%s.files.changed", $name);
    $ADDED_FILE    = sprintf ("/tmp/#%s.files.added", $name);
    $REMOVED_FILE  = sprintf ("/tmp/#%s.files.removed", $name);
    $LOG_FILE      = sprintf ("/tmp/#%s.files.log", $name);

    $FILE_PREFIX   = sprintf ("#%s.files", $name);
}

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 format_names {
    local($dir, @@files) = @@_;
    local(@@lines);

    if ($dir =~ /^\.\//) {
	$dir = $';
    }
    if ($dir =~ /\/$/) {
	$dir = $`;
    }
    if ($dir eq "") {
	$dir = ".";
    }

    $format = "\t%-" . sprintf("%d", length($dir) > 15 ? length($dir) : 15) . "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 accum_subject {
    local(@@lines) = @@_;
    local(@@files, $lastdir);

    $lastdir = shift @@lines;	# first thing is always a directory
    @@files = ($lastdir);
    if ($lastdir !~ /.*\/$/) {
	die("Damn, $lastdir doesn't look like a directory!\n");
    }
    foreach $line (@@lines) {
	if ($line =~ /.*\/$/) {
	    $lastdir = $line;
	    push(@@files, $line);
	} else {
	    push(@@files, $lastdir . $line);
	}
    }

    @@files;
}

sub compile_subject {
    local(@@files) = @@_;
    local($text, @@a, @@b, @@c, $dir, $topdir);

    # find the highest common directory
    $dir = '-';
    do {
	$topdir = $dir;
	foreach $file (@@files) {
	    if ($file =~ /.*\/$/) {
		if ($dir eq '-') {
		    $dir = $file;
		} else {
		    if (index($dir,$file) == 0) {
			$dir = $file;
		    } elsif (index($file,$dir) != 0) {
			@@a = split /\//,$file;
			@@b = split /\//,$dir;
			@@c = ();
			CMP: while ($#a > 0 && $#b > 0) {
			    if ($a[0] eq $b[0]) {
				push(@@c, $a[0]);
				shift @@a;
				shift @@b;
			    } else {
				last CMP;
			    }
			}
			$dir = join('/',@@c) . '/';
		    }
		}
	    }
	}
    } until $dir eq $topdir;

    # strip out directories and the common prefix topdir.
    chop $topdir;
    @@c = ($modulename . '/' . $topdir);
    foreach $file (@@files) {
	if (!($file =~ /.*\/$/)) {
	    push(@@c, substr($file, length($topdir)+1));
	}
    }

    # put it together and limit the length.
    $text = join(' ',@@c);
    if (length($text) > 50) {
	$text = substr($text, 0, 46) . ' ...';
    }

    $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) = localtime(time);
    $header = sprintf("CVSROOT:\t%s\nModule name:\t%s\n",
		      $cvsroot,
		      $modulename);
    if (defined($branch)) {
	$header .= sprintf("Branch: \t%s\n",
		      $branch);
    }
    $header .= sprintf("Changes by:\t%s@@%s\t%02d/%02d/%02d %02d:%02d:%02d",
		      $login, $hostdomain,
		      $year%100, $mon+1, $mday,
		      $hour, $min, $sec);
}

sub mail_notification {
    local($name, $subject, @@text) = @@_;
    open(MAIL, "| $MAILER -s \"$subject\" $name");
    print MAIL join("\n", @@text), "\n";
    close(MAIL);
}

sub write_commitlog {
    local($logfile, @@text) = @@_;

    open(FILE, ">>$logfile");
    print FILE join("\n", @@text), "\n\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 = $ENV{'USER'} || (getpwuid($<))[0] || "nobody";
chop($hostname = `hostname`);
if ($hostname !~ /\./) {
    chop($domainname = `domainname`);
    $hostdomain = $hostname . "." . $domainname;
} else {
    $hostdomain = $hostname;
}
$cvsroot = $ENV{'CVSROOT'};
$do_status = 1;
$modulename = "";
$temp_name = "temp";

# 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') {
	$mailto = "$mailto " . shift @@ARGV;
    } elsif ($arg eq '-M') {
	$modulename = shift @@ARGV;
    } elsif ($arg eq '-s') {
	$do_status = 0;
    } elsif ($arg eq '-f') {
	($commitlog) && die("Too many '-f' args\n");
	$commitlog = shift @@ARGV;
    } elsif ($arg eq '-G') {
	($gnatsdb) && die("Too many '-G' args\n");
	$gnatsdb = shift @@ARGV;
    } elsif ($arg eq '-T') {
	$temp_name = shift @@ARGV;
    } else {
	($donefiles) && die("Too many arguments!  Check usage.\n");
	$donefiles = 1;
	@@files = split(/ /, $arg);
    }
}
($mailto) || die("No -m mail recipient specified\n");
&set_temp_vars ($temp_name);

# 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.
#
if ($modulename eq "") {
    $modulename = $path[0];	# I.e. the module name == top-level dir
}
if ($commitlog ne "") {
    $commitlog = $cvsroot . "/" . $modulename . "/" . $commitlog unless ($commitlog =~ /^\//);
}
if ($#path == 0) {
    $dir = ".";
} else {
    $dir = join('/', @@path[1..$#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, $files[0], @@text);

    if ($commitlog) {
	&write_commitlog($commitlog, @@text);
    }

    exit 0;
}

# Iterate over the body of the message collecting information.
#
while (<STDIN>) {
    chop;			# Drop the newline

    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; }
    if (/^Revision\/Branch/) { /^[^:]+:\s*(.*)/; $branch = $+; 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)     { 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);
    }
}

# 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, "Log message:");
    while ($#log_lines > -1) {
	push (@@text, "    " . $log_lines[0]);
	shift(@@log_lines);
    }

    &mail_notification($mailto, "Import $file[0]", @@text);

    if ($commitlog) {
	&write_commitlog($commitlog, @@text);
    }

    exit 0;
}

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 = ();
@@subject_files = ();
@@log_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));
	push(@@subject_files, &accum_subject(@@lines));
    }
    @@lines = &read_logfile("$ADDED_FILE.$i.$id", "");
    if ($#lines >= 0) {
	push(@@text, "Added files:");
	push(@@text, &format_lists(@@lines));
	push(@@subject_files, &accum_subject(@@lines));
    }
    @@lines = &read_logfile("$REMOVED_FILE.$i.$id", "");
    if ($#lines >= 0) {
	push(@@text, "Removed files:");
	push(@@text, &format_lists(@@lines));
	push(@@subject_files, &accum_subject(@@lines));
    }
    if ($#text >= 0) {
	push(@@text, "");
    }
    @@log_txt = &read_logfile("$LOG_FILE.$i.$id", "\t");
    if ($#log_txt >= 0) {
	push(@@text, "Log message:");
	push(@@text, @@log_txt);
	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 status on $dofile\n";
	    }
	    open(STATUS, "-|") || exec 'cvs', '-n', 'status', '-Qqv', $dofile;
	    while (<STATUS>) {
		chop;
		push(@@status_txt, $_);
	    }
	}
    }
}

$subject_txt = &compile_subject(@@subject_files);

# Write to the commitlog file
#
if ($commitlog) {
    &write_commitlog($commitlog, @@text);
}

if ($#status_txt >= 0) {
    push(@@text, @@status_txt);
}

# Mailout the notification.
#
&mail_notification($mailto, $subject_txt, @@text);

# Send mail to Gnats, if required.
if ($gnatsdb ne '') {
    $log_txt = join ("\n", @@log_txt);
    if ($log_txt =~ m,PR ([a-z.]+/[0-9]+),) {
	$pr = $1;
	$file = sprintf ($GNATS_ROOT_FORMAT, $gnatsdb) . "/" . $pr;
	if (-f $file) {
	    &mail_notification(sprintf ($GNATS_MAIL_FORMAT, $gnatsdb),
			       $pr, $log_txt);
	}
    }
}

# cleanup
#
if (! $debug) {
    &cleanup_tmpfiles();
}

exit 0;
@


1.2
log
@CVS / Gnats integration
@
text
@@


1.1
log
@Standard sourceware CVS setup.
@
text
@d18 1
d23 1
d32 9
d49 3
a51 1
$LAST_FILE     = "/tmp/#javacvs.lastdir";
d53 2
a54 4
$CHANGED_FILE  = "/tmp/#java.files.changed";
$ADDED_FILE    = "/tmp/#java.files.added";
$REMOVED_FILE  = "/tmp/#java.files.removed";
$LOG_FILE      = "/tmp/#java.files.log";
d56 1
a56 1
$FILE_PREFIX   = "#java.files";
d58 7
a64 3
#
#	Subroutines
#
d308 1
d327 5
d339 1
d525 1
d552 2
a553 2
    @@lines = &read_logfile("$LOG_FILE.$i.$id", "\t");
    if ($#lines >= 0) {
d555 1
a555 1
	push(@@text, @@lines);
d605 13
@


