#!/usr/bin/perl
#
# $Id$
#
# Perl filter to handle cvs editinfo preparation of log messge
# This filter should be named in the $CVSROOT/CVSROOT/editinfo file
# for any modules you wish to add the gnats bug system bugid and
# header.
# A line like "DEFAULT /path/to/this/perl/script"
# will do the trick.
# Some tracking information may be logged
$debugging = 0; # used in sub debug for tracking
$logging = 0; # used in sub log for tracking
$debugging = 1 if (defined $ENV{'CVSDEBUGEDIT'});
#########################################################################
#
# derrived information needed below
#
#########################################################################
$login = (getpwuid($<))[0] || "Anonymous"; # get user's login name
#########################################################################
#
# Paths to utilities
#
#########################################################################
$defaultEditorCmd = '/usr/bin/vi'; # use env $EDITOR to override
$editorCmd = ((defined $ENV{'EDITOR'}) ?
$ENV{'EDITOR'} :
((defined $ENV{'EDITOR'}) ?
$ENV{'EDITOR'} : $defaultEditorCmd));
#########################################################################
#
# Constants
#
#########################################################################
$BadTmpDir = "
It is a very bad idea for the environment variable TMPDIR
currently set to \'$ENV{'TMPDIR'}\' to be a relative pathname.
Please choose something with a leading slash (/tmp) for example
\n";
$MissingTmpDir = "
It is a very bad idea for the environment variable TMPDIR
currently set to \'$ENV{'TMPDIR'}\' to be directory which does not
exist. Please create this directory and give it read-write permissions
to group cisco.
\n";
#########################################################################
#
# Inter-filter communication file names
#
#########################################################################
# Do not change these constants without changing them in the other
# scripts as well.
$pgrp = getpgrp(); # parent process id
if (defined $ENV{'TMPDIR'}) {
# trivial sanity check, are we able to write into this directory?
if ($ENV{'TMPDIR'} !~ /^\//) {
&debug("Bad... TMPDIR is not a full pathname");
&abort($BadTmpDir);
}
elsif ( -d $ENV{'TMPDIR'} && -w _ ) {
&debug("Good.. TMPDIR references a directory we can use");
$tmpDir = $ENV{'TMPDIR'};
}
else {
&debug("Bad... TMPDIR is not a directory in which we can write files");
&abort($MissingTmpDir);
}
}
else {
$tmpDir = '/tmp';
if ( -d $tmpDir && -w _ ) {
&debug("Good.. $tmpDir looks like we can use it");
}
else {
&debug("Bad... $tmpDir is not a directory in which we can write files");
&abort("This system is in real trouble $tmpDir is not writable");
}
}
$gnatsReportFile = $tmpDir . '/' . $login . '.cvsgnats' . $pgrp;
$logFile = $tmpDir . '/' . $login . '.cvslog' . $pgrp;
#########################################################################
#
# main
#
#########################################################################
if (! @ARGV) {
&abort( "No message filename was given. Usage: $0 cvs-log-file\n" );
}
$userLogMessageFile = $ARGV[0];
&create_timestamps();
$hostname = `hostname`;
chop($hostname);
if ($logging) {
if (!open(LOGFILE, ">> $logFile")) {
warn "Cannot open log file: $logFile\nturning logging off";
$logging = 0;
}
&log("Start: sys_editinfo attempt by $login at $date");
&log("Command: $0" . join(' ',@ARGV));
}
&debug("Command: $0 \'" . join('\' \'',@ARGV).'\'');
# $gnatsReportFile will already have built by commitinfo
if ( -e $gnatsReportFile ) {
&debug("using existing gnats report: $gnatsReportFile");
# Get bug Id from gnats report
$bugId = '';
$bugBranch = '';
($bugId, $bugBranch, $headline) =
&getBugId_from_gnatsreport( $gnatsReportFile );
$bugBranch =~ s/^-//; # remove leading dash
&log("Bug Id $bugId extracted from $gnatsReportFile");
&log("Headline $headline extracted from $gnatsReportFile");
&log("Branch \'$bugBranch\' extracted from $gnatsReportFile");
# If we have a valid bug id, complete bug file names
if ( $bugId eq '' ) {
warn "Bug Id not found in GNATS report $gnatsReportFile";
}
}
@loglines = &read_file( $userLogMessageFile );
if (($bugId ne '') && ($bugId ne 'sync') && ($bugId ne 'new') &&
($bugId ne 'placeholder')) {
if (! grep(/$bugId/, @loglines)) {
&debug("adding bugid and headline to $userLogMessageFile");
&debug(">>>$bugId: $headline");
push(@loglines, $bugId.': '.$headline);
&write_file( $userLogMessageFile, @loglines );
}
}
if ($bugBranch ne '') {
if (! grep(/^Branch:/, @loglines)) {
&debug("adding Branch information to $userLogMessageFile");
&debug(">>>Branch: $bugBranch");
push(@loglines, 'Branch: '.$bugBranch);
&write_file( $userLogMessageFile, @loglines );
}
}
$doedit = 1;
while ($doedit) {
$doedit = 0;
$diff = 0;
while (&editFile( $editorCmd, $userLogMessageFile )) {
print "A non-zero return code usually means that either something\n";
print "went wrong with your edit session, or you were using vi\n";
print "and executed a bad command. You may wish to edit $userLogMessageFile\n";
print "from some other window before answering this next query.\n";
print "a \'y\' will try the edit command once more and a \'n\'\n";
print "will fall through and proces the log message at once\n";
print "Do you wish me to run the command\n";
$ans = &yes_no("$editorCmd $userLogMessageFile ?", 'n');
last if ($ans eq 'n');
}
@statlines = grep(/^Reviewed by:\s*\S+|^Submitted by:\s*\S+|Obtained from:\s*\S+/, &read_file ( $userLogMessageFile ));
@loglines = grep(!/^CVS:|^Reviewed by:|^Submitted by:|^Obtained from:/, &read_file( $userLogMessageFile ));
$orig = join("\n", @loglines);
$maxlen = 0;
foreach (@loglines) {
$save = $_;
s/[ \t\n]+$//; # delete trailing space
1 while s/\t+/' 'x (length($&) * 8 - length($`) % 8)/e; # expand tabs
s,/\*,/ *,g; # defuse C Comment start
s,\*/,* /,g; # defuse C Comment end
$diff = 1 if ($save ne $_);
$maxlen = length($_) if (length($_) > $maxlen);
}
if ($maxlen > 72) {
&write_file( $userLogMessageFile, @loglines );
open(FMT, "fmt -s $userLogMessageFile |")
|| die "unable to use fmt: $!";
@loglines = <FMT>;
close(FMT);
foreach (@loglines) {
chop;
1 while s/\t+/' 'x (length($&) * 8 - length($`) % 8)/e; # expand tabs
}
}
$leading = 200;
foreach (@loglines) {
next if /^$/;
# notice leading whitespace
($tmp = $_) =~ s/^(\s*).*$/$1/;
$leading = length($tmp) if (length($tmp) < $leading);
}
# nuke $leading blanks
if ($leading > 0) {
&debug("removing $leading leading spaces");
$tmp = ' ' x $leading;
foreach (@loglines) {
s/^$tmp//;
}
}
$new = join("\n",@loglines);
if ($orig ne $new) {
@loglines = ( @statlines, @loglines );
&write_file( $userLogMessageFile, @loglines );
print STDERR "=============== New log text ===============\n";
print STDERR join("\n", @loglines);
print STDERR "\n=============== end log text ===============\n\n";
$ans = &yes_no("Formated log differs from original text. Use it?", 'y');
exit(0) if ($ans eq 'y');
$ans = &yes_no("Do you wish to edit the text?", 'y');
if ($ans eq 'n') {
$ans = &yes_no("Ok to abort?", 'n');
&abort("terminated by user request") if ($ans eq 'y');
}
$doedit = 1;
}
}
@loglines = &read_file( $userLogMessageFile );
if (($bugId ne '') && ($bugId ne 'sync') && ($bugId ne 'new') &&
($bugId ne 'placeholder')) {
if (! grep(/$bugId/, @loglines)) {
print STDERR "\n Please note that removing $bugId\n";
print STDERR " from the log message is not good.\n\n";
}
}
close(LOGFILE) if ($logging);
exit(0);
#########################################################################
#
# Subroutines
#
#########################################################################
sub abort {
local( $abortMsg ) = @_;
die ( "[ERROR] ", "$abortMsg\n" );
}
sub create_timestamps {
($sec,$min,$hour,$mday,$mon,$year) = localtime;
$today = sprintf("%02d/%02d/%02d", $mon+1, $mday, $year);
$nowtime = sprintf("%02d:%02d:%02d", $hour, $min, $sec);
$date = $today . ' ' . $nowtime;
}
sub debug {
print @_, "\n" if ( $debugging );
}
sub editFile {
local( $editor, $filename ) = @_;
local( $return_code );
&debug("editing file: $filename");
print "invoking editor...\n$editor $filename\n";
$return_code = system("$editor $filename");
$return_code = $return_code / 256;
if ($return_code) {
print "$editor returned with an error code of $return_code\n";
}
$return_code;
}
sub log {
print LOGFILE @_, "\n" if ($logging);
}
sub read_file {
local($filename) = @_;
local(@text);
&debug("read file $filename");
open(FILE, "<$filename") || return ();
while (<FILE>) {
chop;
push(@text, $_);
}
close(FILE);
@text;
}
sub write_file {
local($filename, @lines) = @_;
&debug("Writing file $filename");
open(FILE, ">$filename") || die ("Cannot open log file $filename.\n");
foreach (@lines) {
print FILE $_, "\n";
}
close(FILE);
}
sub getBugId_from_gnatsreport {
local( $diffsFile ) = @_;
local( $bug_id, $branch, $headline );
&debug( "Opening file $diffsFile to get Bug Id");
open ( GNATSREPORT, $diffsFile ) ||
&abort( "Can't open gnats report $diffsFile" );
$bug_id = 'NULL';
$branch = 'NULL';
$headline = '';
# Find the Bug Id
while ( <GNATSREPORT> ) {
chop;
if ( /^Start: / ) {
($bug_id = $_) =~ s/[^:]*: //;
&debug( "Bug Id = $bug_id" );
$headline = 'syncronization commit' if ($bug_id eq 'sync');
}
elsif ( /^Headline:/) {
($headline = $_) =~ s/[^:]*://;
&debug( "Headline = $headline" );
}
elsif ( /^Branch: / ) {
($branch = $_) =~ s/[^:]*: //;
if ( $branch eq '' ) {
&debug( "Branch = \'\' (main trunk)" );
}
else {
&debug( "Branch = $branch" );
}
}
last if (($bug_id ne 'NULL') && ($branch ne 'NULL') &&
($headline ne ''));
}
close ( GNATSREPORT );
return ($bug_id, $branch, $headline);
}
# yes_no - ask the user the specified question - repeatedly, until
# they answer with a valid answer (i.e. yes or no). Returns
# "n" or "y".
sub yes_no {
local($ques, $default) = @_;
for (;;) {
print "$ques [$default] ";
$_ = <STDIN>;
chop;
/^[yY]$/ && return "y";
/^[nN]$/ && return "n";
return $default if (!$_ && $default);
}
}