package CPAN::HandleConfig;
use strict;
use vars qw(%can %keys $loading $VERSION);
use File::Path ();
use File::Spec ();
use File::Basename ();
use Carp ();
=head1 NAME
CPAN::HandleConfig - internal configuration handling for CPAN.pm
=cut
$VERSION = "5.5012"; # see also CPAN::Config::VERSION at end of file
%can = (
commit => "Commit changes to disk",
defaults => "Reload defaults from disk",
help => "Short help about 'o conf' usage",
init => "Interactive setting of all options",
);
# Q: where is the "How do I add a new config option" HOWTO?
# A1: svn diff -r 757:758 # where dagolden added test_report [git e997b71de88f1019a1472fc13cb97b1b7f96610f]
# A2: svn diff -r 985:986 # where andk added yaml_module [git 312b6d9b12b1bdec0b6e282d853482145475021f]
# A3: 1. add new config option to %keys below
# 2. add a Pod description in CPAN::FirstTime in the DESCRIPTION
# section; it should include a prompt line; see others for
# examples
# 3. add a "matcher" section in CPAN::FirstTime::init that includes
# a prompt function; see others for examples
# 4. add config option to documentation section in CPAN.pm
%keys = map { $_ => undef }
(
"allow_installing_module_downgrades",
"allow_installing_outdated_dists",
"applypatch",
"auto_commit",
"build_cache",
"build_dir",
"build_dir_reuse",
"build_requires_install_policy",
"bzip2",
"cache_metadata",
"check_sigs",
"cleanup_after_install",
"colorize_debug",
"colorize_output",
"colorize_print",
"colorize_warn",
"commandnumber_in_prompt",
"commands_quote",
"connect_to_internet_ok",
"cpan_home",
"curl",
"dontload_hash", # deprecated after 1.83_68 (rev. 581)
"dontload_list",
"ftp",
"ftp_passive",
"ftp_proxy",
"ftpstats_size",
"ftpstats_period",
"getcwd",
"gpg",
"gzip",
"halt_on_failure",
"histfile",
"histsize",
"http_proxy",
"inactivity_timeout",
"index_expire",
"inhibit_startup_message",
"keep_source_where",
"load_module_verbosity",
"lynx",
"make",
"make_arg",
"make_install_arg",
"make_install_make_command",
"makepl_arg",
"mbuild_arg",
"mbuild_install_arg",
"mbuild_install_build_command",
"mbuildpl_arg",
"ncftp",
"ncftpget",
"no_proxy",
"pager",
"password",
"patch",
"patches_dir",
"perl5lib_verbosity",
"plugin_list",
"prefer_external_tar",
"prefer_installer",
"prefs_dir",
"prerequisites_policy",
"proxy_pass",
"proxy_user",
"pushy_https",
"randomize_urllist",
"recommends_policy",
"scan_cache",
"shell",
"show_unparsable_versions",
"show_upload_date",
"show_zero_versions",
"suggests_policy",
"tar",
"tar_verbosity",
"term_is_latin",
"term_ornaments",
"test_report",
"trust_test_report_history",
"unzip",
"urllist",
"urllist_ping_verbose",
"urllist_ping_external",
"use_prompt_default",
"use_sqlite",
"username",
"version_timeout",
"wait_list",
"wget",
"yaml_load_code",
"yaml_module",
);
my %prefssupport = map { $_ => 1 }
(
"allow_installing_module_downgrades",
"allow_installing_outdated_dists",
"build_requires_install_policy",
"check_sigs",
"make",
"make_install_make_command",
"prefer_installer",
"test_report",
);
# returns true on successful action
sub edit {
my($self,@args) = @_;
return unless @args;
CPAN->debug("self[$self]args[".join(" | ",@args)."]");
my($o,$str,$func,$args,$key_exists);
$o = shift @args;
if($can{$o}) {
my $success = $self->$o(args => \@args); # o conf init => sub init => sub load
unless ($success) {
die "Panic: could not configure CPAN.pm for args [@args]. Giving up.";
}
} else {
CPAN->debug("o[$o]") if $CPAN::DEBUG;
unless (exists $keys{$o}) {
$CPAN::Frontend->mywarn("Warning: unknown configuration variable '$o'\n");
}
my $changed;
# one day I used randomize_urllist for a boolean, so we must
# list them explicitly --ak
if (0) {
} elsif ($o =~ /^(wait_list|urllist|dontload_list|plugin_list)$/) {
#
# ARRAYS
#
$func = shift @args;
$func ||= "";
CPAN->debug("func[$func]args[@args]") if $CPAN::DEBUG;
# Let's avoid eval, it's easier to comprehend without.
if ($func eq "push") {
push @{$CPAN::Config->{$o}}, @args;
$changed = 1;
} elsif ($func eq "pop") {
pop @{$CPAN::Config->{$o}};
$changed = 1;
} elsif ($func eq "shift") {
shift @{$CPAN::Config->{$o}};
$changed = 1;
} elsif ($func eq "unshift") {
unshift @{$CPAN::Config->{$o}}, @args;
$changed = 1;
} elsif ($func eq "splice") {
my $offset = shift @args || 0;
my $length = shift @args || 0;
splice @{$CPAN::Config->{$o}}, $offset, $length, @args; # may warn
$changed = 1;
} elsif ($func) {
$CPAN::Config->{$o} = [$func, @args];
$changed = 1;
} else {
$self->prettyprint($o);
}
if ($changed) {
if ($o eq "urllist") {
# reset the cached values
undef $CPAN::FTP::Thesite;
undef $CPAN::FTP::Themethod;
$CPAN::Index::LAST_TIME = 0;
} elsif ($o eq "dontload_list") {
# empty it, it will be built up again
$CPAN::META->{dontload_hash} = {};
}
}
} elsif ($o =~ /_hash$/) {
#
# HASHES
#
if (@args==1 && $args[0] eq "") {
@args = ();
} elsif (@args % 2) {
push @args, "";
}
$CPAN::Config->{$o} = { @args };
$changed = 1;
} else {
#
# SCALARS
#
if (defined $args[0]) {
$CPAN::CONFIG_DIRTY = 1;
$CPAN::Config->{$o} = $args[0];
$changed = 1;
}
$self->prettyprint($o)
if exists $keys{$o} or defined $CPAN::Config->{$o};
}
if ($changed) {
if ($CPAN::Config->{auto_commit}) {
$self->commit;
} else {
$CPAN::CONFIG_DIRTY = 1;
$CPAN::Frontend->myprint("Please use 'o conf commit' to ".
"make the config permanent!\n\n");
}
}
}
}
sub prettyprint {
my($self,$k) = @_;
my $v = $CPAN::Config->{$k};
if (ref $v) {
my(@report);
if (ref $v eq "ARRAY") {
@report = map {"\t$_ \[$v->[$_]]\n"} 0..$#$v;
} else {
@report = map
{
sprintf "\t%-18s => %s\n",
"[$_]",
defined $v->{$_} ? "[$v->{$_}]" : "undef"
} sort keys %$v;
}
$CPAN::Frontend->myprint(
join(
"",
sprintf(
" %-18s\n",
$k
),
@report
)
);
} elsif (defined $v) {
$CPAN::Frontend->myprint(sprintf " %-18s [%s]\n", $k, $v);
} else {
$CPAN::Frontend->myprint(sprintf " %-18s undef\n", $k);
}
}
# generally, this should be called without arguments so that the currently
# loaded config file is where changes are committed.
sub commit {
my($self,@args) = @_;
CPAN->debug("args[@args]") if $CPAN::DEBUG;
if ($CPAN::RUN_DEGRADED) {
$CPAN::Frontend->mydie(
"'o conf commit' disabled in ".
"degraded mode. Maybe try\n".
" !undef \$CPAN::RUN_DEGRADED\n"
);
}
my ($configpm, $must_reload);
# XXX does anything do this? can it be simplified? -- dagolden, 2011-01-19
if (@args) {
if ($args[0] eq "args") {
# we have not signed that contract
} else {
$configpm = $args[0];
}
}
# use provided name or the current config or create a new MyConfig
$configpm ||= require_myconfig_or_config() || make_new_config();
# commit to MyConfig if we can't write to Config
if ( ! -w $configpm && $configpm =~ m{CPAN/Config\.pm} ) {
my $myconfig = _new_config_name();
$CPAN::Frontend->mywarn(
"Your $configpm file\n".
"is not writable. I will attempt to write your configuration to\n" .
"$myconfig instead.\n\n"
);
$configpm = make_new_config();
$must_reload++; # so it gets loaded as $INC{'CPAN/MyConfig.pm'}
}
# XXX why not just "-w $configpm"? -- dagolden, 2011-01-19
my($mode);
if (-f $configpm) {
$mode = (stat $configpm)[2];
if ($mode && ! -w _) {
_die_cant_write_config($configpm);
}
}
$self->_write_config_file($configpm);
require_myconfig_or_config() if $must_reload;
#$mode = 0444 | ( $mode & 0111 ? 0111 : 0 );
#chmod $mode, $configpm;
###why was that so? $self->defaults;
$CPAN::Frontend->myprint("commit: wrote '$configpm'\n");
$CPAN::CONFIG_DIRTY = 0;
1;
}
sub _write_config_file {
my ($self, $configpm) = @_;
my $msg;
$msg = <