Upgrade to CPAN-1.8801.
[p5sagit/p5-mst-13.2.git] / lib / CPAN / FirstTime.pm
1 # -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*-
2 package CPAN::Mirrored::By;
3 use strict;
4 use vars qw($VERSION);
5 $VERSION = sprintf "%.6f", substr(q$Rev: 879 $,4)/1000000 + 5.4;
6
7 sub new { 
8     my($self,@arg) = @_;
9     bless [@arg], $self;
10 }
11 sub continent { shift->[0] }
12 sub country { shift->[1] }
13 sub url { shift->[2] }
14
15 package CPAN::FirstTime;
16
17 use strict;
18 use ExtUtils::MakeMaker ();
19 use FileHandle ();
20 use File::Basename ();
21 use File::Path ();
22 use File::Spec;
23 use vars qw($VERSION $urllist);
24 $VERSION = sprintf "%.6f", substr(q$Rev: 879 $,4)/1000000 + 5.4;
25
26 =head1 NAME
27
28 CPAN::FirstTime - Utility for CPAN::Config file Initialization
29
30 =head1 SYNOPSIS
31
32 CPAN::FirstTime::init()
33
34 =head1 DESCRIPTION
35
36 The init routine asks a few questions and writes a CPAN/Config.pm or
37 CPAN/MyConfig.pm file (depending on what it is currently using).
38
39 =head1 LICENSE
40
41 This program is free software; you can redistribute it and/or
42 modify it under the same terms as Perl itself.
43
44 =cut
45
46 use vars qw( %prompts );
47
48 sub init {
49     my($configpm, %args) = @_;
50     use Config;
51     # extra args after 'o conf init'
52     my $matcher = $args{args} && @{$args{args}} ? $args{args}[0] : '';
53     if ($matcher =~ /^\/(.*)\/$/) {
54         # case /regex/ => take the first, ignore the rest
55         $matcher = $1;
56         shift @{$args{args}};
57         if (@{$args{args}}) {
58             local $" = " ";
59             $CPAN::Frontend->mywarn("Ignoring excessive arguments '@{$args{args}}'");
60             $CPAN::Frontend->mysleep(2);
61         }
62     } elsif (0 == length $matcher) {
63     } else {
64         # case WORD... => all arguments must be valid
65         for my $arg (@{$args{args}}) {
66             unless (exists $CPAN::HandleConfig::keys{$arg}) {
67                 $CPAN::Frontend->mywarn("'$arg' is not a valid configuration variable");
68                 return;
69             }
70         }
71         $matcher = "\\b(".join("|",@{$args{args}}).")\\b";
72     }
73     CPAN->debug("matcher[$matcher]") if $CPAN::DEBUG;
74
75     unless ($CPAN::VERSION) {
76         require CPAN::Nox;
77     }
78     require CPAN::HandleConfig;
79     CPAN::HandleConfig::require_myconfig_or_config();
80     $CPAN::Config ||= {};
81     local($/) = "\n";
82     local($\) = "";
83     local($|) = 1;
84
85     my($ans,$default);
86
87     #
88     #= Files, directories
89     #
90
91     unless ($matcher) {
92         $CPAN::Frontend->myprint($prompts{manual_config});
93     }
94
95     my $manual_conf;
96
97     local *_real_prompt;
98     if ( $args{autoconfig} ) {
99         $manual_conf = "no";
100     } elsif ($matcher) {
101         $manual_conf = "yes";
102     } else {
103         my $_conf = prompt("Would you like me to configure as much as possible ".
104                            "automatically?", "yes");
105         $manual_conf = ($_conf and $_conf =~ /^y/i) ? "no" : "yes";
106     }
107     CPAN->debug("manual_conf[$manual_conf]") if $CPAN::DEBUG;
108     my $fastread;
109     {
110       if ($manual_conf =~ /^y/i) {
111         $fastread = 0;
112       } else {
113         $fastread = 1;
114         $CPAN::Config->{urllist} ||= [];
115
116         local $^W = 0;
117         # prototype should match that of &MakeMaker::prompt
118         my $current_second = time;
119         my $current_second_count = 0;
120         my $i_am_mad = 0;
121         *_real_prompt = sub ($;$) {
122           my($q,$a) = @_;
123           my($ret) = defined $a ? $a : "";
124           $CPAN::Frontend->myprint(sprintf qq{%s [%s]\n\n}, $q, $ret);
125           eval { require Time::HiRes };
126           unless ($@) {
127               if (time == $current_second) {
128                   $current_second_count++;
129                   if ($current_second_count > 20) {
130                       # I don't like more than 20 prompts per second
131                       $i_am_mad++;
132                   }
133               } else {
134                   $current_second = time;
135                   $current_second_count = 0;
136                   $i_am_mad-- if $i_am_mad>0;
137               }
138               if ($i_am_mad>0){
139                   #require Carp;
140                   #Carp::cluck("SLEEEEEEEEPIIIIIIIIIIINGGGGGGGGGGG");
141                   Time::HiRes::sleep(0.1);
142               }
143           }
144           $ret;
145         };
146       }
147     }
148
149     if (!$matcher or 'cpan_home keep_source_where build_dir' =~ /$matcher/){
150         $CPAN::Frontend->myprint($prompts{config_intro});
151
152         if (!$matcher or 'cpan_home' =~ /$matcher/) {
153             my $cpan_home = $CPAN::Config->{cpan_home}
154                 || File::Spec->catdir($ENV{HOME}, ".cpan");
155
156             if (-d $cpan_home) {
157                 $CPAN::Frontend->myprint(qq{
158
159 I see you already have a  directory
160     $cpan_home
161 Shall we use it as the general CPAN build and cache directory?
162
163 });
164             } else {
165                 # no cpan-home, must prompt and get one
166                 $CPAN::Frontend->myprint($prompts{cpan_home_where});
167             }
168
169             $default = $cpan_home;
170             while ($ans = prompt("CPAN build and cache directory?",$default)) {
171                 unless (File::Spec->file_name_is_absolute($ans)) {
172                     require Cwd;
173                     my $cwd = Cwd::cwd();
174                     my $absans = File::Spec->catdir($cwd,$ans);
175                     $CPAN::Frontend->mywarn("The path '$ans' is not an ".
176                                             "absolute path. Please specify ".
177                                             "an absolute path\n");
178                     $default = $absans;
179                     next;
180                 }
181                 eval { File::Path::mkpath($ans); }; # dies if it can't
182                 if ($@) {
183                     $CPAN::Frontend->mywarn("Couldn't create directory $ans.\n".
184                                             "Please retry.\n");
185                     next;
186                 }
187                 if (-d $ans && -w _) {
188                     last;
189                 } else {
190                     $CPAN::Frontend->mywarn("Couldn't find directory $ans\n".
191                                             "or directory is not writable. Please retry.\n");
192                 }
193             }
194             $CPAN::Config->{cpan_home} = $ans;
195         }
196
197         if (!$matcher or 'keep_source_where' =~ /$matcher/) {
198             my_dflt_prompt("keep_source_where",
199                            File::Spec->catdir($CPAN::Config->{cpan_home},"sources"),
200                            $matcher,
201                           );
202         }
203
204         if (!$matcher or 'build_dir' =~ /$matcher/) {
205             my_dflt_prompt("build_dir",
206                            File::Spec->catdir($CPAN::Config->{cpan_home},"build"),
207                            $matcher
208                           );
209         }
210     }
211
212     #
213     #= Cache size, Index expire
214     #
215
216     if (!$matcher or 'build_cache' =~ /$matcher/){
217         $CPAN::Frontend->myprint($prompts{build_cache_intro});
218
219         # large enough to build large dists like Tk
220         my_dflt_prompt(build_cache => 100, $matcher);
221     }
222
223     if (!$matcher or 'index_expire' =~ /$matcher/) {
224         $CPAN::Frontend->myprint($prompts{index_expire_intro});
225
226         my_dflt_prompt(index_expire => 1, $matcher);
227     }
228
229     if (!$matcher or 'scan_cache' =~ /$matcher/){
230         $CPAN::Frontend->myprint($prompts{scan_cache_intro});
231
232         my_prompt_loop(scan_cache => 'atstart', $matcher, 'atstart|never');
233     }
234
235     #
236     #= cache_metadata
237     #
238
239     my_yn_prompt(cache_metadata => 1, $matcher);
240
241     #
242     #= Do we follow PREREQ_PM?
243     #
244
245     if (!$matcher or 'prerequisites_policy' =~ /$matcher/){
246         $CPAN::Frontend->myprint($prompts{prerequisites_policy_intro});
247
248         my_prompt_loop(prerequisites_policy => 'ask', $matcher,
249                        'follow|ask|ignore');
250     }
251
252     #
253     #= Module::Signature
254     #
255     if (!$matcher or 'check_sigs' =~ /$matcher/) {
256         my_yn_prompt(check_sigs => 0, $matcher);
257     }
258
259     #
260     #= CPAN::Reporter
261     #
262     if (!$matcher or 'test_report' =~ /$matcher/) {
263         my_yn_prompt(test_report => 0, $matcher);
264         if (
265             $CPAN::Config->{test_report} && 
266             $CPAN::META->has_inst("CPAN::Reporter") &&
267             CPAN::Reporter->can('configure')
268            ) {
269             $CPAN::Frontend->myprint("\nProceeding to configure CPAN::Reporter.\n");
270             CPAN::Reporter::configure();
271             $CPAN::Frontend->myprint("\nReturning to CPAN configuration.\n");
272         }
273     }
274
275     #
276     #= External programs
277     #
278
279     my @external_progs = qw/bzip2 gzip tar unzip make
280                       curl lynx wget ncftpget ncftp ftp
281                       gpg/;
282     my(@path) = split /$Config{'path_sep'}/, $ENV{'PATH'};
283     if (!$matcher or "@external_progs" =~ /$matcher/) {
284         $CPAN::Frontend->myprint($prompts{external_progs});
285
286         my $old_warn = $^W;
287         local $^W if $^O eq 'MacOS';
288         local $^W = $old_warn;
289         my $progname;
290         for $progname (@external_progs) {
291             next if $matcher && $progname !~ /$matcher/;
292             if ($^O eq 'MacOS') {
293                 $CPAN::Config->{$progname} = 'not_here';
294                 next;
295             }
296
297             my $progcall = $progname;
298             unless ($matcher) {
299                 # we really don't need ncftp if we have ncftpget, but
300                 # if they chose this dialog via matcher, they shall have it
301                 next if $progname eq "ncftp" && $CPAN::Config->{ncftpget} gt " ";
302             }
303             my $path = $CPAN::Config->{$progname}
304                 || $Config::Config{$progname}
305                     || "";
306             if (File::Spec->file_name_is_absolute($path)) {
307                 # testing existence is not good enough, some have these exe
308                 # extensions
309
310                 # warn "Warning: configured $path does not exist\n" unless -e $path;
311                 # $path = "";
312             } elsif ($path =~ /^\s+$/) {
313                 # preserve disabled programs
314             } else {
315                 $path = '';
316             }
317             unless ($path) {
318                 # e.g. make -> nmake
319                 $progcall = $Config::Config{$progname} if $Config::Config{$progname};
320             }
321
322             $path ||= find_exe($progcall,[@path]);
323             $CPAN::Frontend->mywarn("Warning: $progcall not found in PATH\n") unless
324                 $path; # not -e $path, because find_exe already checked that
325             $ans = prompt("Where is your $progname program?",$path) || $path;
326             $CPAN::Config->{$progname} = $ans;
327         }
328     }
329
330     if (!$matcher or 'pager' =~ /$matcher/) {
331         my $path = $CPAN::Config->{'pager'} || 
332             $ENV{PAGER} || find_exe("less",[@path]) || 
333                 find_exe("more",[@path]) || ($^O eq 'MacOS' ? $ENV{EDITOR} : 0 )
334                     || "more";
335         $ans = prompt("What is your favorite pager program?",$path);
336         $CPAN::Config->{'pager'} = $ans;
337     }
338
339     if (!$matcher or 'shell' =~ /$matcher/) {
340         my $path = $CPAN::Config->{'shell'};
341         if (File::Spec->file_name_is_absolute($path)) {
342             $CPAN::Frontend->mywarn("Warning: configured $path does not exist\n")
343                 unless -e $path;
344             $path = "";
345         }
346         $path ||= $ENV{SHELL};
347         $path ||= $ENV{COMSPEC} if $^O eq "MSWin32";
348         if ($^O eq 'MacOS') {
349             $CPAN::Config->{'shell'} = 'not_here';
350         } else {
351             $path =~ s,\\,/,g if $^O eq 'os2';  # Cosmetic only
352             $ans = prompt("What is your favorite shell?",$path);
353             $CPAN::Config->{'shell'} = $ans;
354         }
355     }
356
357     #
358     #= Installer, arguments to make etc.
359     #
360
361     if (!$matcher or 'prefer_installer' =~ /$matcher/){
362         $CPAN::Frontend->myprint($prompts{prefer_installer_intro});
363
364         my_prompt_loop(prefer_installer => 'EUMM', $matcher, 'MB|EUMM');
365     }
366
367     if (!$matcher or 'makepl_arg make_arg' =~ /$matcher/){
368         $CPAN::Frontend->myprint($prompts{makepl_arg_intro});
369
370         my_dflt_prompt(makepl_arg => "", $matcher);
371         my_dflt_prompt(make_arg => "", $matcher);
372     }
373
374     require CPAN::HandleConfig;
375     if (exists $CPAN::HandleConfig::keys{make_install_make_command}) {
376         # as long as Windows needs $self->_build_command, we cannot
377         # support sudo on windows :-)
378         my_dflt_prompt(make_install_make_command => $CPAN::Config->{make} || "",
379                        $matcher);
380     }
381
382     my_dflt_prompt(make_install_arg => $CPAN::Config->{make_arg} || "", 
383                    $matcher);
384
385     if (!$matcher or 'mbuildpl_arg mbuild_arg' =~ /$matcher/){
386         $CPAN::Frontend->myprint($prompts{mbuildpl_arg_intro});
387
388         my_dflt_prompt(mbuildpl_arg => "", $matcher);
389
390         my_dflt_prompt(mbuild_arg => "", $matcher);
391     }
392
393     if (exists $CPAN::HandleConfig::keys{mbuild_install_build_command}) {
394         # as long as Windows needs $self->_build_command, we cannot
395         # support sudo on windows :-)
396         my_dflt_prompt(mbuild_install_build_command => "./Build", $matcher);
397     }
398
399     my_dflt_prompt(mbuild_install_arg => "", $matcher);
400
401     #
402     #= Alarm period
403     #
404
405     if (!$matcher or 'inactivity_timeout' =~ /$matcher/) {
406         $CPAN::Frontend->myprint($prompts{inactivity_timeout_intro});
407         $default = $CPAN::Config->{inactivity_timeout} || 0;
408         $CPAN::Config->{inactivity_timeout} =
409             prompt("Timeout for inactivity during {Makefile,Build}.PL?",$default);
410     }
411
412     #
413     #= Proxies
414     #
415
416     my @proxy_vars = qw/ftp_proxy http_proxy no_proxy/;
417     my @proxy_user_vars = qw/proxy_user proxy_pass/;
418     if (!$matcher or "@proxy_vars @proxy_user_vars" =~ /$matcher/){
419         $CPAN::Frontend->myprint($prompts{proxy_intro});
420
421         for (@proxy_vars) {
422             if (!$matcher or /$matcher/){
423                 $default = $CPAN::Config->{$_} || $ENV{$_} || "";
424                 $CPAN::Config->{$_} = prompt("Your $_?",$default);
425             }
426         }
427
428         if ($CPAN::Config->{ftp_proxy} ||
429             $CPAN::Config->{http_proxy}) {
430
431             $default = $CPAN::Config->{proxy_user} || $CPAN::LWP::UserAgent::USER || "";
432
433             $CPAN::Frontend->myprint($prompts{proxy_user});
434
435             if ($CPAN::Config->{proxy_user} = prompt("Your proxy user id?",$default)) {
436                 $CPAN::Frontend->myprint($prompts{proxy_pass});
437
438                 if ($CPAN::META->has_inst("Term::ReadKey")) {
439                     Term::ReadKey::ReadMode("noecho");
440                 } else {
441                     $CPAN::Frontend->myprint($prompts{password_warn});
442                 }
443                 $CPAN::Config->{proxy_pass} = prompt_no_strip("Your proxy password?");
444                 if ($CPAN::META->has_inst("Term::ReadKey")) {
445                     Term::ReadKey::ReadMode("restore");
446                 }
447                 $CPAN::Frontend->myprint("\n\n");
448             }
449         }
450     }
451
452     #
453     #= how FTP works
454     #
455
456     my_yn_prompt(ftp_passive => 1, $matcher);
457
458     #
459     #= how cwd works
460     #
461
462     if (!$matcher or 'getcwd' =~ /$matcher/){
463         $CPAN::Frontend->myprint($prompts{getcwd_intro});
464
465         my_prompt_loop(getcwd => 'cwd', $matcher,
466                        'cwd|getcwd|fastcwd|backtickcwd');
467     }
468
469     #
470     #= the CPAN shell itself
471     #
472
473     my_yn_prompt(commandnumber_in_prompt => 1, $matcher);
474     my_yn_prompt(term_ornaments => 1, $matcher);
475     if ("colorize_output colorize_print colorize_warn" =~ $matcher) {
476         my_yn_prompt(colorize_output => 0, $matcher);
477         if ($CPAN::Config->{colorize_output}) {
478             for my $tuple (
479                            ["colorize_print", "bold blue on_white"],
480                            ["colorize_warn", "bold red on_white"],
481                           ) {
482                 my_dflt_prompt($tuple->[0] => $tuple->[1], $matcher);
483                 if ($CPAN::META->has_inst("Term::ANSIColor")) {
484                     eval { Term::ANSIColor::color($CPAN::Config->{$tuple->[0]})};
485                     if ($@) {
486                         $CPAN::Config->{$tuple->[0]} = $tuple->[1];
487                         $CPAN::Frontend->mywarn($@."setting to default '$tuple->[1]'\n");
488                     }
489                 }
490             }
491         }
492     }
493
494     #
495     #== term_is_latin
496     #
497
498     if (!$matcher or 'term_is_latin' =~ /$matcher/){
499         $CPAN::Frontend->myprint($prompts{term_is_latin});
500         my_yn_prompt(term_is_latin => 1, $matcher);
501     }
502
503     #
504     #== save history in file 'histfile'
505     #
506
507     if (!$matcher or 'histfile histsize' =~ /$matcher/) {
508         $CPAN::Frontend->myprint($prompts{histfile_intro});
509         defined($default = $CPAN::Config->{histfile}) or
510             $default = File::Spec->catfile($CPAN::Config->{cpan_home},"histfile");
511         $ans = prompt("File to save your history?", $default);
512         $CPAN::Config->{histfile} = $ans;
513
514         if ($CPAN::Config->{histfile}) {
515             defined($default = $CPAN::Config->{histsize}) or $default = 100;
516             $ans = prompt("Number of lines to save?", $default);
517             $CPAN::Config->{histsize} = $ans;
518         }
519     }
520
521     #
522     #== do an ls on the m or the d command
523     #
524     if (!$matcher or 'show_upload_date' =~ /$matcher/) {
525         $CPAN::Frontend->myprint($prompts{show_upload_date_intro});
526
527         defined($default = $CPAN::Config->{show_upload_date}) or
528             $default = 'n';
529         $ans = prompt("Always try to show upload date with 'd' and 'm' command (yes/no)?",
530                       ($default ? 'yes' : 'no'));
531         $CPAN::Config->{show_upload_date} = ($ans =~ /^[y1]/i ? 1 : 0);
532     }
533
534     #
535     #= MIRRORED.BY and conf_sites()
536     #
537
538     if ($matcher){
539         if ("urllist" =~ $matcher) {
540             # conf_sites would go into endless loop with the smash prompt
541             local *_real_prompt;
542             *_real_prompt = \&CPAN::Shell::colorable_makemaker_prompt;
543             conf_sites();
544         }
545     } elsif ($fastread) {
546         $CPAN::Frontend->myprint("Autoconfigured everything but 'urllist'.\n".
547                                  "Please call 'o conf init urllist' to configure ".
548                                  "your CPAN server(s) now!");
549     } else {
550         conf_sites();
551     }
552
553     # We don't ask this one now, it's plain silly and maybe is not
554     # even used correctly everywhere.
555     $CPAN::Config->{inhibit_startup_message} = 0;
556
557     $CPAN::Frontend->myprint("\n\n");
558     if ($matcher) {
559         $CPAN::Frontend->myprint("Please remember to call 'o conf commit' to ".
560                                  "make the config permanent!\n\n");
561     } else {
562         CPAN::HandleConfig->commit($configpm);
563     }
564 }
565
566 sub my_dflt_prompt {
567     my ($item, $dflt, $m) = @_;
568     my $default = $CPAN::Config->{$item} || $dflt;
569
570     $DB::single = 1;
571     if (!$m || $item =~ /$m/) {
572         $CPAN::Config->{$item} = prompt($prompts{$item}, $default);
573     } else {
574         $CPAN::Config->{$item} = $default;
575     }
576 }
577
578 sub my_yn_prompt {
579     my ($item, $dflt, $m) = @_;
580     my $default;
581     defined($default = $CPAN::Config->{$item}) or $default = $dflt;
582
583     $DB::single = 1;
584     if (!$m || $item =~ /$m/) {
585         if (my $intro = $prompts{$item . "_intro"}) {
586             $CPAN::Frontend->myprint($intro);
587         }
588         my $ans = prompt($prompts{$item}, $default ? 'yes' : 'no');
589         $CPAN::Config->{$item} = ($ans =~ /^[y1]/i ? 1 : 0);
590     } else {
591         $CPAN::Config->{$item} = $default;
592     }
593 }
594
595 sub my_prompt_loop {
596     my ($item, $dflt, $m, $ok) = @_;
597     my $default = $CPAN::Config->{$item} || $dflt;
598     my $ans;
599
600     $DB::single = 1;
601     if (!$m || $item =~ /$m/) {
602         do { $ans = prompt($prompts{$item}, $default);
603         } until $ans =~ /$ok/;
604         $CPAN::Config->{$item} = $ans;
605     } else {
606         $CPAN::Config->{$item} = $default;
607     }
608 }
609
610
611 sub conf_sites {
612   my $m = 'MIRRORED.BY';
613   my $mby = File::Spec->catfile($CPAN::Config->{keep_source_where},$m);
614   File::Path::mkpath(File::Basename::dirname($mby));
615   if (-f $mby && -f $m && -M $m < -M $mby) {
616     require File::Copy;
617     File::Copy::copy($m,$mby) or die "Could not update $mby: $!";
618   }
619   my $loopcount = 0;
620   local $^T = time;
621   my $overwrite_local = 0;
622   if ($mby && -f $mby && -M _ <= 60 && -s _ > 0) {
623       my $mtime = localtime((stat _)[9]);
624       my $prompt = qq{Found $mby as of $mtime
625
626 I\'d use that as a database of CPAN sites. If that is OK for you,
627 please answer 'y', but if you want me to get a new database now,
628 please answer 'n' to the following question.
629
630 Shall I use the local database in $mby?};
631       my $ans = prompt($prompt,"y");
632       $overwrite_local = 1 unless $ans =~ /^y/i;
633   }
634   while ($mby) {
635     if ($overwrite_local) {
636       $CPAN::Frontend->myprint(qq{Trying to overwrite $mby\n});
637       $mby = CPAN::FTP->localize($m,$mby,3);
638       $overwrite_local = 0;
639     } elsif ( ! -f $mby ){
640       $CPAN::Frontend->myprint(qq{You have no $mby\n  I\'m trying to fetch one\n});
641       $mby = CPAN::FTP->localize($m,$mby,3);
642     } elsif (-M $mby > 60 && $loopcount == 0) {
643         $CPAN::Frontend->myprint(qq{Your $mby is older than 60 days,\n  I\'m trying }.
644                                  qq{to fetch one\n});
645         $mby = CPAN::FTP->localize($m,$mby,3);
646         $loopcount++;
647     } elsif (-s $mby == 0) {
648       $CPAN::Frontend->myprint(qq{You have an empty $mby,\n  I\'m trying to fetch one\n});
649       $mby = CPAN::FTP->localize($m,$mby,3);
650     } else {
651       last;
652     }
653   }
654   local $urllist = [];
655   read_mirrored_by($mby);
656   bring_your_own();
657   $CPAN::Config->{urllist} = $urllist;
658 }
659
660 sub find_exe {
661     my($exe,$path) = @_;
662     my($dir);
663     #warn "in find_exe exe[$exe] path[@$path]";
664     for $dir (@$path) {
665         my $abs = File::Spec->catfile($dir,$exe);
666         if (($abs = MM->maybe_command($abs))) {
667             return $abs;
668         }
669     }
670 }
671
672 sub picklist {
673     my($items,$prompt,$default,$require_nonempty,$empty_warning)=@_;
674     CPAN->debug("picklist('$items','$prompt','$default','$require_nonempty',".
675                 "'$empty_warning')") if $CPAN::DEBUG;
676     $default ||= '';
677
678     my $pos = 0;
679
680     my @nums;
681   SELECTION: while (1) {
682
683         # display, at most, 15 items at a time
684         my $limit = $#{ $items } - $pos;
685         $limit = 15 if $limit > 15;
686
687         # show the next $limit items, get the new position
688         $pos = display_some($items, $limit, $pos, $default);
689         $pos = 0 if $pos >= @$items;
690
691         my $num = prompt($prompt,$default);
692
693         @nums = split (' ', $num);
694         {
695             my %seen;
696             @nums = grep { !$seen{$_}++ } @nums;
697         }
698         my $i = scalar @$items;
699         unrangify(\@nums);
700         if (grep (/\D/ || $_ < 1 || $_ > $i, @nums)){
701             $CPAN::Frontend->mywarn("invalid items entered, try again\n");
702             if ("@nums" =~ /\D/) {
703                 $CPAN::Frontend->mywarn("(we are expecting only numbers between 1 and $i)\n");
704             }
705             next SELECTION;
706         }
707         if ($require_nonempty && !@nums) {
708             $CPAN::Frontend->mywarn("$empty_warning\n");
709         }
710         $CPAN::Frontend->myprint("\n");
711
712         # a blank line continues...
713         next SELECTION unless @nums;
714         last;
715     }
716     for (@nums) { $_-- }
717     @{$items}[@nums];
718 }
719
720 sub unrangify ($) {
721     my($nums) = $_[0];
722     my @nums2 = ();
723     while (@{$nums||[]}) {
724         my $n = shift @$nums;
725         if ($n =~ /^(\d+)-(\d+)$/) {
726             my @range = $1 .. $2;
727             # warn "range[@range]";
728             push @nums2, @range;
729         } else {
730             push @nums2, $n;
731         }
732     }
733     push @$nums, @nums2;
734 }
735
736 sub display_some {
737     my ($items, $limit, $pos, $default) = @_;
738     $pos ||= 0;
739
740     my @displayable = @$items[$pos .. ($pos + $limit)];
741     for my $item (@displayable) {
742         $CPAN::Frontend->myprint(sprintf "(%d) %s\n", ++$pos, $item);
743     }
744     my $hit_what = $default ? "SPACE RETURN" : "RETURN";
745     $CPAN::Frontend->myprint(sprintf("%d more items, hit %s to show them\n",
746                                      (@$items - $pos),
747                                      $hit_what,
748                                     ))
749         if $pos < @$items;
750     return $pos;
751 }
752
753 sub read_mirrored_by {
754     my $local = shift or return;
755     my(%all,$url,$expected_size,$default,$ans,$host,
756        $dst,$country,$continent,@location);
757     my $fh = FileHandle->new;
758     $fh->open($local) or die "Couldn't open $local: $!";
759     local $/ = "\012";
760     while (<$fh>) {
761         ($host) = /^([\w\.\-]+)/ unless defined $host;
762         next unless defined $host;
763         next unless /\s+dst_(dst|location)/;
764         /location\s+=\s+\"([^\"]+)/ and @location = (split /\s*,\s*/, $1) and
765             ($continent, $country) = @location[-1,-2];
766         $continent =~ s/\s\(.*//;
767         $continent =~ s/\W+$//; # if Jarkko doesn't know latitude/longitude
768         /dst_dst\s+=\s+\"([^\"]+)/  and $dst = $1;
769         next unless $host && $dst && $continent && $country;
770         $all{$continent}{$country}{$dst} = CPAN::Mirrored::By->new($continent,$country,$dst);
771         undef $host;
772         $dst=$continent=$country="";
773     }
774     $fh->close;
775     $CPAN::Config->{urllist} ||= [];
776     my @previous_urls = @{$CPAN::Config->{urllist}};
777
778     $CPAN::Frontend->myprint($prompts{urls_intro});
779
780     my (@cont, $cont, %cont, @countries, @urls, %seen);
781     my $no_previous_warn =
782         "Sorry! since you don't have any existing picks, you must make a\n" .
783             "geographic selection.";
784     my $offer_cont = [sort keys %all];
785     if (@previous_urls) {
786         push @$offer_cont, "(edit previous picks)";
787         $default = @$offer_cont;
788     }
789     @cont = picklist($offer_cont,
790                      "Select your continent (or several nearby continents)",
791                      $default,
792                      ! @previous_urls,
793                      $no_previous_warn);
794
795
796     foreach $cont (@cont) {
797         my @c = sort keys %{$all{$cont}};
798         @cont{@c} = map ($cont, 0..$#c);
799         @c = map ("$_ ($cont)", @c) if @cont > 1;
800         push (@countries, @c);
801     }
802     if (@previous_urls && @countries) {
803         push @countries, "(edit previous picks)";
804         $default = @countries;
805     }
806
807     if (@countries) {
808         @countries = picklist (\@countries,
809                                "Select your country (or several nearby countries)",
810                                $default,
811                                ! @previous_urls,
812                                $no_previous_warn);
813         %seen = map (($_ => 1), @previous_urls);
814         # hmmm, should take list of defaults from CPAN::Config->{'urllist'}...
815         foreach $country (@countries) {
816             next if $country =~ /edit previous picks/;
817             (my $bare_country = $country) =~ s/ \(.*\)//;
818             my @u = sort keys %{$all{$cont{$bare_country}}{$bare_country}};
819             @u = grep (! $seen{$_}, @u);
820             @u = map ("$_ ($bare_country)", @u)
821                 if @countries > 1;
822             push (@urls, @u);
823         }
824     }
825     push (@urls, map ("$_ (previous pick)", @previous_urls));
826     my $prompt = "Select as many URLs as you like (by number),
827 put them on one line, separated by blanks, hyphenated ranges allowed
828  e.g. '1 4 5' or '7 1-4 8'";
829     if (@previous_urls) {
830         $default = join (' ', ((scalar @urls) - (scalar @previous_urls) + 1) ..
831                          (scalar @urls));
832         $prompt .= "\n(or just hit RETURN to keep your previous picks)";
833     }
834
835     @urls = picklist (\@urls, $prompt, $default);
836     foreach (@urls) { s/ \(.*\)//; }
837     push @$urllist, @urls;
838 }
839
840 sub bring_your_own {
841     my %seen = map (($_ => 1), @$urllist);
842     my($ans,@urls);
843     do {
844         my $prompt = "Enter another URL or RETURN to quit:";
845         unless (%seen) {
846             $prompt = qq{CPAN.pm needs at least one URL where it can fetch CPAN files from.
847
848 Please enter your CPAN site:};
849         }
850         $ans = prompt ($prompt, "");
851
852         if ($ans) {
853             $ans =~ s|/?\z|/|; # has to end with one slash
854             $ans = "file:$ans" unless $ans =~ /:/; # without a scheme is a file:
855             if ($ans =~ /^\w+:\/./) {
856                 push @urls, $ans unless $seen{$ans}++;
857             } else {
858                 $CPAN::Frontend->
859                     myprint(sprintf(qq{"%s" doesn\'t look like an URL at first sight.
860 I\'ll ignore it for now.
861 You can add it to your %s
862 later if you\'re sure it\'s right.\n},
863                                    $ans,
864                                    $INC{'CPAN/MyConfig.pm'}
865                                    || $INC{'CPAN/Config.pm'}
866                                    || "configuration file",
867                                   ));
868             }
869         }
870     } while $ans || !%seen;
871
872     push @$urllist, @urls;
873     # xxx delete or comment these out when you're happy that it works
874     $CPAN::Frontend->myprint("New set of picks:\n");
875     map { $CPAN::Frontend->myprint("  $_\n") } @$urllist;
876 }
877
878
879 sub _strip_spaces {
880     $_[0] =~ s/^\s+//;  # no leading spaces
881     $_[0] =~ s/\s+\z//; # no trailing spaces
882 }
883
884 sub prompt ($;$) {
885     unless (defined &_real_prompt) {
886         *_real_prompt = \&CPAN::Shell::colorable_makemaker_prompt;
887     }
888     my $ans = _real_prompt(@_);
889
890     _strip_spaces($ans);
891
892     return $ans;
893 }
894
895
896 sub prompt_no_strip ($;$) {
897     return _real_prompt(@_);
898 }
899
900
901 BEGIN {
902
903 my @prompts = (
904
905 manual_config => qq[
906
907 CPAN is the world-wide archive of perl resources. It consists of about
908 300 sites that all replicate the same contents around the globe. Many
909 countries have at least one CPAN site already. The resources found on
910 CPAN are easily accessible with the CPAN.pm module. If you want to use
911 CPAN.pm, lots of things have to be configured. Fortunately, most of
912 them can be determined automatically. If you prefer the automatic
913 configuration, answer 'yes' below.
914
915 If you prefer to enter a dialog instead, you can answer 'no' to this
916 question and I'll let you configure in small steps one thing after the
917 other. (Note: you can revisit this dialog anytime later by typing 'o
918 conf init' at the cpan prompt.)
919
920 ],
921
922 config_intro => qq{
923
924 The following questions are intended to help you with the
925 configuration. The CPAN module needs a directory of its own to cache
926 important index files and maybe keep a temporary mirror of CPAN files.
927 This may be a site-wide directory or a personal directory.
928
929 },
930
931 # cpan_home => qq{ },
932
933 cpan_home_where => qq{
934
935 First of all, I\'d like to create this directory. Where?
936
937 },
938
939 keep_source_where => qq{
940
941 Unless you are accessing the CPAN via the filesystem directly CPAN.pm
942 needs to keep the source files it downloads somewhere. Please supply a
943 directory where the downloaded files are to be kept.},
944
945 build_cache_intro => qq{
946
947 How big should the disk cache be for keeping the build directories
948 with all the intermediate files\?
949
950 },
951
952 build_cache =>
953 "Cache size for build directory (in MB)?",
954
955 build_dir =>
956
957 "Directory where the build process takes place?",
958
959 scan_cache_intro => qq{
960
961 By default, each time the CPAN module is started, cache scanning is
962 performed to keep the cache size in sync. To prevent this, answer
963 'never'.
964
965 },
966
967 scan_cache => "Perform cache scanning (atstart or never)?",
968
969 cache_metadata_intro => qq{
970
971 To considerably speed up the initial CPAN shell startup, it is
972 possible to use Storable to create a cache of metadata. If Storable
973 is not available, the normal index mechanism will be used.
974
975 },
976
977 cache_metadata => qq{Cache metadata (yes/no)?},
978
979 term_is_latin_intro => qq{
980
981 The next option deals with the charset (aka character set) your
982 terminal supports. In general, CPAN is English speaking territory, so
983 the charset does not matter much, but some of the aliens out there who
984 upload their software to CPAN bear names that are outside the ASCII
985 range. If your terminal supports UTF-8, you should say no to the next
986 question.  If it supports ISO-8859-1 (also known as LATIN1) then you
987 should say yes.  If it supports neither, your answer does not matter
988 because you will not be able to read the names of some authors
989 anyway. If you answer no, names will be output in UTF-8.
990
991 },
992
993 term_is_latin => qq{Your terminal expects ISO-8859-1 (yes/no)?},
994
995 histfile_intro => qq{
996
997 If you have one of the readline packages (Term::ReadLine::Perl,
998 Term::ReadLine::Gnu, possibly others) installed, the interactive CPAN
999 shell will have history support. The next two questions deal with the
1000 filename of the history file and with its size. If you do not want to
1001 set this variable, please hit SPACE RETURN to the following question.
1002
1003 },
1004
1005 histfile => qq{File to save your history?},
1006
1007 show_upload_date_intro => qq{
1008
1009 The 'd' and the 'm' command normally only show you information they
1010 have in their in-memory database and thus will never connect to the
1011 internet. If you set the 'show_upload_date' variable to true, 'm' and
1012 'd' will additionally show you the upload date of the module or
1013 distribution. Per default this feature is off because it may require a
1014 net connection to get at the upload date.
1015
1016 },
1017
1018 show_upload_date =>
1019 "Always try to show upload date with 'd' and 'm' command (yes/no)?",
1020
1021 prerequisites_policy_intro => qq{
1022
1023 The CPAN module can detect when a module which you are trying to build
1024 depends on prerequisites. If this happens, it can build the
1025 prerequisites for you automatically ('follow'), ask you for
1026 confirmation ('ask'), or just ignore them ('ignore'). Please set your
1027 policy to one of the three values.
1028
1029 },
1030
1031 prerequisites_policy =>
1032 "Policy on building prerequisites (follow, ask or ignore)?",
1033
1034 check_sigs_intro  => qq{
1035
1036 CPAN packages can be digitally signed by authors and thus verified
1037 with the security provided by strong cryptography. The exact mechanism
1038 is defined in the Module::Signature module. While this is generally
1039 considered a good thing, it is not always convenient to the end user
1040 to install modules that are signed incorrectly or where the key of the
1041 author is not available or where some prerequisite for
1042 Module::Signature has a bug and so on.
1043
1044 With the check_sigs parameter you can turn signature checking on and
1045 off. The default is off for now because the whole tool chain for the
1046 functionality is not yet considered mature by some. The author of
1047 CPAN.pm would recommend setting it to true most of the time and
1048 turning it off only if it turns out to be annoying.
1049
1050 Note that if you do not have Module::Signature installed, no signature
1051 checks will be performed at all.
1052
1053 },
1054
1055 check_sigs =>
1056 qq{Always try to check and verify signatures if a SIGNATURE file is in the package
1057 and Module::Signature is installed (yes/no)?},
1058
1059 test_report_intro =>
1060 qq{
1061
1062 The goal of the CPAN Testers project (http://testers.cpan.org/) is to
1063 test as many CPAN packages as possible on as many platforms as
1064 possible.  This provides valuable feedback to module authors and
1065 potential users to identify bugs or platform compatibility issues and
1066 improves the overall quality and value of CPAN.
1067
1068 One way you can contribute is to send test results for each module
1069 that you install.  If you install the CPAN::Reporter module, you have
1070 the option to automatically generate and email test reports to CPAN
1071 Testers whenever you run tests on a CPAN package.
1072
1073 See the CPAN::Reporter documentation for additional details and
1074 configuration settings.  If your firewall blocks outgoing email,
1075 you will need to configure CPAN::Reporter before sending reports.
1076
1077 },
1078
1079 test_report =>
1080 qq{Email test reports if CPAN::Reporter is installed (yes/no)?},
1081
1082 external_progs => qq{
1083
1084 The CPAN module will need a few external programs to work properly.
1085 Please correct me, if I guess the wrong path for a program. Don\'t
1086 panic if you do not have some of them, just press ENTER for those. To
1087 disable the use of a program, you can type a space followed by ENTER.
1088
1089 },
1090
1091 prefer_installer_intro => qq{
1092
1093 When you have Module::Build installed and a module comes with both a
1094 Makefile.PL and a Build.PL, which shall have precedence? The two
1095 installer modules we have are the old and well established
1096 ExtUtils::MakeMaker (for short: EUMM) which uses the Makefile.PL and
1097 the next generation installer Module::Build (MB) works with the
1098 Build.PL.
1099
1100 },
1101
1102 prefer_installer =>
1103 qq{In case you could choose, which installer would you prefer (EUMM or MB)?},
1104
1105 makepl_arg_intro => qq{
1106
1107 Every Makefile.PL is run by perl in a separate process. Likewise we
1108 run \'make\' and \'make install\' in separate processes. If you have
1109 any parameters \(e.g. PREFIX, LIB, UNINST or the like\) you want to
1110 pass to the calls, please specify them here.
1111
1112 If you don\'t understand this question, just press ENTER.
1113 },
1114
1115 makepl_arg => qq{
1116 Parameters for the 'perl Makefile.PL' command?
1117 Typical frequently used settings:
1118
1119     PREFIX=~/perl    # non-root users (please see manual for more hints)
1120
1121 Your choice: },
1122
1123 make_arg => qq{Parameters for the 'make' command?
1124 Typical frequently used setting:
1125
1126     -j3              # dual processor system
1127
1128 Your choice: },
1129
1130
1131 make_install_make_command => qq{Do you want to use a different make command for 'make install'?
1132 Cautious people will probably prefer:
1133
1134     su root -c make
1135 or
1136     sudo make
1137 or
1138     /path1/to/sudo -u admin_account /path2/to/make
1139
1140 or some such. Your choice: },
1141
1142
1143 make_install_arg => qq{Parameters for the 'make install' command?
1144 Typical frequently used setting:
1145
1146     UNINST=1         # to always uninstall potentially conflicting files
1147
1148 Your choice: },
1149
1150
1151 mbuildpl_arg_intro => qq{
1152
1153 The next questions deal with Module::Build support.
1154
1155 A Build.PL is run by perl in a separate process. Likewise we run
1156 './Build' and './Build install' in separate processes. If you have any
1157 parameters you want to pass to the calls, please specify them here.
1158
1159 },
1160
1161 mbuildpl_arg => qq{Parameters for the 'perl Build.PL' command?
1162 Typical frequently used settings:
1163
1164     --install_base /home/xxx             # different installation directory
1165
1166 Your choice: },
1167
1168 mbuild_arg => qq{Parameters for the './Build' command?
1169 Setting might be:
1170
1171     --extra_linker_flags -L/usr/foo/lib  # non-standard library location
1172
1173 Your choice: },
1174
1175
1176 mbuild_install_build_command => qq{Do you want to use a different command for './Build install'?
1177 Sudo users will probably prefer:
1178
1179     su root -c ./Build
1180 or
1181     sudo ./Build
1182 or
1183     /path1/to/sudo -u admin_account ./Build
1184
1185 or some such. Your choice: },
1186
1187
1188 mbuild_install_arg => qq{Parameters for the './Build install' command?
1189 Typical frequently used setting:
1190
1191     --uninst 1                           # uninstall conflicting files
1192
1193 Your choice: },
1194
1195
1196
1197 inactivity_timeout_intro => qq{
1198
1199 Sometimes you may wish to leave the processes run by CPAN alone
1200 without caring about them. Because the Makefile.PL or the Build.PL
1201 sometimes contains question you\'re expected to answer, you can set a
1202 timer that will kill a 'perl Makefile.PL' process after the specified
1203 time in seconds.
1204
1205 If you set this value to 0, these processes will wait forever. This is
1206 the default and recommended setting.
1207
1208 },
1209
1210 inactivity_timeout => 
1211 qq{Timeout for inactivity during {Makefile,Build}.PL? },
1212
1213
1214 proxy_intro => qq{
1215
1216 If you\'re accessing the net via proxies, you can specify them in the
1217 CPAN configuration or via environment variables. The variable in
1218 the \$CPAN::Config takes precedence.
1219
1220 },
1221
1222 proxy_user => qq{
1223
1224 If your proxy is an authenticating proxy, you can store your username
1225 permanently. If you do not want that, just press RETURN. You will then
1226 be asked for your username in every future session.
1227
1228 },
1229
1230 proxy_pass => qq{
1231
1232 Your password for the authenticating proxy can also be stored
1233 permanently on disk. If this violates your security policy, just press
1234 RETURN. You will then be asked for the password in every future
1235 session.
1236
1237 },
1238
1239 urls_intro => qq{
1240
1241 Now we need to know where your favorite CPAN sites are located. Push
1242 a few sites onto the array (just in case the first on the array won\'t
1243 work). If you are mirroring CPAN to your local workstation, specify a
1244 file: URL.
1245
1246 First, pick a nearby continent and country by typing in the number(s)
1247 in front of the item(s) you want to select. You can pick several of
1248 each, separated by spaces. Then, you will be presented with a list of
1249 URLs of CPAN mirrors in the countries you selected, along with
1250 previously selected URLs. Select some of those URLs, or just keep the
1251 old list. Finally, you will be prompted for any extra URLs -- file:,
1252 ftp:, or http: -- that host a CPAN mirror.
1253
1254 },
1255
1256 password_warn => qq{
1257
1258 Warning: Term::ReadKey seems not to be available, your password will
1259 be echoed to the terminal!
1260
1261 },
1262
1263 commandnumber_in_prompt => qq{
1264
1265 The prompt of the cpan shell can contain the current command number
1266 for easier tracking of the session or be a plain string. Do you want
1267 the command number in the prompt (yes/no)?},
1268
1269 ftp_passive => qq{
1270
1271 Shall we always set FTP_PASSIVE envariable when dealing with ftp
1272 download (yes/no)?},
1273
1274 # taken from the manpage:
1275 getcwd_intro => qq{
1276
1277 CPAN.pm changes the current working directory often and needs to
1278 determine its own current working directory. Per default it uses
1279 Cwd::cwd but if this doesn't work on your system for some reason,
1280 alternatives can be configured according to the following table:
1281
1282     cwd         Cwd::cwd
1283     getcwd      Cwd::getcwd
1284     fastcwd     Cwd::fastcwd
1285     backtickcwd external command cwd
1286
1287 },
1288
1289 getcwd => qq{Preferred method for determining the current working directory?},
1290
1291 index_expire_intro => qq{
1292
1293 The CPAN indexes are usually rebuilt once or twice per hour, but the
1294 typical CPAN mirror mirrors only once or twice per day. Depending on
1295 the quality of your mirror and your desire to be on the bleeding edge,
1296 you may want to set the following value to more or less than one day
1297 (which is the default). It determines after how many days CPAN.pm
1298 downloads new indexes.
1299
1300 },
1301
1302 index_expire => qq{Let the index expire after how many days?},
1303
1304 term_ornaments => qq{
1305
1306 When using Term::ReadLine, you can turn ornaments on so that your
1307 input stands out against the output from CPAN.pm. Do you want to turn
1308 ornaments on?},
1309
1310 colorize_output => qq{
1311
1312 When you have Term::ANSIColor installed, you can turn on colorized
1313 output to have some visual differences between normal CPAN.pm output,
1314 warnings, and the output of the modules being installed. Set your
1315 favorite colors after some experimenting with the Term::ANSIColor
1316 module. Do you want to turn on colored output?},
1317
1318 colorize_print => qq{Color for normal output?},
1319
1320 colorize_warn => qq{Color for warnings?},
1321
1322 );
1323
1324 die "Coding error in \@prompts declaration.  Odd number of elements, above"
1325   if (@prompts % 2);
1326
1327 %prompts = @prompts;
1328
1329 if (scalar(keys %prompts) != scalar(@prompts)/2) {
1330     my %already;
1331     for my $item (0..$#prompts) {
1332         next if $item % 2;
1333         die "$prompts[$item] is duplicated\n" if $already{$prompts[$item]}++;
1334     }
1335 }
1336
1337 } # EOBEGIN
1338
1339 1;