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