1175261Sobrien#! @PERL@ -T
281404Speter# -*-Perl-*-
3175261Sobrien
4175261Sobrien# Copyright (C) 1994-2005 The Free Software Foundation, Inc.
5175261Sobrien
6175261Sobrien# This program is free software; you can redistribute it and/or modify
7175261Sobrien# it under the terms of the GNU General Public License as published by
8175261Sobrien# the Free Software Foundation; either version 2, or (at your option)
9175261Sobrien# any later version.
1081404Speter#
11175261Sobrien# This program is distributed in the hope that it will be useful,
12175261Sobrien# but WITHOUT ANY WARRANTY; without even the implied warranty of
13175261Sobrien# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14175261Sobrien# GNU General Public License for more details.
15175261Sobrien
16175261Sobrien###############################################################################
17175261Sobrien###############################################################################
18175261Sobrien###############################################################################
19175261Sobrien#
20175261Sobrien# THIS SCRIPT IS PROBABLY BROKEN.  REMOVING THE -T SWITCH ON THE #! LINE ABOVE
21175261Sobrien# WOULD FIX IT, BUT THIS IS INSECURE.  WE RECOMMEND FIXING THE ERRORS WHICH THE
22175261Sobrien# -T SWITCH WILL CAUSE PERL TO REPORT BEFORE RUNNING THIS SCRIPT FROM A CVS
23175261Sobrien# SERVER TRIGGER.  PLEASE SEND PATCHES CONTAINING THE CHANGES YOU FIND
24175261Sobrien# NECESSARY TO RUN THIS SCRIPT WITH THE TAINT-CHECKING ENABLED BACK TO THE
25175261Sobrien# <@PACKAGE_BUGREPORT@> MAILING LIST.
26175261Sobrien#
27175261Sobrien# For more on general Perl security and taint-checking, please try running the
28175261Sobrien# `perldoc perlsec' command.
29175261Sobrien#
30175261Sobrien###############################################################################
31175261Sobrien###############################################################################
32175261Sobrien###############################################################################
33175261Sobrien
3481404Speter# Perl filter to handle the log messages from the checkin of files in
3581404Speter# a directory.  This script will group the lists of files by log
3681404Speter# message, and mail a single consolidated log message at the end of
3781404Speter# the commit.
3881404Speter#
3981404Speter# This file assumes a pre-commit checking program that leaves the
4081404Speter# names of the first and last commit directories in a temporary file.
4181404Speter#
42128266Speter# IMPORTANT: what the above means is, this script interacts with
43128266Speter# commit_prep, in that they have to agree on the tmpfile name to use.
44128266Speter# See $LAST_FILE below. 
45128266Speter#
46128266Speter# How this works: CVS triggers this script once for each directory
47128266Speter# involved in the commit -- in other words, a single commit can invoke
48128266Speter# this script N times.  It knows when it's on the last invocation by
49128266Speter# examining the contents of $LAST_FILE.  Between invocations, it
50128266Speter# caches information for its future incarnations in various temporary
51128266Speter# files in /tmp, which are named according to the process group and
52128266Speter# the committer (by themselves, neither of these are unique, but
53128266Speter# together they almost always are, unless the same user is doing two
54128266Speter# commits simultaneously).  The final invocation is the one that
55128266Speter# actually sends the mail -- it gathers up the cached information,
56128266Speter# combines that with what it found out on this pass, and sends a
57128266Speter# commit message to the appropriate mailing list.
58128266Speter#
59128266Speter# (Ask Karl Fogel <kfogel@collab.net> if questions.)
60128266Speter#
6181404Speter# Contributed by David Hampton <hampton@cisco.com>
62128266Speter# Roy Fielding removed useless code and added log/mail of new files
63128266Speter# Ken Coar added special processing (i.e., no diffs) for binary files
6481404Speter#
6581404Speter
66128266Speter############################################################
6781404Speter#
68128266Speter# Configurable options
6981404Speter#
70128266Speter############################################################
71128266Speter#
72128266Speter# Where do you want the RCS ID and delta info?
73128266Speter# 0 = none,
74128266Speter# 1 = in mail only,
75128266Speter# 2 = in both mail and logs.
76128266Speter#
77128266Speter$rcsidinfo = 2;
7881404Speter
79128266Speter#if you are using CVS web then set this to some value... if not set it to ""
80128266Speter#
81128266Speter# When set properly, this will cause links to aspects of the project to
82128266Speter# print in the commit emails.
83128266Speter#$CVSWEB_SCHEME = "http";
84175261Sobrien#$CVSWEB_DOMAIN = "nongnu.org";
85128266Speter#$CVSWEB_PORT = "80";
86128266Speter#$CVSWEB_URI = "source/browse/";
87128266Speter#$SEND_URL = "true";
88128266Speter$SEND_DIFF = "true";
8981404Speter
90128266Speter
91128266Speter# Set this to a domain to have CVS pretend that all users who make
92128266Speter# commits have mail accounts within that domain.
93175261Sobrien#$EMULATE_LOCAL_MAIL_USER="nongnu.org"; 
94128266Speter
95128266Speter# Set this to '-c' for context diffs; defaults to '-u' for unidiff format.
96128266Speter$difftype = '-uN';
97128266Speter
98128266Speter############################################################
9981404Speter#
100128266Speter# Constants
10181404Speter#
102128266Speter############################################################
10381404Speter$STATE_NONE    = 0;
10481404Speter$STATE_CHANGED = 1;
10581404Speter$STATE_ADDED   = 2;
10681404Speter$STATE_REMOVED = 3;
10781404Speter$STATE_LOG     = 4;
10881404Speter
109128266Speter$TMPDIR        = $ENV{'TMPDIR'} || '/tmp';
110128266Speter$FILE_PREFIX   = '#cvs.';
11181404Speter
112128266Speter$LAST_FILE     = "$TMPDIR/${FILE_PREFIX}lastdir";  # Created by commit_prep!
113128266Speter$ADDED_FILE    = "$TMPDIR/${FILE_PREFIX}files.added";
114128266Speter$REMOVED_FILE  = "$TMPDIR/${FILE_PREFIX}files.removed";
115128266Speter$LOG_FILE      = "$TMPDIR/${FILE_PREFIX}files.log";
116128266Speter$BRANCH_FILE   = "$TMPDIR/${FILE_PREFIX}files.branch";
117128266Speter$MLIST_FILE    = "$TMPDIR/${FILE_PREFIX}files.mlist";
118128266Speter$SUMMARY_FILE  = "$TMPDIR/${FILE_PREFIX}files.summary";
11981404Speter
120128266Speter$CVSROOT       = $ENV{'CVSROOT'};
12181404Speter
122128266Speter$MAIL_CMD      = "| /usr/lib/sendmail -i -t";
123128266Speter#$MAIL_CMD      = "| /var/qmail/bin/qmail-inject";
124128266Speter$MAIL_FROM     = 'commitlogger';  #not needed if EMULATE_LOCAL_MAIL_USER
125128266Speter$SUBJECT_PRE   = 'CVS update:';
126128266Speter
127128266Speter
128128266Speter############################################################
12981404Speter#
130128266Speter# Subroutines
13181404Speter#
132128266Speter############################################################
13381404Speter
134128266Spetersub format_names {
135128266Speter    local($dir, @files) = @_;
136128266Speter    local(@lines);
137128266Speter
138128266Speter    $lines[0] = sprintf(" %-08s", $dir);
139128266Speter    foreach $file (@files) {
140128266Speter        if (length($lines[$#lines]) + length($file) > 60) {
141128266Speter            $lines[++$#lines] = sprintf(" %8s", " ");
142128266Speter        }
143128266Speter        $lines[$#lines] .= " ".$file;
144128266Speter    }
145128266Speter    @lines;
146128266Speter}
147128266Speter
14881404Spetersub cleanup_tmpfiles {
149128266Speter    local(@files);
15081404Speter
151128266Speter    opendir(DIR, $TMPDIR);
152128266Speter    push(@files, grep(/^${FILE_PREFIX}.*\.${id}\.${cvs_user}$/, readdir(DIR)));
15381404Speter    closedir(DIR);
15481404Speter    foreach (@files) {
155128266Speter        unlink "$TMPDIR/$_";
15681404Speter    }
15781404Speter}
15881404Speter
15981404Spetersub write_logfile {
16081404Speter    local($filename, @lines) = @_;
16181404Speter
162128266Speter    open(FILE, ">$filename") || die ("Cannot open log file $filename: $!\n");
163128266Speter    print(FILE join("\n", @lines), "\n");
16481404Speter    close(FILE);
16581404Speter}
16681404Speter
167128266Spetersub append_to_file {
168128266Speter    local($filename, $dir, @files) = @_;
16981404Speter
170128266Speter    if (@files) {
171128266Speter        local(@lines) = &format_names($dir, @files);
172128266Speter        open(FILE, ">>$filename") || die ("Cannot open file $filename: $!\n");
173128266Speter        print(FILE join("\n", @lines), "\n");
174128266Speter        close(FILE);
175128266Speter    }
176128266Speter}
177128266Speter
178128266Spetersub write_line {
179128266Speter    local($filename, $line) = @_;
180128266Speter
181128266Speter    open(FILE, ">$filename") || die("Cannot open file $filename: $!\n");
182128266Speter    print(FILE $line, "\n");
18381404Speter    close(FILE);
18481404Speter}
18581404Speter
186128266Spetersub append_line {
187128266Speter    local($filename, $line) = @_;
18881404Speter
189128266Speter    open(FILE, ">>$filename") || die("Cannot open file $filename: $!\n");
190128266Speter    print(FILE $line, "\n");
191128266Speter    close(FILE);
192128266Speter}
19381404Speter
194128266Spetersub read_line {
195128266Speter    local($filename) = @_;
196128266Speter    local($line);
19781404Speter
198128266Speter    open(FILE, "<$filename") || die("Cannot open file $filename: $!\n");
199128266Speter    $line = <FILE>;
200128266Speter    close(FILE);
201128266Speter    chomp($line);
202128266Speter    $line;
203128266Speter}
20481404Speter
205128266Spetersub read_line_nodie {
206128266Speter    local($filename) = @_;
207128266Speter    local($line);
208128266Speter    open(FILE, "<$filename") || return ("");
209128266Speter
210128266Speter    $line = <FILE>;
211128266Speter    close(FILE);
212128266Speter    chomp($line);
213128266Speter    $line;
21481404Speter}
21581404Speter
216128266Spetersub read_file_lines {
217128266Speter    local($filename) = @_;
218128266Speter    local(@text) = ();
21981404Speter
220128266Speter    open(FILE, "<$filename") || return ();
221128266Speter    while (<FILE>) {
222128266Speter        chomp;
223128266Speter        push(@text, $_);
22481404Speter    }
225128266Speter    close(FILE);
22681404Speter    @text;
22781404Speter}
22881404Speter
229128266Spetersub read_file {
230128266Speter    local($filename, $leader) = @_;
231128266Speter    local(@text) = ();
23281404Speter
233128266Speter    open(FILE, "<$filename") || return ();
234128266Speter    while (<FILE>) {
235128266Speter        chomp;
236128266Speter        push(@text, sprintf("  %-10s  %s", $leader, $_));
237128266Speter        $leader = "";
23881404Speter    }
23981404Speter    close(FILE);
240128266Speter    @text;
24181404Speter}
24281404Speter
24381404Spetersub read_logfile {
24481404Speter    local($filename, $leader) = @_;
245128266Speter    local(@text) = ();
24681404Speter
247128266Speter    open(FILE, "<$filename") || die ("Cannot open log file $filename: $!\n");
24881404Speter    while (<FILE>) {
249128266Speter        chomp;
250128266Speter        push(@text, $leader.$_);
25181404Speter    }
25281404Speter    close(FILE);
25381404Speter    @text;
25481404Speter}
25581404Speter
256128266Speter#
257128266Speter# do an 'cvs -Qn status' on each file in the arguments, and extract info.
258128266Speter#
259128266Spetersub change_summary {
260128266Speter    local($out, @filenames) = @_;
261128266Speter    local(@revline);
262128266Speter    local($file, $rev, $rcsfile, $line, $vhost, $cvsweb_base);
263128266Speter
264128266Speter    while (@filenames) {
265128266Speter        $file = shift @filenames;
266128266Speter
267128266Speter        if ("$file" eq "") {
268128266Speter            next;
269128266Speter        }
270128266Speter
271128266Speter        open(RCS, "-|") || exec "$cvsbin/cvs", '-Qn', 'status', '--', $file;
272128266Speter
273128266Speter        $rev = "";
274128266Speter        $delta = "";
275128266Speter        $rcsfile = "";
276128266Speter
277128266Speter
278128266Speter        while (<RCS>) {
279128266Speter            if (/^[ \t]*Repository revision/) {
280128266Speter                chomp;
281128266Speter                @revline = split(' ', $_);
282128266Speter                $rev = $revline[2];
283128266Speter                $rcsfile = $revline[3];
284128266Speter                $rcsfile =~ s,^$CVSROOT/,,;
285128266Speter                $rcsfile =~ s/,v$//;
286128266Speter            }
287128266Speter        }
288128266Speter        close(RCS);
289128266Speter
290128266Speter
291128266Speter        if ($rev ne '' && $rcsfile ne '') {
292128266Speter            open(RCS, "-|") || exec "$cvsbin/cvs", '-Qn', 'log', "-r$rev",
293128266Speter				    '--', $file;
294128266Speter            while (<RCS>) {
295128266Speter                if (/^date:/) {
296128266Speter                    chomp;
297128266Speter                    $delta = $_;
298128266Speter                    $delta =~ s/^.*;//;
299128266Speter                    $delta =~ s/^[\s]+lines://;
300128266Speter                }
301128266Speter            }
302128266Speter            close(RCS);
303128266Speter        }
304128266Speter
305128266Speter        $diff = "\n\n";
306175261Sobrien        $vhost = $path[0];
307128266Speter        if ($CVSWEB_PORT eq "80") {
308128266Speter          $cvsweb_base = "$CVSWEB_SCHEME://$vhost.$CVSWEB_DOMAIN/$CVSWEB_URI";
309128266Speter        }
310128266Speter        else {
311128266Speter          $cvsweb_base = "$CVSWEB_SCHEME://$vhost.$CVSWEB_DOMAIN:$CVSWEB_PORT/$CVSWEB_URI";
312128266Speter        }
313128266Speter        if ($SEND_URL eq "true") {
314128266Speter          $diff .= $cvsweb_base . join("/", @path) . "/$file";
315128266Speter        }
316128266Speter
317128266Speter        #
318128266Speter        # If this is a binary file, don't try to report a diff; not only is
319128266Speter        # it meaningless, but it also screws up some mailers.  We rely on
320128266Speter        # Perl's 'is this binary' algorithm; it's pretty good.  But not
321128266Speter        # perfect.
322128266Speter        #
323128266Speter        if (($file =~ /\.(?:pdf|gif|jpg|mpg)$/i) || (-B $file)) {
324128266Speter          if ($SEND_URL eq "true") {
325128266Speter            $diff .= "?rev=$rev&content-type=text/x-cvsweb-markup\n\n";
326128266Speter          }
327128266Speter          if ($SEND_DIFF eq "true") {
328128266Speter            $diff .= "\t<<Binary file>>\n\n";
329128266Speter          }
330128266Speter        }
331128266Speter        else {
332128266Speter            #
333128266Speter            # Get the differences between this and the previous revision,
334128266Speter            # being aware that new files always have revision '1.1' and
335128266Speter            # new branches always end in '.n.1'.
336128266Speter            #
337128266Speter            if ($rev =~ /^(.*)\.([0-9]+)$/) {
338128266Speter                $prev = $2 - 1;
339128266Speter                $prev_rev = $1 . '.' .  $prev;
340128266Speter
341128266Speter                $prev_rev =~ s/\.[0-9]+\.0$//;# Truncate if first rev on branch
342128266Speter
343128266Speter                if ($rev eq '1.1') {
344128266Speter                  if ($SEND_URL eq "true") {
345128266Speter                    $diff .= "?rev=$rev&content-type=text/x-cvsweb-markup\n\n";
346128266Speter                  }
347128266Speter                  if ($SEND_DIFF eq "true") {
348128266Speter                    open(DIFF, "-|")
349128266Speter                      || exec "$cvsbin/cvs", '-Qn', 'update', '-p', '-r1.1',
350128266Speter			      '--', $file;
351128266Speter                    $diff .= "Index: $file\n=================================="
352128266Speter                      . "=================================\n";
353128266Speter                  }
354128266Speter                }
355128266Speter                else {
356128266Speter                  if ($SEND_URL eq "true") {
357128266Speter                    $diff .= ".diff?r1=$prev_rev&r2=$rev\n\n";
358128266Speter                  }
359128266Speter                  if ($SEND_DIFF eq "true") {
360128266Speter                    $diff .= "(In the diff below, changes in quantity "
361128266Speter                      . "of whitespace are not shown.)\n\n";
362128266Speter                    open(DIFF, "-|")
363128266Speter                      || exec "$cvsbin/cvs", '-Qn', 'diff', "$difftype",
364128266Speter                      '-b', "-r$prev_rev", "-r$rev", '--', $file;
365128266Speter                  }
366128266Speter                }
367128266Speter
368128266Speter                if ($SEND_DIFF eq "true") {
369128266Speter                  while (<DIFF>) {
370128266Speter                    $diff .= $_;
371128266Speter                  }
372128266Speter                  close(DIFF);
373128266Speter                }
374128266Speter                $diff .= "\n\n";
375128266Speter            }
376128266Speter        }
377128266Speter
378128266Speter        &append_line($out, sprintf("%-9s%-12s%s%s", $rev, $delta,
379128266Speter                                   $rcsfile, $diff));
380128266Speter    }
381128266Speter}
382128266Speter
383128266Speter
38481404Spetersub build_header {
38581404Speter    local($header);
386128266Speter    delete $ENV{'TZ'};
387128266Speter    local($sec,$min,$hour,$mday,$mon,$year) = localtime(time);
388128266Speter
389128266Speter    $header = sprintf("  User: %-8s\n  Date: %02d/%02d/%02d %02d:%02d:%02d",
390128266Speter                       $cvs_user, $year%100, $mon+1, $mday,
391128266Speter                       $hour, $min, $sec);
392128266Speter#    $header = sprintf("%-8s    %02d/%02d/%02d %02d:%02d:%02d",
393128266Speter#                       $login, $year%100, $mon+1, $mday,
394128266Speter#                       $hour, $min, $sec);
39581404Speter}
39681404Speter
397128266Speter# !!! Destination Mailing-list and history file mappings here !!!
39881404Speter
399128266Speter#sub mlist_map
400128266Speter#{
401128266Speter#    local($path) = @_;
402175261Sobrien#    my $domain = "nongnu.org";
403128266Speter#    
404128266Speter#    if ($path =~ /^([^\/]+)/) {
405128266Speter#        return "cvs\@$1.$domain";
406128266Speter#    } else {
407128266Speter#        return "cvs\@$domain";
408128266Speter#    }
409128266Speter#}    
41081404Speter
411128266Spetersub derive_subject_from_changes_file ()
412128266Speter{
413128266Speter  my $subj = "";
41481404Speter
415128266Speter  for ($i = 0; ; $i++)
416128266Speter  {
417128266Speter    open (CH, "<$CHANGED_FILE.$i.$id.$cvs_user") or last;
41881404Speter
419128266Speter    while (my $change = <CH>)
420128266Speter    {
421128266Speter      # A changes file looks like this:
422128266Speter      #
423128266Speter      #  src      foo.c newfile.html
424128266Speter      #  www      index.html project_nav.html
425128266Speter      #
426128266Speter      # Each line is " Dir File1 File2 ..."
427128266Speter      # We only care about Dir, since the subject line should
428128266Speter      # summarize. 
429128266Speter      
430128266Speter      $change =~ s/^[ \t]*//;
431128266Speter      $change =~ /^([^ \t]+)[ \t]*/;
432128266Speter      my $dir = $1;
433128266Speter      # Fold to rightmost directory component
434128266Speter      $dir =~ /([^\/]+)$/;
435128266Speter      $dir = $1;
436128266Speter      if ($subj eq "") {
437128266Speter        $subj = $dir;
438128266Speter      } else {
439128266Speter        $subj .= ", $dir"; 
440128266Speter      }
44181404Speter    }
442128266Speter    close (CH);
443128266Speter  }
44481404Speter
445128266Speter  if ($subj ne "") {
446128266Speter      $subj = "MODIFIED: $subj ..."; 
447128266Speter  }
448128266Speter  else {
449128266Speter      # NPM: See if there's any file-addition notifications.
450128266Speter      my $added = &read_line_nodie("$ADDED_FILE.$i.$id.$cvs_user");
451128266Speter      if ($added ne "") {
452128266Speter          $subj .= "ADDED: $added "; 
453128266Speter      }
454128266Speter    
455128266Speter#    print "derive_subject_from_changes_file().. added== $added \n";
456128266Speter    
457128266Speter       ## NPM: See if there's any file-removal notications.
458128266Speter      my $removed = &read_line_nodie("$REMOVED_FILE.$i.$id.$cvs_user");
459128266Speter      if ($removed ne "") {
460128266Speter          $subj .= "REMOVED: $removed "; 
461128266Speter      }
462128266Speter    
463128266Speter#    print "derive_subject_from_changes_file().. removed== $removed \n";
464128266Speter    
465128266Speter      ## NPM: See if there's any branch notifications.
466128266Speter      my $branched = &read_line_nodie("$BRANCH_FILE.$i.$id.$cvs_user");
467128266Speter      if ($branched ne "") {
468128266Speter          $subj .= "BRANCHED: $branched"; 
469128266Speter      }
470128266Speter    
471128266Speter#    print "derive_subject_from_changes_file().. branched== $branched \n";
472128266Speter    
473128266Speter      ## NPM: DEFAULT: DIRECTORY CREATION (c.f. "Check for a new directory first" in main mody)
474128266Speter      if ($subj eq "") {
475128266Speter          my $subject = join("/", @path);
476128266Speter          $subj = "NEW: $subject"; 
477128266Speter      }    
478128266Speter  }
47981404Speter
480128266Speter  return $subj;
481128266Speter}
48281404Speter
483128266Spetersub mail_notification
484128266Speter{
485128266Speter    local($addr_list, @text) = @_;
486128266Speter    local($mail_to);
487128266Speter
488128266Speter    my $subj = &derive_subject_from_changes_file ();
489128266Speter
490175261Sobrien    if ($EMULATE_LOCAL_MAIL_USER ne "") {
491128266Speter        $MAIL_FROM = "$cvs_user\@$EMULATE_LOCAL_MAIL_USER";
49281404Speter    }
49381404Speter
494128266Speter    $mail_to = join(", ", @{$addr_list});
49581404Speter
496128266Speter    print "Mailing the commit message to $mail_to (from $MAIL_FROM)\n";
497128266Speter
498128266Speter    $ENV{'MAILUSER'} = $MAIL_FROM;
499128266Speter    # Commented out on hocus, so comment it out here.  -kff
500128266Speter    # $ENV{'QMAILINJECT'} = 'f';
501128266Speter
502128266Speter    open(MAIL, "$MAIL_CMD -f$MAIL_FROM");
503128266Speter    print MAIL "From: $MAIL_FROM\n";
504128266Speter    print MAIL "To: $mail_to\n";
505128266Speter    print MAIL "Subject: $SUBJECT_PRE $subj\n\n";
506128266Speter    print(MAIL join("\n", @text));
50781404Speter    close(MAIL);
508128266Speter#    print "Mailing the commit message to $MAIL_TO...\n";
509128266Speter#
510128266Speter#    #added by jrobbins@collab.net 1999/12/15
511128266Speter#    # attempt to get rid of anonymous
512128266Speter#    $ENV{'MAILUSER'} = 'commitlogger';
513128266Speter#    $ENV{'QMAILINJECT'} = 'f';
514128266Speter#
515128266Speter#    open(MAIL, "| /var/qmail/bin/qmail-inject");
516128266Speter#    print(MAIL "To: $MAIL_TO\n"); 
517128266Speter#    print(MAIL "Subject: cvs commit: $ARGV[0]\n"); 
518128266Speter#    print(MAIL join("\n", @text));
519128266Speter#    close(MAIL);
52081404Speter}
52181404Speter
522128266Speter## process the command line arguments sent to this script
523128266Speter## it returns an array of files, %s, sent from the loginfo
524128266Speter## command
525128266Spetersub process_argv
526128266Speter{
527128266Speter    local(@argv) = @_;
528128266Speter    local(@files);
529128266Speter    local($arg);
530128266Speter    print "Processing log script arguments...\n";
53181404Speter
532128266Speter    while (@argv) {
533128266Speter        $arg = shift @argv;
534128266Speter
535128266Speter        if ($arg eq '-u') {
536128266Speter                $cvs_user = shift @argv;
537128266Speter        } else {
538128266Speter                ($donefiles) && die "Too many arguments!\n";
539128266Speter                $donefiles = 1;
540128266Speter                $ARGV[0] = $arg;
541128266Speter                @files = split(' ', $arg);
542128266Speter        }
543128266Speter    }
544128266Speter    return @files;
54581404Speter}
54681404Speter
547128266Speter
548128266Speter#############################################################
54981404Speter#
550128266Speter# Main Body
55181404Speter#
552128266Speter############################################################
55381404Speter#
554128266Speter# Setup environment
55581404Speter#
556128266Speterumask (002);
55781404Speter
558128266Speter# Connect to the database
559128266Speter$cvsbin = "/usr/bin";
56081404Speter
56181404Speter#
562128266Speter# Initialize basic variables
563128266Speter#
564128266Speter$id = getpgrp();
565128266Speter$state = $STATE_NONE;
566128266Speter$cvs_user = $ENV{'USER'} || getlogin || (getpwuid($<))[0] || sprintf("uid#%d",$<);
567128266Speter@files = process_argv(@ARGV);
56881404Speter@path = split('/', $files[0]);
56981404Speterif ($#path == 0) {
57081404Speter    $dir = ".";
57181404Speter} else {
572128266Speter    $dir = join('/', @path[1..$#path]);
57381404Speter}
574128266Speter#print("ARGV  - ", join(":", @ARGV), "\n");
575128266Speter#print("files - ", join(":", @files), "\n");
576128266Speter#print("path  - ", join(":", @path), "\n");
577128266Speter#print("dir   - ", $dir, "\n");
578128266Speter#print("id    - ", $id, "\n");
57981404Speter
58081404Speter#
581128266Speter# Map the repository directory to an email address for commitlogs to be sent
582128266Speter# to.
58381404Speter#
584128266Speter#$mlist = &mlist_map($files[0]);
58581404Speter
586128266Speter##########################
58781404Speter#
588128266Speter# Check for a new directory first.  This will always appear as a
589128266Speter# single item in the argument list, and an empty log message.
59081404Speter#
591128266Speterif ($ARGV[0] =~ /New directory/) {
592128266Speter    $header = &build_header;
59381404Speter    @text = ();
594128266Speter    push(@text, $header);
59581404Speter    push(@text, "");
596128266Speter    push(@text, "  ".$ARGV[0]);
597128266Speter    &mail_notification([ $mlist ], @text);
59881404Speter    exit 0;
59981404Speter}
60081404Speter
601128266Speter#
60281404Speter# Iterate over the body of the message collecting information.
60381404Speter#
60481404Speterwhile (<STDIN>) {
605128266Speter    chomp;                      # Drop the newline
606128266Speter    if (/^Revision\/Branch:/) {
607128266Speter        s,^Revision/Branch:,,;
608128266Speter        push (@branch_lines, split);
609128266Speter        next;
61081404Speter    }
611128266Speter#    next if (/^[ \t]+Tag:/ && $state != $STATE_LOG);
61281404Speter    if (/^Modified Files/) { $state = $STATE_CHANGED; next; }
61381404Speter    if (/^Added Files/)    { $state = $STATE_ADDED;   next; }
61481404Speter    if (/^Removed Files/)  { $state = $STATE_REMOVED; next; }
61581404Speter    if (/^Log Message/)    { $state = $STATE_LOG;     next; }
616128266Speter    s/[ \t\n]+$//;              # delete trailing space
61781404Speter    
618128266Speter    push (@changed_files, split) if ($state == $STATE_CHANGED);
619128266Speter    push (@added_files,   split) if ($state == $STATE_ADDED);
620128266Speter    push (@removed_files, split) if ($state == $STATE_REMOVED);
621128266Speter    if ($state == $STATE_LOG) {
622128266Speter        if (/^PR:$/i ||
623128266Speter            /^Reviewed by:$/i ||
624128266Speter            /^Submitted by:$/i ||
625128266Speter            /^Obtained from:$/i) {
626128266Speter            next;
627128266Speter        }
628128266Speter        push (@log_lines,     $_);
629128266Speter    }
63081404Speter}
63181404Speter
632128266Speter#
63381404Speter# Strip leading and trailing blank lines from the log message.  Also
63481404Speter# compress multiple blank lines in the body of the message down to a
63581404Speter# single blank line.
636128266Speter# (Note, this only does the mail and changes log, not the rcs log).
63781404Speter#
63881404Speterwhile ($#log_lines > -1) {
63981404Speter    last if ($log_lines[0] ne "");
64081404Speter    shift(@log_lines);
64181404Speter}
64281404Speterwhile ($#log_lines > -1) {
64381404Speter    last if ($log_lines[$#log_lines] ne "");
64481404Speter    pop(@log_lines);
64581404Speter}
64681404Speterfor ($i = $#log_lines; $i > 0; $i--) {
64781404Speter    if (($log_lines[$i - 1] eq "") && ($log_lines[$i] eq "")) {
648128266Speter        splice(@log_lines, $i, 1);
64981404Speter    }
65081404Speter}
65181404Speter
65281404Speter#
653128266Speter# Find the log file that matches this log message
654128266Speter#
65581404Speterfor ($i = 0; ; $i++) {
656128266Speter    last if (! -e "$LOG_FILE.$i.$id.$cvs_user");
657128266Speter    @text = &read_logfile("$LOG_FILE.$i.$id.$cvs_user", "");
658128266Speter    last if ($#text == -1);
659128266Speter    last if (join(" ", @log_lines) eq join(" ", @text));
66081404Speter}
66181404Speter
662128266Speter#
66381404Speter# Spit out the information gathered in this pass.
66481404Speter#
665128266Speter&write_logfile("$LOG_FILE.$i.$id.$cvs_user", @log_lines);
666128266Speter&append_to_file("$BRANCH_FILE.$i.$id.$cvs_user",  $dir, @branch_lines);
667128266Speter&append_to_file("$ADDED_FILE.$i.$id.$cvs_user",   $dir, @added_files);
668128266Speter&append_to_file("$CHANGED_FILE.$i.$id.$cvs_user", $dir, @changed_files);
669128266Speter&append_to_file("$REMOVED_FILE.$i.$id.$cvs_user", $dir, @removed_files);
670128266Speter&append_line("$MLIST_FILE.$i.$id.$cvs_user", $mlist);
671128266Speterif ($rcsidinfo) {
672128266Speter    &change_summary("$SUMMARY_FILE.$i.$id.$cvs_user", (@changed_files, @added_files));
673128266Speter}
67481404Speter
675128266Speter#
67681404Speter# Check whether this is the last directory.  If not, quit.
67781404Speter#
678128266Speterif (-e "$LAST_FILE.$id.$cvs_user") {
679128266Speter   $_ = &read_line("$LAST_FILE.$id.$cvs_user");
680128266Speter   $tmpfiles = $files[0];
681128266Speter   $tmpfiles =~ s,([^a-zA-Z0-9_/]),\\$1,g;
682128266Speter   if (! grep(/$tmpfiles$/, $_)) {
683128266Speter        print "More commits to come...\n";
684128266Speter        exit 0
685128266Speter   }
68681404Speter}
68781404Speter
68881404Speter#
68981404Speter# This is it.  The commits are all finished.  Lump everything together
69081404Speter# into a single message, fire a copy off to the mailing list, and drop
69181404Speter# it on the end of the Changes file.
69281404Speter#
693128266Speter$header = &build_header;
69481404Speter
69581404Speter#
69681404Speter# Produce the final compilation of the log messages
69781404Speter#
69881404Speter@text = ();
699128266Speter@mlist_list = ();
700128266Speterpush(@text, $header);
70181404Speterpush(@text, "");
70281404Speterfor ($i = 0; ; $i++) {
703128266Speter    last if (! -e "$LOG_FILE.$i.$id.$cvs_user");
704128266Speter    push(@text, &read_file("$BRANCH_FILE.$i.$id.$cvs_user", "Branch:"));
705128266Speter    push(@text, &read_file("$CHANGED_FILE.$i.$id.$cvs_user", "Modified:"));
706128266Speter    push(@text, &read_file("$ADDED_FILE.$i.$id.$cvs_user", "Added:"));
707128266Speter    push(@text, &read_file("$REMOVED_FILE.$i.$id.$cvs_user", "Removed:"));
708128266Speter    push(@text, "  Log:");
709128266Speter    push(@text, &read_logfile("$LOG_FILE.$i.$id.$cvs_user", "  "));
710128266Speter    push(@mlist_list, &read_file_lines("$MLIST_FILE.$i.$id.$cvs_user"));
711128266Speter    if ($rcsidinfo == 2) {
712128266Speter        if (-e "$SUMMARY_FILE.$i.$id.$cvs_user") {
713128266Speter            push(@text, "  ");
714128266Speter            push(@text, "  Revision  Changes    Path");
715128266Speter            push(@text, &read_logfile("$SUMMARY_FILE.$i.$id.$cvs_user", "  "));
716128266Speter        }
71781404Speter    }
718128266Speter    push(@text, "");
71981404Speter}
72081404Speter
72181404Speter#
722128266Speter# Now generate the extra info for the mail message..
723128266Speter#
724128266Speterif ($rcsidinfo == 1) {
725128266Speter    $revhdr = 0;
726128266Speter    for ($i = 0; ; $i++) {
727128266Speter        last if (! -e "$LOG_FILE.$i.$id.$cvs_user");
728128266Speter        if (-e "$SUMMARY_FILE.$i.$id.$cvs_user") {
729128266Speter            if (!$revhdr++) {
730128266Speter                push(@text, "Revision  Changes    Path");
731128266Speter            }
732128266Speter            push(@text, &read_logfile("$SUMMARY_FILE.$i.$id.$cvs_user", ""));
733128266Speter        }
734128266Speter    }
735128266Speter    if ($revhdr) {
736128266Speter        push(@text, "");        # consistancy...
737128266Speter    }
73881404Speter}
73981404Speter
740128266Speter%mlist_hash = ();
74181404Speter
742128266Speterforeach (@mlist_list) { $mlist_hash{ $_ } = 1; }
74381404Speter
74481404Speter#
745128266Speter# Mail out the notification.
746128266Speter#
747128266Speter&mail_notification([ keys(%mlist_hash) ], @text);
748128266Speter&cleanup_tmpfiles;
74981404Speterexit 0;
750