Changeset 719


Ignore:
Timestamp:
Apr 9, 2008, 9:12:36 PM (16 years ago)
Author:
price
Message:
upgrade commit-email.pl.

This is the Subversion 1.5 upstream version, with my patch
to support an option --summary for setting the subject line
from the first line of the log message.

Also has --stdout for testing without sending mail.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • locker/sbin/commit-email.pl

    r375 r719  
    22
    33# ====================================================================
    4 # commit-email.pl: send a commit email for commit REVISION in
    5 # repository REPOS to some email addresses.
     4# commit-email.pl: send a notification email describing either a
     5# commit or a revprop-change action on a Subversion repository.
    66#
    77# For usage, see the usage subroutine or run the script with no
    88# command line arguments.
    99#
    10 # $HeadURL$
    11 # $LastChangedDate$
    12 # $LastChangedBy$
    13 # $LastChangedRevision$
    14 #   
     10# This script requires Subversion 1.2.0 or later.
     11#
     12# $HeadURL: http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/commit-email.pl.in $
     13# $LastChangedDate: 2008-04-01 13:19:34 -0400 (Tue, 01 Apr 2008) $
     14# $LastChangedBy: glasser $
     15# $LastChangedRevision: 30158 $
     16#
    1517# ====================================================================
    16 # Copyright (c) 2000-2004 CollabNet.  All rights reserved.
     18# Copyright (c) 2000-2006 CollabNet.  All rights reserved.
    1719#
    1820# This software is licensed as described in the file COPYING, which
     
    2830
    2931# Turn on warnings the best way depending on the Perl version.
    30 BEGIN {                                                                         
    31   if ( $] >= 5.006_000)                                                         
    32     { require warnings; import warnings; }               
    33   else                                                                         
    34     { $^W = 1; }                                                 
    35 }                                                                               
    36                                                
     32BEGIN {
     33  if ( $] >= 5.006_000)
     34    { require warnings; import warnings; }
     35  else
     36    { $^W = 1; }
     37}
     38
    3739use strict;
    3840use Carp;
     41use POSIX qw(strftime);
     42my ($sendmail, $smtp_server);
    3943
    4044######################################################################
    4145# Configuration section.
    4246
    43 # Sendmail path.
    44 my $sendmail = "/usr/sbin/sendmail";
     47# Sendmail path, or SMTP server address.
     48# You should define exactly one of these two configuration variables,
     49# leaving the other commented out, to select which method of sending
     50# email should be used.
     51# Using --stdout on the command line overrides both.
     52$sendmail = "/usr/sbin/sendmail";
     53#$smtp_server = "127.0.0.1";
    4554
    4655# Svnlook path.
     
    5261# $no_diff_deleted to 1.
    5362my $no_diff_deleted = 0;
    54 
    55 # Since the path to svnlook depends upon the local installation
    56 # preferences, check that the required programs exist to insure that
    57 # the administrator has set up the script properly.
     63# By default, when a file is added to the repository, svnlook diff
     64# prints the entire contents of the file.  If you want to save space
     65# in the log and email messages by not printing the file, then set
     66# $no_diff_added to 1.
     67my $no_diff_added = 0;
     68
     69# End of Configuration section.
     70######################################################################
     71
     72# Check that the required programs exist, and the email sending method
     73# configuration is sane, to ensure that the administrator has set up
     74# the script properly.
    5875{
    5976  my $ok = 1;
    6077  foreach my $program ($sendmail, $svnlook)
    6178    {
     79      next if not defined $program;
    6280      if (-e $program)
    6381        {
     
    7593        }
    7694    }
     95  if (not (defined $sendmail xor defined $smtp_server))
     96    {
     97      warn "$0: exactly one of \$sendmail or \$smtp_server must be ",
     98           "set, edit $0.\n";
     99      $ok = 0;
     100    }
    77101  exit 1 unless $ok;
    78102}
    79103
     104require Net::SMTP if defined $smtp_server;
    80105
    81106######################################################################
     
    87112my @project_settings_list = (&new_project);
    88113
    89 # Process the command line arguments till there are none left.  The
    90 # first two arguments that are not used by a command line option are
    91 # the repository path and the revision number.
     114# Process the command line arguments till there are none left.
     115# In commit mode: The first two arguments that are not used by a command line
     116# option are the repository path and the revision number.
     117# In revprop-change mode: The first four arguments that are not used by a
     118# command line option are the repository path, the revision number, the
     119# author, and the property name. This script has no support for the fifth
     120# argument (action) added to the post-revprop-change hook in Subversion
     121# 1.2.0 yet - patches welcome!
    92122my $repos;
    93123my $rev;
     124my $author;
     125my $propname;
     126
     127my $mode = 'commit';
     128my $date;
     129my $diff_file;
    94130
    95131# Use the reference to the first project to populate.
     
    100136# command line option is allowed but requires special handling.
    101137my %opt_to_hash_key = ('--from' => 'from_address',
     138                       '--revprop-change' => '',
     139                       '-d'     => '',
    102140                       '-h'     => 'hostname',
    103141                       '-l'     => 'log_file',
    104142                       '-m'     => '',
    105143                       '-r'     => 'reply_to',
    106                        '-s'     => 'subject_prefix');
     144                       '-s'     => 'subject_prefix',
     145                       '--summary' => '',
     146                       '--diff' => '',
     147                       '--stdout' => '');
    107148
    108149while (@ARGV)
     
    117158          }
    118159
    119         unless (@ARGV)
    120           {
    121             die "$0: command line option `$arg' is missing a value.\n";
    122           }
    123         my $value = shift @ARGV;
     160        my $value;
     161        if ($arg ne '--revprop-change' and $arg ne '--stdout' and $arg ne '--summary')
     162          {
     163            unless (@ARGV)
     164              {
     165                die "$0: command line option `$arg' is missing a value.\n";
     166              }
     167            $value = shift @ARGV;
     168          }
    124169
    125170        if ($hash_key)
     
    129174        else
    130175          {
    131             # Here handle -m.
    132             unless ($arg eq '-m')
    133               {
    134                 die "$0: internal error: should only handle -m here.\n";
    135               }
    136             $current_project                = &new_project;
    137             $current_project->{match_regex} = $value;
    138             push(@project_settings_list, $current_project);
    139           }
    140       }
    141     elsif ($arg =~ /^-/)
    142       {
    143         die "$0: command line option `$arg' is not recognized.\n";
     176            if ($arg eq '-m')
     177              {
     178                $current_project                = &new_project;
     179                $current_project->{match_regex} = $value;
     180                push(@project_settings_list, $current_project);
     181              }
     182            elsif ($arg eq '-d')
     183              {
     184                if ($mode ne 'revprop-change')
     185                  {
     186                    die "$0: `-d' is valid only when used after"
     187                      . " `--revprop-change'.\n";
     188                  }
     189                if ($diff_file)
     190                  {
     191                    die "$0: command line option `$arg'"
     192                      . " can only be used once.\n";
     193                  }
     194                $diff_file = $value;
     195              }
     196            elsif ($arg eq '--revprop-change')
     197              {
     198                if (defined $repos)
     199                  {
     200                    die "$0: `--revprop-change' must be specified before"
     201                      . " the first non-option argument.\n";
     202                  }
     203                $mode = 'revprop-change';
     204              }
     205            elsif ($arg eq '--diff')
     206              {
     207                $current_project->{show_diff} = parse_boolean($value);
     208              }
     209            elsif ($arg eq '--stdout')
     210              {
     211                $current_project->{stdout} = 1;
     212              }
     213            elsif ($arg eq '--summary')
     214              {
     215                $current_project->{summary} = 1;
     216              }
     217            else
     218              {
     219                die "$0: internal error:"
     220                  . " should not be handling `$arg' here.\n";
     221              }
     222          }
    144223      }
    145224    else
     
    153232            $rev = $arg;
    154233          }
     234        elsif (! defined $author && $mode eq 'revprop-change')
     235          {
     236            $author = $arg;
     237          }
     238        elsif (! defined $propname && $mode eq 'revprop-change')
     239          {
     240            $propname = $arg;
     241          }
    155242        else
    156243          {
     
    160247  }
    161248
    162 # If the revision number is undefined, then there were not enough
    163 # command line arguments.
    164 &usage("$0: too few arguments.") unless defined $rev;
     249if ($mode eq 'commit')
     250  {
     251    &usage("$0: too few arguments.") unless defined $rev;
     252  }
     253elsif ($mode eq 'revprop-change')
     254  {
     255    &usage("$0: too few arguments.") unless defined $propname;
     256  }
    165257
    166258# Check the validity of the command line arguments.  Check that the
     
    207299}
    208300
    209 ######################################################################
    210 # Harvest data using svnlook.
    211 
    212 # Change into /tmp so that svnlook diff can create its .svnlook
    213 # directory.
    214 my $tmp_dir = '/tmp';
    215 chdir($tmp_dir)
    216   or die "$0: cannot chdir `$tmp_dir': $!\n";
    217 
    218 # Get the author, date, and log from svnlook.
    219 my @svnlooklines = &read_from_process($svnlook, 'info', $repos, '-r', $rev);
    220 my $author = shift @svnlooklines;
    221 my $date = shift @svnlooklines;
    222 shift @svnlooklines;
    223 my @log = map { "$_\n" } @svnlooklines;
     301# Harvest common data needed for both commit or revprop-change.
    224302
    225303# Figure out what directories have changed using svnlook.
    226 my @dirschanged = &read_from_process($svnlook, 'dirs-changed', $repos, 
     304my @dirschanged = &read_from_process($svnlook, 'dirs-changed', $repos,
    227305                                     '-r', $rev);
    228306
     
    243321
    244322# Figure out what files have changed using svnlook.
    245 @svnlooklines = &read_from_process($svnlook, 'changed', $repos, '-r', $rev);
     323my @svnlooklines = &read_from_process($svnlook, 'changed', $repos, '-r', $rev);
    246324
    247325# Parse the changed nodes.
     
    276354  }
    277355
    278 # Get the diff from svnlook.
    279 my @no_diff_deleted = $no_diff_deleted ? ('--no-diff-deleted') : ();
    280 my @difflines = &read_from_process($svnlook, 'diff', $repos,
    281                                    '-r', $rev, @no_diff_deleted);
    282 
    283 ######################################################################
    284 # Modified directory name collapsing.
    285 
    286 # Collapse the list of changed directories only if the root directory
    287 # was not modified, because otherwise everything is under root and
    288 # there's no point in collapsing the directories, and only if more
    289 # than one directory was modified.
    290 my $commondir = '';
    291 if (!$rootchanged and @dirschanged > 1)
    292   {
    293     my $firstline    = shift @dirschanged;
    294     my @commonpieces = split('/', $firstline);
    295     foreach my $line (@dirschanged)
    296       {
    297         my @pieces = split('/', $line);
    298         my $i = 0;
    299         while ($i < @pieces and $i < @commonpieces)
    300           {
    301             if ($pieces[$i] ne $commonpieces[$i])
    302               {
    303                 splice(@commonpieces, $i, @commonpieces - $i);
    304                 last;
    305               }
    306             $i++;
    307           }
    308       }
    309     unshift(@dirschanged, $firstline);
    310 
    311     if (@commonpieces)
    312       {
    313         $commondir = join('/', @commonpieces);
    314         my @new_dirschanged;
    315         foreach my $dir (@dirschanged)
    316           {
    317             if ($dir eq $commondir)
    318               {
    319                 $dir = '.';
    320               }
    321             else
    322               {
    323                 $dir =~ s#^$commondir/##;
    324               }
    325             push(@new_dirschanged, $dir);
    326           }
    327         @dirschanged = @new_dirschanged;
    328       }
    329   }
    330 my $dirlist = join(' ', @dirschanged);
    331 
    332 ######################################################################
    333 # Assembly of log message.
    334 
    335 # Put together the body of the log message.
     356# Declare variables which carry information out of the inner scope of
     357# the conditional blocks below.
     358my $subject_base;
     359my $subject_logbase;
    336360my @body;
    337 push(@body, "Author: $author\n");
    338 push(@body, "Date: $date\n");
    339 push(@body, "New Revision: $rev\n");
    340 push(@body, "\n");
    341 if (@adds)
    342   {
    343     @adds = sort @adds;
    344     push(@body, "Added:\n");
    345     push(@body, map { "   $_\n" } @adds);
    346   }
    347 if (@dels)
    348   {
    349     @dels = sort @dels;
    350     push(@body, "Removed:\n");
    351     push(@body, map { "   $_\n" } @dels);
    352   }
    353 if (@mods)
    354   {
    355     @mods = sort @mods;
    356     push(@body, "Modified:\n");
    357     push(@body, map { "   $_\n" } @mods);
    358   }
    359 push(@body, "Log:\n");
    360 push(@body, @log);
    361 push(@body, "\n");
    362 push(@body, map { /[\r\n]+$/ ? $_ : "$_\n" } @difflines);
     361# $author - declared above for use as a command line parameter in
     362#   revprop-change mode.  In commit mode, gets filled in below.
     363
     364if ($mode eq 'commit')
     365  {
     366    ######################################################################
     367    # Harvest data using svnlook.
     368
     369    # Get the author, date, and log from svnlook.
     370    my @infolines = &read_from_process($svnlook, 'info', $repos, '-r', $rev);
     371    $author = shift @infolines;
     372    $date = shift @infolines;
     373    shift @infolines;
     374    my @log = map { "$_\n" } @infolines;
     375
     376    ######################################################################
     377    # Modified directory name collapsing.
     378
     379    # Collapse the list of changed directories only if the root directory
     380    # was not modified, because otherwise everything is under root and
     381    # there's no point in collapsing the directories, and only if more
     382    # than one directory was modified.
     383    my $commondir = '';
     384    my @edited_dirschanged = @dirschanged;
     385    if (!$rootchanged and @edited_dirschanged > 1)
     386      {
     387        my $firstline    = shift @edited_dirschanged;
     388        my @commonpieces = split('/', $firstline);
     389        foreach my $line (@edited_dirschanged)
     390          {
     391            my @pieces = split('/', $line);
     392            my $i = 0;
     393            while ($i < @pieces and $i < @commonpieces)
     394              {
     395                if ($pieces[$i] ne $commonpieces[$i])
     396                  {
     397                    splice(@commonpieces, $i, @commonpieces - $i);
     398                    last;
     399                  }
     400                $i++;
     401              }
     402          }
     403        unshift(@edited_dirschanged, $firstline);
     404
     405        if (@commonpieces)
     406          {
     407            $commondir = join('/', @commonpieces);
     408            my @new_dirschanged;
     409            foreach my $dir (@edited_dirschanged)
     410              {
     411                if ($dir eq $commondir)
     412                  {
     413                    $dir = '.';
     414                  }
     415                else
     416                  {
     417                    $dir =~ s#^\Q$commondir/\E##;
     418                  }
     419                push(@new_dirschanged, $dir);
     420              }
     421            @edited_dirschanged = @new_dirschanged;
     422          }
     423      }
     424    my $dirlist = join(' ', @edited_dirschanged);
     425
     426    ######################################################################
     427    # Assembly of log message.
     428
     429    if ($commondir ne '')
     430      {
     431        $subject_base = "r$rev - in $commondir: $dirlist";
     432      }
     433    else
     434      {
     435        $subject_base = "r$rev - $dirlist";
     436      }
     437    my $summary = @log ? $log[0] : '';
     438    chomp($summary);
     439    $subject_logbase = "r$rev - $summary";
     440
     441    # Put together the body of the log message.
     442    push(@body, "Author: $author\n");
     443    push(@body, "Date: $date\n");
     444    push(@body, "New Revision: $rev\n");
     445    push(@body, "\n");
     446    if (@adds)
     447      {
     448        @adds = sort @adds;
     449        push(@body, "Added:\n");
     450        push(@body, map { "   $_\n" } @adds);
     451      }
     452    if (@dels)
     453      {
     454        @dels = sort @dels;
     455        push(@body, "Removed:\n");
     456        push(@body, map { "   $_\n" } @dels);
     457      }
     458    if (@mods)
     459      {
     460        @mods = sort @mods;
     461        push(@body, "Modified:\n");
     462        push(@body, map { "   $_\n" } @mods);
     463      }
     464    push(@body, "Log:\n");
     465    push(@body, @log);
     466    push(@body, "\n");
     467  }
     468elsif ($mode eq 'revprop-change')
     469  {
     470    ######################################################################
     471    # Harvest data.
     472
     473    my @svnlines;
     474    # Get the diff file if it was provided, otherwise the property value.
     475    if ($diff_file)
     476      {
     477        open(DIFF_FILE, $diff_file) or die "$0: cannot read `$diff_file': $!\n";
     478        @svnlines = <DIFF_FILE>;
     479        close DIFF_FILE;
     480      }
     481    else
     482      {
     483        @svnlines = &read_from_process($svnlook, 'propget', '--revprop', '-r',
     484                                       $rev, $repos, $propname);
     485      }
     486
     487    ######################################################################
     488    # Assembly of log message.
     489
     490    $subject_base = "propchange - r$rev $propname";
     491
     492    # Put together the body of the log message.
     493    push(@body, "Author: $author\n");
     494    push(@body, "Revision: $rev\n");
     495    push(@body, "Property Name: $propname\n");
     496    push(@body, "\n");
     497    unless ($diff_file)
     498      {
     499        push(@body, "New Property Value:\n");
     500      }
     501    push(@body, map { /[\r\n]+$/ ? $_ : "$_\n" } @svnlines);
     502    push(@body, "\n");
     503  }
     504
     505# Cached information - calculated when first needed.
     506my @difflines;
    363507
    364508# Go through each project and see if there are any matches for this
     
    387531    my $reply_to        = $project->{reply_to};
    388532    my $subject_prefix  = $project->{subject_prefix};
    389     my $subject;
    390 
    391     if ($commondir ne '')
    392       {
    393         $subject = "r$rev - in $commondir: $dirlist";
    394       }
    395     else
    396       {
    397         $subject = "r$rev - $dirlist";
    398       }
     533    my $summary         = $project->{summary};
     534    my $diff_wanted     = ($project->{show_diff} and $mode eq 'commit');
     535    my $stdout          = $project->{stdout};
     536
     537    my $subject         = $summary ? $subject_logbase : $subject_base;
    399538    if ($subject_prefix =~ /\w/)
    400539      {
     
    411550        $mail_from = "$mail_from\@$hostname";
    412551      }
     552    elsif (defined $smtp_server and ! $stdout)
     553      {
     554        die "$0: use of either `-h' or `--from' is mandatory when ",
     555            "sending email using direct SMTP.\n";
     556      }
    413557
    414558    my @head;
     559    my $formatted_date;
     560    if (defined $stdout)
     561      {
     562        $formatted_date = strftime('%a %b %e %X %Y', localtime());
     563        push(@head, "From $mail_from $formatted_date\n");
     564      }
     565    $formatted_date = strftime('%a, %e %b %Y %X %z', localtime());
     566    push(@head, "Date: $formatted_date\n");
    415567    push(@head, "To: $to\n");
    416568    push(@head, "From: $mail_from\n");
     
    425577    # To: dev@subversion.tigris.org
    426578    # Date: Fri, 19 Jul 2002 23:42:32 -0700
    427     # 
     579    #
    428580    # Well... that isn't strictly true. The contents of the files
    429581    # might not be UTF-8, so the "diff" portion will be hosed.
    430     # 
     582    #
    431583    # If you want a truly "proper" commit message, then you'd use
    432584    # multipart MIME messages, with each file going into its own part,
    433585    # and labeled with an appropriate MIME type and charset. Of
    434586    # course, we haven't defined a charset property yet, but no biggy.
    435     # 
     587    #
    436588    # Going with multipart will surely throw out the notion of "cut
    437589    # out the patch from the email and apply." But then again: the
    438590    # commit emailer could see that all portions are in the same
    439     # charset and skip the multipart thang. 
    440     # 
     591    # charset and skip the multipart thang.
     592    #
    441593    # etc etc
    442     # 
     594    #
    443595    # Basically: adding/tweaking the content-type is nice, but don't
    444596    # think that is the proper solution.
     
    448600    push(@head, "\n");
    449601
    450     if ($sendmail =~ /\w/ and @email_addresses)
     602    if ($diff_wanted and not @difflines)
     603      {
     604        # Get the diff from svnlook.
     605        my @no_diff_deleted = $no_diff_deleted ? ('--no-diff-deleted') : ();
     606        my @no_diff_added = $no_diff_added ? ('--no-diff-added') : ();
     607        @difflines = &read_from_process($svnlook, 'diff', $repos,
     608                                        '-r', $rev, @no_diff_deleted,
     609                                        @no_diff_added);
     610        @difflines = map { /[\r\n]+$/ ? $_ : "$_\n" } @difflines;
     611      }
     612
     613    if ($stdout)
     614      {
     615        print @head, @body;
     616        print @difflines if $diff_wanted;
     617      }
     618    elsif (defined $sendmail and @email_addresses)
    451619      {
    452620        # Open a pipe to sendmail.
    453         my $command = "$sendmail $userlist";
     621        my $command = "$sendmail -f'$mail_from' $userlist";
    454622        if (open(SENDMAIL, "| $command"))
    455623          {
    456624            print SENDMAIL @head, @body;
     625            print SENDMAIL @difflines if $diff_wanted;
    457626            close SENDMAIL
    458627              or warn "$0: error in closing `$command' for writing: $!\n";
     
    463632          }
    464633      }
     634    elsif (defined $smtp_server and @email_addresses)
     635      {
     636        my $smtp = Net::SMTP->new($smtp_server)
     637          or die "$0: error opening SMTP session to `$smtp_server': $!\n";
     638        handle_smtp_error($smtp, $smtp->mail($mail_from));
     639        handle_smtp_error($smtp, $smtp->recipient(@email_addresses));
     640        handle_smtp_error($smtp, $smtp->data());
     641        handle_smtp_error($smtp, $smtp->datasend(@head, @body));
     642        if ($diff_wanted)
     643          {
     644            handle_smtp_error($smtp, $smtp->datasend(@difflines));
     645          }
     646        handle_smtp_error($smtp, $smtp->dataend());
     647        handle_smtp_error($smtp, $smtp->quit());
     648      }
    465649
    466650    # Dump the output to logfile (if its name is not empty).
     
    470654          {
    471655            print LOGFILE @head, @body;
     656            print LOGFILE @difflines if $diff_wanted;
    472657            close LOGFILE
    473658              or warn "$0: error in closing `$log_file' for appending: $!\n";
     
    481666
    482667exit 0;
     668
     669sub handle_smtp_error
     670{
     671  my ($smtp, $retval) = @_;
     672  if (not $retval)
     673    {
     674      die "$0: SMTP Error: " . $smtp->message() . "\n";
     675    }
     676}
    483677
    484678sub usage
    485679{
    486680  warn "@_\n" if @_;
    487   die "usage: $0 REPOS REVNUM [[-m regex] [options] [email_addr ...]] ...\n",
    488       "options are\n",
     681  die "usage (commit mode):\n",
     682      "  $0 REPOS REVNUM [[-m regex] [options] [email_addr ...]] ...\n",
     683      "usage: (revprop-change mode):\n",
     684      "  $0 --revprop-change REPOS REVNUM USER PROPNAME [-d diff_file] \\\n",
     685      "    [[-m regex] [options] [email_addr ...]] ...\n",
     686      "options are:\n",
     687      "  -m regex              Regular expression to match committed path\n",
    489688      "  --from email_address  Email address for 'From:' (overrides -h)\n",
    490689      "  -h hostname           Hostname to append to author for 'From:'\n",
    491690      "  -l logfile            Append mail contents to this log file\n",
    492       "  -m regex              Regular expression to match committed path\n",
    493691      "  -r email_address      Email address for 'Reply-To:'\n",
    494692      "  -s subject_prefix     Subject line prefix\n",
     693      "  --summary             Use first line of commit log in subject\n",
     694      "  --diff y|n            Include diff in message (default: y)\n",
     695      "                        (applies to commit mode only)\n",
     696      "  --stdout              Spit the message in mbox format to stdout.\n",
    495697      "\n",
    496698      "This script supports a single repository with multiple projects,\n",
    497       "where each project receives email only for commits that modify that\n",
    498       "project.  A project is identified by using the -m command line\n",
    499       "with a regular expression argument.  If a commit has a path that\n",
    500       "matches the regular expression, then the entire commit matches.\n",
    501       "Any of the following -h, -l, -r and -s command line options and\n",
    502       "following email addresses are associated with this project.  The\n",
    503       "next -m resets the -h, -l, -r and -s command line options and the\n",
    504       "list of email addresses.\n",
     699      "where each project receives email only for actions that affect that\n",
     700      "project.  A project is identified by using the -m command line\n".
     701      "option with a regular expression argument.  If the given revision\n",
     702      "contains modifications to a path that matches the regular\n",
     703      "expression, then the action applies to the project.\n",
     704      "\n",
     705      "Any of the following email addresses and command line options\n",
     706      "(other than -d) are associated with this project, until the next -m,\n",
     707      "which resets the options and the list of email addresses.\n",
    505708      "\n",
    506709      "To support a single project conveniently, the script initializes\n",
    507710      "itself with an implicit -m . rule that matches any modifications\n",
    508       "to the repository.  Therefore, to use the script for a single\n",
    509       "project repository, just use the other comand line options and\n",
     711      "to the repository.  Therefore, to use the script for a single-\n",
     712      "project repository, just use the other command line options and\n",
    510713      "a list of email addresses on the command line.  If you do not want\n",
    511       "a project that matches the entire repository, then use a -m with a\n",
     714      "a rule that matches the entire repository, then use -m with a\n",
    512715      "regular expression before any other command line options or email\n",
    513       "addresses.\n";
     716      "addresses.\n",
     717      "\n",
     718      "'revprop-change' mode:\n",
     719      "The message will contain a copy of the diff_file if it is provided,\n",
     720      "otherwise a copy of the (assumed to be new) property value.\n",
     721      "\n";
    514722}
    515723
     
    524732          match_regex     => '.',
    525733          reply_to        => '',
    526           subject_prefix  => ''};
     734          subject_prefix  => '',
     735          show_diff       => 1,
     736          stdout          => 0};
     737}
     738
     739sub parse_boolean
     740{
     741  if ($_[0] eq 'y') { return 1; };
     742  if ($_[0] eq 'n') { return 0; };
     743
     744  die "$0: valid boolean options are 'y' or 'n', not '$_[0]'\n";
    527745}
    528746
     
    535753    }
    536754
    537   my $pid = open(SAFE_READ, '-|');
    538   unless (defined $pid)
    539     {
    540       die "$0: cannot fork: $!\n";
    541     }
    542   unless ($pid)
    543     {
    544       open(STDERR, ">&STDOUT")
    545         or die "$0: cannot dup STDOUT: $!\n";
    546       exec(@_)
    547         or die "$0: cannot exec `@_': $!\n";
     755  my $openfork_available = $^O ne "MSWin32";
     756  if ($openfork_available) # We can fork on this system.
     757    {
     758      my $pid = open(SAFE_READ, '-|');
     759      unless (defined $pid)
     760        {
     761          die "$0: cannot fork: $!\n";
     762        }
     763      unless ($pid)
     764        {
     765          open(STDERR, ">&STDOUT")
     766            or die "$0: cannot dup STDOUT: $!\n";
     767          exec(@_)
     768            or die "$0: cannot exec `@_': $!\n";
     769        }
     770    }
     771  else  # Running on Windows.  No fork.
     772    {
     773      my @commandline = ();
     774      my $arg;
     775     
     776      while ($arg = shift)
     777        {
     778          $arg =~ s/\"/\\\"/g;
     779          if ($arg eq "" or $arg =~ /\s/) { $arg = "\"$arg\""; }
     780          push(@commandline, $arg);
     781        }
     782       
     783      # Now do the pipe.
     784      open(SAFE_READ, "@commandline |")
     785        or die "$0: cannot pipe to command: $!\n";
    548786    }
    549787  my @output;
Note: See TracChangeset for help on using the changeset viewer.