Add CVS 1.12.11.
[dragonfly.git] / contrib / cvs-1.12.11 / contrib / log_accum.in
1 #! @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 # IMPORTANT: what the above means is, this script interacts with
13 # commit_prep, in that they have to agree on the tmpfile name to use.
14 # See $LAST_FILE below. 
15 #
16 # How this works: CVS triggers this script once for each directory
17 # involved in the commit -- in other words, a single commit can invoke
18 # this script N times.  It knows when it's on the last invocation by
19 # examining the contents of $LAST_FILE.  Between invocations, it
20 # caches information for its future incarnations in various temporary
21 # files in /tmp, which are named according to the process group and
22 # the committer (by themselves, neither of these are unique, but
23 # together they almost always are, unless the same user is doing two
24 # commits simultaneously).  The final invocation is the one that
25 # actually sends the mail -- it gathers up the cached information,
26 # combines that with what it found out on this pass, and sends a
27 # commit message to the appropriate mailing list.
28 #
29 # (Ask Karl Fogel <kfogel@collab.net> if questions.)
30 #
31 # Contributed by David Hampton <hampton@cisco.com>
32 # Roy Fielding removed useless code and added log/mail of new files
33 # Ken Coar added special processing (i.e., no diffs) for binary files
34 #
35
36 ############################################################
37 #
38 # Configurable options
39 #
40 ############################################################
41 #
42 # The newest versions of CVS have UseNewInfoFmtStrings=yes
43 # to change the arguments being passed on the command line.
44 # If you are using %1s on the command line, then set this
45 # value to 0.
46 # 0 = old-style %1s format. use split(' ') to separate ARGV into filesnames.
47 # 1 = new-style %s format. Note: allows spaces in filenames.
48 my $UseNewInfoFmtStrings = 0;
49
50 #
51 # Where do you want the RCS ID and delta info?
52 # 0 = none,
53 # 1 = in mail only,
54 # 2 = in both mail and logs.
55 #
56 $rcsidinfo = 2;
57
58 #if you are using CVS web then set this to some value... if not set it to ""
59 #
60 # When set properly, this will cause links to aspects of the project to
61 # print in the commit emails.
62 #$CVSWEB_SCHEME = "http";
63 #$CVSWEB_DOMAIN = "cvshome.org";
64 #$CVSWEB_PORT = "80";
65 #$CVSWEB_URI = "source/browse/";
66 #$SEND_URL = "true";
67 $SEND_DIFF = "true";
68
69
70 # Set this to a domain to have CVS pretend that all users who make
71 # commits have mail accounts within that domain.
72 #$EMULATE_LOCAL_MAIL_USER="cvshome.org"; 
73
74 # Set this to '-c' for context diffs; defaults to '-u' for unidiff format.
75 $difftype = '-uN';
76
77 ############################################################
78 #
79 # Constants
80 #
81 ############################################################
82 $STATE_NONE    = 0;
83 $STATE_CHANGED = 1;
84 $STATE_ADDED   = 2;
85 $STATE_REMOVED = 3;
86 $STATE_LOG     = 4;
87
88 $TMPDIR        = $ENV{'TMPDIR'} || '/tmp';
89 $FILE_PREFIX   = '#cvs.';
90
91 $LAST_FILE     = "$TMPDIR/${FILE_PREFIX}lastdir";  # Created by commit_prep!
92 $ADDED_FILE    = "$TMPDIR/${FILE_PREFIX}files.added";
93 $REMOVED_FILE  = "$TMPDIR/${FILE_PREFIX}files.removed";
94 $LOG_FILE      = "$TMPDIR/${FILE_PREFIX}files.log";
95 $BRANCH_FILE   = "$TMPDIR/${FILE_PREFIX}files.branch";
96 $MLIST_FILE    = "$TMPDIR/${FILE_PREFIX}files.mlist";
97 $SUMMARY_FILE  = "$TMPDIR/${FILE_PREFIX}files.summary";
98
99 $CVSROOT       = $ENV{'CVSROOT'};
100
101 $MAIL_CMD      = "| /usr/lib/sendmail -i -t";
102 #$MAIL_CMD      = "| /var/qmail/bin/qmail-inject";
103 $MAIL_FROM     = 'commitlogger';  #not needed if EMULATE_LOCAL_MAIL_USER
104 $SUBJECT_PRE   = 'CVS update:';
105
106
107 ############################################################
108 #
109 # Subroutines
110 #
111 ############################################################
112
113 sub format_names {
114     local($dir, @files) = @_;
115     local(@lines);
116
117     $lines[0] = sprintf(" %-08s", $dir);
118     foreach $file (@files) {
119         if (length($lines[$#lines]) + length($file) > 60) {
120             $lines[++$#lines] = sprintf(" %8s", " ");
121         }
122         $lines[$#lines] .= " ".$file;
123     }
124     @lines;
125 }
126
127 sub cleanup_tmpfiles {
128     local(@files);
129
130     opendir(DIR, $TMPDIR);
131     push(@files, grep(/^${FILE_PREFIX}.*\.${id}\.${cvs_user}$/, readdir(DIR)));
132     closedir(DIR);
133     foreach (@files) {
134         unlink "$TMPDIR/$_";
135     }
136 }
137
138 sub write_logfile {
139     local($filename, @lines) = @_;
140
141     open(FILE, ">$filename") || die ("Cannot open log file $filename: $!\n");
142     print(FILE join("\n", @lines), "\n");
143     close(FILE);
144 }
145
146 sub append_to_file {
147     local($filename, $dir, @files) = @_;
148
149     if (@files) {
150         local(@lines) = &format_names($dir, @files);
151         open(FILE, ">>$filename") || die ("Cannot open file $filename: $!\n");
152         print(FILE join("\n", @lines), "\n");
153         close(FILE);
154     }
155 }
156
157 sub write_line {
158     local($filename, $line) = @_;
159
160     open(FILE, ">$filename") || die("Cannot open file $filename: $!\n");
161     print(FILE $line, "\n");
162     close(FILE);
163 }
164
165 sub append_line {
166     local($filename, $line) = @_;
167
168     open(FILE, ">>$filename") || die("Cannot open file $filename: $!\n");
169     print(FILE $line, "\n");
170     close(FILE);
171 }
172
173 sub read_line {
174     local($filename) = @_;
175     local($line);
176
177     open(FILE, "<$filename") || die("Cannot open file $filename: $!\n");
178     $line = <FILE>;
179     close(FILE);
180     chomp($line);
181     $line;
182 }
183
184 sub read_line_nodie {
185     local($filename) = @_;
186     local($line);
187     open(FILE, "<$filename") || return ("");
188
189     $line = <FILE>;
190     close(FILE);
191     chomp($line);
192     $line;
193 }
194
195 sub read_file_lines {
196     local($filename) = @_;
197     local(@text) = ();
198
199     open(FILE, "<$filename") || return ();
200     while (<FILE>) {
201         chomp;
202         push(@text, $_);
203     }
204     close(FILE);
205     @text;
206 }
207
208 sub read_file {
209     local($filename, $leader) = @_;
210     local(@text) = ();
211
212     open(FILE, "<$filename") || return ();
213     while (<FILE>) {
214         chomp;
215         push(@text, sprintf("  %-10s  %s", $leader, $_));
216         $leader = "";
217     }
218     close(FILE);
219     @text;
220 }
221
222 sub read_logfile {
223     local($filename, $leader) = @_;
224     local(@text) = ();
225
226     open(FILE, "<$filename") || die ("Cannot open log file $filename: $!\n");
227     while (<FILE>) {
228         chomp;
229         push(@text, $leader.$_);
230     }
231     close(FILE);
232     @text;
233 }
234
235 #
236 # do an 'cvs -Qn status' on each file in the arguments, and extract info.
237 #
238 sub change_summary {
239     local($out, @filenames) = @_;
240     local(@revline);
241     local($file, $rev, $rcsfile, $line, $vhost, $cvsweb_base);
242
243     while (@filenames) {
244         $file = shift @filenames;
245
246         if ("$file" eq "") {
247             next;
248         }
249
250         open(RCS, "-|") || exec "$cvsbin/cvs", '-Qn', 'status', '--', $file;
251
252         $rev = "";
253         $delta = "";
254         $rcsfile = "";
255
256
257         while (<RCS>) {
258             if (/^[ \t]*Repository revision/) {
259                 chomp;
260                 @revline = split(' ', $_);
261                 $rev = $revline[2];
262                 $rcsfile = $revline[3];
263                 $rcsfile =~ s,^$CVSROOT/,,;
264                 $rcsfile =~ s/,v$//;
265             }
266         }
267         close(RCS);
268
269
270         if ($rev ne '' && $rcsfile ne '') {
271             open(RCS, "-|") || exec "$cvsbin/cvs", '-Qn', 'log', "-r$rev",
272                                     '--', $file;
273             while (<RCS>) {
274                 if (/^date:/) {
275                     chomp;
276                     $delta = $_;
277                     $delta =~ s/^.*;//;
278                     $delta =~ s/^[\s]+lines://;
279                 }
280             }
281             close(RCS);
282         }
283
284         $diff = "\n\n";
285         $vhost = $path[0];
286         if ($CVSWEB_PORT eq "80") {
287           $cvsweb_base = "$CVSWEB_SCHEME://$vhost.$CVSWEB_DOMAIN/$CVSWEB_URI";
288         }
289         else {
290           $cvsweb_base = "$CVSWEB_SCHEME://$vhost.$CVSWEB_DOMAIN:$CVSWEB_PORT/$CVSWEB_URI";
291         }
292         if ($SEND_URL eq "true") {
293           $diff .= $cvsweb_base . join("/", @path) . "/$file";
294         }
295
296         #
297         # If this is a binary file, don't try to report a diff; not only is
298         # it meaningless, but it also screws up some mailers.  We rely on
299         # Perl's 'is this binary' algorithm; it's pretty good.  But not
300         # perfect.
301         #
302         if (($file =~ /\.(?:pdf|gif|jpg|mpg)$/i) || (-B $file)) {
303           if ($SEND_URL eq "true") {
304             $diff .= "?rev=$rev&content-type=text/x-cvsweb-markup\n\n";
305           }
306           if ($SEND_DIFF eq "true") {
307             $diff .= "\t<<Binary file>>\n\n";
308           }
309         }
310         else {
311             #
312             # Get the differences between this and the previous revision,
313             # being aware that new files always have revision '1.1' and
314             # new branches always end in '.n.1'.
315             #
316             if ($rev =~ /^(.*)\.([0-9]+)$/) {
317                 $prev = $2 - 1;
318                 $prev_rev = $1 . '.' .  $prev;
319
320                 $prev_rev =~ s/\.[0-9]+\.0$//;# Truncate if first rev on branch
321
322                 if ($rev eq '1.1') {
323                   if ($SEND_URL eq "true") {
324                     $diff .= "?rev=$rev&content-type=text/x-cvsweb-markup\n\n";
325                   }
326                   if ($SEND_DIFF eq "true") {
327                     open(DIFF, "-|")
328                       || exec "$cvsbin/cvs", '-Qn', 'update', '-p', '-r1.1',
329                               '--', $file;
330                     $diff .= "Index: $file\n=================================="
331                       . "=================================\n";
332                   }
333                 }
334                 else {
335                   if ($SEND_URL eq "true") {
336                     $diff .= ".diff?r1=$prev_rev&r2=$rev\n\n";
337                   }
338                   if ($SEND_DIFF eq "true") {
339                     $diff .= "(In the diff below, changes in quantity "
340                       . "of whitespace are not shown.)\n\n";
341                     open(DIFF, "-|")
342                       || exec "$cvsbin/cvs", '-Qn', 'diff', "$difftype",
343                       '-b', "-r$prev_rev", "-r$rev", '--', $file;
344                   }
345                 }
346
347                 if ($SEND_DIFF eq "true") {
348                   while (<DIFF>) {
349                     $diff .= $_;
350                   }
351                   close(DIFF);
352                 }
353                 $diff .= "\n\n";
354             }
355         }
356
357         &append_line($out, sprintf("%-9s%-12s%s%s", $rev, $delta,
358                                    $rcsfile, $diff));
359     }
360 }
361
362
363 sub build_header {
364     local($header);
365     delete $ENV{'TZ'};
366     local($sec,$min,$hour,$mday,$mon,$year) = localtime(time);
367
368     $header = sprintf("  User: %-8s\n  Date: %02d/%02d/%02d %02d:%02d:%02d",
369                        $cvs_user, $year%100, $mon+1, $mday,
370                        $hour, $min, $sec);
371 #    $header = sprintf("%-8s    %02d/%02d/%02d %02d:%02d:%02d",
372 #                       $login, $year%100, $mon+1, $mday,
373 #                       $hour, $min, $sec);
374 }
375
376 # !!! Destination Mailing-list and history file mappings here !!!
377
378 #sub mlist_map
379 #{
380 #    local($path) = @_;
381 #    my $domain = "cvshome.org";
382 #    
383 #    if ($path =~ /^([^\/]+)/) {
384 #        return "cvs\@$1.$domain";
385 #    } else {
386 #        return "cvs\@$domain";
387 #    }
388 #}    
389
390 sub derive_subject_from_changes_file ()
391 {
392   my $subj = "";
393
394   for ($i = 0; ; $i++)
395   {
396     open (CH, "<$CHANGED_FILE.$i.$id.$cvs_user") or last;
397
398     while (my $change = <CH>)
399     {
400       # A changes file looks like this:
401       #
402       #  src      foo.c newfile.html
403       #  www      index.html project_nav.html
404       #
405       # Each line is " Dir File1 File2 ..."
406       # We only care about Dir, since the subject line should
407       # summarize. 
408       
409       $change =~ s/^[ \t]*//;
410       $change =~ /^([^ \t]+)[ \t]*/;
411       my $dir = $1;
412       # Fold to rightmost directory component
413       $dir =~ /([^\/]+)$/;
414       $dir = $1;
415       if ($subj eq "") {
416         $subj = $dir;
417       } else {
418         $subj .= ", $dir"; 
419       }
420     }
421     close (CH);
422   }
423
424   if ($subj ne "") {
425       $subj = "MODIFIED: $subj ..."; 
426   }
427   else {
428       # NPM: See if there's any file-addition notifications.
429       my $added = &read_line_nodie("$ADDED_FILE.$i.$id.$cvs_user");
430       if ($added ne "") {
431           $subj .= "ADDED: $added "; 
432       }
433     
434 #    print "derive_subject_from_changes_file().. added== $added \n";
435     
436        ## NPM: See if there's any file-removal notications.
437       my $removed = &read_line_nodie("$REMOVED_FILE.$i.$id.$cvs_user");
438       if ($removed ne "") {
439           $subj .= "REMOVED: $removed "; 
440       }
441     
442 #    print "derive_subject_from_changes_file().. removed== $removed \n";
443     
444       ## NPM: See if there's any branch notifications.
445       my $branched = &read_line_nodie("$BRANCH_FILE.$i.$id.$cvs_user");
446       if ($branched ne "") {
447           $subj .= "BRANCHED: $branched"; 
448       }
449     
450 #    print "derive_subject_from_changes_file().. branched== $branched \n";
451     
452       ## NPM: DEFAULT: DIRECTORY CREATION (c.f. "Check for a new directory first" in main mody)
453       if ($subj eq "") {
454           my $subject = join("/", @path);
455           $subj = "NEW: $subject"; 
456       }    
457   }
458
459   return $subj;
460 }
461
462 sub mail_notification
463 {
464     local($addr_list, @text) = @_;
465     local($mail_to);
466
467     my $subj = &derive_subject_from_changes_file ();
468
469     if ($EMULATE_LOCAL_MAIL_USER ne "") {
470         $MAIL_FROM = "$cvs_user\@$EMULATE_LOCAL_MAIL_USER";
471     }
472
473     $mail_to = join(", ", @{$addr_list});
474
475     print "Mailing the commit message to $mail_to (from $MAIL_FROM)\n";
476
477     $ENV{'MAILUSER'} = $MAIL_FROM;
478     # Commented out on hocus, so comment it out here.  -kff
479     # $ENV{'QMAILINJECT'} = 'f';
480
481     open(MAIL, "$MAIL_CMD -f$MAIL_FROM");
482     print MAIL "From: $MAIL_FROM\n";
483     print MAIL "To: $mail_to\n";
484     print MAIL "Subject: $SUBJECT_PRE $subj\n\n";
485     print(MAIL join("\n", @text));
486     close(MAIL);
487 #    print "Mailing the commit message to $MAIL_TO...\n";
488 #
489 #    #added by jrobbins@collab.net 1999/12/15
490 #    # attempt to get rid of anonymous
491 #    $ENV{'MAILUSER'} = 'commitlogger';
492 #    $ENV{'QMAILINJECT'} = 'f';
493 #
494 #    open(MAIL, "| /var/qmail/bin/qmail-inject");
495 #    print(MAIL "To: $MAIL_TO\n"); 
496 #    print(MAIL "Subject: cvs commit: $ARGV[0]\n"); 
497 #    print(MAIL join("\n", @text));
498 #    close(MAIL);
499 }
500
501 ## process the command line arguments sent to this script
502 ## it returns an array of files, %s, sent from the loginfo
503 ## command
504 sub process_argv
505 {
506     local(@argv) = @_;
507     local(@files);
508     local($arg);
509     print "Processing log script arguments...\n";
510
511     if ($UseNewInfoFmtStrings) {
512         while (@argv) {
513             $arg = shift @argv;
514
515             if ($arg eq '-u' && !defined($cvs_user)) {
516                 $cvs_user = shift @argv;
517             }
518             if ($arg eq '- New directory') {
519                 $new_directory = 1;
520             } elsif ($arg eq '- Imported sources') {
521                 $imported_sources = 1;
522             } else {
523                 push(@files, $arg);
524             }
525         }
526     } else {
527         while (@argv) {
528             $arg = shift @argv;
529
530             if ($arg eq '-u') {
531                 $cvs_user = shift @argv;
532             } else {
533                 ($donefiles) && die "Too many arguments!\n";
534                 $donefiles = 1;
535                 $ARGV[0] = $arg;
536                 if ($arg =~ s/ - New directory//) {
537                     $new_directory = 1;
538                 } elsif ($arg =~ s/ - Imported sources//) {
539                     $imported_sources = 1;
540                 }
541                 @files = split(' ', $arg);
542             }
543         }
544     }
545     return @files;
546 }
547
548
549 #############################################################
550 #
551 # Main Body
552 #
553 ############################################################
554 #
555 # Setup environment
556 #
557 umask (002);
558
559 # Connect to the database
560 $cvsbin = "/usr/bin";
561
562 #
563 # Initialize basic variables
564 #
565 $id = getpgrp();
566 $state = $STATE_NONE;
567 $cvs_user = $ENV{'USER'} || getlogin || (getpwuid($<))[0] || sprintf("uid#%d",$<);
568 $new_directory = 0;             # Is this a 'cvs add directory' command?
569 $imported_sources = 0;          # Is this a 'cvs import' command?
570 @files = process_argv(@ARGV);
571 @path = split('/', $files[0]);
572 if ($#path == 0) {
573     $dir = ".";
574 } else {
575     $dir = join('/', @path[1..$#path]);
576 }
577 #print("ARGV  - ", join(":", @ARGV), "\n");
578 #print("files - ", join(":", @files), "\n");
579 #print("path  - ", join(":", @path), "\n");
580 #print("dir   - ", $dir, "\n");
581 #print("id    - ", $id, "\n");
582
583 #
584 # Map the repository directory to an email address for commitlogs to be sent
585 # to.
586 #
587 #$mlist = &mlist_map($files[0]);
588
589 ##########################
590 #
591 # Check for a new directory first.  This will always appear as a
592 # single item in the argument list, and an empty log message.
593 #
594 if ($new_directory) {
595     $header = &build_header;
596     @text = ();
597     push(@text, $header);
598     push(@text, "");
599     push(@text, "  ".$files[0]." - New directory");
600     &mail_notification([ $mlist ], @text);
601     exit 0;
602 }
603
604 #
605 # Iterate over the body of the message collecting information.
606 #
607 while (<STDIN>) {
608     chomp;                      # Drop the newline
609     if (/^Revision\/Branch:/) {
610         s,^Revision/Branch:,,;
611         push (@branch_lines, split);
612         next;
613     }
614 #    next if (/^[ \t]+Tag:/ && $state != $STATE_LOG);
615     if (/^Modified Files/) { $state = $STATE_CHANGED; next; }
616     if (/^Added Files/)    { $state = $STATE_ADDED;   next; }
617     if (/^Removed Files/)  { $state = $STATE_REMOVED; next; }
618     if (/^Log Message/)    { $state = $STATE_LOG;     last; }
619     s/[ \t\n]+$//;              # delete trailing space
620     
621     push (@changed_files, split) if ($state == $STATE_CHANGED);
622     push (@added_files,   split) if ($state == $STATE_ADDED);
623     push (@removed_files, split) if ($state == $STATE_REMOVED);
624 }
625 # Proces the /Log Message/ section now, if it exists.
626 # Do this here rather than above to deal with Log messages
627 # that include lines that confuse the state machine.
628 if (!eof(STDIN)) {
629     while (<STDIN>) {
630         next unless ($state == $STATE_LOG); # eat all STDIN
631
632         if ($state == $STATE_LOG) {
633             if (/^PR:$/i ||
634                 /^Reviewed by:$/i ||
635                 /^Submitted by:$/i ||
636                 /^Obtained from:$/i) {
637                 next;
638             }
639             push (@log_lines,     $_);
640         }
641     }
642 }
643
644 #
645 # Strip leading and trailing blank lines from the log message.  Also
646 # compress multiple blank lines in the body of the message down to a
647 # single blank line.
648 # (Note, this only does the mail and changes log, not the rcs log).
649 #
650 while ($#log_lines > -1) {
651     last if ($log_lines[0] ne "");
652     shift(@log_lines);
653 }
654 while ($#log_lines > -1) {
655     last if ($log_lines[$#log_lines] ne "");
656     pop(@log_lines);
657 }
658 for ($i = $#log_lines; $i > 0; $i--) {
659     if (($log_lines[$i - 1] eq "") && ($log_lines[$i] eq "")) {
660         splice(@log_lines, $i, 1);
661     }
662 }
663
664 #
665 # Find the log file that matches this log message
666 #
667 for ($i = 0; ; $i++) {
668     last if (! -e "$LOG_FILE.$i.$id.$cvs_user");
669     @text = &read_logfile("$LOG_FILE.$i.$id.$cvs_user", "");
670     last if ($#text == -1);
671     last if (join(" ", @log_lines) eq join(" ", @text));
672 }
673
674 #
675 # Spit out the information gathered in this pass.
676 #
677 &write_logfile("$LOG_FILE.$i.$id.$cvs_user", @log_lines);
678 &append_to_file("$BRANCH_FILE.$i.$id.$cvs_user",  $dir, @branch_lines);
679 &append_to_file("$ADDED_FILE.$i.$id.$cvs_user",   $dir, @added_files);
680 &append_to_file("$CHANGED_FILE.$i.$id.$cvs_user", $dir, @changed_files);
681 &append_to_file("$REMOVED_FILE.$i.$id.$cvs_user", $dir, @removed_files);
682 &append_line("$MLIST_FILE.$i.$id.$cvs_user", $mlist);
683 if ($rcsidinfo) {
684     &change_summary("$SUMMARY_FILE.$i.$id.$cvs_user", (@changed_files, @added_files));
685 }
686
687 #
688 # Check whether this is the last directory.  If not, quit.
689 #
690 if (-e "$LAST_FILE.$id.$cvs_user") {
691    $_ = &read_line("$LAST_FILE.$id.$cvs_user");
692    $tmpfiles = $files[0];
693    $tmpfiles =~ s,([^a-zA-Z0-9_/]),\\$1,g;
694    if (! grep(/$tmpfiles$/, $_)) {
695         print "More commits to come...\n";
696         exit 0
697    }
698 }
699
700 #
701 # This is it.  The commits are all finished.  Lump everything together
702 # into a single message, fire a copy off to the mailing list, and drop
703 # it on the end of the Changes file.
704 #
705 $header = &build_header;
706
707 #
708 # Produce the final compilation of the log messages
709 #
710 @text = ();
711 @mlist_list = ();
712 push(@text, $header);
713 push(@text, "");
714 for ($i = 0; ; $i++) {
715     last if (! -e "$LOG_FILE.$i.$id.$cvs_user");
716     push(@text, &read_file("$BRANCH_FILE.$i.$id.$cvs_user", "Branch:"));
717     push(@text, &read_file("$CHANGED_FILE.$i.$id.$cvs_user", "Modified:"));
718     push(@text, &read_file("$ADDED_FILE.$i.$id.$cvs_user", "Added:"));
719     push(@text, &read_file("$REMOVED_FILE.$i.$id.$cvs_user", "Removed:"));
720     push(@text, "  Log:");
721     push(@text, &read_logfile("$LOG_FILE.$i.$id.$cvs_user", "  "));
722     push(@mlist_list, &read_file_lines("$MLIST_FILE.$i.$id.$cvs_user"));
723     if ($rcsidinfo == 2) {
724         if (-e "$SUMMARY_FILE.$i.$id.$cvs_user") {
725             push(@text, "  ");
726             push(@text, "  Revision  Changes    Path");
727             push(@text, &read_logfile("$SUMMARY_FILE.$i.$id.$cvs_user", "  "));
728         }
729     }
730     push(@text, "");
731 }
732
733 #
734 # Now generate the extra info for the mail message..
735 #
736 if ($rcsidinfo == 1) {
737     $revhdr = 0;
738     for ($i = 0; ; $i++) {
739         last if (! -e "$LOG_FILE.$i.$id.$cvs_user");
740         if (-e "$SUMMARY_FILE.$i.$id.$cvs_user") {
741             if (!$revhdr++) {
742                 push(@text, "Revision  Changes    Path");
743             }
744             push(@text, &read_logfile("$SUMMARY_FILE.$i.$id.$cvs_user", ""));
745         }
746     }
747     if ($revhdr) {
748         push(@text, "");        # consistancy...
749     }
750 }
751
752 %mlist_hash = ();
753
754 foreach (@mlist_list) { $mlist_hash{ $_ } = 1; }
755
756 #
757 # Mail out the notification.
758 #
759 &mail_notification([ keys(%mlist_hash) ], @text);
760 &cleanup_tmpfiles;
761 exit 0;