# history and logs, available at http://subversion.tigris.org/.
# ====================================================================
+# Modified by Branden Robinson, Peter Samuelson, and Mark Hymers
+# September 2004 to add $size_limit support.
+
# Turn on warnings the best way depending on the Perl version.
BEGIN {
if ( $] >= 5.006_000)
######################################################################
# Configuration section.
+# Body of the message to be sent
+my @body;
+
# Sendmail path.
my $sendmail = "/usr/sbin/sendmail";
# prints the entire contents of the file. If you want to save space
# in the log and email messages by not printing the file, then set
# $no_diff_deleted to 1.
-my $no_diff_deleted = 0;
+my $no_diff_deleted = 1;
# By default, when a file is added to the repository, svnlook diff
# prints the entire contents of the file. If you want to save space
# in the log and email messages by not printing the file, then set
# $no_diff_added to 1.
-my $no_diff_added = 0;
+my $no_diff_added = 1;
# End of Configuration section.
######################################################################
# Use the reference to the first project to populate.
my $current_project = $project_settings_list[0];
+my $size_limit = 0;
# This hash matches the command line option to the hash key in the
# project. If a key exists but has a false value (''), then the
'-l' => 'log_file',
'-m' => '',
'-r' => 'reply_to',
- '-s' => 'subject_prefix');
+ '-s' => 'subject_prefix',
+ '--size-limit' => '');
while (@ARGV)
{
}
else
{
- # Here handle -m.
- unless ($arg eq '-m')
+ # Handle -m and --size-limit here.
+ if ($arg eq '-m')
+ {
+ $current_project = &new_project;
+ $current_project->{match_regex} = $value;
+ push(@project_settings_list, $current_project);
+ }
+ elsif ($arg eq '--size-limit')
{
- die "$0: internal error: should only handle -m here.\n";
+ $size_limit = $value;
+ # Validate the specified diff size limit.
+ if ($size_limit ne '' and $size_limit ne -1 and $size_limit !~ /^\d+$/)
+ {
+ die "$0: --size-limit takes only a positive integer or -1"
+ . " argument; \"$size_limit\" is neither\n";
+ }
+ }
+ else
+ {
+ die "$0: internal error: should only handle -m and --size-limit"
+ . "here.\n";
}
- $current_project = &new_project;
- $current_project->{match_regex} = $value;
- push(@project_settings_list, $current_project);
}
}
elsif ($arg =~ /^-/)
or die "$0: cannot chdir `$tmp_dir': $!\n";
# Get the author, date, and log from svnlook.
-my @svnlooklines = &read_from_process($svnlook, 'info', $repos, '-r', $rev);
+my ($lines_size, @svnlooklines) = &read_from_process(0, $svnlook, 'info',
+ $repos, '-r', $rev);
my $author = shift @svnlooklines;
my $date = shift @svnlooklines;
shift @svnlooklines;
my @log = map { "$_\n" } @svnlooklines;
+# Add header to body
+push(@body, "Author: $author\n");
+push(@body, "Date: $date\n");
+push(@body, "New Revision: $rev\n");
+push(@body, "\n");
+
# Figure out what directories have changed using svnlook.
-my @dirschanged = &read_from_process($svnlook, 'dirs-changed', $repos,
- '-r', $rev);
+my ($dirs_size, @dirschanged) = &read_from_process(0, $svnlook, 'dirs-changed',
+ $repos, '-r', $rev);
# Lose the trailing slash in the directory names if one exists, except
# in the case of '/'.
}
# Figure out what files have changed using svnlook.
-@svnlooklines = &read_from_process($svnlook, 'changed', $repos, '-r', $rev);
+my $look_size;
+($look_size, @svnlooklines) = &read_from_process(0, $svnlook, 'changed', $repos,
+ '-r', $rev);
# Parse the changed nodes.
my @adds;
}
}
-# Get the diff from svnlook.
-my @no_diff_deleted = $no_diff_deleted ? ('--no-diff-deleted') : ();
-my @no_diff_added = $no_diff_added ? ('--no-diff-added') : ();
-my @difflines = &read_from_process($svnlook, 'diff', $repos,
- '-r', $rev, @no_diff_deleted,
- @no_diff_added);
+# Add the adds, dels and mods to the body of the message.
+if (@adds)
+ {
+ @adds = sort @adds;
+ push(@body, "Added:\n");
+ push(@body, map { " $_\n" } @adds);
+ }
+if (@dels)
+ {
+ @dels = sort @dels;
+ push(@body, "Removed:\n");
+ push(@body, map { " $_\n" } @dels);
+ }
+if (@mods)
+ {
+ @mods = sort @mods;
+ push(@body, "Modified:\n");
+ push(@body, map { " $_\n" } @mods);
+ }
+
+my @difflines;
+my $diff_howto = "Use \"svn diff" . " -r " . ($rev - 1) . ":$rev\" to view"
+ . " diff.\n";
+
+# Work out how many bytes we have available for the diff.
+my $size_avail = 0;
+if ($size_limit > 0)
+ {
+ my $bodylen = 0;
+ for (@body) { $bodylen += length($_); }
+ $size_avail = $size_limit - $bodylen;
+
+ warn "sl= $size_limit -- sa = $size_avail";
+
+ if ($size_avail <= 0)
+ {
+ @difflines = ( "Diff skipped; message reached limit of $size_limit"
+ . " bytes with list of changed paths.\n$diff_howto" );
+ }
+}
+# A $size_limit of -1 means we do not include a diff.
+if ($size_limit ne -1)
+ {
+ # Get the diff from svnlook.
+ my @no_diff_deleted = $no_diff_deleted ? ('--no-diff-deleted') : ();
+ my @no_diff_added = $no_diff_added ? ('--no-diff-added') : ();
+ my $numbytes;
+ ($numbytes, @difflines) = &read_from_process($size_avail, $svnlook, 'diff',
+ $repos, '-r', $rev,
+ @no_diff_deleted, @no_diff_added);
+ # If the diff is larger than the remaining size limit, we must discard
+ # it.
+ if ($numbytes == -1) {
+ @difflines = ( "Including diff would make mail exceed size limit of"
+ . " $size_limit bytes.\n$diff_howto" );
+ }
+ }
+else
+ {
+ @difflines = ( $diff_howto );
+ }
+
######################################################################
# Modified directory name collapsing.
}
my $dirlist = join(' ', @dirschanged);
-######################################################################
-# Assembly of log message.
-
-# Put together the body of the log message.
-my @body;
-push(@body, "Author: $author\n");
-push(@body, "Date: $date\n");
-push(@body, "New Revision: $rev\n");
-push(@body, "\n");
-if (@adds)
- {
- @adds = sort @adds;
- push(@body, "Added:\n");
- push(@body, map { " $_\n" } @adds);
- }
-if (@dels)
- {
- @dels = sort @dels;
- push(@body, "Removed:\n");
- push(@body, map { " $_\n" } @dels);
- }
-if (@mods)
- {
- @mods = sort @mods;
- push(@body, "Modified:\n");
- push(@body, map { " $_\n" } @mods);
- }
push(@body, "Log:\n");
push(@body, @log);
push(@body, "\n");
my @email_addresses = @{$project->{email_addresses}};
my $userlist = join(' ', @email_addresses);
- my $to = join(', ', @email_addresses);
my $from_address = $project->{from_address};
my $hostname = $project->{hostname};
my $log_file = $project->{log_file};
}
my @head;
- push(@head, "To: $to\n");
+ push(@head, "To: $userlist\n");
push(@head, "From: $mail_from\n");
push(@head, "Subject: $subject\n");
push(@head, "Reply-to: $reply_to\n") if $reply_to;
" -m regex Regular expression to match committed path\n",
" -r email_address Email address for 'Reply-To:'\n",
" -s subject_prefix Subject line prefix\n",
+ " --size-limit limit Message size limit in bytes (positive\n",
+ " integer); if message exceeds limit, diff is\n",
+ " omitted; if set to -1, diff is never sent\n",
"\n",
"This script supports a single repository with multiple projects,\n",
"where each project receives email only for commits that modify that\n",
}
# Start a child process safely without using /bin/sh.
+#
+# We take a parameter, $limit, which if greater than zero will limit the
+# amount we read -- this is a hack to avoid OOM errors. If we return
+# $read_size == -1, we exceeded the limit.
sub safe_read_from_pipe
{
unless (@_)
croak "$0: safe_read_from_pipe passed no arguments.\n";
}
+ my $limit = shift @_;
my $pid = open(SAFE_READ, '-|');
unless (defined $pid)
{
or die "$0: cannot exec `@_': $!\n";
}
my @output;
+ my $read_size = 0;
while (<SAFE_READ>)
{
s/[\r\n]+$//;
+ $read_size += length;
+ if (($limit > 0) and ($read_size > $limit))
+ {
+ $read_size = -1;
+ @output = ("output size exceeds specified limit of " . $limit);
+ last;
+ }
push(@output, $_);
}
close(SAFE_READ);
}
if (wantarray)
{
- return ($result, @output);
+ return ($result, $read_size, @output);
}
else
{
# Use safe_read_from_pipe to start a child process safely and return
# the output if it succeeded or an error message followed by the output
-# if it failed.
+# if it failed. Returns number of bytes read and the output.
sub read_from_process
{
unless (@_)
{
croak "$0: read_from_process passed no arguments.\n";
}
- my ($status, @output) = &safe_read_from_pipe(@_);
- if ($status)
+ my ($status, $read_size, @output) = &safe_read_from_pipe(@_);
+ if ($read_size >= 0 and $status)
{
return ("$0: `@_' failed with this output:", @output);
}
else
{
- return @output;
+ return ($read_size, @output);
}
}