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