sequencer.c: teach append_signoff to avoid adding a duplicate newline
[gitweb.git] / git-send-email.perl
index 5790744ae3fd89f83a229f964a5603df94e05c83..5a7c29db9324df48c9da68b08a946e5a357f0a78 100755 (executable)
@@ -56,6 +56,7 @@ sub usage {
     --in-reply-to           <str>  * Email "In-Reply-To:"
     --annotate                     * Review each patch that will be sent in an editor.
     --compose                      * Open an editor for introduction.
+    --compose-encoding      <str>  * Encoding to assume for introduction.
     --8bit-encoding         <str>  * Encoding to assume 8bit mails if undeclared
 
   Sending:
@@ -198,6 +199,7 @@ sub do_edit {
 my ($validate, $confirm);
 my (@suppress_cc);
 my ($auto_8bit_encoding);
+my ($compose_encoding);
 
 my ($debug_net_smtp) = 0;              # Net::SMTP, see send_message()
 
@@ -210,6 +212,7 @@ sub do_edit {
     "signedoffbycc" => [\$signed_off_by_cc, undef],
     "signedoffcc" => [\$signed_off_by_cc, undef],      # Deprecated
     "validate" => [\$validate, 1],
+    "multiedit" => [\$multiedit, undef]
 );
 
 my %config_settings = (
@@ -225,13 +228,16 @@ sub do_edit {
     "cccmd" => \$cc_cmd,
     "aliasfiletype" => \$aliasfiletype,
     "bcc" => \@bcclist,
-    "aliasesfile" => \@alias_files,
     "suppresscc" => \@suppress_cc,
     "envelopesender" => \$envelope_sender,
-    "multiedit" => \$multiedit,
     "confirm"   => \$confirm,
     "from" => \$sender,
     "assume8bitencoding" => \$auto_8bit_encoding,
+    "composeencoding" => \$compose_encoding,
+);
+
+my %config_path_settings = (
+    "aliasesfile" => \@alias_files,
 );
 
 # Help users prepare for 1.7.0
@@ -275,7 +281,9 @@ sub signal_handler {
 # Begin by accumulating all the variables (defined above), that we will end up
 # needing, first, from the command line:
 
-my $rc = GetOptions("sender|from=s" => \$sender,
+my $help;
+my $rc = GetOptions("h" => \$help,
+                   "sender|from=s" => \$sender,
                     "in-reply-to=s" => \$initial_reply_to,
                    "subject=s" => \$initial_subject,
                    "to=s" => \@initial_to,
@@ -310,9 +318,11 @@ sub signal_handler {
                    "validate!" => \$validate,
                    "format-patch!" => \$format_patch,
                    "8bit-encoding=s" => \$auto_8bit_encoding,
+                   "compose-encoding=s" => \$compose_encoding,
                    "force" => \$force,
         );
 
+usage() if $help;
 unless ($rc) {
     usage();
 }
@@ -330,6 +340,19 @@ sub read_config {
                $$target = Git::config_bool(@repo, "$prefix.$setting") unless (defined $$target);
        }
 
+       foreach my $setting (keys %config_path_settings) {
+               my $target = $config_path_settings{$setting};
+               if (ref($target) eq "ARRAY") {
+                       unless (@$target) {
+                               my @values = Git::config_path(@repo, "$prefix.$setting");
+                               @$target = @values if (@values && defined $values[0]);
+                       }
+               }
+               else {
+                       $$target = Git::config_path(@repo, "$prefix.$setting") unless (defined $$target);
+               }
+       }
+
        foreach my $setting (keys %config_settings) {
                my $target = $config_settings{$setting};
                next if $setting eq "to" and defined $no_to;
@@ -613,6 +636,9 @@ sub get_patch_subject {
        my $need_8bit_cte = file_has_nonascii($compose_filename);
        my $in_body = 0;
        my $summary_empty = 1;
+       if (!defined $compose_encoding) {
+               $compose_encoding = "UTF-8";
+       }
        while(<$c>) {
                next if m/^GIT:/;
                if ($in_body) {
@@ -622,7 +648,7 @@ sub get_patch_subject {
                        if ($need_8bit_cte) {
                                print $c2 "MIME-Version: 1.0\n",
                                         "Content-Type: text/plain; ",
-                                          "charset=UTF-8\n",
+                                          "charset=$compose_encoding\n",
                                         "Content-Transfer-Encoding: 8bit\n";
                        }
                } elsif (/^MIME-Version:/i) {
@@ -631,9 +657,7 @@ sub get_patch_subject {
                        $initial_subject = $1;
                        my $subject = $initial_subject;
                        $_ = "Subject: " .
-                               ($subject =~ /[^[:ascii:]]/ ?
-                                quote_rfc2047($subject) :
-                                $subject) .
+                               quote_subject($subject, $compose_encoding) .
                                "\n";
                } elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
                        $initial_reply_to = $1;
@@ -662,6 +686,7 @@ sub ask {
        my ($prompt, %arg) = @_;
        my $valid_re = $arg{valid_re};
        my $default = $arg{default};
+       my $confirm_only = $arg{confirm_only};
        my $resp;
        my $i = 0;
        return defined $default ? $default : undef
@@ -679,6 +704,12 @@ sub ask {
                if (!defined $valid_re or $resp =~ /$valid_re/) {
                        return $resp;
                }
+               if ($confirm_only) {
+                       my $yesno = $term->readline("Are you sure you want to use <$resp> [y/N]? ");
+                       if (defined $yesno && $yesno =~ /y/i) {
+                               return $resp;
+                       }
+               }
        }
        return undef;
 }
@@ -726,13 +757,16 @@ sub file_declares_8bit_cte {
 if (!defined $sender) {
        $sender = $repoauthor || $repocommitter || '';
        $sender = ask("Who should the emails appear to be from? [$sender] ",
-                     default => $sender);
+                     default => $sender,
+                     valid_re => qr/\@.*\./, confirm_only => 1);
        print "Emails will be sent from: ", $sender, "\n";
        $prompting++;
 }
 
 if (!@initial_to && !defined $to_cmd) {
-       my $to = ask("Who should the emails be sent to? ");
+       my $to = ask("Who should the emails be sent to (if any)? ",
+                    default => "",
+                    valid_re => qr/\@.*\./, confirm_only => 1);
        push @initial_to, parse_address_line($to) if defined $to; # sanitized/validated later
        $prompting++;
 }
@@ -758,7 +792,9 @@ sub expand_one_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? ");
+               "Message-ID to be used as In-Reply-To for the first email (if any)? ",
+               default => "",
+               valid_re => qr/\@.*\./, confirm_only => 1);
 }
 if (defined $initial_reply_to) {
        $initial_reply_to =~ s/^\s*<?//;
@@ -843,11 +879,13 @@ sub make_message_id {
 sub unquote_rfc2047 {
        local ($_) = @_;
        my $encoding;
-       if (s/=\?([^?]+)\?q\?(.*)\?=/$2/g) {
+       s{=\?([^?]+)\?q\?(.*?)\?=}{
                $encoding = $1;
-               s/_/ /g;
-               s/=([0-9A-F]{2})/chr(hex($1))/eg;
-       }
+               my $e = $2;
+               $e =~ s/_/ /g;
+               $e =~ s/=([0-9A-F]{2})/chr(hex($1))/eg;
+               $e;
+       }eg;
        return wantarray ? ($_, $encoding) : $_;
 }
 
@@ -867,6 +905,22 @@ sub is_rfc2047_quoted {
        $s =~ m/^(?:"[[:ascii:]]*"|=\?$token\?$token\?$encoded_text\?=)$/o;
 }
 
+sub subject_needs_rfc2047_quoting {
+       my $s = shift;
+
+       return ($s =~ /[^[:ascii:]]/) || ($s =~ /=\?/);
+}
+
+sub quote_subject {
+       local $subject = shift;
+       my $encoding = shift || 'UTF-8';
+
+       if (subject_needs_rfc2047_quoting($subject)) {
+               return quote_rfc2047($subject, $encoding);
+       }
+       return $subject;
+}
+
 # use the simplest quoting being able to handle the recipient
 sub sanitize_address {
        my ($recipient) = @_;
@@ -1095,6 +1149,12 @@ sub send_message {
                }
 
                if (defined $smtp_authuser) {
+                       # Workaround AUTH PLAIN/LOGIN interaction defect
+                       # with Authen::SASL::Cyrus
+                       eval {
+                               require Authen::SASL;
+                               Authen::SASL->import(qw(Perl));
+                       };
 
                        if (!defined $smtp_authpass) {
 
@@ -1282,7 +1342,7 @@ sub send_message {
        }
 
        if ($broken_encoding{$t} && !is_rfc2047_quoted($subject)) {
-               $subject = quote_rfc2047($subject, $auto_8bit_encoding);
+               $subject = quote_subject($subject, $auto_8bit_encoding);
        }
 
        if (defined $author and $author ne $sender) {