aboutsummaryrefslogblamecommitdiffstats
path: root/CVSROOT/log_accum.pl
blob: 80dd20a847645c12384fd42b1b494cfc41053c4e (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
                  
 
           







                                                                     
                                                 
 
                                                                      
                                                                    


                                                                 
 

                                                     





                                                            





                                              
                      









                                                            
                                           


                                           
                                           
                                        
 
                                                 
 




                                                            
                      



                       
                                                              





                      
                                  
 
                                                                                  

                
                                 
                                                                                 


                            

                          
                                                                              




                   
                  
                      
                                   





                                     

          



                                                                                   
                





                             
                       



















                                                                                                              
                                                   






                                                                 
                  

                                 
                                                             





                                             
                                                 

                                                
                                                                             





                                                 

          










                                                                                     

                                                                         

                                       





















                                                              
                                              
                                    
                     










                                                                         
                         







                                                                           


                            

                  
                             
                      


                                                      
 
                                                               
               
                                                           
   

                                                       
                                                   

                                                       
 
                      






                                                          
                                                   










                                                         

                     
                                         
                      
                   
 
                 
                                                     
                                     
                                   
                                                        

                                                               
 



                                                                          
 
                       
                                                            
 
                 
 
                                            
 
                                                     
 
                                                              
            
                                                                                  
     
                                                      
                                                                          




                                        
                      


























                                                                          
 









                                                             

                   














                                                      
 


                            
          
                                                                               
                              



                                       







                                             
 
                                                           
                                                            




                                                                
                              

                         


                                                               
                               
                               
                        

           
 


                                                            
                              


                         


                                                               








                                                  
                        

           

                                                              
              
                 
                                                       
                               










                              
     


                                                             
    

                                                                          
                               
                                
                                 
                                  
                 
                                  
     




                                                                    
                                                                   







                                            

                                                               














                                                        










                                                             
                                               
 
                 










                                                             
 




                                                          
                                          


                                          





                                                                      
                          





                                                   


                                                           
                                      
















                                                       





                                                                     
                        
 
 
                                                          
                        
 
















                                                                   
 

                            
                          
                    
       
     
#!/usr/bin/perl -w
#
# $FreeBSD$
#
# 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.
#
# Originally by David Hampton <hampton@cisco.com>
#
# Extensively hacked for FreeBSD by Peter Wemm <peter@netplex.com.au>,
#  with parts stolen from Greg Woods <woods@most.wierd.com> version.
#
# Lightly hacked by Mark Murray to allow it to work unmodified
#  on both the master repository (freefall) and the international
#  crypto repository (internat).
#

require 5.003;      # might work with older perl5

use Sys::Hostname;  # get hostname() function

############################################################
#
# Configurable options
#
############################################################
#
# Where do you want the RCS ID and delta info?
# 0 = none,
# 1 = in mail only,
# 2 = rcsids in both mail and logs.
#
$rcsidinfo = 2;

# Debug level, 0 = off
$debug = 0;
############################################################
#
# Constants
#
############################################################
$STATE_NONE    = 0;
$STATE_CHANGED = 1;
$STATE_ADDED   = 2;
$STATE_REMOVED = 3;
$STATE_LOG     = 4;

$FILE_PREFIX   = "#cvs.files";
$LAST_FILE     = "/tmp/#cvs.files.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";
$SUMMARY_FILE  = "/tmp/#cvs.files.summary";
$MAIL_FILE     = "/tmp/#cvs.files.mail";
$SUBJ_FILE     = "/tmp/#cvs.files.subj";

$CVSROOT       = $ENV{'CVSROOT'} || "/home/ncvs";

############################################################
#
# Subroutines
#
############################################################

sub cleanup_tmpfiles {
    local($wd, @files);

    $wd = `pwd`;
    chdir("/tmp");
    opendir(DIR, ".");
    push(@files, grep(/^$FILE_PREFIX\..*$id$/, readdir(DIR)));
    closedir(DIR);
    foreach (@files) {
    unlink $_;
    }
    chdir($wd);
}

sub append_to_logfile {
    local($filename, @files) = @_;

    open(FILE, ">>$filename") || die ("Cannot open for append file $filename.\n");
    print(FILE join("\n", @lines), "\n");
    close(FILE);
}

sub append_line {
    local($filename, $line) = @_;
    open(FILE, ">>$filename") || die("Cannot open for append file $filename.\n");
    print(FILE $line, "\n");
    close(FILE);
}

sub read_line {
    local($line);
    local($filename) = @_;
    open(FILE, "<$filename") || die("Cannot open for read file $filename.\n");
    $line = <FILE>;
    close(FILE);
    chop($line);
    $line;
}

sub read_logfile {
    local(@text) = ();
    local($filename, $leader) = @_;
    open(FILE, "<$filename") and do {
    while (<FILE>) {
        chop;
        push(@text, $leader.$_);
    }
    close(FILE);
    };
    @text;
}

sub write_logfile {
    local($filename, @lines) = @_;

    open(FILE, ">$filename") || die("Cannot open for write log file $filename.\n");
    print FILE join("\n", @lines), "\n";
    close(FILE);
}

sub format_names {
    local($dir, @files) = @_;
    local(@lines, $indent);

    $indent = length($dir);
    if ($indent < 20) {
      $indent = 20;
    }

    $format = "    %-" . sprintf("%d", $indent) . "s ";

    $lines[0] = sprintf($format, $dir);

    if ($debug) {
    print STDERR "format_names(): dir = ", $dir, "; tag = ", $tag, "; files = ", join(":", @files), ".\n";
    }
    foreach $file (@files) {
    if (length($lines[$#lines]) + length($file) > 66) {
        $lines[++$#lines] = sprintf($format, "", "");
    }
    $lines[$#lines] .= $file . " ";
    }

    @lines;
}

sub format_lists {
    local($header, @lines) = @_;
    local(@text, @files, $lastdir, $lastsep, $tag);

    if ($debug) {
    print STDERR "format_lists(): ", join(":", @lines), "\n";
    }
    @text = ();
    @files = ();

    $lastdir = '';
    $lastsep = '';
    foreach $line (@lines) {
    if ($line =~ /.*\/$/) {
        if ($lastdir ne '') {
            push(@text, &format_names($lastdir, @files));
        }
        $lastdir = $line;
        $lastdir =~ s,/$,,;
        $tag = "";  # next thing is a tag
        @files = ();
    } elsif ($tag eq '') {
        $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, @files));

    @text;
}

sub append_names_to_file {
    local($filename, $dir, $tag, @files) = @_;

    if (@files) {
    open(FILE, ">>$filename") || die("Cannot open for append file $filename.\n");
    print FILE $dir, "\n";
    print FILE $tag, "\n";
    print FILE join("\n", @files), "\n";
    close(FILE);
    }
}

#
# do an 'cvs -Qn status' on each file in the arguments, and extract info.
#

sub change_summary_changed {
    local($out, $tag, @filenames) = @_;
    local(@revline);
    local($file, $rev, $rcsfile, $line);

    while (@filenames) {
    $file = shift @filenames;

    if ("$file" eq "") {
        next;
    }

    open(RCS, "-|") || exec 'cvs', '-Qn', 'status', $file;

    $rev = "";
    $delta = "";
    $rcsfile = "";


    while (<RCS>) {
        if (/^[ \t]*Repository revision/) {
        chop;
        @revline = split(' ', $_);
        $rev = $revline[2];
        $rcsfile = $revline[3];
        $rcsfile =~ s,^$CVSROOT[/]+,,;
        $rcsfile =~ s/,v$//;
        last;
        }
    }
    close(RCS);

    if ($rev ne '' && $rcsfile ne '') {
        open(RCS, "-|") || exec 'cvs', '-Qn', 'log', "-r$rev", $file;
        while (<RCS>) {
        if (/^date:/) {
            chop;
            $delta = $_;
            $delta =~ s/^.*;//;
            $delta =~ s/^[\s]+lines://;
            last;
        }
        }
        close(RCS);
    }

    &append_line($out, sprintf("%-9s%-12s%s", $rev, $delta, $rcsfile));
    }
}

# Write these one day.
sub change_summary_added {
}
sub change_summary_removed {
}

sub build_header {
    local($header, $datestr);
    delete $ENV{'TZ'};

    $datestr = `/bin/date +"%Y/%m/%d %H:%M:%S %Z"`;
    chop($datestr);
    $header = sprintf("%-8s    %s", $login, $datestr);
}

# !!! Mailing-list and commitlog history file mappings here !!!
sub mlist_map {
    local($dir) = @_;       # perl warns about this....
   
    return 'cvs-CVSROOT'      if($dir =~ /^CVSROOT\//);
    return 'cvs-ports'        if($dir =~ /^ports\//);
    return 'cvs-www'          if($dir =~ /^www\//);
    return 'cvs-doc'          if($dir =~ /^doc\//);
    return 'cvs-distrib'      if($dir =~ /^distrib\//);

    return 'cvs-other'        unless($dir =~ /^src\//);

    $dir =~ s,^src/,,;

    return 'cvs-bin'          if($dir =~ /^bin\//);
    return 'cvs-contrib'      if($dir =~ /^contrib\//);
    return 'cvs-eBones'       if($dir =~ /^eBones\//);
    return 'cvs-etc'          if($dir =~ /^etc\//);
    return 'cvs-games'        if($dir =~ /^games\//);
    return 'cvs-gnu'          if($dir =~ /^gnu\//);
    return 'cvs-include'      if($dir =~ /^include\//);
    return 'cvs-kerberosIV'   if($dir =~ /^kerberosIV\//);
    return 'cvs-lib'          if($dir =~ /^lib\//);
    return 'cvs-libexec'      if($dir =~ /^libexec\//);
    return 'cvs-lkm'          if($dir =~ /^lkm\//);
    return 'cvs-release'      if($dir =~ /^release\//);
    return 'cvs-sbin'         if($dir =~ /^sbin\//);
    return 'cvs-share'        if($dir =~ /^share\//);
    return 'cvs-sys'          if($dir =~ /^sys\//);
    return 'cvs-tools'        if($dir =~ /^tools\//);
    return 'cvs-usrbin'       if($dir =~ /^usr\.bin\//);
    return 'cvs-usrsbin'      if($dir =~ /^usr\.sbin\//);

    return 'cvs-user';

}    

sub do_changes_file {
    local($changes,$category,@mailaddrs);
    local(@text) = @_;
    local(%unique);

    %unique = ();
    @mailaddrs = &read_logfile("$MAIL_FILE.$id", "");
    foreach $category (@mailaddrs) {
    next if ($unique{$category});
    $unique{$category} = 1;
    if ($category =~ /^cvs-/) {
        # convert mailing list name back to category
        $category =~ s,\n,,;
        $category =~ s/^cvs-//;
        $changes = "$CVSROOT/CVSROOT/commitlogs/$category";

        open(CHANGES, ">>$changes") || die("Cannot open $changes.\n");
        print(CHANGES join("\n", @text), "\n\n");
        close(CHANGES);
    }
    }
}

sub mail_notification {
    local(@text) = @_;
    local($line, $word, $subjlines, $subjwords, @mailaddrs);
#   local(%unique);

#   %unique = ();

    print "Mailing the commit message...\n";

    @mailaddrs = &read_logfile("$MAIL_FILE.$id", "");

    if ($debug or !$freebsd) {
    open(MAIL, "| /usr/local/bin/mailsend -H $owner$dom");
    } else {
    open(MAIL, "| /usr/local/bin/mailsend -H cvs-committers$dom cvs-all$dom");
    }

# This is turned off since the To: lines go overboard.
# - but keep it for the time being in case we do something like cvs-stable
#    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");

    $subject = 'Subject: cvs commit:';
    @subj = &read_logfile("$SUBJ_FILE.$id", "");
    $subjlines = 0;
    $subjwords = 0; # minimum of two "words" per line
    LINE: foreach $line (@subj) {
    foreach $word (split(/ /, $line)) {
        if ($subjwords > 2 && length($subject . " " . $word) > 75) {
        if ($subjlines > 2) {
            $subject .= " ...";
        }
        print(MAIL $subject, "\n");
        if ($subjlines > 2) {
            $subject = "";
            last LINE;
        }
        $subject = "        ";      # rfc822 continuation line
        $subjwords = 0;
        $subjlines++;
        }
        $subject .= " " . $word;
        $subjwords++;
    }
    }
    if ($subject ne "") {
    print(MAIL $subject, "\n");
    }
    print (MAIL "\n");

    print(MAIL join("\n", @text));
    close(MAIL);
}

#############################################################
#
# Main Body
#
############################################################

#
# Setup environment
#
umask (002);
$host = hostname();
if ($host =~ /^(freefall|internat)\.freebsd\.org$/i) {
    $freebsd = 1;
    $dom = '@FreeBSD.org';
    if ($1 =~ /freefall/i) {
    $crypto = '';
    $owner = 'peter';
    } else {
    $crypto = 1;
    $owner = 'markm';
    }
} else {
    $freebsd = $crypto = '';
    $owner = 'postmaster';  # Change this!!!
    $dom = ''; # Change this!!
}

#
# Initialize basic variables
#
$id = getpgrp();
$state = $STATE_NONE;
$tag = '';
$login = $ENV{'USER'} || getlogin || (getpwuid($<))[0] || sprintf("uid#%d",$<);
@files = split(' ', $ARGV[0]);
@path = split('/', $files[0]);
if ($#path == 0) {
    $dir = ".";
} else {
    $dir = join('/', @path[1..$#path]);
}
$dir = $dir . "/";

if ($debug) {
  print("ARGV  - ", join(":", @ARGV), "\n");
  print("files - ", join(":", @files), "\n");
  print("path  - ", join(":", @path), "\n");
  print("dir   - ", $dir, "\n");
  print("id    - ", $id, "\n");
}

# Was used for To: lines, still used for commitlogs naming.
&append_line("$MAIL_FILE.$id", &mlist_map($files[0] . "/"));
&append_line("$SUBJ_FILE.$id", $ARGV[0]);

#
# Check for a new directory first.  This will always appear as a
# single item in the argument list, and an empty log message.
#
if ($ARGV[0] =~ /New directory/) {
    $header = &build_header();
    @text = ();
    push(@text, $header);
    push(@text, "");
    if ($freebsd and $crypto) {
    push(@text, "FreeBSD International Crypto Repository");
    push(@text, "");
    }
    push(@text, "  ".$ARGV[0]);
    &do_changes_file(@text);
    #&mail_notification(@text);
    &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 ($ARGV[0] =~ /Imported sources/) {
    $header = &build_header();

    @text = ();
    push(@text, $header);
    push(@text, "");
    if ($freebsd and $crypto) {
    push(@text, "FreeBSD International Crypto Repository");
    push(@text, "");
    }

    push(@text, "  ".$ARGV[0]);
    &do_changes_file(@text);

    while (<STDIN>) {
    chop;                   # Drop the newline
    push(@text, "  ".$_);
    }

    &mail_notification(@text);
    &cleanup_tmpfiles();
    exit 0;
}    

#
# Iterate over the body of the message collecting information.
#
$tag = "HEAD";
while (<STDIN>) {
    s/[ \t\n]+$//;      # delete trailing space
    if (/^Revision\/Branch:/) {
    s,^Revision/Branch:,,;
    $tag = $_;
    next;
    }
    if (/^[ \t]+Tag:/) {
    s,^[ \t]+Tag: ,,;
    $tag = $_;
    next;
    }
    if (/^[ \t]+No tag$/) {
    $tag = "HEAD";
    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; }
    
    push (@{ $changed_files{$tag} }, split) if ($state == $STATE_CHANGED);
    push (@{ $added_files{$tag} },   split) if ($state == $STATE_ADDED);
    push (@{ $removed_files{$tag} }, split) if ($state == $STATE_REMOVED);
    if ($state == $STATE_LOG) {
    if (/^PR:$/i ||
        /^Reviewed by:$/i ||
        /^Submitted by:$/i ||
        /^Obtained from:$/i ||
        /^Approved by:$/i) {
        next;
    }
    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.
# (Note, this only does the mail and changes log, not the rcs log).
#
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 ($l = $#log_lines; $l > 0; $l--) {
    if (($log_lines[$l - 1] eq "") && ($log_lines[$l] eq "")) {
    splice(@log_lines, $l, 1);
    }
}

#
# Find the log file that matches this log message
#
for ($i = 0; ; $i++) {
    last if (! -e "$LOG_FILE.$i.$id");
    @text = &read_logfile("$LOG_FILE.$i.$id", "");
    last if ($#text == -1);
    last if (join(" ", @log_lines) eq join(" ", @text));
}

#
# Spit out the information gathered in this pass.
#
foreach $tag ( keys %added_files ) {
    &append_names_to_file("$ADDED_FILE.$i.$id",   $dir, $tag,
    @{ $added_files{$tag} });
}
foreach $tag ( keys %changed_files ) {
    &append_names_to_file("$CHANGED_FILE.$i.$id", $dir, $tag,
    @{ $changed_files{$tag} });
}
foreach $tag ( keys %removed_files ) {
    &append_names_to_file("$REMOVED_FILE.$i.$id", $dir, $tag,
    @{ $removed_files{$tag} });
}
&write_logfile("$LOG_FILE.$i.$id", @log_lines);

if ($rcsidinfo) {
    foreach $tag ( keys %added_files ) {
    &change_summary_added("$SUMMARY_FILE.$i.$id", $tag,
        @{ $added_files{$tag} });
    }
    foreach $tag ( keys %changed_files ) {
    &change_summary_changed("$SUMMARY_FILE.$i.$id", $tag,
        @{ $changed_files{$tag} });
    }
    foreach $tag ( keys %removed_files ) {
    &change_summary_removed("$SUMMARY_FILE.$i.$id", $tag,
        @{ $removed_files{$tag} });
    }
}

#
# Check whether this is the last directory.  If not, quit.
#
if (-e "$LAST_FILE.$id") {
   $_ = &read_line("$LAST_FILE.$id");
   $tmpfiles=$files[0];
   $tmpfiles =~ s,([^a-zA-Z0-9_/]),\\$1,g;
   if (! 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.
#
$header = &build_header();

#
# Produce the final compilation of the log messages
#
@text = ();
push(@text, $header);
push(@text, "");
if ($freebsd and $crypto) {
    push(@text, "FreeBSD International Crypto Repository");
    push(@text, "");
}
for ($i = 0; ; $i++) {
    last if (! -e "$LOG_FILE.$i.$id");
    @lines = &read_logfile("$CHANGED_FILE.$i.$id", "");
    if ($#lines >= 0) {
    push(@text, &format_lists("Modified", @lines));
    }
    @lines = &read_logfile("$ADDED_FILE.$i.$id", "");
    if ($#lines >= 0) {
    push(@text, &format_lists("Added", @lines));
    }
    @lines = &read_logfile("$REMOVED_FILE.$i.$id", "");
    if ($#lines >= 0) {
    push(@text, &format_lists("Removed", @lines));
    }

    @lines = &read_logfile("$LOG_FILE.$i.$id", "  ");
    if ($#lines >= 0) {
        push(@text, "  Log:");
    push(@text, @lines);
    }
    if ($rcsidinfo == 2) {
    if (-e "$SUMMARY_FILE.$i.$id") {
        push(@text, "  ");
        push(@text, "  Revision  Changes    Path");
        push(@text, &read_logfile("$SUMMARY_FILE.$i.$id", "  "));
    }
    }
    push(@text, "", "");
}
#
# Put the log message at the beginning of the Changes file
#
&do_changes_file(@text);

#
# Now generate the extra info for the mail message..
#
if ($rcsidinfo == 1) {
    $revhdr = 0;
    for ($i = 0; ; $i++) {
    last if (! -e "$LOG_FILE.$i.$id");
    if (-e "$SUMMARY_FILE.$i.$id") {
        if (!$revhdr++) {
        push(@text, "Revision  Changes    Path");
        }
        push(@text, &read_logfile("$SUMMARY_FILE.$i.$id", ""));
    }
    }
    if ($revhdr) {
    push(@text, "");    # consistancy...
    }
}

#
# Mail out the notification.
#
&mail_notification(@text);
&cleanup_tmpfiles();
exit 0;
# EOF