home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / cvs-1.8.7-bin.lha / lib / cvs / contrib / log_accum < prev    next >
Text File  |  1996-10-12  |  14KB  |  561 lines

  1. #! /bin/perl
  2. # -*-Perl-*-
  3. #
  4. #ident    "@(#)ccvs/contrib:$Name:  $:$Id: log_accum.pl,v 1.4 1996/03/06 15:27:09 woods Exp $"
  5. #
  6. # Perl filter to handle the log messages from the checkin of files in
  7. # a directory.  This script will group the lists of files by log
  8. # message, and mail a single consolidated log message at the end of
  9. # the commit.
  10. #
  11. # This file assumes a pre-commit checking program that leaves the
  12. # names of the first and last commit directories in a temporary file.
  13. #
  14. # Contributed by David Hampton <hampton@cisco.com>
  15. #
  16. # hacked greatly by Greg A. Woods <woods@planix.com>
  17.  
  18. # Usage: log_accum.pl [-d] [-s] [-M module] [[-m mailto] ...] [[-R replyto] ...] [-f logfile]
  19. #    -d        - turn on debugging
  20. #    -m mailto    - send mail to "mailto" (multiple)
  21. #    -R replyto    - set the "Reply-To:" to "replyto" (multiple)
  22. #    -M modulename    - set module name to "modulename"
  23. #    -f logfile    - write commit messages to logfile too
  24. #    -s        - *don't* run "cvs status -v" for each file
  25.  
  26. #
  27. #    Configurable options
  28. #
  29.  
  30. # set this to something that takes a whole message on stdin
  31. $MAILER           = "/usr/lib/sendmail -t";
  32.  
  33. #
  34. #    End user configurable options.
  35. #
  36.  
  37. # Constants (don't change these!)
  38. #
  39. $STATE_NONE    = 0;
  40. $STATE_CHANGED = 1;
  41. $STATE_ADDED   = 2;
  42. $STATE_REMOVED = 3;
  43. $STATE_LOG     = 4;
  44.  
  45. $LAST_FILE     = "/tmp/#cvs.lastdir";
  46.  
  47. $CHANGED_FILE  = "/tmp/#cvs.files.changed";
  48. $ADDED_FILE    = "/tmp/#cvs.files.added";
  49. $REMOVED_FILE  = "/tmp/#cvs.files.removed";
  50. $LOG_FILE      = "/tmp/#cvs.files.log";
  51.  
  52. $FILE_PREFIX   = "#cvs.files";
  53.  
  54. #
  55. #    Subroutines
  56. #
  57.  
  58. sub cleanup_tmpfiles {
  59.     local($wd, @files);
  60.  
  61.     $wd = `pwd`;
  62.     chdir("/tmp") || die("Can't chdir('/tmp')\n");
  63.     opendir(DIR, ".");
  64.     push(@files, grep(/^$FILE_PREFIX\..*\.$id$/, readdir(DIR)));
  65.     closedir(DIR);
  66.     foreach (@files) {
  67.     unlink $_;
  68.     }
  69.     unlink $LAST_FILE . "." . $id;
  70.  
  71.     chdir($wd);
  72. }
  73.  
  74. sub write_logfile {
  75.     local($filename, @lines) = @_;
  76.  
  77.     open(FILE, ">$filename") || die("Cannot open log file $filename.\n");
  78.     print FILE join("\n", @lines), "\n";
  79.     close(FILE);
  80. }
  81.  
  82. sub append_to_logfile {
  83.     local($filename, @lines) = @_;
  84.  
  85.     open(FILE, ">$filename") || die("Cannot open log file $filename.\n");
  86.     print FILE join("\n", @lines), "\n";
  87.     close(FILE);
  88. }
  89.  
  90. sub format_names {
  91.     local($dir, @files) = @_;
  92.     local(@lines);
  93.  
  94.     $format = "\t%-" . sprintf("%d", length($dir)) . "s%s ";
  95.  
  96.     $lines[0] = sprintf($format, $dir, ":");
  97.  
  98.     if ($debug) {
  99.     print STDERR "format_names(): dir = ", $dir, "; files = ", join(":", @files), ".\n";
  100.     }
  101.     foreach $file (@files) {
  102.     if (length($lines[$#lines]) + length($file) > 65) {
  103.         $lines[++$#lines] = sprintf($format, " ", " ");
  104.     }
  105.     $lines[$#lines] .= $file . " ";
  106.     }
  107.  
  108.     @lines;
  109. }
  110.  
  111. sub format_lists {
  112.     local(@lines) = @_;
  113.     local(@text, @files, $lastdir);
  114.  
  115.     if ($debug) {
  116.     print STDERR "format_lists(): ", join(":", @lines), "\n";
  117.     }
  118.     @text = ();
  119.     @files = ();
  120.     $lastdir = shift @lines;    # first thing is always a directory
  121.     if ($lastdir !~ /.*\/$/) {
  122.     die("Damn, $lastdir doesn't look like a directory!\n");
  123.     }
  124.     foreach $line (@lines) {
  125.     if ($line =~ /.*\/$/) {
  126.         push(@text, &format_names($lastdir, @files));
  127.         $lastdir = $line;
  128.         @files = ();
  129.     } else {
  130.         push(@files, $line);
  131.     }
  132.     }
  133.     push(@text, &format_names($lastdir, @files));
  134.  
  135.     @text;
  136. }
  137.  
  138. sub append_names_to_file {
  139.     local($filename, $dir, @files) = @_;
  140.  
  141.     if (@files) {
  142.     open(FILE, ">>$filename") || die("Cannot open file $filename.\n");
  143.     print FILE $dir, "\n";
  144.     print FILE join("\n", @files), "\n";
  145.     close(FILE);
  146.     }
  147. }
  148.  
  149. sub read_line {
  150.     local($line);
  151.     local($filename) = @_;
  152.  
  153.     open(FILE, "<$filename") || die("Cannot open file $filename.\n");
  154.     $line = <FILE>;
  155.     close(FILE);
  156.     chop($line);
  157.     $line;
  158. }
  159.  
  160. sub read_logfile {
  161.     local(@text);
  162.     local($filename, $leader) = @_;
  163.  
  164.     open(FILE, "<$filename");
  165.     while (<FILE>) {
  166.     chop;
  167.     push(@text, $leader.$_);
  168.     }
  169.     close(FILE);
  170.     @text;
  171. }
  172.  
  173. sub build_header {
  174.     local($header);
  175.     local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  176.     $header = sprintf("CVSROOT:\t%s\nModule name:\t%s\nChanges by:\t%s@%s\t%02d/%02d/%02d %02d:%02d:%02d",
  177.               $cvsroot,
  178.               $modulename,
  179.               $login, $hostdomain,
  180.               $year%100, $mon+1, $mday,
  181.               $hour, $min, $sec);
  182. }
  183.  
  184. sub mail_notification {
  185.     local(@text) = @_;
  186.  
  187.     # if only we had strftime()...  stuff stolen from perl's ctime.pl:
  188.     local($[) = 0;
  189.  
  190.     @DoW = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
  191.     @MoY = ('Jan','Feb','Mar','Apr','May','Jun',
  192.         'Jul','Aug','Sep','Oct','Nov','Dec');
  193.  
  194.     # Determine what time zone is in effect.
  195.     # Use GMT if TZ is defined as null, local time if TZ undefined.
  196.     # There's no portable way to find the system default timezone.
  197.     #
  198.     $TZ = defined($ENV{'TZ'}) ? ( $ENV{'TZ'} ? $ENV{'TZ'} : 'GMT' ) : '';
  199.  
  200.     # Hack to deal with 'PST8PDT' format of TZ
  201.     # Note that this can't deal with all the esoteric forms, but it
  202.     # does recognize the most common: [:]STDoff[DST[off][,rule]]
  203.     #
  204.     if ($TZ =~ /^([^:\d+\-,]{3,})([+-]?\d{1,2}(:\d{1,2}){0,2})([^\d+\-,]{3,})?/) {
  205.         $TZ = $isdst ? $4 : $1;
  206.     $tzoff = sprintf("%05d", -($2) * 100);
  207.     }
  208.  
  209.     # perl-4.036 doesn't have the $zone or $gmtoff...
  210.     ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst, $zone, $gmtoff) =
  211.         ($TZ eq 'GMT') ? gmtime(time) : localtime(time);
  212.  
  213.     $year += ($year < 70) ? 2000 : 1900;
  214.  
  215.     if ($gmtoff != 0) {
  216.     $tzoff = sprintf("%05d", ($gmtoff / 60) * 100);
  217.     }
  218.     if ($zone ne '') {
  219.     $TZ = $zone;
  220.     }
  221.  
  222.     # ok, let's try....
  223.     $rfc822date = sprintf("%s, %2d %s %4d %2d:%02d:%02d %s (%s)",
  224.               $DoW[$wday], $mday, $MoY[$mon], $year,
  225.               $hour, $min, $sec, $tzoff, $TZ);
  226.  
  227.     open(MAIL, "| $MAILER");
  228.     print MAIL "Date:     " . $rfc822date . "\n";
  229.     print MAIL "Subject:  CVS Update: " . $modulename . "\n";
  230.     print MAIL "To:       " . $mailto . "\n";
  231.     print MAIL "From:     " . $login . "@" . $hostdomain . "\n";
  232.     print MAIL "Reply-To: " . $replyto . "\n";
  233.     print MAIL "\n";
  234.     print MAIL join("\n", @text), "\n";
  235.     close(MAIL);
  236. }
  237.  
  238. sub write_commitlog {
  239.     local($logfile, @text) = @_;
  240.  
  241.     open(FILE, ">>$logfile");
  242.     print FILE join("\n", @text), "\n";
  243.     close(FILE);
  244. }
  245.  
  246. #
  247. #    Main Body
  248. #
  249.  
  250. # Initialize basic variables
  251. #
  252. $debug = 0;
  253. $id = getpgrp();        # note, you *must* use a shell which does setpgrp()
  254. $state = $STATE_NONE;
  255. $login = getlogin || (getpwuid($<))[0] || "nobody";
  256. chop($hostname = `hostname`);
  257. chop($domainname = `domainname`);
  258. $hostdomain = $hostname . $domainname;
  259. $cvsroot = $ENV{'CVSROOT'};
  260. $do_status = 1;
  261. $modulename = "";
  262.  
  263. # parse command line arguments (file list is seen as one arg)
  264. #
  265. while (@ARGV) {
  266.     $arg = shift @ARGV;
  267.  
  268.     if ($arg eq '-d') {
  269.     $debug = 1;
  270.     print STDERR "Debug turned on...\n";
  271.     } elsif ($arg eq '-m') {
  272.     if ($mailto eq '') {
  273.         $mailto = shift @ARGV;
  274.     } else {
  275.         $mailto = $mailto . ", " . shift @ARGV;
  276.     }
  277.     } elsif ($arg eq '-R') {
  278.     if ($replyto eq '') {
  279.         $replyto = shift @ARGV;
  280.     } else {
  281.         $replyto = $replyto . ", " . shift @ARGV;
  282.     }
  283.     } elsif ($arg eq '-M') {
  284.     $modulename = shift @ARGV;
  285.     } elsif ($arg eq '-s') {
  286.     $do_status = 0;
  287.     } elsif ($arg eq '-f') {
  288.     ($commitlog) && die("Too many '-f' args\n");
  289.     $commitlog = shift @ARGV;
  290.     } else {
  291.     ($donefiles) && die("Too many arguments!  Check usage.\n");
  292.     $donefiles = 1;
  293.     @files = split(/ /, $arg);
  294.     }
  295. }
  296. ($mailto) || die("No mail recipient specified (use -m)\n");
  297. if ($replyto eq '') {
  298.     $replyto = $login;
  299. }
  300.  
  301. # for now, the first "file" is the repository directory being committed,
  302. # relative to the $CVSROOT location
  303. #
  304. @path = split('/', $files[0]);
  305.  
  306. # XXX there are some ugly assumptions in here about module names and
  307. # XXX directories relative to the $CVSROOT location -- really should
  308. # XXX read $CVSROOT/CVSROOT/modules, but that's not so easy to do, since
  309. # XXX we have to parse it backwards.
  310. #
  311. if ($modulename eq "") {
  312.     $modulename = $path[0];    # I.e. the module name == top-level dir
  313. }
  314. if ($#path == 0) {
  315.     $dir = ".";
  316. } else {
  317.     $dir = join('/', @path);
  318. }
  319. $dir = $dir . "/";
  320.  
  321. if ($debug) {
  322.     print STDERR "module - ", $modulename, "\n";
  323.     print STDERR "dir    - ", $dir, "\n";
  324.     print STDERR "path   - ", join(":", @path), "\n";
  325.     print STDERR "files  - ", join(":", @files), "\n";
  326.     print STDERR "id     - ", $id, "\n";
  327. }
  328.  
  329. # Check for a new directory first.  This appears with files set as follows:
  330. #
  331. #    files[0] - "path/name/newdir"
  332. #    files[1] - "-"
  333. #    files[2] - "New"
  334. #    files[3] - "directory"
  335. #
  336. if ($files[2] =~ /New/ && $files[3] =~ /directory/) {
  337.     local(@text);
  338.  
  339.     @text = ();
  340.     push(@text, &build_header());
  341.     push(@text, "");
  342.     push(@text, $files[0]);
  343.     push(@text, "");
  344.  
  345.     while (<STDIN>) {
  346.     chop;            # Drop the newline
  347.     push(@text, $_);
  348.     }
  349.  
  350.     &mail_notification($mailto, @text);
  351.  
  352.     exit 0;
  353. }
  354.  
  355. # Check for an import command.  This appears with files set as follows:
  356. #
  357. #    files[0] - "path/name"
  358. #    files[1] - "-"
  359. #    files[2] - "Imported"
  360. #    files[3] - "sources"
  361. #
  362. if ($files[2] =~ /Imported/ && $files[3] =~ /sources/) {
  363.     local(@text);
  364.  
  365.     @text = ();
  366.     push(@text, &build_header());
  367.     push(@text, "");
  368.     push(@text, $files[0]);
  369.     push(@text, "");
  370.  
  371.     while (<STDIN>) {
  372.     chop;            # Drop the newline
  373.     push(@text, $_);
  374.     }
  375.  
  376.     &mail_notification(@text);
  377.  
  378.     exit 0;
  379. }
  380.  
  381. # Iterate over the body of the message collecting information.
  382. #
  383. while (<STDIN>) {
  384.     chop;            # Drop the newline
  385.  
  386.     if (/^In directory/) {
  387.     push(@log_lines, $_);
  388.     push(@log_lines, "");
  389.     next;
  390.     }
  391.  
  392.     if (/^Modified Files/) { $state = $STATE_CHANGED; next; }
  393.     if (/^Added Files/)    { $state = $STATE_ADDED;   next; }
  394.     if (/^Removed Files/)  { $state = $STATE_REMOVED; next; }
  395.     if (/^Log Message/)    { $state = $STATE_LOG;     next; }
  396.  
  397.     s/^[ \t\n]+//;        # delete leading whitespace
  398.     s/[ \t\n]+$//;        # delete trailing whitespace
  399.     
  400.     if ($state == $STATE_CHANGED) { push(@changed_files, split); }
  401.     if ($state == $STATE_ADDED)   { push(@added_files,   split); }
  402.     if ($state == $STATE_REMOVED) { push(@removed_files, split); }
  403.     if ($state == $STATE_LOG)     { push(@log_lines,     $_); }
  404. }
  405.  
  406. # Strip leading and trailing blank lines from the log message.  Also
  407. # compress multiple blank lines in the body of the message down to a
  408. # single blank line.
  409. #
  410. while ($#log_lines > -1) {
  411.     last if ($log_lines[0] ne "");
  412.     shift(@log_lines);
  413. }
  414. while ($#log_lines > -1) {
  415.     last if ($log_lines[$#log_lines] ne "");
  416.     pop(@log_lines);
  417. }
  418. for ($i = $#log_lines; $i > 0; $i--) {
  419.     if (($log_lines[$i - 1] eq "") && ($log_lines[$i] eq "")) {
  420.     splice(@log_lines, $i, 1);
  421.     }
  422. }
  423.  
  424. if ($debug) {
  425.     print STDERR "Searching for log file index...";
  426. }
  427. # Find an index to a log file that matches this log message
  428. #
  429. for ($i = 0; ; $i++) {
  430.     local(@text);
  431.  
  432.     last if (! -e "$LOG_FILE.$i.$id"); # the next available one
  433.     @text = &read_logfile("$LOG_FILE.$i.$id", "");
  434.     last if ($#text == -1);    # nothing in this file, use it
  435.     last if (join(" ", @log_lines) eq join(" ", @text)); # it's the same log message as another
  436. }
  437. if ($debug) {
  438.     print STDERR " found log file at $i.$id, now writing tmp files.\n";
  439. }
  440.  
  441. # Spit out the information gathered in this pass.
  442. #
  443. &append_names_to_file("$CHANGED_FILE.$i.$id", $dir, @changed_files);
  444. &append_names_to_file("$ADDED_FILE.$i.$id",   $dir, @added_files);
  445. &append_names_to_file("$REMOVED_FILE.$i.$id", $dir, @removed_files);
  446. &write_logfile("$LOG_FILE.$i.$id", @log_lines);
  447.  
  448. # Check whether this is the last directory.  If not, quit.
  449. #
  450. if ($debug) {
  451.     print STDERR "Checking current dir against last dir.\n";
  452. }
  453. $_ = &read_line("$LAST_FILE.$id");
  454.  
  455. if ($_ ne $cvsroot . "/" . $files[0]) {
  456.     if ($debug) {
  457.     print STDERR sprintf("Current directory %s is not last directory %s.\n", $cvsroot . "/" .$files[0], $_);
  458.     }
  459.     exit 0;
  460. }
  461. if ($debug) {
  462.     print STDERR sprintf("Current directory %s is last directory %s -- all commits done.\n", $files[0], $_);
  463. }
  464.  
  465. #
  466. #    End Of Commits!
  467. #
  468.  
  469. # This is it.  The commits are all finished.  Lump everything together
  470. # into a single message, fire a copy off to the mailing list, and drop
  471. # it on the end of the Changes file.
  472. #
  473.  
  474. #
  475. # Produce the final compilation of the log messages
  476. #
  477. @text = ();
  478. @status_txt = ();
  479. push(@text, &build_header());
  480. push(@text, "");
  481.  
  482. for ($i = 0; ; $i++) {
  483.     last if (! -e "$LOG_FILE.$i.$id"); # we're done them all!
  484.     @lines = &read_logfile("$CHANGED_FILE.$i.$id", "");
  485.     if ($#lines >= 0) {
  486.     push(@text, "Modified files:");
  487.     push(@text, &format_lists(@lines));
  488.     }
  489.     @lines = &read_logfile("$ADDED_FILE.$i.$id", "");
  490.     if ($#lines >= 0) {
  491.     push(@text, "Added files:");
  492.     push(@text, &format_lists(@lines));
  493.     }
  494.     @lines = &read_logfile("$REMOVED_FILE.$i.$id", "");
  495.     if ($#lines >= 0) {
  496.     push(@text, "Removed files:");
  497.     push(@text, &format_lists(@lines));
  498.     }
  499.     if ($#text >= 0) {
  500.     push(@text, "");
  501.     }
  502.     @lines = &read_logfile("$LOG_FILE.$i.$id", "\t");
  503.     if ($#lines >= 0) {
  504.     push(@text, "Log message:");
  505.     push(@text, @lines);
  506.     push(@text, "");
  507.     }
  508.     if ($do_status) {
  509.     local(@changed_files);
  510.  
  511.     @changed_files = ();
  512.     push(@changed_files, &read_logfile("$CHANGED_FILE.$i.$id", ""));
  513.     push(@changed_files, &read_logfile("$ADDED_FILE.$i.$id", ""));
  514.     push(@changed_files, &read_logfile("$REMOVED_FILE.$i.$id", ""));
  515.  
  516.     if ($debug) {
  517.         print STDERR "main: pre-sort changed_files = ", join(":", @changed_files), ".\n";
  518.     }
  519.     sort(@changed_files);
  520.     if ($debug) {
  521.         print STDERR "main: post-sort changed_files = ", join(":", @changed_files), ".\n";
  522.     }
  523.  
  524.     foreach $dofile (@changed_files) {
  525.         if ($dofile =~ /\/$/) {
  526.         next;        # ignore the silly "dir" entries
  527.         }
  528.         if ($debug) {
  529.         print STDERR "main(): doing 'cvs -nQq status -v $dofile'\n";
  530.         }
  531.         open(STATUS, "-|") || exec 'cvs', '-nQq', 'status', '-v', $dofile;
  532.         while (<STATUS>) {
  533.         chop;
  534.         push(@status_txt, $_);
  535.         }
  536.     }
  537.     }
  538. }
  539.  
  540. # Write to the commitlog file
  541. #
  542. if ($commitlog) {
  543.     &write_commitlog($commitlog, @text);
  544. }
  545.  
  546. if ($#status_txt >= 0) {
  547.     push(@text, @status_txt);
  548. }
  549.  
  550. # Mailout the notification.
  551. #
  552. &mail_notification(@text);
  553.  
  554. # cleanup
  555. #
  556. if (! $debug) {
  557.     &cleanup_tmpfiles();
  558. }
  559.  
  560. exit 0;
  561.