use 5.008;
use strict;
use warnings;
+use POSIX qw/strftime/;
use Term::ReadLine;
use Getopt::Long;
use Text::ParseWords;
-use Data::Dumper;
use Term::ANSIColor;
use File::Temp qw/ tempdir tempfile /;
use File::Spec::Functions qw(catfile);
use Error qw(:try);
use Git;
+use Git::I18N;
Getopt::Long::Configure qw/ pass_through /;
my $localmin = $localtm[1] + $localtm[2] * 60;
my $gmtmin = $gmttm[1] + $gmttm[2] * 60;
if ($localtm[0] != $gmttm[0]) {
- die "local zone differs from GMT by a non-minute interval\n";
+ die __("local zone differs from GMT by a non-minute interval\n");
}
if ((($gmttm[6] + 1) % 7) == $localtm[6]) {
$localmin += 1440;
} elsif ((($gmttm[6] - 1) % 7) == $localtm[6]) {
$localmin -= 1440;
} elsif ($gmttm[6] != $localtm[6]) {
- die "local time offset greater than or equal to 24 hours\n";
+ die __("local time offset greater than or equal to 24 hours\n");
}
my $offset = $localmin - $gmtmin;
my $offhour = $offset / 60;
my $offmin = abs($offset % 60);
if (abs($offhour) >= 24) {
- die ("local time offset greater than or equal to 24 hours\n");
+ die __("local time offset greater than or equal to 24 hours\n");
}
return sprintf("%s, %2d %s %d %02d:%02d:%02d %s%02d%02d",
map {
system('sh', '-c', $editor.' "$@"', $editor, $_);
if (($? & 127) || ($? >> 8)) {
- die("the editor exited uncleanly, aborting everything");
+ die(__("the editor exited uncleanly, aborting everything"));
}
} @_;
} else {
system('sh', '-c', $editor.' "$@"', $editor, @_);
if (($? & 127) || ($? >> 8)) {
- die("the editor exited uncleanly, aborting everything");
+ die(__("the editor exited uncleanly, aborting everything"));
}
}
}
# tmp files from --compose
if (defined $compose_filename) {
if (-e $compose_filename) {
- print "'$compose_filename' contains an intermediate version of the email you were composing.\n";
+ printf __("'%s' contains an intermediate version ".
+ "of the email you were composing.\n"),
+ $compose_filename;
}
if (-e ($compose_filename . ".final")) {
- print "'$compose_filename.final' contains the composed email.\n"
+ printf __("'%s.final' contains the composed email.\n"),
+ $compose_filename;
}
}
my $rc = GetOptions("h" => \$help,
"dump-aliases" => \$dump_aliases);
usage() unless $rc;
-die "--dump-aliases incompatible with other options\n"
+die __("--dump-aliases incompatible with other options\n")
if !$help and $dump_aliases and @ARGV;
$rc = GetOptions(
"sender|from=s" => \$sender,
usage();
}
-die "Cannot run git format-patch from outside a repository\n"
+die __("Cannot run git format-patch from outside a repository\n")
if $format_patch and not $repo;
# Now, let's fill any that aren't set in with defaults:
my(%suppress_cc);
if (@suppress_cc) {
foreach my $entry (@suppress_cc) {
- die "Unknown --suppress-cc field: '$entry'\n"
+ die sprintf(__("Unknown --suppress-cc field: '%s'\n"), $entry)
unless $entry =~ /^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/;
$suppress_cc{$entry} = 1;
}
if ($confirm_unconfigured) {
$confirm = scalar %suppress_cc ? 'compose' : 'auto';
};
-die "Unknown --confirm setting: '$confirm'\n"
+die sprintf(__("Unknown --confirm setting: '%s'\n"), $confirm)
unless $confirm =~ /^(?:auto|cc|compose|always|never)/;
# Debugging, print out the suppressions.
sub parse_sendmail_alias {
local $_ = shift;
if (/"/) {
- print STDERR "warning: sendmail alias with quotes is not supported: $_\n";
+ printf STDERR __("warning: sendmail alias with quotes is not supported: %s\n"), $_;
} elsif (/:include:/) {
- print STDERR "warning: `:include:` not supported: $_\n";
+ printf STDERR __("warning: `:include:` not supported: %s\n"), $_;
} elsif (/[\/|]/) {
- print STDERR "warning: `/file` or `|pipe` redirection not supported: $_\n";
+ printf STDERR __("warning: `/file` or `|pipe` redirection not supported: %s\n"), $_;
} elsif (/^(\S+?)\s*:\s*(.+)$/) {
my ($alias, $addr) = ($1, $2);
$aliases{$alias} = [ split_addrs($addr) ];
} else {
- print STDERR "warning: sendmail line is not recognized: $_\n";
+ printf STDERR __("warning: sendmail line is not recognized: %s\n"), $_;
}
}
$aliases{$alias} = \@addr
}}},
mailrc => sub { my $fh = shift; while (<$fh>) {
- if (/^alias\s+(\S+)\s+(.*)$/) {
+ if (/^alias\s+(\S+)\s+(.*?)\s*$/) {
# spaces delimit multiple addresses
$aliases{$1} = [ quotewords('\s+', 0, $2) ];
}}},
if (defined($format_patch)) {
return $format_patch;
}
- die(<<EOF);
-File '$f' exists but it could also be the range of commits
+ die sprintf(__ <<EOF, $f, $f);
+File '%s' exists but it could also be the range of commits
to produce patches for. Please disambiguate by...
- * Saying "./$f" if you mean a file; or
+ * Saying "./%s" if you mean a file; or
* Giving --format-patch option if you mean a range.
EOF
} catch Git::Error::Command with {
@ARGV = ();
} elsif (-d $f and !is_format_patch_arg($f)) {
opendir my $dh, $f
- or die "Failed to opendir $f: $!";
+ or die sprintf(__("Failed to opendir %s: %s"), $f, $!);
push @files, grep { -f $_ } map { catfile($f, $_) }
sort readdir $dh;
}
if (@rev_list_opts) {
- die "Cannot run git format-patch from outside a repository\n"
+ die __("Cannot run git format-patch from outside a repository\n")
unless $repo;
push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts);
}
+@files = handle_backup_files(@files);
+
if ($validate) {
foreach my $f (@files) {
unless (-p $f) {
my $error = validate_patch($f);
- $error and die "fatal: $f: $error\nwarning: no patches were sent\n";
+ $error and die sprintf(__("fatal: %s: %s\nwarning: no patches were sent\n"),
+ $f, $error);
}
}
}
print $_,"\n" for (@files);
}
} else {
- print STDERR "\nNo patch files specified!\n\n";
+ print STDERR __("\nNo patch files specified!\n\n");
usage();
}
return "GIT: $1\n";
}
close $fh;
- die "No subject line in $fn ?";
+ die sprintf(__("No subject line in %s?"), $fn);
}
if ($compose) {
tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) :
tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1];
open my $c, ">", $compose_filename
- or die "Failed to open for writing $compose_filename: $!";
+ or die sprintf(__("Failed to open for writing %s: %s"), $compose_filename, $!);
my $tpl_sender = $sender || $repoauthor || $repocommitter || '';
my $tpl_subject = $initial_subject || '';
my $tpl_reply_to = $initial_reply_to || '';
- print $c <<EOT;
+ print $c <<EOT1, Git::prefix_lines("GIT: ", __ <<EOT2), <<EOT3;
From $tpl_sender # This line is ignored.
-GIT: Lines beginning in "GIT:" will be removed.
-GIT: Consider including an overall diffstat or table of contents
-GIT: for the patch you are writing.
-GIT:
-GIT: Clear the body content if you don't wish to send a summary.
+EOT1
+Lines beginning in "GIT:" will be removed.
+Consider including an overall diffstat or table of contents
+for the patch you are writing.
+
+Clear the body content if you don't wish to send a summary.
+EOT2
From: $tpl_sender
Subject: $tpl_subject
In-Reply-To: $tpl_reply_to
-EOT
+EOT3
for my $f (@files) {
print $c get_patch_subject($f);
}
}
open my $c2, ">", $compose_filename . ".final"
- or die "Failed to open $compose_filename.final : " . $!;
+ or die sprintf(__("Failed to open %s.final: %s"), $compose_filename, $!);
open $c, "<", $compose_filename
- or die "Failed to open $compose_filename : " . $!;
+ or die sprintf(__("Failed to open %s: %s"), $compose_filename, $!);
my $need_8bit_cte = file_has_nonascii($compose_filename);
my $in_body = 0;
$sender = $1;
next;
} elsif (/^(?:To|Cc|Bcc):/i) {
- print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n";
+ print __("To/Cc/Bcc fields are not interpreted yet, they have been ignored\n");
next;
}
print $c2 $_;
close $c2;
if ($summary_empty) {
- print "Summary email is empty, skipping it\n";
+ print __("Summary email is empty, skipping it\n");
$compose = -1;
}
} elsif ($annotate) {
return $resp;
}
if ($confirm_only) {
- my $yesno = $term->readline("Are you sure you want to use <$resp> [y/N]? ");
+ my $yesno = $term->readline(
+ # TRANSLATORS: please keep [y/N] as is.
+ sprintf(__("Are you sure you want to use <%s> [y/N]? "), $resp));
if (defined $yesno && $yesno =~ /y/i) {
return $resp;
}
}
if (!defined $auto_8bit_encoding && scalar %broken_encoding) {
- print "The following files are 8bit, but do not declare " .
- "a Content-Transfer-Encoding.\n";
+ print __("The following files are 8bit, but do not declare " .
+ "a Content-Transfer-Encoding.\n");
foreach my $f (sort keys %broken_encoding) {
print " $f\n";
}
- $auto_8bit_encoding = ask("Which 8bit encoding should I declare [UTF-8]? ",
+ $auto_8bit_encoding = ask(__("Which 8bit encoding should I declare [UTF-8]? "),
valid_re => qr/.{4}/, confirm_only => 1,
default => "UTF-8");
}
if (!$force) {
for my $f (@files) {
if (get_patch_subject($f) =~ /\Q*** SUBJECT HERE ***\E/) {
- die "Refusing to send because the patch\n\t$f\n"
+ die sprintf(__("Refusing to send because the patch\n\t%s\n"
. "has the template subject '*** SUBJECT HERE ***'. "
- . "Pass --force if you really want to send.\n";
+ . "Pass --force if you really want to send.\n"), $f);
}
}
}
# But it's a no-op to run sanitize_address on an already sanitized address.
$sender = sanitize_address($sender);
+my $to_whom = __("To whom should the emails be sent (if anyone)?");
my $prompting = 0;
if (!@initial_to && !defined $to_cmd) {
- my $to = ask("Who should the emails be sent to (if any)? ",
+ my $to = ask("$to_whom ",
default => "",
valid_re => qr/\@.*\./, confirm_only => 1);
push @initial_to, parse_address_line($to) if defined $to; # sanitized/validated later
sub expand_one_alias {
my $alias = shift;
if ($EXPANDED_ALIASES{$alias}) {
- die "fatal: alias '$alias' expands to itself\n";
+ die sprintf(__("fatal: alias '%s' expands to itself\n"), $alias);
}
local $EXPANDED_ALIASES{$alias} = 1;
return $aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) : $alias;
if ($thread && !defined $initial_reply_to && $prompting) {
$initial_reply_to = ask(
- "Message-ID to be used as In-Reply-To for the first email (if any)? ",
+ __("Message-ID to be used as In-Reply-To for the first email (if any)? "),
default => "",
valid_re => qr/\@.*\./, confirm_only => 1);
}
sub extract_valid_address_or_die {
my $address = shift;
$address = extract_valid_address($address);
- die "error: unable to extract a valid address from: $address\n"
+ die sprintf(__("error: unable to extract a valid address from: %s\n"), $address)
if !$address;
return $address;
}
sub validate_address {
my $address = shift;
while (!extract_valid_address($address)) {
- print STDERR "error: unable to extract a valid address from: $address\n";
- $_ = ask("What to do with this address? ([q]uit|[d]rop|[e]dit): ",
+ printf STDERR __("error: unable to extract a valid address from: %s\n"), $address;
+ # TRANSLATORS: Make sure to include [q] [d] [e] in your
+ # translation. The program will only accept English input
+ # at this point.
+ $_ = ask(__("What to do with this address? ([q]uit|[d]rop|[e]dit): "),
valid_re => qr/^(?:quit|q|drop|d|edit|e)/i,
default => 'q');
if (/^d/i) {
cleanup_compose_files();
exit(0);
}
- $address = ask("Who should the email be sent to (if any)? ",
+ $address = ask("$to_whom ",
default => "",
valid_re => qr/\@.*\./, confirm_only => 1);
}
sub make_message_id {
my $uniq;
if (!defined $message_id_stamp) {
- $message_id_stamp = sprintf("%s-%s", time, $$);
+ $message_id_stamp = strftime("%Y%m%d%H%M%S.$$", gmtime(time));
$message_id_serial = 0;
}
$message_id_serial++;
require Sys::Hostname;
$du_part = 'user@' . Sys::Hostname::hostname();
}
- my $message_id_template = "<%s-git-send-email-%s>";
+ my $message_id_template = "<%s-%s>";
$message_id = sprintf($message_id_template, $uniq, $du_part);
#print "new message id = $message_id\n"; # Was useful for debugging
}
return (SSL_verify_mode => SSL_VERIFY_PEER(),
SSL_ca_file => $smtp_ssl_cert_path);
} else {
- die "CA path \"$smtp_ssl_cert_path\" does not exist";
+ die sprintf(__("CA path \"%s\" does not exist"), $smtp_ssl_cert_path);
}
}
if ($needs_confirm eq "inform") {
$confirm_unconfigured = 0; # squelch this message for the rest of this run
$ask_default = "y"; # assume yes on EOF since user hasn't explicitly asked for confirmation
- print " The Cc list above has been expanded by additional\n";
- print " addresses found in the patch commit message. By default\n";
- print " send-email prompts before sending whenever this occurs.\n";
- print " This behavior is controlled by the sendemail.confirm\n";
- print " configuration setting.\n";
- print "\n";
- print " For additional information, run 'git send-email --help'.\n";
- print " To retain the current behavior, but squelch this message,\n";
- print " run 'git config --global sendemail.confirm auto'.\n\n";
+ print __ <<EOF ;
+ The Cc list above has been expanded by additional
+ addresses found in the patch commit message. By default
+ send-email prompts before sending whenever this occurs.
+ This behavior is controlled by the sendemail.confirm
+ configuration setting.
+
+ For additional information, run 'git send-email --help'.
+ To retain the current behavior, but squelch this message,
+ run 'git config --global sendemail.confirm auto'.
+
+EOF
}
- $_ = ask("Send this email? ([y]es|[n]o|[q]uit|[a]ll): ",
+ # TRANSLATORS: Make sure to include [y] [n] [q] [a] in your
+ # translation. The program will only accept English input
+ # at this point.
+ $_ = ask(__("Send this email? ([y]es|[n]o|[q]uit|[a]ll): "),
valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i,
default => $ask_default);
- die "Send this email reply required" unless defined $_;
+ die __("Send this email reply required") unless defined $_;
if (/^n/i) {
return 0;
} elsif (/^q/i) {
} else {
if (!defined $smtp_server) {
- die "The required SMTP server is not properly defined."
+ die __("The required SMTP server is not properly defined.")
}
if ($smtp_encryption eq 'ssl') {
# supported commands
$smtp->hello($smtp_domain);
} else {
- die "Server does not support STARTTLS! ".$smtp->message;
+ die sprintf(__("Server does not support STARTTLS! %s"), $smtp->message);
}
}
}
if (!$smtp) {
- die "Unable to initialize SMTP properly. Check config and use --smtp-debug. ",
- "VALUES: server=$smtp_server ",
+ die __("Unable to initialize SMTP properly. Check config and use --smtp-debug."),
+ " VALUES: server=$smtp_server ",
"encryption=$smtp_encryption ",
"hello=$smtp_domain",
defined $smtp_server_port ? " port=$smtp_server_port" : "";
$smtp->datasend("$line") or die $smtp->message;
}
$smtp->dataend() or die $smtp->message;
- $smtp->code =~ /250|200/ or die "Failed to send $subject\n".$smtp->message;
+ $smtp->code =~ /250|200/ or die sprintf(__("Failed to send %s\n"), $subject).$smtp->message;
}
if ($quiet) {
- printf (($dry_run ? "Dry-" : "")."Sent %s\n", $subject);
+ printf($dry_run ? __("Dry-Sent %s\n") : __("Sent %s\n"), $subject);
} else {
- print (($dry_run ? "Dry-" : "")."OK. Log says:\n");
+ print($dry_run ? __("Dry-OK. Log says:\n") : __("OK. Log says:\n"));
if (!file_name_is_absolute($smtp_server)) {
print "Server: $smtp_server\n";
print "MAIL FROM:<$raw_from>\n";
}
print $header, "\n";
if ($smtp) {
- print "Result: ", $smtp->code, ' ',
+ print __("Result: "), $smtp->code, ' ',
($smtp->message =~ /\n([^\n]+\n)$/s), "\n";
} else {
- print "Result: OK\n";
+ print __("Result: OK\n");
}
}
$message_num = 0;
foreach my $t (@files) {
- open my $fh, "<", $t or die "can't open file $t";
+ open my $fh, "<", $t or die sprintf(__("can't open file %s"), $t);
my $author = undef;
my $sauthor = undef;
$sauthor = sanitize_address($author);
next if $suppress_cc{'author'};
next if $suppress_cc{'self'} and $sauthor eq $sender;
- printf("(mbox) Adding cc: %s from line '%s'\n",
+ printf(__("(mbox) Adding cc: %s from line '%s'\n"),
$1, $_) unless $quiet;
push @cc, $1;
}
elsif (/^To:\s+(.*)$/i) {
foreach my $addr (parse_address_line($1)) {
- printf("(mbox) Adding to: %s from line '%s'\n",
+ printf(__("(mbox) Adding to: %s from line '%s'\n"),
$addr, $_) unless $quiet;
push @to, $addr;
}
} else {
next if ($suppress_cc{'cc'});
}
- printf("(mbox) Adding cc: %s from line '%s'\n",
+ printf(__("(mbox) Adding cc: %s from line '%s'\n"),
$addr, $_) unless $quiet;
push @cc, $addr;
}
# So let's support that, too.
$input_format = 'lots';
if (@cc == 0 && !$suppress_cc{'cc'}) {
- printf("(non-mbox) Adding cc: %s from line '%s'\n",
+ printf(__("(non-mbox) Adding cc: %s from line '%s'\n"),
$_, $_) unless $quiet;
push @cc, $_;
} elsif (!defined $subject) {
# Now parse the message body
while(<$fh>) {
$message .= $_;
- if (/^(Signed-off-by|Cc): (.*)$/i) {
+ if (/^(Signed-off-by|Cc): ([^>]*>?)/i) {
chomp;
my ($what, $c) = ($1, $2);
chomp $c;
next if $suppress_cc{'bodycc'} and $what =~ /Cc/i;
}
push @cc, $c;
- printf("(body) Adding cc: %s from line '%s'\n",
+ printf(__("(body) Adding cc: %s from line '%s'\n"),
$c, $_) unless $quiet;
}
}
my @addresses = ();
open my $fh, "-|", "$cmd \Q$file\E"
- or die "($prefix) Could not execute '$cmd'";
+ or die sprintf(__("(%s) Could not execute '%s'"), $prefix, $cmd);
while (my $address = <$fh>) {
$address =~ s/^\s*//g;
$address =~ s/\s*$//g;
$address = sanitize_address($address);
next if ($address eq $sender and $suppress_cc{'self'});
push @addresses, $address;
- printf("($prefix) Adding %s: %s from: '%s'\n",
- $what, $address, $cmd) unless $quiet;
+ printf(__("(%s) Adding %s: %s from: '%s'\n"),
+ $prefix, $what, $address, $cmd) unless $quiet;
}
close $fh
- or die "($prefix) failed to close pipe to '$cmd'";
+ or die sprintf(__("(%s) failed to close pipe to '%s'"), $prefix, $cmd);
return @addresses;
}
$message = MIME::Base64::decode($message)
if ($from eq 'base64');
- die "cannot send message as 7bit"
+ die __("cannot send message as 7bit")
if ($to eq '7bit' and $message =~ /[^[:ascii:]]/);
return $message
if ($to eq '7bit' or $to eq '8bit');
if ($to eq 'quoted-printable');
return MIME::Base64::encode($message, "\n")
if ($to eq 'base64');
- die "invalid transfer encoding";
+ die __("invalid transfer encoding");
}
sub unique_email_list {
sub validate_patch {
my $fn = shift;
open(my $fh, '<', $fn)
- or die "unable to open $fn: $!\n";
+ or die sprintf(__("unable to open %s: %s\n"), $fn, $!);
while (my $line = <$fh>) {
if (length($line) > 998) {
- return "$.: patch contains a line longer than 998 characters";
+ return sprintf(__("%s: patch contains a line longer than 998 characters"), $.);
}
}
return;
}
+sub handle_backup {
+ my ($last, $lastlen, $file, $known_suffix) = @_;
+ my ($suffix, $skip);
+
+ $skip = 0;
+ if (defined $last &&
+ ($lastlen < length($file)) &&
+ (substr($file, 0, $lastlen) eq $last) &&
+ ($suffix = substr($file, $lastlen)) !~ /^[a-z0-9]/i) {
+ if (defined $known_suffix && $suffix eq $known_suffix) {
+ printf(__("Skipping %s with backup suffix '%s'.\n"), $file, $known_suffix);
+ $skip = 1;
+ } else {
+ # TRANSLATORS: please keep "[y|N]" as is.
+ my $answer = ask(sprintf(__("Do you really want to send %s? [y|N]: "), $file),
+ valid_re => qr/^(?:y|n)/i,
+ default => 'n');
+ $skip = ($answer ne 'y');
+ if ($skip) {
+ $known_suffix = $suffix;
+ }
+ }
+ }
+ return ($skip, $known_suffix);
+}
+
+sub handle_backup_files {
+ my @file = @_;
+ my ($last, $lastlen, $known_suffix, $skip, @result);
+ for my $file (@file) {
+ ($skip, $known_suffix) = handle_backup($last, $lastlen,
+ $file, $known_suffix);
+ push @result, $file unless $skip;
+ $last = $file;
+ $lastlen = length($file);
+ }
+ return @result;
+}
+
sub file_has_nonascii {
my $fn = shift;
open(my $fh, '<', $fn)
- or die "unable to open $fn: $!\n";
+ or die sprintf(__("unable to open %s: %s\n"), $fn, $!);
while (my $line = <$fh>) {
return 1 if $line =~ /[^[:ascii:]]/;
}
sub body_or_subject_has_nonascii {
my $fn = shift;
open(my $fh, '<', $fn)
- or die "unable to open $fn: $!\n";
+ or die sprintf(__("unable to open %s: %s\n"), $fn, $!);
while (my $line = <$fh>) {
last if $line =~ /^$/;
return 1 if $line =~ /^Subject.*[^[:ascii:]]/;