Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
git-send-email2516 linesDownload Raw Back to git-core
1#!/usr/bin/perl2 3# BEGIN RUNTIME_PREFIX generated code.4#5# This finds our Git::* libraries relative to the script's runtime path.6sub __git_system_path {7	my ($relpath) = @_;8	my $gitexecdir_relative = 'libexec/git-core';9 10	# GIT_EXEC_PATH is supplied by `git` or the test suite.11	my $exec_path;12	if (exists $ENV{GIT_EXEC_PATH}) {13		$exec_path = $ENV{GIT_EXEC_PATH};14	} else {15		# This can happen if this script is being directly invoked instead of run16		# by "git".17		require FindBin;18		$exec_path = $FindBin::Bin;19	}20 21	# Trim off the relative gitexecdir path to get the system path.22	(my $prefix = $exec_path) =~ s/\Q$gitexecdir_relative\E$//;23 24	require File::Spec;25	return File::Spec->catdir($prefix, $relpath);26}27 28BEGIN {29	use lib split /;/,30	(31		$ENV{GITPERLLIB} ||32		do {33			my $perllibdir = __git_system_path('share/perl5');34			(-e $perllibdir) || die("Invalid system path ($relpath): $path");35			$perllibdir;36		}37	);38 39	# Export the system locale directory to the I18N module. The locale directory40	# is only installed if NO_GETTEXT is set.41	$Git::I18N::TEXTDOMAINDIR = __git_system_path('share/locale');42}43 44# END RUNTIME_PREFIX generated code.45#46# Copyright 2002,2005 Greg Kroah-Hartman <greg@kroah.com>47# Copyright 2005 Ryan Anderson <ryan@michonline.com>48#49# GPL v2 (See COPYING)50#51# Ported to support git "mbox" format files by Ryan Anderson <ryan@michonline.com>52#53# Sends a collection of emails to the given email addresses, disturbingly fast.54#55# Supports two formats:56# 1. mbox format files (ignoring most headers and MIME formatting - this is designed for sending patches)57# 2. The original format support by Greg's script:58#    first line of the message is who to CC,59#    and second line is the subject of the message.60#61 62require v5.26;63use strict;64use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();65use Getopt::Long;66use Git::LoadCPAN::Error qw(:try);67use Git;68use Git::I18N;69use Encode qw(find_encoding);70 71Getopt::Long::Configure qw/ pass_through /;72 73sub usage {74	print <<EOT;75git send-email [<options>] <file|directory>76git send-email [<options>] <format-patch options>77git send-email --dump-aliases78git send-email --translate-aliases79 80  Composing:81    --from                  <str>  * Email From:82    --[no-]to               <str>  * Email To:83    --[no-]cc               <str>  * Email Cc:84    --[no-]bcc              <str>  * Email Bcc:85    --subject               <str>  * Email "Subject:"86    --reply-to              <str>  * Email "Reply-To:"87    --in-reply-to           <str>  * Email "In-Reply-To:"88    --[no-]outlook-id-fix          * The SMTP host is an Outlook server that munges the89                                     Message-ID. Retrieve it from the server.90    --[no-]xmailer                 * Add "X-Mailer:" header (default).91    --[no-]annotate                * Review each patch that will be sent in an editor.92    --compose                      * Open an editor for introduction.93    --compose-encoding      <str>  * Encoding to assume for introduction.94    --8bit-encoding         <str>  * Encoding to assume 8bit mails if undeclared95    --transfer-encoding     <str>  * Transfer encoding to use (quoted-printable, 8bit, base64)96    --[no-]mailmap                 * Use mailmap file to map all email addresses to canonical97                                     real names and email addresses.98 99  Sending:100    --envelope-sender       <str>  * Email envelope sender.101    --sendmail-cmd          <str>  * Command to run to send email.102    --smtp-server       <str:int>  * Outgoing SMTP server to use. The port103                                     is optional. Default 'localhost'.104    --smtp-server-option    <str>  * Outgoing SMTP server option to use.105    --smtp-server-port      <int>  * Outgoing SMTP server port.106    --smtp-user             <str>  * Username for SMTP-AUTH.107    --smtp-pass             <str>  * Password for SMTP-AUTH; not necessary.108    --smtp-encryption       <str>  * tls or ssl; anything else disables.109    --smtp-ssl                     * Deprecated. Use `--smtp-encryption ssl`.110    --smtp-ssl-cert-path    <str>  * Path to ca-certificates (either directory or file).111                                     Pass an empty string to disable certificate112                                     verification.113    --smtp-ssl-client-cert  <str>  * Path to the client certificate file114    --smtp-ssl-client-key   <str>  * Path to the private key file for the client certificate115    --smtp-domain           <str>  * The domain name sent to HELO/EHLO handshake116    --smtp-auth             <str>  * Space-separated list of allowed AUTH mechanisms, or117                                     "none" to disable authentication.118                                     This setting forces to use one of the listed mechanisms.119    --no-smtp-auth                 * Disable SMTP authentication. Shorthand for120                                     `--smtp-auth=none`121    --smtp-debug            <0|1>  * Disable, enable Net::SMTP debug.122    --imap-sent-folder      <str>  * IMAP folder where a copy of the emails should be sent.123                                     Make sure `git imap-send` is set up to use this feature.124    --[no-]use-imap-only           * Only copy emails to the IMAP folder specified by125                                     `--imap-sent-folder` instead of actually sending them.126 127    --batch-size            <int>  * send max <int> message per connection.128    --relogin-delay         <int>  * delay <int> seconds between two successive login.129                                     This option can only be used with --batch-size130 131  Automating:132    --identity              <str>  * Use the sendemail.<id> options.133    --to-cmd                <str>  * Email To: via `<str> \$patch_path`.134    --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`.135    --header-cmd            <str>  * Add headers via `<str> \$patch_path`.136    --no-header-cmd                * Disable any header command in use.137    --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, misc-by, all.138    --[no-]cc-cover                * Email Cc: addresses in the cover letter.139    --[no-]to-cover                * Email To: addresses in the cover letter.140    --[no-]signed-off-by-cc        * Send to Signed-off-by: addresses. Default on.141    --[no-]suppress-from           * Send to self. Default off.142    --[no-]chain-reply-to          * Chain In-Reply-To: fields. Default off.143    --[no-]thread                  * Use In-Reply-To: field. Default on.144 145  Administering:146    --confirm               <str>  * Confirm recipients before sending;147                                     auto, cc, compose, always, or never.148    --quiet                        * Output one line of info per email.149    --dry-run                      * Don't actually send the emails.150    --[no-]validate                * Perform patch sanity checks. Default on.151    --[no-]format-patch            * understand any non optional arguments as152                                     `git format-patch` ones.153    --force                        * Send even if safety checks would prevent it.154 155  Information:156    --dump-aliases                 * Dump configured aliases and exit.157    --translate-aliases            * Translate aliases read from standard158                                     input according to the configured email159                                     alias file(s), outputting the result to160                                     standard output.161 162EOT163	exit(1);164}165 166sub uniq {167	my %seen;168	grep !$seen{$_}++, @_;169}170 171sub completion_helper {172	my ($original_opts) = @_;173	my %not_for_completion = (174		"git-completion-helper" => undef,175		"h" => undef,176	);177	my @send_email_opts = ();178 179	foreach my $key (keys %$original_opts) {180		unless (exists $not_for_completion{$key}) {181			my $negatable = ($key =~ s/!$//);182 183			if ($key =~ /[:=][si]$/) {184				$key =~ s/[:=][si]$//;185				push (@send_email_opts, "--$_=") foreach (split (/\|/, $key));186			} else {187				push (@send_email_opts, "--$_") foreach (split (/\|/, $key));188				if ($negatable) {189					push (@send_email_opts, "--no-$_") foreach (split (/\|/, $key));190				}191			}192		}193	}194 195	my @format_patch_opts = split(/ /, Git::command('format-patch', '--git-completion-helper'));196	my @opts = (@send_email_opts, @format_patch_opts);197	@opts = uniq (grep !/^$/, @opts);198	# There's an implicit '\n' here already, no need to add an explicit one.199	print "@opts";200	exit(0);201}202 203# most mail servers generate the Date: header, but not all...204sub format_2822_time {205	my ($time) = @_;206	my @localtm = localtime($time);207	my @gmttm = gmtime($time);208	my $localmin = $localtm[1] + $localtm[2] * 60;209	my $gmtmin = $gmttm[1] + $gmttm[2] * 60;210	if ($localtm[0] != $gmttm[0]) {211		die __("local zone differs from GMT by a non-minute interval\n");212	}213	if ((($gmttm[6] + 1) % 7) == $localtm[6]) {214		$localmin += 1440;215	} elsif ((($gmttm[6] - 1) % 7) == $localtm[6]) {216		$localmin -= 1440;217	} elsif ($gmttm[6] != $localtm[6]) {218		die __("local time offset greater than or equal to 24 hours\n");219	}220	my $offset = $localmin - $gmtmin;221	my $offhour = $offset / 60;222	my $offmin = abs($offset % 60);223	if (abs($offhour) >= 24) {224		die __("local time offset greater than or equal to 24 hours\n");225	}226 227	return sprintf("%s, %2d %s %d %02d:%02d:%02d %s%02d%02d",228		       qw(Sun Mon Tue Wed Thu Fri Sat)[$localtm[6]],229		       $localtm[3],230		       qw(Jan Feb Mar Apr May Jun231			  Jul Aug Sep Oct Nov Dec)[$localtm[4]],232		       $localtm[5]+1900,233		       $localtm[2],234		       $localtm[1],235		       $localtm[0],236		       ($offset >= 0) ? '+' : '-',237		       abs($offhour),238		       $offmin,239		       );240}241 242my $smtp;243my $auth;244my $num_sent = 0;245 246# Regexes for RFC 2047 productions.247my $re_token = qr/[^][()<>@,;:\\"\/?.= \000-\037\177-\377]+/;248my $re_encoded_text = qr/[^? \000-\037\177-\377]+/;249my $re_encoded_word = qr/=\?($re_token)\?($re_token)\?($re_encoded_text)\?=/;250 251# Variables we fill in automatically, or via prompting:252my (@to,@cc,@xh,$envelope_sender,253	$initial_in_reply_to,$reply_to,$initial_subject,@files,@imap_copy,254	$author,$sender,$smtp_authpass,$annotate,$compose,$time);255# Things we either get from config, *or* are overridden on the256# command-line.257my ($no_cc, $no_to, $no_bcc, $no_identity, $no_header_cmd);258my (@config_to, @getopt_to);259my (@config_cc, @getopt_cc);260my (@config_bcc, @getopt_bcc);261 262# Example reply to:263#$initial_in_reply_to = ''; #<20050203173208.GA23964@foobar.com>';264 265my $repo = eval { Git->repository() };266my @repo = $repo ? ($repo) : ();267 268# Behavior modification variables269my ($quiet, $dry_run) = (0, 0);270my $format_patch;271my $compose_filename;272my $force = 0;273my $dump_aliases = 0;274my $translate_aliases = 0;275 276# Variables to prevent short format-patch options from being captured277# as abbreviated send-email options278my $reroll_count;279 280# Handle interactive edition of files.281my $multiedit;282my $editor;283 284sub system_or_msg {285	my ($args, $msg, $cmd_name) = @_;286	system(@$args);287	my $signalled = $? & 127;288	my $exit_code = $? >> 8;289	return unless $signalled or $exit_code;290 291	my @sprintf_args = ($cmd_name ? $cmd_name : $args->[0], $exit_code);292	if (defined $msg) {293		# Quiet the 'redundant' warning category, except we294		# need to support down to Perl 5.8.1, so we can't do a295		# "no warnings 'redundant'", since that category was296		# introduced in perl 5.22, and asking for it will die297		# on older perls.298		no warnings;299		return sprintf($msg, @sprintf_args);300	}301	return sprintf(__("fatal: command '%s' died with exit code %d"),302		       @sprintf_args);303}304 305sub system_or_die {306	my $msg = system_or_msg(@_);307	die $msg if $msg;308}309 310sub do_edit {311	if (!defined($editor)) {312		$editor = Git::command_oneline('var', 'GIT_EDITOR');313	}314	my $die_msg = __("the editor exited uncleanly, aborting everything");315	if (defined($multiedit) && !$multiedit) {316		system_or_die(['sh', '-c', $editor.' "$@"', $editor, $_], $die_msg) for @_;317	} else {318		system_or_die(['sh', '-c', $editor.' "$@"', $editor, @_], $die_msg);319	}320}321 322# Variables with corresponding config settings323my ($suppress_from, $signed_off_by_cc);324my ($cover_cc, $cover_to);325my ($to_cmd, $cc_cmd, $header_cmd);326my ($smtp_server, $smtp_server_port, @smtp_server_options);327my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);328my ($smtp_ssl_client_cert, $smtp_ssl_client_key);329my ($batch_size, $relogin_delay);330my ($identity, $aliasfiletype, @alias_files, $smtp_domain, $smtp_auth);331my ($imap_sent_folder);332my ($confirm);333my (@suppress_cc);334my ($auto_8bit_encoding);335my ($compose_encoding);336my ($sendmail_cmd);337my ($mailmap_file, $mailmap_blob);338# Variables with corresponding config settings & hardcoded defaults339my ($debug_net_smtp) = 0;		# Net::SMTP, see send_message()340my $thread = 1;341my $chain_reply_to = 0;342my $use_xmailer = 1;343my $validate = 1;344my $mailmap = 0;345my $target_xfer_encoding = 'auto';346my $forbid_sendmail_variables = 1;347my $outlook_id_fix = 'auto';348my $use_imap_only = 0;349 350my %config_bool_settings = (351    "thread" => \$thread,352    "chainreplyto" => \$chain_reply_to,353    "suppressfrom" => \$suppress_from,354    "signedoffbycc" => \$signed_off_by_cc,355    "cccover" => \$cover_cc,356    "tocover" => \$cover_to,357    "signedoffcc" => \$signed_off_by_cc,358    "validate" => \$validate,359    "multiedit" => \$multiedit,360    "annotate" => \$annotate,361    "xmailer" => \$use_xmailer,362    "forbidsendmailvariables" => \$forbid_sendmail_variables,363    "mailmap" => \$mailmap,364    "outlookidfix" => \$outlook_id_fix,365    "useimaponly" => \$use_imap_only,366);367 368my %config_settings = (369    "smtpencryption" => \$smtp_encryption,370    "smtpserver" => \$smtp_server,371    "smtpserverport" => \$smtp_server_port,372    "smtpserveroption" => \@smtp_server_options,373    "smtpuser" => \$smtp_authuser,374    "smtppass" => \$smtp_authpass,375    "smtpdomain" => \$smtp_domain,376    "smtpauth" => \$smtp_auth,377    "smtpbatchsize" => \$batch_size,378    "smtprelogindelay" => \$relogin_delay,379    "imapsentfolder" => \$imap_sent_folder,380    "to" => \@config_to,381    "tocmd" => \$to_cmd,382    "cc" => \@config_cc,383    "cccmd" => \$cc_cmd,384    "headercmd" => \$header_cmd,385    "aliasfiletype" => \$aliasfiletype,386    "bcc" => \@config_bcc,387    "suppresscc" => \@suppress_cc,388    "envelopesender" => \$envelope_sender,389    "confirm"   => \$confirm,390    "from" => \$sender,391    "assume8bitencoding" => \$auto_8bit_encoding,392    "composeencoding" => \$compose_encoding,393    "transferencoding" => \$target_xfer_encoding,394    "sendmailcmd" => \$sendmail_cmd,395);396 397my %config_path_settings = (398    "aliasesfile" => \@alias_files,399    "smtpsslcertpath" => \$smtp_ssl_cert_path,400    "smtpsslclientcert" => \$smtp_ssl_client_cert,401    "smtpsslclientkey" => \$smtp_ssl_client_key,402    "mailmap.file" => \$mailmap_file,403    "mailmap.blob" => \$mailmap_blob,404);405 406# Handle Uncouth Termination407sub signal_handler {408	# Make text normal409	require Term::ANSIColor;410	print Term::ANSIColor::color("reset"), "\n";411 412	# SMTP password masked413	system "stty echo";414 415	# tmp files from --compose416	if (defined $compose_filename) {417		if (-e $compose_filename) {418			printf __("'%s' contains an intermediate version ".419				  "of the email you were composing.\n"),420				  $compose_filename;421		}422		if (-e ($compose_filename . ".final")) {423			printf __("'%s.final' contains the composed email.\n"),424				  $compose_filename;425		}426	}427 428	exit;429};430 431$SIG{TERM} = \&signal_handler;432$SIG{INT}  = \&signal_handler;433 434# Read our sendemail.* config435sub read_config {436	my ($known_keys, $configured, $prefix) = @_;437 438	foreach my $setting (keys %config_bool_settings) {439		my $target = $config_bool_settings{$setting};440		my $key = "$prefix.$setting";441		next unless exists $known_keys->{$key};442		my $v = (@{$known_keys->{$key}} == 1 &&443			 (defined $known_keys->{$key}->[0] &&444			  $known_keys->{$key}->[0] =~ /^(?:true|false)$/s))445			? $known_keys->{$key}->[0] eq 'true'446			: Git::config_bool(@repo, $key);447		next unless defined $v;448		next if $configured->{$setting}++;449		$$target = $v;450	}451 452	foreach my $setting (keys %config_path_settings) {453		my $target = $config_path_settings{$setting};454		my $key = "$prefix.$setting";455		next unless exists $known_keys->{$key};456		if (ref($target) eq "ARRAY") {457			my @values = Git::config_path(@repo, $key);458			next unless @values;459			next if $configured->{$setting}++;460			@$target = @values;461		}462		else {463			my $v = Git::config_path(@repo, "$prefix.$setting");464			next unless defined $v;465			next if $configured->{$setting}++;466			$$target = $v;467		}468	}469 470	foreach my $setting (keys %config_settings) {471		my $target = $config_settings{$setting};472		my $key = "$prefix.$setting";473		next unless exists $known_keys->{$key};474		if (ref($target) eq "ARRAY") {475			my @values = @{$known_keys->{$key}};476			@values = grep { defined } @values;477			next if $configured->{$setting}++;478			@$target = @values;479		}480		else {481			my $v = $known_keys->{$key}->[-1];482			next unless defined $v;483			next if $configured->{$setting}++;484			$$target = $v;485		}486	}487}488 489sub config_regexp {490	my ($regex) = @_;491	my @ret;492	eval {493		my $ret = Git::command(494			'config',495			'--null',496			'--get-regexp',497			$regex,498		);499		@ret = map {500			# We must always return ($k, $v) here, since501			# empty config values will be just "key\0",502			# not "key\nvalue\0".503			my ($k, $v) = split /\n/, $_, 2;504			($k, $v);505		} split /\0/, $ret;506		1;507	} or do {508		# If we have no keys we're OK, otherwise re-throw509		die $@ if $@->value != 1;510	};511	return @ret;512}513 514# Save ourselves a lot of work of shelling out to 'git config' (it515# parses 'bool' etc.) by only doing so for config keys that exist.516my %known_config_keys;517{518	my @kv = config_regexp("^sende?mail[.]");519	while (my ($k, $v) = splice @kv, 0, 2) {520		push @{$known_config_keys{$k}} => $v;521	}522}523 524# sendemail.identity yields to --identity. We must parse this525# special-case first before the rest of the config is read.526{527	my $key = "sendemail.identity";528	$identity = Git::config(@repo, $key) if exists $known_config_keys{$key};529}530my %identity_options = (531	"identity=s" => \$identity,532	"no-identity" => \$no_identity,533);534my $rc = GetOptions(%identity_options);535usage() unless $rc;536undef $identity if $no_identity;537 538# Now we know enough to read the config539{540    my %configured;541    read_config(\%known_config_keys, \%configured, "sendemail.$identity") if defined $identity;542    read_config(\%known_config_keys, \%configured, "sendemail");543}544 545# Begin by accumulating all the variables (defined above), that we will end up546# needing, first, from the command line:547 548my $help;549my $git_completion_helper;550my %dump_aliases_options = (551	"h" => \$help,552	"dump-aliases" => \$dump_aliases,553	"translate-aliases" => \$translate_aliases,554);555$rc = GetOptions(%dump_aliases_options);556usage() unless $rc;557die __("--dump-aliases incompatible with other options\n")558    if !$help and ($dump_aliases or $translate_aliases) and @ARGV;559die __("--dump-aliases and --translate-aliases are mutually exclusive\n")560    if !$help and $dump_aliases and $translate_aliases;561my %options = (562		    "sender|from=s" => \$sender,563		    "in-reply-to=s" => \$initial_in_reply_to,564		    "reply-to=s" => \$reply_to,565		    "subject=s" => \$initial_subject,566		    "to=s" => \@getopt_to,567		    "to-cmd=s" => \$to_cmd,568		    "no-to" => \$no_to,569		    "cc=s" => \@getopt_cc,570		    "no-cc" => \$no_cc,571		    "bcc=s" => \@getopt_bcc,572		    "no-bcc" => \$no_bcc,573		    "chain-reply-to!" => \$chain_reply_to,574		    "sendmail-cmd=s" => \$sendmail_cmd,575		    "smtp-server=s" => \$smtp_server,576		    "smtp-server-option=s" => \@smtp_server_options,577		    "smtp-server-port=s" => \$smtp_server_port,578		    "smtp-user=s" => \$smtp_authuser,579		    "smtp-pass:s" => \$smtp_authpass,580		    "smtp-ssl" => sub { $smtp_encryption = 'ssl' },581		    "smtp-encryption=s" => \$smtp_encryption,582		    "smtp-ssl-cert-path=s" => \$smtp_ssl_cert_path,583		    "smtp-ssl-client-cert=s" => \$smtp_ssl_client_cert,584		    "smtp-ssl-client-key=s" => \$smtp_ssl_client_key,585		    "smtp-debug:i" => \$debug_net_smtp,586		    "smtp-domain:s" => \$smtp_domain,587		    "smtp-auth=s" => \$smtp_auth,588		    "no-smtp-auth" => sub {$smtp_auth = 'none'},589		    "imap-sent-folder=s" => \$imap_sent_folder,590		    "use-imap-only!" => \$use_imap_only,591		    "annotate!" => \$annotate,592		    "compose" => \$compose,593		    "quiet" => \$quiet,594		    "cc-cmd=s" => \$cc_cmd,595		    "header-cmd=s" => \$header_cmd,596		    "no-header-cmd" => \$no_header_cmd,597		    "suppress-from!" => \$suppress_from,598		    "suppress-cc=s" => \@suppress_cc,599		    "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc,600		    "cc-cover!" => \$cover_cc,601		    "to-cover!" => \$cover_to,602		    "confirm=s" => \$confirm,603		    "dry-run" => \$dry_run,604		    "envelope-sender=s" => \$envelope_sender,605		    "thread!" => \$thread,606		    "validate!" => \$validate,607		    "transfer-encoding=s" => \$target_xfer_encoding,608		    "mailmap!" => \$mailmap,609		    "use-mailmap!" => \$mailmap,610		    "format-patch!" => \$format_patch,611		    "8bit-encoding=s" => \$auto_8bit_encoding,612		    "compose-encoding=s" => \$compose_encoding,613		    "force" => \$force,614		    "xmailer!" => \$use_xmailer,615		    "batch-size=i" => \$batch_size,616		    "relogin-delay=i" => \$relogin_delay,617		    "git-completion-helper" => \$git_completion_helper,618		    "v=s" => \$reroll_count,619		    "outlook-id-fix!" => \$outlook_id_fix,620);621$rc = GetOptions(%options);622 623# Munge any "either config or getopt, not both" variables624my @initial_to = @getopt_to ? @getopt_to : ($no_to ? () : @config_to);625my @initial_cc = @getopt_cc ? @getopt_cc : ($no_cc ? () : @config_cc);626my @initial_bcc = @getopt_bcc ? @getopt_bcc : ($no_bcc ? () : @config_bcc);627 628usage() if $help;629my %all_options = (%options, %dump_aliases_options, %identity_options);630completion_helper(\%all_options) if $git_completion_helper;631unless ($rc) {632    usage();633}634 635if ($forbid_sendmail_variables && grep { /^sendmail/s } keys %known_config_keys) {636	die __("fatal: found configuration options for 'sendmail'\n" .637		"git-send-email is configured with the sendemail.* options - note the 'e'.\n" .638		"Set sendemail.forbidSendmailVariables to false to disable this check.\n");639}640 641die __("Cannot run git format-patch from outside a repository\n")642	if $format_patch and not $repo;643 644die __("`batch-size` and `relogin` must be specified together " .645	"(via command-line or configuration option)\n")646	if defined $relogin_delay and not defined $batch_size;647 648# 'default' encryption is none -- this only prevents a warning649$smtp_encryption = '' unless (defined $smtp_encryption);650 651# Set CC suppressions652my(%suppress_cc);653if (@suppress_cc) {654	foreach my $entry (@suppress_cc) {655		# Please update $__git_send_email_suppresscc_options656		# in git-completion.bash when you add new options.657		die sprintf(__("Unknown --suppress-cc field: '%s'\n"), $entry)658			unless $entry =~ /^(?:all|cccmd|cc|author|self|sob|body|bodycc|misc-by)$/;659		$suppress_cc{$entry} = 1;660	}661}662 663if ($suppress_cc{'all'}) {664	foreach my $entry (qw (cccmd cc author self sob body bodycc misc-by)) {665		$suppress_cc{$entry} = 1;666	}667	delete $suppress_cc{'all'};668}669 670# If explicit old-style ones are specified, they trump --suppress-cc.671$suppress_cc{'self'} = $suppress_from if defined $suppress_from;672$suppress_cc{'sob'} = !$signed_off_by_cc if defined $signed_off_by_cc;673 674if ($suppress_cc{'body'}) {675	foreach my $entry (qw (sob bodycc misc-by)) {676		$suppress_cc{$entry} = 1;677	}678	delete $suppress_cc{'body'};679}680 681# Set confirm's default value682my $confirm_unconfigured = !defined $confirm;683if ($confirm_unconfigured) {684	$confirm = scalar %suppress_cc ? 'compose' : 'auto';685};686# Please update $__git_send_email_confirm_options in687# git-completion.bash when you add new options.688die sprintf(__("Unknown --confirm setting: '%s'\n"), $confirm)689	unless $confirm =~ /^(?:auto|cc|compose|always|never)/;690 691# Debugging, print out the suppressions.692if (0) {693	print "suppressions:\n";694	foreach my $entry (keys %suppress_cc) {695		printf "  %-5s -> $suppress_cc{$entry}\n", $entry;696	}697}698 699my ($repoauthor, $repocommitter);700{701	my %cache;702	my ($author, $committer);703	my $common = sub {704		my ($what) = @_;705		return $cache{$what} if exists $cache{$what};706		($cache{$what}) = Git::ident_person(@repo, $what);707		return $cache{$what};708	};709	$repoauthor = sub { $common->('author') };710	$repocommitter = sub { $common->('committer') };711}712 713sub parse_address_line {714	require Git::LoadCPAN::Mail::Address;715	return map { $_->format } Mail::Address->parse($_[0]);716}717 718sub split_addrs {719	require Text::ParseWords;720	return Text::ParseWords::quotewords('\s*,\s*', 1, @_);721}722 723my %aliases;724 725sub parse_sendmail_alias {726	local $_ = shift;727	if (/"/) {728		printf STDERR __("warning: sendmail alias with quotes is not supported: %s\n"), $_;729	} elsif (/:include:/) {730		printf STDERR __("warning: `:include:` not supported: %s\n"), $_;731	} elsif (/[\/|]/) {732		printf STDERR __("warning: `/file` or `|pipe` redirection not supported: %s\n"), $_;733	} elsif (/^(\S+?)\s*:\s*(.+)$/) {734		my ($alias, $addr) = ($1, $2);735		$aliases{$alias} = [ split_addrs($addr) ];736	} else {737		printf STDERR __("warning: sendmail line is not recognized: %s\n"), $_;738	}739}740 741sub parse_sendmail_aliases {742	my $fh = shift;743	my $s = '';744	while (<$fh>) {745		chomp;746		next if /^\s*$/ || /^\s*#/;747		$s .= $_, next if $s =~ s/\\$// || s/^\s+//;748		parse_sendmail_alias($s) if $s;749		$s = $_;750	}751	$s =~ s/\\$//; # silently tolerate stray '\' on last line752	parse_sendmail_alias($s) if $s;753}754 755my %parse_alias = (756	# multiline formats can be supported in the future757	mutt => sub { my $fh = shift; while (<$fh>) {758		if (/^\s*alias\s+(?:-group\s+\S+\s+)*(\S+)\s+(.*)$/) {759			my ($alias, $addr) = ($1, $2);760			$addr =~ s/#.*$//; # mutt allows # comments761			# commas delimit multiple addresses762			my @addr = split_addrs($addr);763 764			# quotes may be escaped in the file,765			# unescape them so we do not double-escape them later.766			s/\\"/"/g foreach @addr;767			$aliases{$alias} = \@addr768		}}},769	mailrc => sub {	my $fh = shift; while (<$fh>) {770		if (/^alias\s+(\S+)\s+(.*?)\s*$/) {771			require Text::ParseWords;772			# spaces delimit multiple addresses773			$aliases{$1} = [ Text::ParseWords::quotewords('\s+', 0, $2) ];774		}}},775	pine => sub { my $fh = shift; my $f='\t[^\t]*';776	        for (my $x = ''; defined($x); $x = $_) {777			chomp $x;778		        $x .= $1 while(defined($_ = <$fh>) && /^ +(.*)$/);779			$x =~ /^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ or next;780			$aliases{$1} = [ split_addrs($2) ];781		}},782	elm => sub  { my $fh = shift;783		      while (<$fh>) {784			  if (/^(\S+)\s+=\s+[^=]+=\s(\S+)/) {785			      my ($alias, $addr) = ($1, $2);786			       $aliases{$alias} = [ split_addrs($addr) ];787			  }788		      } },789	sendmail => \&parse_sendmail_aliases,790	gnus => sub { my $fh = shift; while (<$fh>) {791		if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) {792			$aliases{$1} = [ $2 ];793		}}}794	# Please update _git_config() in git-completion.bash when you795	# add new MUAs.796);797 798if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {799	foreach my $file (@alias_files) {800		open my $fh, '<', $file or die "opening $file: $!\n";801		$parse_alias{$aliasfiletype}->($fh);802		close $fh;803	}804}805 806if ($dump_aliases) {807    print "$_\n" for (sort keys %aliases);808    exit(0);809}810 811if ($translate_aliases) {812	while (<STDIN>) {813		my @addr_list = parse_address_line($_);814		@addr_list = expand_aliases(@addr_list);815		@addr_list = sanitize_address_list(@addr_list);816		print "$_\n" for @addr_list;817	}818	exit(0);819}820 821# is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if822# $f is a revision list specification to be passed to format-patch.823sub is_format_patch_arg {824	return unless $repo;825	my $f = shift;826	try {827		$repo->command('rev-parse', '--verify', '--quiet', $f);828		if (defined($format_patch)) {829			return $format_patch;830		}831		die sprintf(__(<<EOF), $f, $f);832File '%s' exists but it could also be the range of commits833to produce patches for.  Please disambiguate by...834 835    * Saying "./%s" if you mean a file; or836    * Giving --format-patch option if you mean a range.837EOF838	} catch Git::Error::Command with {839		# Not a valid revision.  Treat it as a filename.840		return 0;841	}842}843 844# Now that all the defaults are set, process the rest of the command line845# arguments and collect up the files that need to be processed.846my @rev_list_opts;847while (defined(my $f = shift @ARGV)) {848	if ($f eq "--") {849		push @rev_list_opts, "--", @ARGV;850		@ARGV = ();851	} elsif (-d $f and !is_format_patch_arg($f)) {852		opendir my $dh, $f853			or die sprintf(__("Failed to opendir %s: %s"), $f, $!);854 855		require File::Spec;856		push @files, grep { -f $_ } map { File::Spec->catfile($f, $_) }857				sort readdir $dh;858		closedir $dh;859	} elsif ((-f $f or -p $f) and !is_format_patch_arg($f)) {860		push @files, $f;861	} else {862		push @rev_list_opts, $f;863	}864}865 866if (@rev_list_opts) {867	die __("Cannot run git format-patch from outside a repository\n")868		unless $repo;869	require File::Temp;870	push @files, $repo->command('format-patch', '-o', File::Temp::tempdir(CLEANUP => 1),871				    defined $reroll_count ? ('-v', $reroll_count) : (),872				    @rev_list_opts);873}874 875if (defined $sender) {876	$sender =~ s/^\s+|\s+$//g;877	($sender) = expand_aliases($sender);878} else {879	$sender = $repoauthor->() || $repocommitter->() || '';880}881 882# $sender could be an already sanitized address883# (e.g. sendemail.from could be manually sanitized by user).884# But it's a no-op to run sanitize_address on an already sanitized address.885$sender = sanitize_address($sender);886 887$time = time - scalar $#files;888 889@files = handle_backup_files(@files);890 891if (@files) {892	unless ($quiet) {893		print $_,"\n" for (@files);894	}895} else {896	print STDERR __("\nNo patch files specified!\n\n");897	usage();898}899 900sub get_patch_subject {901	my $fn = shift;902	open (my $fh, '<', $fn);903	while (my $line = <$fh>) {904		next unless ($line =~ /^Subject: (.*)$/);905		close $fh;906		return "GIT: $1\n";907	}908	close $fh;909	die sprintf(__("No subject line in %s?"), $fn);910}911 912if ($compose) {913	# Note that this does not need to be secure, but we will make a small914	# effort to have it be unique915	require File::Temp;916	$compose_filename = ($repo ?917		File::Temp::tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) :918		File::Temp::tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1];919	open my $c, ">", $compose_filename920		or die sprintf(__("Failed to open for writing %s: %s"), $compose_filename, $!);921 922 923	my $tpl_sender = $sender || $repoauthor->() || $repocommitter->() || '';924	my $tpl_subject = $initial_subject || '';925	my $tpl_in_reply_to = $initial_in_reply_to || '';926	my $tpl_reply_to = $reply_to || '';927	my $tpl_to = join(',', @initial_to);928	my $tpl_cc = join(',', @initial_cc);929	my $tpl_bcc = join(', ', @initial_bcc);930 931	print $c <<EOT1, Git::prefix_lines("GIT: ", __(<<EOT2)), <<EOT3;932From $tpl_sender # This line is ignored.933EOT1934Lines beginning in "GIT:" will be removed.935Consider including an overall diffstat or table of contents936for the patch you are writing.937 938Clear the body content if you don't wish to send a summary.939EOT2940From: $tpl_sender941To: $tpl_to942Cc: $tpl_cc943Bcc: $tpl_bcc944Reply-To: $tpl_reply_to945Subject: $tpl_subject946In-Reply-To: $tpl_in_reply_to947 948EOT3949	for my $f (@files) {950		print $c get_patch_subject($f);951	}952	close $c;953 954	if ($annotate) {955		do_edit($compose_filename, @files);956	} else {957		do_edit($compose_filename);958	}959 960	open my $c2, ">", $compose_filename . ".final"961		or die sprintf(__("Failed to open %s.final: %s"), $compose_filename, $!);962 963	open $c, "<", $compose_filename964		or die sprintf(__("Failed to open %s: %s"), $compose_filename, $!);965 966	my $need_8bit_cte = file_has_nonascii($compose_filename);967	my $in_body = 0;968	my $summary_empty = 1;969	if (!defined $compose_encoding) {970		$compose_encoding = "UTF-8";971	}972	while(<$c>) {973		next if m/^GIT:/;974		if ($in_body) {975			$summary_empty = 0 unless (/^\n$/);976		} elsif (/^\n$/) {977			$in_body = 1;978			if ($need_8bit_cte) {979				print $c2 "MIME-Version: 1.0\n",980					 "Content-Type: text/plain; ",981					   "charset=$compose_encoding\n",982					 "Content-Transfer-Encoding: 8bit\n";983			}984		} elsif (/^MIME-Version:/i) {985			$need_8bit_cte = 0;986		} elsif (/^Subject:\s*(.+)\s*$/i) {987			$initial_subject = $1;988			my $subject = $initial_subject;989			$_ = "Subject: " .990				quote_subject($subject, $compose_encoding) .991				"\n";992		} elsif (/^In-Reply-To:\s*(.+)\s*$/i) {993			$initial_in_reply_to = $1;994			next;995		} elsif (/^Reply-To:\s*(.+)\s*$/i) {996			$reply_to = $1;997		} elsif (/^From:\s*(.+)\s*$/i) {998			$sender = $1;999			next;1000		} elsif (/^To:\s*(.+)\s*$/i) {1001			@initial_to = parse_address_line($1);1002			next;1003		} elsif (/^Cc:\s*(.+)\s*$/i) {1004			@initial_cc = parse_address_line($1);1005			next;1006		} elsif (/^Bcc:/i) {1007			@initial_bcc = parse_address_line($1);1008			next;1009		}1010		print $c2 $_;1011	}1012	close $c;1013	close $c2;1014 1015	if ($summary_empty) {1016		print __("Summary email is empty, skipping it\n");1017		$compose = -1;1018	}1019} elsif ($annotate) {1020	do_edit(@files);1021}1022 1023{1024	# Only instantiate one $term per program run, since some1025	# Term::ReadLine providers refuse to create a second instance.1026	my $term;1027	sub term {1028		require Term::ReadLine;1029		if (!defined $term) {1030			$term = $ENV{"GIT_SEND_EMAIL_NOTTY"}1031				? Term::ReadLine->new('git-send-email', \*STDIN, \*STDOUT)1032				: Term::ReadLine->new('git-send-email');1033		}1034		return $term;1035	}1036}1037 1038sub ask {1039	my ($prompt, %arg) = @_;1040	my $valid_re = $arg{valid_re};1041	my $default = $arg{default};1042	my $confirm_only = $arg{confirm_only};1043	my $resp;1044	my $i = 0;1045	my $term = term();1046	return defined $default ? $default : undef1047		unless defined $term->IN and defined fileno($term->IN) and1048		       defined $term->OUT and defined fileno($term->OUT);1049	while ($i++ < 10) {1050		$resp = $term->readline($prompt);1051		if (!defined $resp) { # EOF1052			print "\n";1053			return defined $default ? $default : undef;1054		}1055		if ($resp eq '' and defined $default) {1056			return $default;1057		}1058		if (!defined $valid_re or $resp =~ /$valid_re/) {1059			return $resp;1060		}1061		if ($confirm_only) {1062			my $yesno = $term->readline(1063				# TRANSLATORS: please keep [y/N] as is.1064				sprintf(__("Are you sure you want to use <%s> [y/N]? "), $resp));1065			if (defined $yesno && $yesno =~ /y/i) {1066				return $resp;1067			}1068		}1069	}1070	return;1071}1072 1073my %broken_encoding;1074 1075sub file_declares_8bit_cte {1076	my $fn = shift;1077	open (my $fh, '<', $fn);1078	while (my $line = <$fh>) {1079		last if ($line =~ /^$/);1080		return 1 if ($line =~ /^Content-Transfer-Encoding: .*8bit.*$/);1081	}1082	close $fh;1083	return 0;1084}1085 1086foreach my $f (@files) {1087	next unless (body_or_subject_has_nonascii($f)1088		     && !file_declares_8bit_cte($f));1089	$broken_encoding{$f} = 1;1090}1091 1092if (!defined $auto_8bit_encoding && scalar %broken_encoding) {1093	print __("The following files are 8bit, but do not declare " .1094		 "a Content-Transfer-Encoding.\n");1095	foreach my $f (sort keys %broken_encoding) {1096		print "    $f\n";1097	}1098	while (1) {1099		my $encoding = ask(1100			__("Declare which 8bit encoding to use [default: UTF-8]? "),1101			valid_re => qr/^\S+$/,1102			default  => "UTF-8");1103		next unless defined $encoding;1104		if (find_encoding($encoding)) {1105			$auto_8bit_encoding = $encoding;1106			last;1107		}1108		my $yesno = ask(1109			sprintf(1110			__("'%s' does not appear to be a valid charset name. Use it anyway [y/N]? "),1111			$encoding),1112			valid_re => qr/^(?:y|n)/i,1113			default => "n");1114		if (defined $yesno && $yesno =~ /^y/i) {1115			$auto_8bit_encoding = $encoding;1116			last;1117		}1118	}1119}1120 1121if (!$force) {1122	for my $f (@files) {1123		if (get_patch_subject($f) =~ /\Q*** SUBJECT HERE ***\E/) {1124			die sprintf(__("Refusing to send because the patch\n\t%s\n"1125				. "has the template subject '*** SUBJECT HERE ***'. "1126				. "Pass --force if you really want to send.\n"), $f);1127		}1128	}1129}1130 1131my $to_whom = __("To whom should the emails be sent (if anyone)?");1132my $prompting = 0;1133if (!@initial_to && !defined $to_cmd) {1134	my $to = ask("$to_whom ",1135		     default => "",1136		     valid_re => qr/\@.*\./, confirm_only => 1);1137	push @initial_to, parse_address_line($to) if defined $to; # sanitized/validated later1138	$prompting++;1139}1140 1141sub expand_aliases {1142	return map { expand_one_alias($_) } @_;1143}1144 1145my %EXPANDED_ALIASES;1146sub expand_one_alias {1147	my $alias = shift;1148	if ($EXPANDED_ALIASES{$alias}) {1149		die sprintf(__("fatal: alias '%s' expands to itself\n"), $alias);1150	}1151	local $EXPANDED_ALIASES{$alias} = 1;1152	return $aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) : $alias;1153}1154 1155@initial_to = process_address_list(@initial_to);1156@initial_cc = process_address_list(@initial_cc);1157@initial_bcc = process_address_list(@initial_bcc);1158 1159if ($thread && !defined $initial_in_reply_to && $prompting) {1160	$initial_in_reply_to = ask(1161		__("Message-ID to be used as In-Reply-To for the first email (if any)? "),1162		default => "",1163		valid_re => qr/\@.*\./, confirm_only => 1);1164}1165if (defined $initial_in_reply_to) {1166	$initial_in_reply_to =~ s/^\s*<?//;1167	$initial_in_reply_to =~ s/>?\s*$//;1168	$initial_in_reply_to = "<$initial_in_reply_to>" if $initial_in_reply_to ne '';1169}1170 1171if (defined $reply_to) {1172	$reply_to =~ s/^\s+|\s+$//g;1173	($reply_to) = expand_aliases($reply_to);1174	$reply_to = sanitize_address($reply_to);1175}1176 1177if (!defined $sendmail_cmd && !defined $smtp_server) {1178	my @sendmail_paths = qw( /usr/sbin/sendmail /usr/lib/sendmail );1179	push @sendmail_paths, map {"$_/sendmail"} split /:/, $ENV{PATH};1180	foreach (@sendmail_paths) {1181		if (-x $_) {1182			$sendmail_cmd = $_;1183			last;1184		}1185	}1186 1187	if (!defined $sendmail_cmd) {1188		$smtp_server = 'localhost'; # could be 127.0.0.1, too... *shrug*1189	}1190}1191 1192if ($compose && $compose > 0) {1193	@files = ($compose_filename . ".final", @files);1194}1195 1196# Variables we set as part of the loop over files1197our ($message_id, %mail, $subject, $in_reply_to, $references, $message,1198	$needs_confirm, $message_num, $ask_default);1199 1200sub mailmap_address_list {

Showing the first 1,200 of 2516 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai