This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
document Git_Data
[perl5.git] / lib / CPAN / HandleConfig.pm
... / ...
CommitLineData
1package CPAN::HandleConfig;
2use strict;
3use vars qw(%can %keys $loading $VERSION);
4
5$VERSION = "5.5";
6
7%can = (
8 commit => "Commit changes to disk",
9 defaults => "Reload defaults from disk",
10 help => "Short help about 'o conf' usage",
11 init => "Interactive setting of all options",
12);
13
14# Q: where is the "How do I add a new config option" HOWTO?
15# A1: svn diff -r 757:758 # where dagolden added test_report
16# A2: svn diff -r 985:986 # where andk added yaml_module
17# A3: 1. add new config option to %keys below
18# 2. add a Pod description in CPAN::FirstTime; it should include a
19# prompt line; see others for examples
20# 3. add a "matcher" section in CPAN::FirstTime::init that includes
21# a prompt function; see others for examples
22# 4. add config option to documentation section in CPAN.pm
23
24%keys = map { $_ => undef }
25 (
26 "applypatch",
27 "auto_commit",
28 "build_cache",
29 "build_dir",
30 "build_dir_reuse",
31 "build_requires_install_policy",
32 "bzip2",
33 "cache_metadata",
34 "check_sigs",
35 "colorize_debug",
36 "colorize_output",
37 "colorize_print",
38 "colorize_warn",
39 "commandnumber_in_prompt",
40 "commands_quote",
41 "connect_to_internet_ok",
42 "cpan_home",
43 "curl",
44 "dontload_hash", # deprecated after 1.83_68 (rev. 581)
45 "dontload_list",
46 "ftp",
47 "ftp_passive",
48 "ftp_proxy",
49 "ftpstats_size",
50 "ftpstats_period",
51 "getcwd",
52 "gpg",
53 "gzip",
54 "halt_on_failure",
55 "histfile",
56 "histsize",
57 "http_proxy",
58 "inactivity_timeout",
59 "index_expire",
60 "inhibit_startup_message",
61 "keep_source_where",
62 "load_module_verbosity",
63 "lynx",
64 "make",
65 "make_arg",
66 "make_install_arg",
67 "make_install_make_command",
68 "makepl_arg",
69 "mbuild_arg",
70 "mbuild_install_arg",
71 "mbuild_install_build_command",
72 "mbuildpl_arg",
73 "ncftp",
74 "ncftpget",
75 "no_proxy",
76 "pager",
77 "password",
78 "patch",
79 "patches_dir",
80 "perl5lib_verbosity",
81 "prefer_installer",
82 "prefs_dir",
83 "prerequisites_policy",
84 "proxy_pass",
85 "proxy_user",
86 "randomize_urllist",
87 "scan_cache",
88 "shell",
89 "show_unparsable_versions",
90 "show_upload_date",
91 "show_zero_versions",
92 "tar",
93 "tar_verbosity",
94 "term_is_latin",
95 "term_ornaments",
96 "test_report",
97 "trust_test_report_history",
98 "unzip",
99 "urllist",
100 "use_sqlite",
101 "username",
102 "wait_list",
103 "wget",
104 "yaml_load_code",
105 "yaml_module",
106 );
107
108my %prefssupport = map { $_ => 1 }
109 (
110 "build_requires_install_policy",
111 "check_sigs",
112 "make",
113 "make_install_make_command",
114 "prefer_installer",
115 "test_report",
116 );
117
118# returns true on successful action
119sub edit {
120 my($self,@args) = @_;
121 return unless @args;
122 CPAN->debug("self[$self]args[".join(" | ",@args)."]");
123 my($o,$str,$func,$args,$key_exists);
124 $o = shift @args;
125 if($can{$o}) {
126 $self->$o(args => \@args); # o conf init => sub init => sub load
127 return 1;
128 } else {
129 CPAN->debug("o[$o]") if $CPAN::DEBUG;
130 unless (exists $keys{$o}) {
131 $CPAN::Frontend->mywarn("Warning: unknown configuration variable '$o'\n");
132 }
133 my $changed;
134
135
136 # one day I used randomize_urllist for a boolean, so we must
137 # list them explicitly --ak
138 if (0) {
139 } elsif ($o =~ /^(wait_list|urllist|dontload_list)$/) {
140
141 #
142 # ARRAYS
143 #
144
145 $func = shift @args;
146 $func ||= "";
147 CPAN->debug("func[$func]args[@args]") if $CPAN::DEBUG;
148 # Let's avoid eval, it's easier to comprehend without.
149 if ($func eq "push") {
150 push @{$CPAN::Config->{$o}}, @args;
151 $changed = 1;
152 } elsif ($func eq "pop") {
153 pop @{$CPAN::Config->{$o}};
154 $changed = 1;
155 } elsif ($func eq "shift") {
156 shift @{$CPAN::Config->{$o}};
157 $changed = 1;
158 } elsif ($func eq "unshift") {
159 unshift @{$CPAN::Config->{$o}}, @args;
160 $changed = 1;
161 } elsif ($func eq "splice") {
162 my $offset = shift @args || 0;
163 my $length = shift @args || 0;
164 splice @{$CPAN::Config->{$o}}, $offset, $length, @args; # may warn
165 $changed = 1;
166 } elsif ($func) {
167 $CPAN::Config->{$o} = [$func, @args];
168 $changed = 1;
169 } else {
170 $self->prettyprint($o);
171 }
172 if ($changed) {
173 if ($o eq "urllist") {
174 # reset the cached values
175 undef $CPAN::FTP::Thesite;
176 undef $CPAN::FTP::Themethod;
177 $CPAN::Index::LAST_TIME = 0;
178 } elsif ($o eq "dontload_list") {
179 # empty it, it will be built up again
180 $CPAN::META->{dontload_hash} = {};
181 }
182 }
183 } elsif ($o =~ /_hash$/) {
184
185 #
186 # HASHES
187 #
188
189 if (@args==1 && $args[0] eq "") {
190 @args = ();
191 } elsif (@args % 2) {
192 push @args, "";
193 }
194 $CPAN::Config->{$o} = { @args };
195 $changed = 1;
196 } else {
197
198 #
199 # SCALARS
200 #
201
202 if (defined $args[0]) {
203 $CPAN::CONFIG_DIRTY = 1;
204 $CPAN::Config->{$o} = $args[0];
205 $changed = 1;
206 }
207 $self->prettyprint($o)
208 if exists $keys{$o} or defined $CPAN::Config->{$o};
209 }
210 if ($changed) {
211 if ($CPAN::Config->{auto_commit}) {
212 $self->commit;
213 } else {
214 $CPAN::CONFIG_DIRTY = 1;
215 $CPAN::Frontend->myprint("Please use 'o conf commit' to ".
216 "make the config permanent!\n\n");
217 }
218 }
219 }
220}
221
222sub prettyprint {
223 my($self,$k) = @_;
224 my $v = $CPAN::Config->{$k};
225 if (ref $v) {
226 my(@report);
227 if (ref $v eq "ARRAY") {
228 @report = map {"\t$_ \[$v->[$_]]\n"} 0..$#$v;
229 } else {
230 @report = map
231 {
232 sprintf "\t%-18s => %s\n",
233 "[$_]",
234 defined $v->{$_} ? "[$v->{$_}]" : "undef"
235 } keys %$v;
236 }
237 $CPAN::Frontend->myprint(
238 join(
239 "",
240 sprintf(
241 " %-18s\n",
242 $k
243 ),
244 @report
245 )
246 );
247 } elsif (defined $v) {
248 $CPAN::Frontend->myprint(sprintf " %-18s [%s]\n", $k, $v);
249 } else {
250 $CPAN::Frontend->myprint(sprintf " %-18s undef\n", $k);
251 }
252}
253
254sub commit {
255 my($self,@args) = @_;
256 CPAN->debug("args[@args]") if $CPAN::DEBUG;
257 if ($CPAN::RUN_DEGRADED) {
258 $CPAN::Frontend->mydie(
259 "'o conf commit' disabled in ".
260 "degraded mode. Maybe try\n".
261 " !undef \$CPAN::RUN_DEGRADED\n"
262 );
263 }
264 my $configpm;
265 if (@args) {
266 if ($args[0] eq "args") {
267 # we have not signed that contract
268 } else {
269 $configpm = $args[0];
270 }
271 }
272 unless (defined $configpm) {
273 $configpm ||= $INC{"CPAN/MyConfig.pm"};
274 $configpm ||= $INC{"CPAN/Config.pm"};
275 $configpm || Carp::confess(q{
276CPAN::Config::commit called without an argument.
277Please specify a filename where to save the configuration or try
278"o conf init" to have an interactive course through configing.
279});
280 }
281 my($mode);
282 if (-f $configpm) {
283 $mode = (stat $configpm)[2];
284 if ($mode && ! -w _) {
285 Carp::confess("$configpm is not writable");
286 }
287 }
288
289 my $msg;
290 my $home = home();
291 $msg = <<EOF unless $configpm =~ /MyConfig/;
292
293# This is CPAN.pm's systemwide configuration file. This file provides
294# defaults for users, and the values can be changed in a per-user
295# configuration file. The user-config file is being looked for as
296# $home/.cpan/CPAN/MyConfig.pm.
297
298EOF
299 $msg ||= "\n";
300 my($fh) = FileHandle->new;
301 rename $configpm, "$configpm~" if -f $configpm;
302 open $fh, ">$configpm" or
303 $CPAN::Frontend->mydie("Couldn't open >$configpm: $!");
304 $fh->print(qq[$msg\$CPAN::Config = \{\n]);
305 foreach (sort keys %$CPAN::Config) {
306 unless (exists $keys{$_}) {
307 # do not drop them: forward compatibility!
308 $CPAN::Frontend->mywarn("Unknown config variable '$_'\n");
309 next;
310 }
311 $fh->print(
312 " '$_' => ",
313 $self->neatvalue($CPAN::Config->{$_}),
314 ",\n"
315 );
316 }
317
318 $fh->print("};\n1;\n__END__\n");
319 close $fh;
320
321 #$mode = 0444 | ( $mode & 0111 ? 0111 : 0 );
322 #chmod $mode, $configpm;
323###why was that so? $self->defaults;
324 $CPAN::Frontend->myprint("commit: wrote '$configpm'\n");
325 $CPAN::CONFIG_DIRTY = 0;
326 1;
327}
328
329# stolen from MakeMaker; not taking the original because it is buggy;
330# bugreport will have to say: keys of hashes remain unquoted and can
331# produce syntax errors
332sub neatvalue {
333 my($self, $v) = @_;
334 return "undef" unless defined $v;
335 my($t) = ref $v;
336 unless ($t) {
337 $v =~ s/\\/\\\\/g;
338 return "q[$v]";
339 }
340 if ($t eq 'ARRAY') {
341 my(@m, @neat);
342 push @m, "[";
343 foreach my $elem (@$v) {
344 push @neat, "q[$elem]";
345 }
346 push @m, join ", ", @neat;
347 push @m, "]";
348 return join "", @m;
349 }
350 return "$v" unless $t eq 'HASH';
351 my(@m, $key, $val);
352 while (($key,$val) = each %$v) {
353 last unless defined $key; # cautious programming in case (undef,undef) is true
354 push(@m,"q[$key]=>".$self->neatvalue($val)) ;
355 }
356 return "{ ".join(', ',@m)." }";
357}
358
359sub defaults {
360 my($self) = @_;
361 if ($CPAN::RUN_DEGRADED) {
362 $CPAN::Frontend->mydie(
363 "'o conf defaults' disabled in ".
364 "degraded mode. Maybe try\n".
365 " !undef \$CPAN::RUN_DEGRADED\n"
366 );
367 }
368 my $done;
369 for my $config (qw(CPAN/MyConfig.pm CPAN/Config.pm)) {
370 if ($INC{$config}) {
371 CPAN->debug("INC{'$config'}[$INC{$config}]") if $CPAN::DEBUG;
372 CPAN::Shell->_reload_this($config,{reloforce => 1});
373 $CPAN::Frontend->myprint("'$INC{$config}' reread\n");
374 last;
375 }
376 }
377 $CPAN::CONFIG_DIRTY = 0;
378 1;
379}
380
381=head2 C<< CLASS->safe_quote ITEM >>
382
383Quotes an item to become safe against spaces
384in shell interpolation. An item is enclosed
385in double quotes if:
386
387 - the item contains spaces in the middle
388 - the item does not start with a quote
389
390This happens to avoid shell interpolation
391problems when whitespace is present in
392directory names.
393
394This method uses C<commands_quote> to determine
395the correct quote. If C<commands_quote> is
396a space, no quoting will take place.
397
398
399if it starts and ends with the same quote character: leave it as it is
400
401if it contains no whitespace: leave it as it is
402
403if it contains whitespace, then
404
405if it contains quotes: better leave it as it is
406
407else: quote it with the correct quote type for the box we're on
408
409=cut
410
411{
412 # Instead of patching the guess, set commands_quote
413 # to the right value
414 my ($quotes,$use_quote)
415 = $^O eq 'MSWin32'
416 ? ('"', '"')
417 : (q{"'}, "'")
418 ;
419
420 sub safe_quote {
421 my ($self, $command) = @_;
422 # Set up quote/default quote
423 my $quote = $CPAN::Config->{commands_quote} || $quotes;
424
425 if ($quote ne ' '
426 and defined($command )
427 and $command =~ /\s/
428 and $command !~ /[$quote]/) {
429 return qq<$use_quote$command$use_quote>
430 }
431 return $command;
432 }
433}
434
435sub init {
436 my($self,@args) = @_;
437 CPAN->debug("self[$self]args[".join(",",@args)."]");
438 $self->load(doit => 1, @args);
439 1;
440}
441
442# This is a piece of repeated code that is abstracted here for
443# maintainability. RMB
444#
445sub _configpmtest {
446 my($configpmdir, $configpmtest) = @_;
447 if (-w $configpmtest) {
448 return $configpmtest;
449 } elsif (-w $configpmdir) {
450 #_#_# following code dumped core on me with 5.003_11, a.k.
451 my $configpm_bak = "$configpmtest.bak";
452 unlink $configpm_bak if -f $configpm_bak;
453 if( -f $configpmtest ) {
454 if( rename $configpmtest, $configpm_bak ) {
455 $CPAN::Frontend->mywarn(<<END);
456Old configuration file $configpmtest
457 moved to $configpm_bak
458END
459 }
460 }
461 my $fh = FileHandle->new;
462 if ($fh->open(">$configpmtest")) {
463 $fh->print("1;\n");
464 return $configpmtest;
465 } else {
466 # Should never happen
467 Carp::confess("Cannot open >$configpmtest");
468 }
469 } else { return }
470}
471
472sub require_myconfig_or_config () {
473 return if $INC{"CPAN/MyConfig.pm"};
474 local @INC = @INC;
475 my $home = home();
476 unshift @INC, File::Spec->catdir($home,'.cpan');
477 eval { require CPAN::MyConfig };
478 my $err_myconfig = $@;
479 if ($err_myconfig and $err_myconfig !~ m#locate CPAN/MyConfig\.pm#) {
480 die "Error while requiring CPAN::MyConfig:\n$err_myconfig";
481 }
482 unless ($INC{"CPAN/MyConfig.pm"}) { # this guy has settled his needs already
483 eval {require CPAN::Config;}; # not everybody has one
484 my $err_config = $@;
485 if ($err_config and $err_config !~ m#locate CPAN/Config\.pm#) {
486 die "Error while requiring CPAN::Config:\n$err_config";
487 }
488 }
489}
490
491sub home () {
492 my $home;
493 # Suppress load messages until we load the config and know whether
494 # load messages are desired. Otherwise, it's unexpected and odd
495 # why one load message pops up even when verbosity is turned off.
496 # This means File::HomeDir load messages are never seen, but I
497 # think that's probably OK -- DAGOLDEN
498
499 # 5.6.2 seemed to segfault localizing a value in a hashref
500 # so do it manually instead
501 my $old_v = $CPAN::Config->{load_module_verbosity};
502 $CPAN::Config->{load_module_verbosity} = q[none];
503 if ($CPAN::META->has_usable("File::HomeDir")) {
504 $home = File::HomeDir->can('my_dot_config')
505 ? File::HomeDir->my_dot_config
506 : File::HomeDir->my_data;
507 unless (defined $home) {
508 $home = File::HomeDir->my_home
509 }
510 }
511 unless (defined $home) {
512 $home = $ENV{HOME};
513 }
514 $CPAN::Config->{load_module_verbosity} = $old_v;
515 $home;
516}
517
518sub load {
519 my($self, %args) = @_;
520 $CPAN::Be_Silent++ if $args{be_silent};
521 my $doit;
522 $doit = delete $args{doit};
523
524 use Carp;
525 require_myconfig_or_config;
526 my @miss = $self->missing_config_data;
527 CPAN->debug("doit[$doit]loading[$loading]miss[@miss]") if $CPAN::DEBUG;
528 return unless $doit || @miss;
529 return if $loading;
530 $loading++;
531
532 require CPAN::FirstTime;
533 my($configpm,$fh,$redo);
534 $redo ||= "";
535 if (defined $INC{"CPAN/Config.pm"} && -w $INC{"CPAN/Config.pm"}) {
536 $configpm = $INC{"CPAN/Config.pm"};
537 $redo++;
538 } elsif (defined $INC{"CPAN/MyConfig.pm"} && -w $INC{"CPAN/MyConfig.pm"}) {
539 $configpm = $INC{"CPAN/MyConfig.pm"};
540 $redo++;
541 } else {
542 my($path_to_cpan) = File::Basename::dirname($INC{"CPAN.pm"});
543 my($configpmdir) = File::Spec->catdir($path_to_cpan,"CPAN");
544 my($configpmtest) = File::Spec->catfile($configpmdir,"Config.pm");
545 my $inc_key;
546 if (-d $configpmdir or File::Path::mkpath($configpmdir)) {
547 $configpm = _configpmtest($configpmdir,$configpmtest);
548 $inc_key = "CPAN/Config.pm";
549 }
550 unless ($configpm) {
551 $configpmdir = File::Spec->catdir(home,".cpan","CPAN");
552 File::Path::mkpath($configpmdir);
553 $configpmtest = File::Spec->catfile($configpmdir,"MyConfig.pm");
554 $configpm = _configpmtest($configpmdir,$configpmtest);
555 $inc_key = "CPAN/MyConfig.pm";
556 }
557 if ($configpm) {
558 $INC{$inc_key} = $configpm;
559 } else {
560 my $text = qq{WARNING: CPAN.pm is unable to } .
561 qq{create a configuration file.};
562 output($text, 'confess');
563 }
564
565 }
566 local($") = ", ";
567 if ($redo && !$doit) {
568 $CPAN::Frontend->myprint(<<END);
569Sorry, we have to rerun the configuration dialog for CPAN.pm due to
570some missing parameters...
571
572END
573 $args{args} = \@miss;
574 }
575 CPAN::FirstTime::init($configpm, %args);
576 $loading--;
577 return;
578}
579
580
581# returns mandatory but missing entries in the Config
582sub missing_config_data {
583 my(@miss);
584 for (
585 "auto_commit",
586 "build_cache",
587 "build_dir",
588 "cache_metadata",
589 "cpan_home",
590 "ftp_proxy",
591 #"gzip",
592 "http_proxy",
593 "index_expire",
594 #"inhibit_startup_message",
595 "keep_source_where",
596 #"make",
597 "make_arg",
598 "make_install_arg",
599 "makepl_arg",
600 "mbuild_arg",
601 "mbuild_install_arg",
602 ($^O eq "MSWin32" ? "" : "mbuild_install_build_command"),
603 "mbuildpl_arg",
604 "no_proxy",
605 #"pager",
606 "prerequisites_policy",
607 "scan_cache",
608 #"tar",
609 #"unzip",
610 "urllist",
611 ) {
612 next unless exists $keys{$_};
613 push @miss, $_ unless defined $CPAN::Config->{$_};
614 }
615 return @miss;
616}
617
618sub help {
619 $CPAN::Frontend->myprint(q[
620Known options:
621 commit commit session changes to disk
622 defaults reload default config values from disk
623 help this help
624 init enter a dialog to set all or a set of parameters
625
626Edit key values as in the following (the "o" is a literal letter o):
627 o conf build_cache 15
628 o conf build_dir "/foo/bar"
629 o conf urllist shift
630 o conf urllist unshift ftp://ftp.foo.bar/
631 o conf inhibit_startup_message 1
632
633]);
634 undef; #don't reprint CPAN::Config
635}
636
637sub cpl {
638 my($word,$line,$pos) = @_;
639 $word ||= "";
640 CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG;
641 my(@words) = split " ", substr($line,0,$pos+1);
642 if (
643 defined($words[2])
644 and
645 $words[2] =~ /list$/
646 and
647 (
648 @words == 3
649 ||
650 @words == 4 && length($word)
651 )
652 ) {
653 return grep /^\Q$word\E/, qw(splice shift unshift pop push);
654 } elsif (defined($words[2])
655 and
656 $words[2] eq "init"
657 and
658 (
659 @words == 3
660 ||
661 @words >= 4 && length($word)
662 )) {
663 return sort grep /^\Q$word\E/, keys %keys;
664 } elsif (@words >= 4) {
665 return ();
666 }
667 my %seen;
668 my(@o_conf) = sort grep { !$seen{$_}++ }
669 keys %can,
670 keys %$CPAN::Config,
671 keys %keys;
672 return grep /^\Q$word\E/, @o_conf;
673}
674
675sub prefs_lookup {
676 my($self,$distro,$what) = @_;
677
678 if ($prefssupport{$what}) {
679 return $CPAN::Config->{$what} unless
680 $distro
681 and $distro->prefs
682 and $distro->prefs->{cpanconfig}
683 and defined $distro->prefs->{cpanconfig}{$what};
684 return $distro->prefs->{cpanconfig}{$what};
685 } else {
686 $CPAN::Frontend->mywarn("Warning: $what not yet officially ".
687 "supported for distroprefs, doing a normal lookup");
688 return $CPAN::Config->{$what};
689 }
690}
691
692
693{
694 package
695 CPAN::Config; ####::###### #hide from indexer
696 # note: J. Nick Koston wrote me that they are using
697 # CPAN::Config->commit although undocumented. I suggested
698 # CPAN::Shell->o("conf","commit") even when ugly it is at least
699 # documented
700
701 # that's why I added the CPAN::Config class with autoload and
702 # deprecated warning
703
704 use strict;
705 use vars qw($AUTOLOAD $VERSION);
706 $VERSION = "5.5";
707
708 # formerly CPAN::HandleConfig was known as CPAN::Config
709 sub AUTOLOAD { ## no critic
710 my $class = shift; # e.g. in dh-make-perl: CPAN::Config
711 my($l) = $AUTOLOAD;
712 $CPAN::Frontend->mywarn("Dispatching deprecated method '$l' to CPAN::HandleConfig\n");
713 $l =~ s/.*:://;
714 CPAN::HandleConfig->$l(@_);
715 }
716}
717
7181;
719
720__END__
721
722=head1 LICENSE
723
724This program is free software; you can redistribute it and/or
725modify it under the same terms as Perl itself.
726
727=cut
728
729# Local Variables:
730# mode: cperl
731# cperl-indent-level: 4
732# End: