$MAILER = "/usr/lib/sendmail -t";
$STATE_NONE = 0;
$STATE_CHANGED = 1;
$STATE_ADDED = 2;
$STATE_REMOVED = 3;
$STATE_LOG = 4;
$LAST_FILE = "/tmp/#cvs.lastdir";
$CHANGED_FILE = "/tmp/#cvs.files.changed";
$ADDED_FILE = "/tmp/#cvs.files.added";
$REMOVED_FILE = "/tmp/#cvs.files.removed";
$LOG_FILE = "/tmp/#cvs.files.log";
$FILE_PREFIX = "#cvs.files";
sub cleanup_tmpfiles {
local($wd, @files);
$wd = `pwd`;
chdir("/tmp") || die("Can't chdir('/tmp')\n");
opendir(DIR, ".");
push(@files, grep(/^$FILE_PREFIX\..*\.$id$/, readdir(DIR)));
closedir(DIR);
foreach (@files) {
unlink $_;
}
unlink $LAST_FILE . "." . $id;
chdir($wd);
}
sub write_logfile {
local($filename, @lines) = @_;
open(FILE, ">$filename") || die("Cannot open log file $filename.\n");
print FILE join("\n", @lines), "\n";
close(FILE);
}
sub append_to_logfile {
local($filename, @lines) = @_;
open(FILE, ">$filename") || die("Cannot open log file $filename.\n");
print FILE join("\n", @lines), "\n";
close(FILE);
}
sub format_names {
local($dir, @files) = @_;
local(@lines);
$format = "\t%-" . sprintf("%d", length($dir)) . "s%s ";
$lines[0] = sprintf($format, $dir, ":");
if ($debug) {
print STDERR "format_names(): dir = ", $dir, "; files = ", join(":", @files), ".\n";
}
foreach $file (@files) {
if (length($lines[$ $lines[++$ }
$lines[$ }
@lines;
}
sub format_lists {
local(@lines) = @_;
local(@text, @files, $lastdir);
if ($debug) {
print STDERR "format_lists(): ", join(":", @lines), "\n";
}
@text = ();
@files = ();
$lastdir = shift @lines; if ($lastdir !~ /.*\/$/) {
die("Damn, $lastdir doesn't look like a directory!\n");
}
foreach $line (@lines) {
if ($line =~ /.*\/$/) {
push(@text, &format_names($lastdir, @files));
$lastdir = $line;
@files = ();
} else {
push(@files, $line);
}
}
push(@text, &format_names($lastdir, @files));
@text;
}
sub append_names_to_file {
local($filename, $dir, @files) = @_;
if (@files) {
open(FILE, ">>$filename") || die("Cannot open file $filename.\n");
print FILE $dir, "\n";
print FILE join("\n", @files), "\n";
close(FILE);
}
}
sub read_line {
local($line);
local($filename) = @_;
open(FILE, "<$filename") || die("Cannot open file $filename.\n");
$line = <FILE>;
close(FILE);
chop($line);
$line;
}
sub read_logfile {
local(@text);
local($filename, $leader) = @_;
open(FILE, "<$filename");
while (<FILE>) {
chop;
push(@text, $leader.$_);
}
close(FILE);
@text;
}
sub build_header {
local($header);
local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$header = sprintf("CVSROOT:\t%s\nModule name:\t%s\nRepository:\t%s\nChanges by:\t%s@%s\t%02d/%02d/%02d %02d:%02d:%02d",
$cvsroot,
$modulename,
$dir,
$login, $hostdomain,
$year%100, $mon+1, $mday,
$hour, $min, $sec);
}
sub mail_notification {
local(@text) = @_;
local($[) = 0;
@DoW = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
@MoY = ('Jan','Feb','Mar','Apr','May','Jun',
'Jul','Aug','Sep','Oct','Nov','Dec');
$TZ = defined($ENV{'TZ'}) ? ( $ENV{'TZ'} ? $ENV{'TZ'} : 'GMT' ) : '';
if ($TZ =~ /^([^:\d+\-,]{3,})([+-]?\d{1,2}(:\d{1,2}){0,2})([^\d+\-,]{3,})?/) {
$TZ = $isdst ? $4 : $1;
$tzoff = sprintf("%05d", -($2) * 100);
}
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst, $zone, $gmtoff) =
($TZ eq 'GMT') ? gmtime(time) : localtime(time);
$year += ($year < 70) ? 2000 : 1900;
if ($gmtoff != 0) {
$tzoff = sprintf("%05d", ($gmtoff / 60) * 100);
}
if ($zone ne '') {
$TZ = $zone;
}
$rfc822date = sprintf("%s, %2d %s %4d %2d:%02d:%02d %s (%s)",
$DoW[$wday], $mday, $MoY[$mon], $year,
$hour, $min, $sec, $tzoff, $TZ);
open(MAIL, "| $MAILER");
print MAIL "Date: " . $rfc822date . "\n";
print MAIL "Subject: CVS Update: " . $modulename . "\n";
print MAIL "To: " . $mailto . "\n";
print MAIL "Reply-To: " . $replyto . "\n";
print MAIL "\n";
print MAIL join("\n", @text), "\n";
close(MAIL);
}
sub write_commitlog {
local($logfile, @text) = @_;
open(FILE, ">>$logfile");
print FILE join("\n", @text), "\n";
close(FILE);
}
$debug = 0;
$id = getpgrp(); $state = $STATE_NONE;
$login = getlogin || (getpwuid($<))[0] || "nobody";
chop($hostname = `hostname`);
chop($domainname = `domainname`);
if ($domainname !~ '^\..*') {
$domainname = '.' . $domainname;
}
$hostdomain = $hostname . $domainname;
$cvsroot = $ENV{'CVSROOT'};
$do_status = 1; $show_wd = 0; $modulename = "";
while (@ARGV) {
$arg = shift @ARGV;
if ($arg eq '-d') {
$debug = 1;
print STDERR "Debug turned on...\n";
} elsif ($arg eq '-m') {
if ($mailto eq '') {
$mailto = shift @ARGV;
} else {
$mailto = $mailto . ", " . shift @ARGV;
}
} elsif ($arg eq '-R') {
if ($replyto eq '') {
$replyto = shift @ARGV;
} else {
$replyto = $replyto . ", " . shift @ARGV;
}
} elsif ($arg eq '-M') {
$modulename = shift @ARGV;
} elsif ($arg eq '-s') {
$do_status = 0;
} elsif ($arg eq '-w') {
$show_wd = 1;
} elsif ($arg eq '-f') {
($commitlog) && die("Too many '-f' args\n");
$commitlog = shift @ARGV;
} else {
($donefiles) && die("Too many arguments! Check usage.\n");
$donefiles = 1;
@files = split(/ /, $arg);
}
}
($mailto) || die("No mail recipient specified (use -m)\n");
if ($replyto eq '') {
$replyto = $login;
}
@path = split('/', $files[0]);
if ($modulename eq "") {
$modulename = $path[0]; }
if ($ $dir = ".";
} else {
$dir = join('/', @path);
}
$dir = $dir . "/";
if ($debug) {
print STDERR "module - ", $modulename, "\n";
print STDERR "dir - ", $dir, "\n";
print STDERR "path - ", join(":", @path), "\n";
print STDERR "files - ", join(":", @files), "\n";
print STDERR "id - ", $id, "\n";
}
if ($files[2] =~ /New/ && $files[3] =~ /directory/) {
local(@text);
@text = ();
push(@text, &build_header());
push(@text, "");
push(@text, $files[0]);
push(@text, "");
while (<STDIN>) {
chop; push(@text, $_);
}
&mail_notification($mailto, @text);
exit 0;
}
if ($files[2] =~ /Imported/ && $files[3] =~ /sources/) {
local(@text);
@text = ();
push(@text, &build_header());
push(@text, "");
push(@text, $files[0]);
push(@text, "");
while (<STDIN>) {
chop; push(@text, $_);
}
&mail_notification(@text);
exit 0;
}
while (<STDIN>) {
chop;
if (/^In directory/) {
if ($show_wd) { push(@log_lines, $_);
push(@log_lines, "");
}
next;
}
if (/^Modified Files/) { $state = $STATE_CHANGED; next; }
if (/^Added Files/) { $state = $STATE_ADDED; next; }
if (/^Removed Files/) { $state = $STATE_REMOVED; next; }
if (/^Log Message/) { $state = $STATE_LOG; next; }
s/^[ \t\n]+//; # delete leading whitespace
s/[ \t\n]+$//; # delete trailing whitespace
if ($state == $STATE_CHANGED) { push(@changed_files, split); }
if ($state == $STATE_ADDED) { push(@added_files, split); }
if ($state == $STATE_REMOVED) { push(@removed_files, split); }
if ($state == $STATE_LOG) { push(@log_lines, $_); }
}
while ($ last if ($log_lines[0] ne "");
shift(@log_lines);
}
while ($ last if ($log_lines[$ pop(@log_lines);
}
for ($i = $ if (($log_lines[$i - 1] eq "") && ($log_lines[$i] eq "")) {
splice(@log_lines, $i, 1);
}
}
if ($debug) {
print STDERR "Searching for log file index...";
}
for ($i = 0; ; $i++) {
local(@text);
last if (! -e "$LOG_FILE.$i.$id"); @text = &read_logfile("$LOG_FILE.$i.$id", "");
last if ($ last if (join(" ", @log_lines) eq join(" ", @text)); }
if ($debug) {
print STDERR " found log file at $i.$id, now writing tmp files.\n";
}
&append_names_to_file("$CHANGED_FILE.$i.$id", $dir, @changed_files);
&append_names_to_file("$ADDED_FILE.$i.$id", $dir, @added_files);
&append_names_to_file("$REMOVED_FILE.$i.$id", $dir, @removed_files);
&write_logfile("$LOG_FILE.$i.$id", @log_lines);
if ($debug) {
print STDERR "Checking current dir against last dir.\n";
}
$_ = &read_line("$LAST_FILE.$id");
if ($_ ne $cvsroot . "/" . $files[0]) {
if ($debug) {
print STDERR sprintf("Current directory %s is not last directory %s.\n", $cvsroot . "/" .$files[0], $_);
}
exit 0;
}
if ($debug) {
print STDERR sprintf("Current directory %s is last directory %s -- all commits done.\n", $files[0], $_);
}
@text = ();
@status_txt = ();
push(@text, &build_header());
push(@text, "");
for ($i = 0; ; $i++) {
last if (! -e "$LOG_FILE.$i.$id"); @lines = &read_logfile("$CHANGED_FILE.$i.$id", "");
if ($ push(@text, "Modified files:");
push(@text, &format_lists(@lines));
}
@lines = &read_logfile("$ADDED_FILE.$i.$id", "");
if ($ push(@text, "Added files:");
push(@text, &format_lists(@lines));
}
@lines = &read_logfile("$REMOVED_FILE.$i.$id", "");
if ($ push(@text, "Removed files:");
push(@text, &format_lists(@lines));
}
if ($ push(@text, "");
}
@lines = &read_logfile("$LOG_FILE.$i.$id", "\t");
if ($ push(@text, "Log message:");
push(@text, @lines);
push(@text, "");
}
if ($do_status) {
local(@changed_files);
@changed_files = ();
push(@changed_files, &read_logfile("$CHANGED_FILE.$i.$id", ""));
push(@changed_files, &read_logfile("$ADDED_FILE.$i.$id", ""));
push(@changed_files, &read_logfile("$REMOVED_FILE.$i.$id", ""));
if ($debug) {
print STDERR "main: pre-sort changed_files = ", join(":", @changed_files), ".\n";
}
sort(@changed_files);
if ($debug) {
print STDERR "main: post-sort changed_files = ", join(":", @changed_files), ".\n";
}
foreach $dofile (@changed_files) {
if ($dofile =~ /\/$/) {
next; }
if ($debug) {
print STDERR "main(): doing 'cvs -nQq status -v $dofile'\n";
}
open(STATUS, "-|") || exec 'cvs', '-nQq', 'status', '-v', $dofile;
while (<STATUS>) {
chop;
push(@status_txt, $_);
}
}
}
}
if ($commitlog) {
&write_commitlog($commitlog, @text);
}
if ($ push(@text, @status_txt);
}
&mail_notification(@text);
if (! $debug) {
&cleanup_tmpfiles();
}
exit 0;