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