[PAUSE] CPAN Upload: A/AN/ANDK/CPAN-1.84.tar.gz
[p5sagit/p5-mst-13.2.git] / lib / CPAN / HandleConfig.pm
1 package CPAN::HandleConfig;
2 use strict;
3 use vars qw(%can %keys $dot_cpan $VERSION);
4
5 $VERSION = sprintf "%.6f", substr(q$Rev: 581 $,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 %keys = map { $_ => undef } (
15                              "build_cache",
16                              "build_dir",
17                              "bzip2",
18                              "cache_metadata",
19                              "commandnumber_in_prompt",
20                              "cpan_home",
21                              "curl",
22                              "dontload_hash", # deprecated after 1.83_68 (rev. 581)
23                              "dontload_list",
24                              "ftp",
25                              "ftp_passive",
26                              "ftp_proxy",
27                              "getcwd",
28                              "gpg",
29                              "gzip",
30                              "histfile",
31                              "histsize",
32                              "http_proxy",
33                              "inactivity_timeout",
34                              "index_expire",
35                              "inhibit_startup_message",
36                              "keep_source_where",
37                              "lynx",
38                              "make",
39                              "make_arg",
40                              "make_install_arg",
41                              "make_install_make_command",
42                              "makepl_arg",
43                              "mbuild_arg",
44                              "mbuild_install_arg",
45                              "mbuild_install_build_command",
46                              "mbuildpl_arg",
47                              "ncftp",
48                              "ncftpget",
49                              "no_proxy",
50                              "pager",
51                              "prefer_installer",
52                              "prerequisites_policy",
53                              "scan_cache",
54                              "shell",
55                              "show_upload_date",
56                              "tar",
57                              "term_is_latin",
58                              "unzip",
59                              "urllist",
60                              "wait_list",
61                              "wget",
62                             );
63 if ($^O eq "MSWin32") {
64     for my $k (qw(
65                   mbuild_install_build_command
66                   make_install_make_command
67                  )) {
68         delete $keys{$k};
69         if (exists $CPAN::Config->{$k}) {
70             $CPAN::Frontend->mywarn("deleting previously set config variable ".
71                                     "'$k' => '$CPAN::Config->{$k}'");
72             delete $CPAN::Config->{$k};
73         }
74     }
75 }
76
77 # returns true on successful action
78 sub edit {
79     my($self,@args) = @_;
80     return unless @args;
81     CPAN->debug("self[$self]args[".join(" | ",@args)."]");
82     my($o,$str,$func,$args,$key_exists);
83     $o = shift @args;
84     $DB::single = 1;
85     if($can{$o}) {
86         $self->$o(args => \@args);
87         return 1;
88     } else {
89         CPAN->debug("o[$o]") if $CPAN::DEBUG;
90         unless (exists $keys{$o}) {
91             $CPAN::Frontend->mywarn("Warning: unknown configuration variable '$o'\n");
92         }
93         if ($o =~ /list$/) {
94             $func = shift @args;
95             $func ||= "";
96             CPAN->debug("func[$func]") if $CPAN::DEBUG;
97             my $changed;
98             # Let's avoid eval, it's easier to comprehend without.
99             if ($func eq "push") {
100                 push @{$CPAN::Config->{$o}}, @args;
101                 $changed = 1;
102             } elsif ($func eq "pop") {
103                 pop @{$CPAN::Config->{$o}};
104                 $changed = 1;
105             } elsif ($func eq "shift") {
106                 shift @{$CPAN::Config->{$o}};
107                 $changed = 1;
108             } elsif ($func eq "unshift") {
109                 unshift @{$CPAN::Config->{$o}}, @args;
110                 $changed = 1;
111             } elsif ($func eq "splice") {
112                 splice @{$CPAN::Config->{$o}}, @args;
113                 $changed = 1;
114             } elsif (@args) {
115                 $CPAN::Config->{$o} = [@args];
116                 $changed = 1;
117             } else {
118                 $self->prettyprint($o);
119             }
120             if ($changed) {
121                 if ($o eq "urllist") {
122                     # reset the cached values
123                     undef $CPAN::FTP::Thesite;
124                     undef $CPAN::FTP::Themethod;
125                 } elsif ($o eq "dontload_list") {
126                     # empty it, it will be built up again
127                     $CPAN::META->{dontload_hash} = {};
128                 }
129             }
130             return $changed;
131         } elsif ($o =~ /_hash$/) {
132             @args = () if @args==1 && $args[0] eq "";
133             push @args, "" if @args % 2;
134             $CPAN::Config->{$o} = { @args };
135         } else {
136             $CPAN::Config->{$o} = $args[0] if defined $args[0];
137             $self->prettyprint($o);
138         }
139     }
140 }
141
142 sub prettyprint {
143   my($self,$k) = @_;
144   my $v = $CPAN::Config->{$k};
145   if (ref $v) {
146     my(@report);
147     if (ref $v eq "ARRAY") {
148       @report = map {"\t[$_]\n"} @$v;
149     } else {
150       @report = map { sprintf("\t%-18s => %s\n",
151                               map { "[$_]" } $_,
152                               defined $v->{$_} ? $v->{$_} : "UNDEFINED"
153                              )} keys %$v;
154     }
155     $CPAN::Frontend->myprint(
156                              join(
157                                   "",
158                                   sprintf(
159                                           "    %-18s\n",
160                                           $k
161                                          ),
162                                   @report
163                                  )
164                             );
165   } elsif (defined $v) {
166     $CPAN::Frontend->myprint(sprintf "    %-18s [%s]\n", $k, $v);
167   } else {
168     $CPAN::Frontend->myprint(sprintf "    %-18s [%s]\n", $k, "UNDEFINED");
169   }
170 }
171
172 sub commit {
173     my($self,@args) = @_;
174     my $configpm;
175     if (@args) {
176       if ($args[0] eq "args") {
177         # we have not signed that contract
178       } else {
179         $configpm = $args[0];
180       }
181     }
182     unless (defined $configpm){
183         $configpm ||= $INC{"CPAN/MyConfig.pm"};
184         $configpm ||= $INC{"CPAN/Config.pm"};
185         $configpm || Carp::confess(q{
186 CPAN::Config::commit called without an argument.
187 Please specify a filename where to save the configuration or try
188 "o conf init" to have an interactive course through configing.
189 });
190     }
191     my($mode);
192     if (-f $configpm) {
193         $mode = (stat $configpm)[2];
194         if ($mode && ! -w _) {
195             Carp::confess("$configpm is not writable");
196         }
197     }
198
199     my $msg;
200     $msg = <<EOF unless $configpm =~ /MyConfig/;
201
202 # This is CPAN.pm's systemwide configuration file. This file provides
203 # defaults for users, and the values can be changed in a per-user
204 # configuration file. The user-config file is being looked for as
205 # ~/.cpan/CPAN/MyConfig.pm.
206
207 EOF
208     $msg ||= "\n";
209     my($fh) = FileHandle->new;
210     rename $configpm, "$configpm~" if -f $configpm;
211     open $fh, ">$configpm" or
212         $CPAN::Frontend->mydie("Couldn't open >$configpm: $!");
213     $fh->print(qq[$msg\$CPAN::Config = \{\n]);
214     foreach (sort keys %$CPAN::Config) {
215         unless (exists $keys{$_}) {
216             $CPAN::Frontend->mywarn("Dropping unknown config variable '$_'\n");
217             delete $CPAN::Config->{$_};
218             next;
219         }
220         $fh->print(
221                    "  '$_' => ",
222                    $self->neatvalue($CPAN::Config->{$_}),
223                    ",\n"
224                   );
225     }
226
227     $fh->print("};\n1;\n__END__\n");
228     close $fh;
229
230     #$mode = 0444 | ( $mode & 0111 ? 0111 : 0 );
231     #chmod $mode, $configpm;
232 ###why was that so?    $self->defaults;
233     $CPAN::Frontend->myprint("commit: wrote '$configpm'\n");
234     1;
235 }
236
237 # stolen from MakeMaker; not taking the original because it is buggy;
238 # bugreport will have to say: keys of hashes remain unquoted and can
239 # produce syntax errors
240 sub neatvalue {
241     my($self, $v) = @_;
242     return "undef" unless defined $v;
243     my($t) = ref $v;
244     return "q[$v]" unless $t;
245     if ($t eq 'ARRAY') {
246         my(@m, @neat);
247         push @m, "[";
248         foreach my $elem (@$v) {
249             push @neat, "q[$elem]";
250         }
251         push @m, join ", ", @neat;
252         push @m, "]";
253         return join "", @m;
254     }
255     return "$v" unless $t eq 'HASH';
256     my(@m, $key, $val);
257     while (($key,$val) = each %$v){
258         last unless defined $key; # cautious programming in case (undef,undef) is true
259         push(@m,"q[$key]=>".$self->neatvalue($val)) ;
260     }
261     return "{ ".join(', ',@m)." }";
262 }
263
264 sub defaults {
265     my($self) = @_;
266     my $done;
267     for my $config (qw(CPAN/MyConfig.pm CPAN/Config.pm)) {
268       CPAN::Shell->reload_this($config) and $done++;
269       last if $done;
270     }
271     1;
272 }
273
274 sub init {
275     my($self,@args) = @_;
276     undef $CPAN::Config->{'inhibit_startup_message'}; # lazy trick to
277                                                       # have the least
278                                                       # important
279                                                       # variable
280                                                       # undefined
281     $self->load(@args);
282     1;
283 }
284
285 # This is a piece of repeated code that is abstracted here for
286 # maintainability.  RMB
287 #
288 sub _configpmtest {
289     my($configpmdir, $configpmtest) = @_; 
290     if (-w $configpmtest) {
291         return $configpmtest;
292     } elsif (-w $configpmdir) {
293         #_#_# following code dumped core on me with 5.003_11, a.k.
294         my $configpm_bak = "$configpmtest.bak";
295         unlink $configpm_bak if -f $configpm_bak;
296         if( -f $configpmtest ) {
297             if( rename $configpmtest, $configpm_bak ) {
298                                 $CPAN::Frontend->mywarn(<<END);
299 Old configuration file $configpmtest
300     moved to $configpm_bak
301 END
302             }
303         }
304         my $fh = FileHandle->new;
305         if ($fh->open(">$configpmtest")) {
306             $fh->print("1;\n");
307             return $configpmtest;
308         } else {
309             # Should never happen
310             Carp::confess("Cannot open >$configpmtest");
311         }
312     } else { return }
313 }
314
315 sub load {
316     my($self, %args) = @_;
317         $CPAN::Be_Silent++ if $args{be_silent};
318
319     my(@miss);
320     use Carp;
321     unless ($INC{"CPAN/MyConfig.pm"}) { # this guy has settled his needs already
322       eval {require CPAN::Config;}; # not everybody has one
323     }
324     unless ($dot_cpan++){
325       unshift @INC, File::Spec->catdir($ENV{HOME},".cpan");
326       eval {require CPAN::MyConfig;}; # override system wide settings
327       shift @INC;
328     }
329     return unless @miss = $self->missing_config_data;
330
331     require CPAN::FirstTime;
332     my($configpm,$fh,$redo,$theycalled);
333     $redo ||= "";
334     $theycalled++ if @miss==1 && $miss[0] eq 'inhibit_startup_message';
335     if (defined $INC{"CPAN/Config.pm"} && -w $INC{"CPAN/Config.pm"}) {
336         $configpm = $INC{"CPAN/Config.pm"};
337         $redo++;
338     } elsif (defined $INC{"CPAN/MyConfig.pm"} && -w $INC{"CPAN/MyConfig.pm"}) {
339         $configpm = $INC{"CPAN/MyConfig.pm"};
340         $redo++;
341     } else {
342         my($path_to_cpan) = File::Basename::dirname($INC{"CPAN.pm"});
343         my($configpmdir) = File::Spec->catdir($path_to_cpan,"CPAN");
344         my($configpmtest) = File::Spec->catfile($configpmdir,"Config.pm");
345         my $inc_key;
346         if (-d $configpmdir or File::Path::mkpath($configpmdir)) {
347             $configpm = _configpmtest($configpmdir,$configpmtest);
348             $inc_key = "CPAN/Config.pm";
349         }
350         unless ($configpm) {
351             $configpmdir = File::Spec->catdir($ENV{HOME},".cpan","CPAN");
352             File::Path::mkpath($configpmdir);
353             $configpmtest = File::Spec->catfile($configpmdir,"MyConfig.pm");
354             $configpm = _configpmtest($configpmdir,$configpmtest);
355             $inc_key = "CPAN/MyConfig.pm";
356         }
357         if ($configpm) {
358           $INC{$inc_key} = $configpm;
359         } else {
360           my $text = qq{WARNING: CPAN.pm is unable to } .
361               qq{create a configuration file.};
362           output($text, 'confess');
363         }
364
365     }
366     local($") = ", ";
367     $CPAN::Frontend->myprint(<<END) if $redo && ! $theycalled;
368 Sorry, we have to rerun the configuration dialog for CPAN.pm due to
369 the following indispensable but missing parameters:
370
371 @miss
372 END
373     $CPAN::Frontend->myprint(qq{
374 $configpm initialized.
375 });
376
377     sleep 2;
378     CPAN::FirstTime::init($configpm, %args);
379 }
380
381 sub missing_config_data {
382     my(@miss);
383     for (
384          "build_cache",
385          "build_dir",
386          "cache_metadata",
387          "cpan_home",
388          "ftp_proxy",
389          "gzip",
390          "http_proxy",
391          "index_expire",
392          "inhibit_startup_message",
393          "keep_source_where",
394          "make",
395          "make_arg",
396          "make_install_arg",
397          "makepl_arg",
398          "mbuild_arg",
399          "mbuild_install_arg",
400          "mbuild_install_build_command",
401          "mbuildpl_arg",
402          "no_proxy",
403          "pager",
404          "prerequisites_policy",
405          "scan_cache",
406          "tar",
407          "unzip",
408          "urllist",
409         ) {
410         next unless exists $keys{$_};
411         push @miss, $_ unless defined $CPAN::Config->{$_};
412     }
413     return @miss;
414 }
415
416 sub help {
417     $CPAN::Frontend->myprint(q[
418 Known options:
419   commit    commit session changes to disk
420   defaults  reload default config values from disk
421   help      this help
422   init      go through a dialog to set all parameters
423
424 Edit key values as in the following (the "o" is a literal letter o):
425   o conf build_cache 15
426   o conf build_dir "/foo/bar"
427   o conf urllist shift
428   o conf urllist unshift ftp://ftp.foo.bar/
429   o conf inhibit_startup_message 1
430
431 ]);
432     undef; #don't reprint CPAN::Config
433 }
434
435 sub cpl {
436     my($word,$line,$pos) = @_;
437     $word ||= "";
438     CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG;
439     my(@words) = split " ", substr($line,0,$pos+1);
440     if (
441         defined($words[2])
442         and
443         (
444          $words[2] =~ /list$/ && @words == 3
445          ||
446          $words[2] =~ /list$/ && @words == 4 && length($word)
447         )
448        ) {
449         return grep /^\Q$word\E/, qw(splice shift unshift pop push);
450     } elsif (@words >= 4) {
451         return ();
452     }
453     my %seen;
454     my(@o_conf) =  sort grep { !$seen{$_}++ }
455         keys %can,
456             keys %$CPAN::Config,
457                 keys %keys;
458     return grep /^\Q$word\E/, @o_conf;
459 }
460
461
462 package
463     CPAN::Config; ####::###### #hide from indexer
464 # note: J. Nick Koston wrote me that they are using
465 # CPAN::Config->commit although undocumented. I suggested
466 # CPAN::Shell->o("conf","commit") even when ugly it is at least
467 # documented
468
469 # that's why I added the CPAN::Config class with autoload and
470 # deprecated warning
471
472 use strict;
473 use vars qw($AUTOLOAD $VERSION);
474 $VERSION = sprintf "%.2f", substr(q$Rev: 581 $,4)/100;
475
476 # formerly CPAN::HandleConfig was known as CPAN::Config
477 sub AUTOLOAD {
478   my($l) = $AUTOLOAD;
479   $CPAN::Frontend->mywarn("Dispatching deprecated method '$l' to CPAN::HandleConfig");
480   $l =~ s/.*:://;
481   CPAN::HandleConfig->$l(@_);
482 }
483
484 1;
485
486 __END__
487 # Local Variables:
488 # mode: cperl
489 # cperl-indent-level: 4
490 # End: