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