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