home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Unix / cvs-960311 / lib / cvs / contrib / log_accum < prev    next >
Encoding:
Text File  |  1996-01-20  |  11.9 KB  |  497 lines

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