#!/usr/bin/perl -w # # $FreeBSD$ # # 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("%d/%02d/%02d", $year+1900, $mon+1, $mday); $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); } }