Move CPANPLUS from lib/ to ext/
[p5sagit/p5-mst-13.2.git] / ext / CPANPLUS / lib / CPANPLUS / Configure / Setup.pm
CommitLineData
6aaee015 1package CPANPLUS::Configure::Setup;
2
3use strict;
4use vars qw(@ISA);
5
6use base qw[CPANPLUS::Internals::Utils];
7use base qw[Object::Accessor];
8
9use Config;
10use Term::UI;
11use Module::Load;
12use Term::ReadLine;
13
14
15use CPANPLUS::Internals::Utils;
16use CPANPLUS::Internals::Constants;
17use CPANPLUS::Error;
18
19use IPC::Cmd qw[can_run];
20use Params::Check qw[check];
21use Module::Load::Conditional qw[check_install];
22use Locale::Maketext::Simple Class => 'CPANPLUS', Style => 'gettext';
23
24### silence Term::UI
25$Term::UI::VERBOSE = 0;
26
27#Can't ioctl TIOCGETP: Unknown error
28#Consider installing Term::ReadKey from CPAN site nearby
29# at http://www.perl.com/CPAN
30#Or use
31# perl -MCPAN -e shell
32#to reach CPAN. Falling back to 'stty'.
33# If you do not want to see this warning, set PERL_READLINE_NOWARN
34#in your environment.
35#'stty' is not recognized as an internal or external command,
36#operable program or batch file.
37#Cannot call `stty': No such file or directory at C:/Perl/site/lib/Term/ReadLine/
38
39### setting this var in the meantime to avoid this warning ###
40$ENV{PERL_READLINE_NOWARN} = 1;
41
42
43sub new {
44 my $class = shift;
45 my %hash = @_;
46
47 my $tmpl = {
48 configure_object => { },
49 term => { },
50 backend => { },
51 autoreply => { default => 0, },
52 skip_mirrors => { default => 0, },
53 use_previous => { default => 1, },
54 config_type => { default => CONFIG_USER },
55 };
56
57 my $args = check( $tmpl, \%hash ) or return;
58
59 ### initialize object
60 my $obj = $class->SUPER::new( keys %$tmpl );
61 for my $acc ( $obj->ls_accessors ) {
62 $obj->$acc( $args->{$acc} );
63 }
64
65 ### otherwise there's a circular use ###
66 load CPANPLUS::Configure;
67 load CPANPLUS::Backend;
68
69 $obj->configure_object( CPANPLUS::Configure->new() )
70 unless $obj->configure_object;
71
72 $obj->backend( CPANPLUS::Backend->new( $obj->configure_object ) )
73 unless $obj->backend;
74
75 ### use empty string in case user only has T::R::Stub -- it complains
76 $obj->term( Term::ReadLine->new('') )
77 unless $obj->term;
78
79 ### enable autoreply if that was passed ###
80 $Term::UI::AUTOREPLY = $obj->autoreply;
81
82 return $obj;
83}
84
85sub init {
86 my $self = shift;
87 my $term = $self->term;
88
89 ### default setting, unless changed
90 $self->config_type( CONFIG_USER ) unless $self->config_type;
91
92 my $save = loc('Save & exit');
93 my $exit = loc('Quit without saving');
94 my @map = (
95 # key on the display # method to dispatch to
96 [ loc('Select Configuration file') => '_save_where' ],
97 [ loc('Setup CLI Programs') => '_setup_program' ],
98 [ loc('Setup CPANPLUS Home directory') => '_setup_base' ],
99 [ loc('Setup FTP/Email settings') => '_setup_ftp' ],
100 [ loc('Setup basic preferences') => '_setup_conf' ],
101 [ loc('Setup installer settings') => '_setup_installer' ],
102 [ loc('Select mirrors'), => '_setup_hosts' ],
103 [ loc('Edit configuration file') => '_edit' ],
104 [ $save => '_save' ],
105 [ $exit => 1 ],
106 );
107
108 my @keys = map { $_->[0] } @map; # sorted keys
109 my %map = map { @$_ } @map; # lookup hash
110
111 PICK_SECTION: {
112 print loc("
113=================> MAIN MENU <=================
114
115Welcome to the CPANPLUS configuration. Please select which
116parts you wish to configure
117
118Defaults are taken from your current configuration.
119If you would save now, your settings would be written to:
120
121 %1
122
123 ", $self->config_type );
124
125 my $choice = $term->get_reply(
126 prompt => "Section to configure:",
127 choices => \@keys,
128 default => $keys[0]
129 );
130
131 ### exit configuration?
132 if( $choice eq $exit ) {
133 print loc("
134Quitting setup, changes will not be saved.
135 ");
136 return 1;
137 }
138
139 my $method = $map{$choice};
140
141 my $rv = $self->$method or print loc("
142There was an error setting up this section. You might want to try again
143 ");
144
145 ### was it save & exit?
146 if( $choice eq $save and $rv ) {
147 print loc("
148Quitting setup, changes are saved to '%1'
149 ", $self->config_type
150 );
151 return 1;
152 }
153
154 ### otherwise, present choice again
155 redo PICK_SECTION;
156 }
157
158 return 1;
159}
160
161
162
163### sub that figures out what kind of config type the user wants
164sub _save_where {
165 my $self = shift;
166 my $term = $self->term;
167 my $conf = $self->configure_object;
168
169
170 ASK_CONFIG_TYPE: {
171
172 print loc( q[
173Where would you like to save your CPANPLUS Configuration file?
174
175If you want to configure CPANPLUS for this user only,
176select the '%1' option.
177The file will then be saved in your homedirectory.
178
179If you are the system administrator of this machine,
180and would like to make this config available globally,
181select the '%2' option.
182The file will be then be saved in your CPANPLUS
183installation directory.
184
185 ], CONFIG_USER, CONFIG_SYSTEM );
186
187
188 ### ask what config type we should save to
189 my $type = $term->get_reply(
190 prompt => loc("Type of configuration file"),
191 default => $self->config_type || CONFIG_USER,
192 choices => [CONFIG_USER, CONFIG_SYSTEM],
193 );
194
195 my $file = $conf->_config_pm_to_file( $type );
196
197 ### can we save to this file?
198 unless( $conf->can_save( $file ) ) {
199 error(loc(
200 "Can not save to file '%1'-- please check permissions " .
201 "and try again", $file
202 ));
203
204 redo ASK_CONFIG_FILE;
205 }
206
207 ### you already have the file -- are we allowed to overwrite
208 ### or should we try again?
209 if ( -e $file and -w _ ) {
210 print loc(q[
211I see you already have this file:
212 %1
213
4443dd53 214The file will not be overwritten until you explicitly save it.
6aaee015 215
216 ], $file );
217
218 redo ASK_CONFIG_TYPE
219 unless $term->ask_yn(
4443dd53 220 prompt => loc( "Do you wish to use this file?"),
6aaee015 221 default => 'n',
222 );
223 }
224
225 print $/, loc("Using '%1' as your configuration type", $type);
226
227 return $self->config_type($type);
228 }
229}
230
231
232### setup the build & cache dirs
233sub _setup_base {
234 my $self = shift;
235 my $term = $self->term;
236 my $conf = $self->configure_object;
237
238 my $base = $conf->get_conf('base');
239 my $home = File::Spec->catdir( $self->_home_dir, DOT_CPANPLUS );
240
241 print loc("
242CPANPLUS needs a directory of its own to cache important index
243files and maybe keep a temporary mirror of CPAN files.
244This may be a site-wide directory or a personal directory.
245
246For a single-user installation, we suggest using your home directory.
247
248");
249
250 my $where;
251 ASK_HOME_DIR: {
252 my $other = loc('Somewhere else');
253 if( $base and ($base ne $home) ) {
254 print loc("You have several choices:");
255
256 $where = $term->get_reply(
257 prompt => loc('Please pick one'),
258 choices => [$home, $base, $other],
259 default => $home,
260 );
261 } else {
262 $where = $base;
263 }
264
265 if( $where and -d $where ) {
266 print loc("
267I see you already have a directory:
268 %1
269
270 "), $where;
271
272 my $yn = $term->ask_yn(
273 prompt => loc('Should I use it?'),
274 default => 'y',
275 );
276 $where = '' unless $yn;
277 }
278
279 if( $where and ($where ne $other) and not -d $where ) {
280 if (!$self->_mkdir( dir => $where ) ) {
281 print "\n", loc("Unable to create directory '%1'", $where);
282 redo ASK_HOME_DIR;
283 }
284
285 } elsif( not $where or ($where eq $other) ) {
286 print loc("
287First of all, I'd like to create this directory.
288
289 ");
290
291 NEW_HOME: {
292 $where = $term->get_reply(
293 prompt => loc('Where shall I create it?'),
294 default => $home,
295 );
296
297 my $again;
298 if( -d $where and not -w _ ) {
299 print "\n", loc("I can't seem to write in this directory");
300 $again++;
301 } elsif (!$self->_mkdir( dir => $where ) ) {
302 print "\n", loc("Unable to create directory '%1'", $where);
303 $again++;
304 }
305
306 if( $again ) {
307 print "\n", loc('Please select another directory'), "\n\n";
308 redo NEW_HOME;
309 }
310 }
311 }
312 }
313
314 ### tidy up the path and store it
315 $where = File::Spec->rel2abs($where);
316 $conf->set_conf( base => $where );
317
318 ### create subdirectories ###
319 my @dirs =
320 File::Spec->catdir( $where, $self->_perl_version(perl => $^X),
321 $conf->_get_build('moddir') ),
322 map {
323 File::Spec->catdir( $where, $conf->_get_build($_) )
324 } qw[autdir distdir];
325
326 for my $dir ( @dirs ) {
327 unless( $self->_mkdir( dir => $dir ) ) {
328 warn loc("I wasn't able to create '%1'", $dir), "\n";
329 }
330 }
331
332 ### clear away old storable images before 0.031
333 for my $src (qw[dslip mailrc packages]) {
334 1 while unlink File::Spec->catfile( $where, $src );
335
336 }
337
338 print loc(q[
339Your CPANPLUS build and cache directory has been set to:
340 %1
341
342 ], $where);
343
344 return 1;
345}
346
347sub _setup_ftp {
348 my $self = shift;
349 my $term = $self->term;
350 my $conf = $self->configure_object;
351
352 #########################
353 ## are you a pacifist? ##
354 #########################
355
356 print loc("
357If you are connecting through a firewall or proxy that doesn't handle
358FTP all that well you can use passive FTP.
359
360");
361
362 my $yn = $term->ask_yn(
363 prompt => loc("Use passive FTP?"),
364 default => $conf->get_conf('passive'),
365 );
366
367 $conf->set_conf(passive => $yn);
368
369 ### set the ENV var as well, else it won't get set till AFTER
370 ### the configuration is saved. but we fetch files BEFORE that.
371 $ENV{FTP_PASSIVE} = $yn;
372
373 print "\n";
374 print $yn
375 ? loc("I will use passive FTP.")
376 : loc("I won't use passive FTP.");
377 print "\n";
378
379 #############################
380 ## should fetches timeout? ##
381 #############################
382
383 print loc("
384CPANPLUS can specify a network timeout for downloads (in whole seconds).
385If none is desired (or to skip this question), enter '0'.
386
387");
388
389 my $timeout = 0 + $term->get_reply(
390 prompt => loc("Network timeout for downloads"),
391 default => $conf->get_conf('timeout') || 0,
392 allow => qr/(?!\D)/, ### whole numbers only
393 );
394
395 $conf->set_conf(timeout => $timeout);
396
397 print "\n";
398 print $timeout
399 ? loc("The network timeout for downloads is %1 seconds.", $timeout)
400 : loc("The network timeout for downloads is not set.");
401 print "\n";
402
403 ############################
404 ## where can I reach you? ##
405 ############################
406
407 print loc("
408What email address should we send as our anonymous password when
409fetching modules from CPAN servers? Some servers will NOT allow you to
410connect without a valid email address, or at least something that looks
411like one.
412Also, if you choose to report test results at some point, a valid email
413is required for the 'from' field, so choose wisely.
414
415 ");
416
417 my $other = 'Something else';
418 my @choices = (DEFAULT_EMAIL, $Config{cf_email}, $other);
419 my $current = $conf->get_conf('email');
420
421 ### if your current address is not in the list, add it to the choices
422 unless (grep { $_ eq $current } @choices) {
423 unshift @choices, $current;
424 }
425
426 my $email = $term->get_reply(
427 prompt => loc('Which email address shall I use?'),
428 default => $current || $choices[0],
429 choices => \@choices,
430 );
431
432 if( $email eq $other ) {
433 EMAIL: {
434 $email = $term->get_reply(
435 prompt => loc('Email address: '),
436 );
437
438 unless( $self->_valid_email($email) ) {
439 print loc("
440You did not enter a valid email address, please try again!
441 ") if length $email;
442
443 redo EMAIL;
444 }
445 }
446 }
447
448 print loc("
449Your 'email' is now:
450 %1
451
452 ", $email);
453
454 $conf->set_conf( email => $email );
455
456 return 1;
457}
458
459
460### commandline programs
461sub _setup_program {
462 my $self = shift;
463 my $term = $self->term;
464 my $conf = $self->configure_object;
465
466 print loc("
467CPANPLUS can use command line utilities to do certain
468tasks, rather than use perl modules.
469
470If you wish to use a certain command utility, just enter
471the full path (or accept the default). If you do not wish
472to use it, enter a single space.
473
474Note that the paths you provide should not contain spaces, which is
475needed to make a distinction between program name and options to that
476program. For Win32 machines, you can use the short name for a path,
477like '%1'.
502c7995 478", 'c:\Progra~1\prog.exe' );
6aaee015 479
480 for my $prog ( sort $conf->options( type => 'program') ) {
481 PROGRAM: {
502c7995 482 print "\n", loc("Where can I find your '%1' utility? ".
483 "(Enter a single space to disable)", $prog ), "\n";
6aaee015 484
485 my $loc = $term->get_reply(
486 prompt => "Path to your '$prog'",
487 default => $conf->get_program( $prog ),
488 );
489
490 ### empty line clears it
491 my $cmd = $loc =~ /^\s*$/ ? undef : $loc;
492 my ($bin) = $cmd =~ /^(\S+)/;
493
494 ### did you provide a valid program ?
495 if( $bin and not can_run( $bin ) ) {
496 print "\n";
497 print loc("Can not find the binary '%1' in your path!", $bin);
498 redo PROGRAM;
499 }
500
501 ### make is special -- we /need/ it!
502 if( $prog eq 'make' and not $bin ) {
503 print loc(
504 "==> Without your '%1' utility, I can not function! <==",
505 'make'
506 );
507 print loc("Please provide one!");
508
509 ### show win32 where to download
510 if ( $^O eq 'MSWin32' ) {
511 print loc("You can get '%1' from:", NMAKE);
512 print "\t". NMAKE_URL ."\n";
513 }
514 print "\n";
515 redo PROGRAM;
516 }
517
518 $conf->set_program( $prog => $cmd );
519 print $cmd
502c7995 520 ? loc( "Your '%1' utility has been set to '%2'.",
6aaee015 521 $prog, $cmd )
502c7995 522 : loc( "Your '%1' has been disabled.", $prog );
6aaee015 523 print "\n";
524 }
525 }
526
527 return 1;
528}
529
530sub _setup_installer {
531 my $self = shift;
532 my $term = $self->term;
533 my $conf = $self->configure_object;
534
535 my $none = 'None';
536 {
537 print loc("
538CPANPLUS uses binary programs as well as Perl modules to accomplish
539various tasks. Normally, CPANPLUS will prefer the use of Perl modules
540over binary programs.
541
542You can change this setting by making CPANPLUS prefer the use of
543certain binary programs if they are available.
544
545 ");
546
547 ### default to using binaries if we don't have compress::zlib only
548 ### -- it'll get very noisy otherwise
549 my $type = 'prefer_bin';
550 my $yn = $term->ask_yn(
551 prompt => loc("Should I prefer the use of binary programs?"),
552 default => $conf->get_conf( $type ),
553 );
554
555 print $yn
556 ? loc("Ok, I will prefer to use binary programs if possible.")
557 : loc("Ok, I will prefer to use Perl modules if possible.");
558 print "\n\n";
559
560
561 $conf->set_conf( $type => $yn );
562 }
563
564 {
565 print loc("
566Makefile.PL is run by perl in a separate process, and accepts various
567flags that controls the module's installation. For instance, if you
568would like to install modules to your private user directory, set
569'makemakerflags' to:
570
571LIB=~/perl/lib INSTALLMAN1DIR=~/perl/man/man1 INSTALLMAN3DIR=~/perl/man/man3
572
573and be sure that you do NOT set UNINST=1 in 'makeflags' below.
574
575Enter a name=value list separated by whitespace, but quote any embedded
576spaces that you want to preserve. (Enter a space to clear any existing
577settings.)
578
579If you don't understand this question, just press ENTER.
580
581 ");
582
583 my $type = 'makemakerflags';
584 my $flags = $term->get_reply(
585 prompt => 'Makefile.PL flags?',
586 default => $conf->get_conf($type),
587 );
588
589 $flags = '' if $flags eq $none || $flags !~ /\S/;
590
591 print "\n", loc("Your '%1' have been set to:", 'Makefile.PL flags'),
592 "\n ", ( $flags ? $flags : loc('*nothing entered*')),
593 "\n\n";
594
595 $conf->set_conf( $type => $flags );
596 }
597
598 {
599 print loc("
600Like Makefile.PL, we run 'make' and 'make install' as separate processes.
601If you have any parameters (e.g. '-j3' in dual processor systems) you want
602to pass to the calls, please specify them here.
603
604In particular, 'UNINST=1' is recommended for root users, unless you have
605fine-tuned ideas of where modules should be installed in the \@INC path.
606
607Enter a name=value list separated by whitespace, but quote any embedded
608spaces that you want to preserve. (Enter a space to clear any existing
609settings.)
610
611Again, if you don't understand this question, just press ENTER.
612
613 ");
614 my $type = 'makeflags';
615 my $flags = $term->get_reply(
616 prompt => 'make flags?',
617 default => $conf->get_conf($type),
618 );
619
620 $flags = '' if $flags eq $none || $flags !~ /\S/;
621
622 print "\n", loc("Your '%1' have been set to:", $type),
623 "\n ", ( $flags ? $flags : loc('*nothing entered*')),
624 "\n\n";
625
626 $conf->set_conf( $type => $flags );
627 }
628
629 {
630 print loc("
631An alternative to ExtUtils::MakeMaker and Makefile.PL there's a module
632called Module::Build which uses a Build.PL.
633
634If you would like to specify any flags to pass when executing the
635Build.PL (and Build) script, please enter them below.
636
637For instance, if you would like to install modules to your private
638user directory, you could enter:
639
640 install_base=/my/private/path
641
642Or to uninstall old copies of modules before updating, you might
643want to enter:
644
645 uninst=1
646
647Again, if you don't understand this question, just press ENTER.
648
649 ");
650
651 my $type = 'buildflags';
652 my $flags = $term->get_reply(
653 prompt => 'Build.PL and Build flags?',
654 default => $conf->get_conf($type),
655 );
656
657 $flags = '' if $flags eq $none || $flags !~ /\S/;
658
659 print "\n", loc("Your '%1' have been set to:",
660 'Build.PL and Build flags'),
661 "\n ", ( $flags ? $flags : loc('*nothing entered*')),
662 "\n\n";
663
664 $conf->set_conf( $type => $flags );
665 }
666
667 ### use EU::MM or module::build? ###
668 {
669 print loc("
670Some modules provide both a Build.PL (Module::Build) and a Makefile.PL
671(ExtUtils::MakeMaker). By default, CPANPLUS prefers Makefile.PL.
672
673Module::Build support is not bundled standard with CPANPLUS, but
674requires you to install 'CPANPLUS::Dist::Build' from CPAN.
675
676Although Module::Build is a pure perl solution, which means you will
677not need a 'make' binary, it does have some limitations. The most
678important is that CPANPLUS is unable to uninstall any modules installed
679by Module::Build.
680
681Again, if you don't understand this question, just press ENTER.
682
683 ");
684 my $type = 'prefer_makefile';
685 my $yn = $term->ask_yn(
686 prompt => loc("Prefer Makefile.PL over Build.PL?"),
687 default => $conf->get_conf($type),
688 );
689
690 $conf->set_conf( $type => $yn );
691 }
692
693 {
694 print loc('
695If you like, CPANPLUS can add extra directories to your @INC list during
696startup. These will just be used by CPANPLUS and will not change your
697external environment or perl interpreter. Enter a space separated list of
698pathnames to be added to your @INC, quoting any with embedded whitespace.
699(To clear the current value enter a single space.)
700
701 ');
702
703 my $type = 'lib';
704 my $flags = $term->get_reply(
705 prompt => loc('Additional @INC directories to add?'),
706 default => (join " ", @{$conf->get_conf($type) || []} ),
707 );
708
709 my $lib;
710 unless( $flags =~ /\S/ ) {
711 $lib = [];
712 } else {
713 (@$lib) = $flags =~ m/\s*("[^"]+"|'[^']+'|[^\s]+)/g;
714 }
715
716 print "\n", loc("Your additional libs are now:"), "\n";
717
718 print scalar @$lib
719 ? map { " $_\n" } @$lib
720 : " ", loc("*nothing entered*"), "\n";
721 print "\n\n";
722
723 $conf->set_conf( $type => $lib );
724 }
725
726 return 1;
727}
728
729
730sub _setup_conf {
731 my $self = shift;
732 my $term = $self->term;
733 my $conf = $self->configure_object;
734
735 my $none = 'None';
736 {
737 ############
738 ## noisy? ##
739 ############
740
741 print loc("
742In normal operation I can just give you basic information about what I
743am doing, or I can be more verbose and give you every little detail.
744
745 ");
746
747 my $type = 'verbose';
748 my $yn = $term->ask_yn(
749 prompt => loc("Should I be verbose?"),
750 default => $conf->get_conf( $type ), );
751
752 print "\n";
753 print $yn
754 ? loc("You asked for it!")
755 : loc("I'll try to be quiet");
756
757 $conf->set_conf( $type => $yn );
758 }
759
760 {
761 #######################
762 ## flush you animal! ##
763 #######################
764
765 print loc("
766In the interest of speed, we keep track of what modules were installed
767successfully and which failed in the current session. We can flush this
768data automatically, or you can explicitly issue a 'flush' when you want
769to purge it.
770
771 ");
772
773 my $type = 'flush';
774 my $yn = $term->ask_yn(
775 prompt => loc("Flush automatically?"),
776 default => $conf->get_conf( $type ),
777 );
778
779 print "\n";
780 print $yn
781 ? loc("I'll flush after every full module install.")
782 : loc("I won't flush until you tell me to.");
783
784 $conf->set_conf( $type => $yn );
785 }
786
787 {
788 #####################
789 ## force installs? ##
790 #####################
791
792 print loc("
793Usually, when a test fails, I won't install the module, but if you
794prefer, I can force the install anyway.
795
796 ");
797
798 my $type = 'force';
799 my $yn = $term->ask_yn(
800 prompt => loc("Force installs?"),
801 default => $conf->get_conf( $type ),
802 );
803
804 print "\n";
805 print $yn
806 ? loc("I will force installs.")
807 : loc("I won't force installs.");
808
809 $conf->set_conf( $type => $yn );
810 }
811
812 {
813 ###################
814 ## about prereqs ##
815 ###################
816
817 print loc("
818Sometimes a module will require other modules to be installed before it
819will work. CPANPLUS can attempt to install these for you automatically
820if you like, or you can do the deed yourself.
821
822If you would prefer that we NEVER try to install extra modules
823automatically, select NO. (Usually you will want this set to YES.)
824
825If you would like to build modules to satisfy testing or prerequisites,
826but not actually install them, select BUILD.
827
828NOTE: This feature requires you to flush the 'lib' cache for longer
829running programs (refer to the CPANPLUS::Backend documentations for
830more details).
831
832Otherwise, select ASK to have us ask your permission to install them.
833
834 ");
835
836 my $type = 'prereqs';
837
838 my @map = (
839 [ PREREQ_IGNORE, # conf value
840 loc('No, do not install prerequisites'), # UI Value
841 loc("I won't install prerequisites") # diag message
842 ],
843 [ PREREQ_INSTALL,
844 loc('Yes, please install prerequisites'),
845 loc("I will install prerequisites")
846 ],
847 [ PREREQ_ASK,
848 loc('Ask me before installing a prerequisite'),
849 loc("I will ask permission to install")
850 ],
851 [ PREREQ_BUILD,
852 loc('Build prerequisites, but do not install them'),
853 loc( "I will only build, but not install prerequisites" )
854 ],
855 );
856
857 my %reply = map { $_->[1] => $_->[0] } @map; # choice => value
858 my %diag = map { $_->[1] => $_->[2] } @map; # choice => diag message
859 my %conf = map { $_->[0] => $_->[1] } @map; # value => ui choice
860
861 my $reply = $term->get_reply(
862 prompt => loc('Follow prerequisites?'),
863 default => $conf{ $conf->get_conf( $type ) },
864 choices => [ @conf{ sort keys %conf } ],
865 );
866 print "\n";
867
868 my $value = $reply{ $reply };
869 my $diag = $diag{ $reply };
870
871 $conf->set_conf( $type => $value );
872 print $diag, "\n";
873 }
874
875 { print loc("
876Modules in the CPAN archives are protected with md5 checksums.
877
878This requires the Perl module Digest::MD5 to be installed (which
879CPANPLUS can do for you later);
880
881 ");
882 my $type = 'md5';
883
884 my $yn = $term->ask_yn(
885 prompt => loc("Shall I use the MD5 checksums?"),
886 default => $conf->get_conf( $type ),
887 );
888
889 print $yn
890 ? loc("I will use the MD5 checksums if you have it")
891 : loc("I won't use the MD5 checksums");
892
893 $conf->set_conf( $type => $yn );
894
895 }
896
897
898 { ###########################################
899 ## sally sells seashells by the seashore ##
900 ###########################################
901
902 print loc("
903By default CPANPLUS uses its own shell when invoked. If you would prefer
904a different shell, such as one you have written or otherwise acquired,
905please enter the full name for your shell module.
906
907 ");
908
909 my $type = 'shell';
910 my $other = 'Other';
911 my @choices = (qw| CPANPLUS::Shell::Default
912 CPANPLUS::Shell::Classic |,
913 $other );
914 my $default = $conf->get_conf($type);
915
916 unshift @choices, $default unless grep { $_ eq $default } @choices;
917
918 my $reply = $term->get_reply(
919 prompt => loc('Which CPANPLUS shell do you want to use?'),
920 default => $default,
921 choices => \@choices,
922 );
923
924 if( $reply eq $other ) {
925 SHELL: {
926 $reply = $term->get_reply(
927 prompt => loc( 'Please enter the name of the shell '.
928 'you wish to use: '),
929 );
930
931 unless( check_install( module => $reply ) ) {
932 print "\n",
933 loc("Could not find '$reply' in your path " .
934 "-- please try again"),
935 "\n";
936 redo SHELL;
937 }
938 }
939 }
940
941 print "\n", loc("Your shell is now: %1", $reply), "\n\n";
942
943 $conf->set_conf( $type => $reply );
944 }
945
946 {
947 ###################
948 ## use storable? ##
949 ###################
950
951 print loc("
952To speed up the start time of CPANPLUS, and maintain a cache over
953multiple runs, we can use Storable to freeze some information.
954Would you like to do this?
955
956");
957 my $type = 'storable';
958 my $yn = $term->ask_yn(
959 prompt => loc("Use Storable?"),
960 default => $conf->get_conf( $type ) ? 1 : 0,
961 );
962 print "\n";
963 print $yn
964 ? loc("I will use Storable if you have it")
965 : loc("I will not use Storable");
966
967 $conf->set_conf( $type => $yn );
968 }
969
970 {
971 ###################
4443dd53 972 ## use sqlite ? ##
973 ###################
974
975 print loc("
976
977To limit the amount of RAM used by CPANPLUS, you can use the SQLite
978source backend instead. Note that it is currently still experimental.
979Would you like to do this?
980
981");
982 my $type = 'source_engine';
983 my $class = 'CPANPLUS::Internals::Source::SQLite';
984 my $yn = $term->ask_yn(
985 prompt => loc("Use SQLite?"),
986 default => $conf->get_conf( $type ) eq $class ? 1 : 0,
987 );
988 print "\n";
989 print $yn
990 ? loc("I will use SQLite")
991 : loc("I will not use SQLite");
992
993 $conf->set_conf( $type => $class );
994 }
995
996 {
997 ###################
6aaee015 998 ## use cpantest? ##
999 ###################
1000
1001 print loc("
1002CPANPLUS has support for the Test::Reporter module, which can be utilized
1003to report success and failures of modules installed by CPANPLUS. Would
1004you like to do this? Note that you will still be prompted before
1005sending each report.
1006
1007If you don't have all the required modules installed yet, you should
1008consider installing '%1'
1009
1010This package bundles all the required modules to enable test reporting
1011and querying from CPANPLUS.
1012You can do so straight after this installation.
1013
1014 ", 'Bundle::CPANPLUS::Test::Reporter');
1015
1016 my $type = 'cpantest';
1017 my $yn = $term->ask_yn(
1018 prompt => loc('Report test results?'),
1019 default => $conf->get_conf( $type ) ? 1 : 0,
1020 );
1021
1022 print "\n";
1023 print $yn
1024 ? loc("I will prompt you to report test results")
1025 : loc("I won't prompt you to report test results");
1026
1027 $conf->set_conf( $type => $yn );
1028 }
1029
1030 {
1031 ###################################
1032 ## use cryptographic signatures? ##
1033 ###################################
1034
1035 print loc("
1036The Module::Signature extension allows CPAN authors to sign their
1037distributions using PGP signatures. Would you like to check for
1038module's cryptographic integrity before attempting to install them?
1039Note that this requires either the 'gpg' utility or Crypt::OpenPGP
1040to be installed.
1041
1042 ");
1043 my $type = 'signature';
1044
1045 my $yn = $term->ask_yn(
1046 prompt => loc('Shall I check module signatures?'),
1047 default => $conf->get_conf($type) ? 1 : 0,
1048 );
1049
1050 print "\n";
1051 print $yn
1052 ? loc("Ok, I will attempt to check module signatures.")
1053 : loc("Ok, I won't attempt to check module signatures.");
1054
1055 $conf->set_conf( $type => $yn );
1056 }
1057
1058 return 1;
1059}
1060
1061sub _setup_hosts {
1062 my $self = shift;
1063 my $term = $self->term;
1064 my $conf = $self->configure_object;
1065
1066
1067 if( scalar @{ $conf->get_conf('hosts') } ) {
1068
1069 my $hosts;
1070 for my $href ( @{$conf->get_conf('hosts')} ) {
1071 $hosts .= "\t$href->{scheme}://$href->{host}$href->{path}\n";
1072 }
1073
1074 print loc("
1075I see you already have some hosts selected:
1076
1077$hosts
1078
1079If you'd like to stick with your current settings, just select 'Yes'.
1080Otherwise, select 'No' and you can reconfigure your hosts
1081
1082");
1083 my $yn = $term->ask_yn(
1084 prompt => loc("Would you like to keep your current hosts?"),
1085 default => 'y',
1086 );
1087 return 1 if $yn;
1088 }
1089
1090 my @hosts;
1091 MAIN: {
1092
1093 print loc("
1094Now we need to know where your favorite CPAN sites are located. Make a
1095list of a few sites (just in case the first on the array won't work).
1096
1097If you are mirroring CPAN to your local workstation, specify a file:
1098URI by picking the CUSTOM option.
1099
1100Otherwise, let us fetch the official CPAN mirror list and you can pick
1101the mirror that suits you best from a list by using the MIRROR option;
1102First, pick a nearby continent and country. Then, you will be presented
1103with a list of URLs of CPAN mirrors in the country you selected. Select
1104one or more of those URLs.
1105
1106Note, the latter option requires a working net connection.
1107
1108You can select VIEW to see your current selection and QUIT when you
1109are done.
1110
1111");
1112
1113 my $reply = $term->get_reply(
1114 prompt => loc('Please choose an option'),
1115 choices => [qw|Mirror Custom View Quit|],
1116 default => 'Mirror',
1117 );
1118
1119 goto MIRROR if $reply eq 'Mirror';
1120 goto CUSTOM if $reply eq 'Custom';
1121 goto QUIT if $reply eq 'Quit';
1122
1123 $self->_view_hosts(@hosts) if $reply eq 'View';
1124 redo MAIN;
1125 }
1126
1127 my $mirror_file;
1128 my $hosts;
1129 MIRROR: {
1130 $mirror_file ||= $self->_get_mirrored_by or return;
1131 $hosts ||= $self->_parse_mirrored_by($mirror_file) or return;
1132
1133 my ($continent, $country, $host) = $self->_guess_from_timezone( $hosts );
1134
1135 CONTINENT: {
1136 my %seen;
1137 my @choices = sort map {
1138 $_->{'continent'}
1139 } grep {
1140 not $seen{$_->{'continent'}}++
1141 } values %$hosts;
1142 push @choices, qw[Custom Up Quit];
1143
1144 my $reply = $term->get_reply(
1145 prompt => loc('Pick a continent'),
1146 default => $continent,
1147 choices => \@choices,
1148 );
1149
1150 goto MAIN if $reply eq 'Up';
1151 goto CUSTOM if $reply eq 'Custom';
1152 goto QUIT if $reply eq 'Quit';
1153
1154 $continent = $reply;
1155 }
1156
1157 COUNTRY: {
1158 my %seen;
1159 my @choices = sort map {
1160 $_->{'country'}
1161 } grep {
1162 not $seen{$_->{'country'}}++
1163 } grep {
1164 ($_->{'continent'} eq $continent)
1165 } values %$hosts;
1166 push @choices, qw[Custom Up Quit];
1167
1168 my $reply = $term->get_reply(
1169 prompt => loc('Pick a country'),
1170 default => $country,
1171 choices => \@choices,
1172 );
1173
1174 goto CONTINENT if $reply eq 'Up';
1175 goto CUSTOM if $reply eq 'Custom';
1176 goto QUIT if $reply eq 'Quit';
1177
1178 $country = $reply;
1179 }
1180
1181 HOST: {
1182 my @list = grep {
1183 $_->{'continent'} eq $continent and
1184 $_->{'country'} eq $country
1185 } values %$hosts;
1186
1187 my %map; my $default;
1188 for my $href (@list) {
1189 for my $con ( @{$href->{'connections'}} ) {
1190 next unless length $con->{'host'};
1191
1192 my $entry = $con->{'scheme'} . '://' . $con->{'host'};
1193 $default = $entry if $con->{'host'} eq $host;
1194
1195 $map{$entry} = $con;
1196 }
1197 }
1198
1199 CHOICE: {
1200
1201 ### doesn't play nice with Term::UI :(
1202 ### should make t::ui figure out pager opens
1203 #$self->_pager_open; # host lists might be long
1204
1205 print loc("
1206You can enter multiple sites by seperating them by a space.
1207For example:
1208 1 4 2 5
1209 ");
1210
1211 my @reply = $term->get_reply(
1212 prompt => loc('Please pick a site: '),
1213 choices => [sort(keys %map),
1214 qw|Custom View Up Quit|],
1215 default => $default,
1216 multi => 1,
1217 );
1218 #$self->_pager_close;
1219
1220
1221 goto COUNTRY if grep { $_ eq 'Up' } @reply;
1222 goto CUSTOM if grep { $_ eq 'Custom' } @reply;
1223 goto QUIT if grep { $_ eq 'Quit' } @reply;
1224
1225 ### add the host, but only if it's not on the stack already ###
1226 unless( grep { $_ eq 'View' } @reply ) {
1227 for my $reply (@reply) {
1228 if( grep { $_ eq $map{$reply} } @hosts ) {
1229 print loc("Host '%1' already selected", $reply);
1230 print "\n\n";
1231 } else {
1232 push @hosts, $map{$reply}
1233 }
1234 }
1235 }
1236
1237 $self->_view_hosts(@hosts);
1238
1239 goto QUIT if $self->autoreply;
1240 redo CHOICE;
1241 }
1242 }
1243 }
1244
1245 CUSTOM: {
1246 print loc("
1247If there are any additional URLs you would like to use, please add them
1248now. You may enter them separately or as a space delimited list.
1249
1250We provide a default fall-back URL, but you are welcome to override it
1251with e.g. 'http://www.cpan.org/' if LWP, wget or curl is installed.
1252
1253(Enter a single space when you are done, or to simply skip this step.)
1254
1255Note that if you want to use a local depository, you will have to enter
1256as follows:
1257
1258file://server/path/to/cpan
1259
1260if the file is on a server on your local network or as:
1261
1262file:///path/to/cpan
1263
1264if the file is on your local disk. Note the three /// after the file: bit
1265
1266");
1267
1268 CHOICE: {
1269 my $reply = $term->get_reply(
1270 prompt => loc("Additionals host(s) to add: "),
1271 default => '',
1272 );
1273
1274 last CHOICE unless $reply =~ /\S/;
1275
1276 my $href = $self->_parse_host($reply);
1277
1278 if( $href ) {
1279 push @hosts, $href
1280 unless grep {
1281 $href->{'scheme'} eq $_->{'scheme'} and
1282 $href->{'host'} eq $_->{'host'} and
1283 $href->{'path'} eq $_->{'path'}
1284 } @hosts;
1285
1286 last CHOICE if $self->autoreply;
1287 } else {
1288 print loc("Invalid uri! Please try again!");
1289 }
1290
1291 $self->_view_hosts(@hosts);
1292
1293 redo CHOICE;
1294 }
1295
1296 DONE: {
1297
1298 print loc("
1299Where would you like to go now?
1300
1301Please pick one of the following options or Quit when you are done
1302
1303");
1304 my $answer = $term->get_reply(
1305 prompt => loc("Where to now?"),
1306 default => 'Quit',
1307 choices => [qw|Mirror Custom View Quit|],
1308 );
1309
1310 if( $answer eq 'View' ) {
1311 $self->_view_hosts(@hosts);
1312 redo DONE;
1313 }
1314
1315 goto MIRROR if $answer eq 'Mirror';
1316 goto CUSTOM if $answer eq 'Custom';
1317 goto QUIT if $answer eq 'Quit';
1318 }
1319 }
1320
1321 QUIT: {
1322 $conf->set_conf( hosts => \@hosts );
1323
1324 print loc("
1325Your host configuration has been saved
1326
1327");
1328 }
1329
1330 return 1;
1331}
1332
1333sub _view_hosts {
1334 my $self = shift;
1335 my @hosts = @_;
1336
1337 print "\n\n";
1338
1339 if( scalar @hosts ) {
1340 my $i = 1;
1341 for my $host (@hosts) {
1342
1343 ### show full path on file uris, otherwise, just show host
1344 my $path = join '', (
1345 $host->{'scheme'} eq 'file'
1346 ? ( ($host->{'host'} || '[localhost]'),
1347 $host->{path} )
1348 : $host->{'host'}
1349 );
1350
1351 printf "%-40s %30s\n",
1352 loc("Selected %1",$host->{'scheme'} . '://' . $path ),
1353 loc("%quant(%2,host) selected thus far.", $i);
1354 $i++;
1355 }
1356 } else {
1357 print loc("No hosts selected so far.");
1358 }
1359
1360 print "\n\n";
1361
1362 return 1;
1363}
1364
1365sub _get_mirrored_by {
1366 my $self = shift;
1367 my $cpan = $self->backend;
1368 my $conf = $self->configure_object;
1369
1370 print loc("
1371Now, we are going to fetch the mirror list for first-time configurations.
1372This may take a while...
1373
1374");
1375
1376 ### use the enew configuratoin ###
1377 $cpan->configure_object( $conf );
1378
1379 load CPANPLUS::Module::Fake;
1380 load CPANPLUS::Module::Author::Fake;
1381
1382 my $mb = CPANPLUS::Module::Fake->new(
1383 module => $conf->_get_source('hosts'),
1384 path => '',
1385 package => $conf->_get_source('hosts'),
1386 author => CPANPLUS::Module::Author::Fake->new(
1387 _id => $cpan->_id ),
1388 _id => $cpan->_id,
1389 );
1390
1391 my $file = $cpan->_fetch( fetchdir => $conf->get_conf('base'),
1392 module => $mb );
1393
1394 return $file if $file;
1395 return;
1396}
1397
1398sub _parse_mirrored_by {
1399 my $self = shift;
1400 my $file = shift;
1401
1402 -s $file or return;
1403
1404 my $fh = new FileHandle;
1405 $fh->open("$file")
1406 or (
1407 warn(loc('Could not open file "%1": %2', $file, $!)),
1408 return
1409 );
1410
1411 ### slurp the file in ###
1412 { local $/; $file = <$fh> }
1413
1414 ### remove comments ###
1415 $file =~ s/#.*$//gm;
1416
1417 $fh->close;
1418
1419 ### sample host entry ###
1420 # ftp.sun.ac.za:
1421 # frequency = "daily"
1422 # dst_ftp = "ftp://ftp.sun.ac.za/CPAN/CPAN/"
1423 # dst_location = "Stellenbosch, South Africa, Africa (-26.1992 28.0564)"
1424 # dst_organisation = "University of Stellenbosch"
1425 # dst_timezone = "+2"
1426 # dst_contact = "ftpadm@ftp.sun.ac.za"
1427 # dst_src = "ftp.funet.fi"
1428 #
1429 # # dst_dst = "ftp://ftp.sun.ac.za/CPAN/CPAN/"
1430 # # dst_contact = "mailto:ftpadm@ftp.sun.ac.za
1431 # # dst_src = "ftp.funet.fi"
1432
1433 ### host name as key, rest of the entry as value ###
1434 my %hosts = $file =~ m/([a-zA-Z0-9\-\.]+):\s+((?:\w+\s+=\s+".*?"\s+)+)/gs;
1435
1436 while (my($host,$data) = each %hosts) {
1437
1438 my $href;
1439 map {
1440 s/^\s*//;
1441 my @a = split /\s*=\s*/;
1442 $a[1] =~ s/^"(.+?)"$/$1/g;
1443 $href->{ pop @a } = pop @a;
1444 } grep /\S/, split /\n/, $data;
1445
1446 ($href->{city_area}, $href->{country}, $href->{continent},
1447 $href->{latitude}, $href->{longitude} ) =
1448 $href->{dst_location} =~
1449 m/
1450 #Aizu-Wakamatsu, Tohoku-chiho, Fukushima
1451 ^"?(
1452 (?:[^,]+?)\s* # city
1453 (?:
1454 (?:,\s*[^,]+?)\s* # optional area
1455 )*? # some have multiple areas listed
1456 )
1457
1458 #Japan
1459 ,\s*([^,]+?)\s* # country
1460
1461 #Asia
1462 ,\s*([^,]+?)\s* # continent
1463
1464 # (37.4333 139.9821)
1465 \((\S+)\s+(\S+?)\)"?$ # (latitude longitude)
1466 /sx;
1467
1468 ### parse the different hosts, store them in config format ###
1469 my @list;
1470
1471 for my $type (qw[dst_ftp dst_rsync dst_http]) {
1472 my $path = $href->{$type};
1473 next unless $path =~ /\w/;
1474 if ($type eq 'dst_rsync' && $path !~ /^rsync:/) {
1475 $path =~ s{::}{/};
1476 $path = "rsync://$path/";
1477 }
1478 my $parts = $self->_parse_host($path);
1479 push @list, $parts;
1480 }
1481
1482 $href->{connections} = \@list;
1483 $hosts{$host} = $href;
1484 }
1485
1486 return \%hosts;
1487}
1488
1489sub _parse_host {
1490 my $self = shift;
1491 my $host = shift;
1492
1493 my @parts = $host =~ m|^(\w*)://([^/]*)(/.*)$|s;
1494
1495 my $href;
1496 for my $key (qw[scheme host path]) {
1497 $href->{$key} = shift @parts;
1498 }
1499
1500 return if lc($href->{'scheme'}) ne 'file' and !$href->{'host'};
1501 return if !$href->{'path'};
1502
1503 return $href;
1504}
1505
1506## tries to figure out close hosts based on your timezone
1507##
1508## Currently can only report on unique items for each of zones, countries, and
1509## sites. In the future this will be combined with something else (perhaps a
1510## ping?) to narrow down multiple choices.
1511##
1512## Tries to return the best zone, country, and site for your location. Any non-
1513## unique items will be set to undef instead.
1514##
1515## (takes hashref, returns array)
1516##
1517sub _guess_from_timezone {
1518 my $self = shift;
1519 my $hosts = shift;
1520 my (%zones, %countries, %sites);
1521
1522 ### autrijus - build time zone table
1523 my %freq_weight = (
1524 'hourly' => 2400,
1525 '4 times a day' => 400,
1526 '4x daily' => 400,
1527 'daily' => 100,
1528 'twice daily' => 50,
1529 'weekly' => 15,
1530 );
1531
1532 while (my ($site, $host) = each %{$hosts}) {
1533 my ($zone, $continent, $country, $frequency) =
1534 @{$host}{qw/dst_timezone continent country frequency/};
1535
1536
1537 # skip non-well-formed ones
1538 next unless $continent and $country and $zone =~ /^[-+]?\d+(?::30)?/;
1539 ### fix style
1540 chomp $zone;
1541 $zone =~ s/:30/.5/;
1542 $zone =~ s/^\+//;
1543 $zone =~ s/"//g;
1544
1545 $zones{$zone}{$continent}++;
1546 $countries{$zone}{$continent}{$country}++;
1547 $sites{$zone}{$continent}{$country}{$site} = $freq_weight{$frequency};
1548 }
1549
1550 use Time::Local;
1551 my $offset = ((timegm(localtime) - timegm(gmtime)) / 3600);
1552
1553 local $_;
1554
1555 ## pick the entry with most country/site/frequency, one level each;
1556 ## note it has to be sorted -- otherwise we're depending on the hash order.
1557 ## also, the list context assignment (pick first one) is deliberate.
1558
1559 my ($continent) = map {
1560 (sort { ($_->{$b} <=> $_->{$a}) or $b cmp $a } keys(%{$_}))
1561 } $zones{$offset};
1562
1563 my ($country) = map {
1564 (sort { ($_->{$b} <=> $_->{$a}) or $b cmp $a } keys(%{$_}))
1565 } $countries{$offset}{$continent};
1566
1567 my ($site) = map {
1568 (sort { ($_->{$b} <=> $_->{$a}) or $b cmp $a } keys(%{$_}))
1569 } $sites{$offset}{$continent}{$country};
1570
1571 return ($continent, $country, $site);
1572} # _guess_from_timezone
1573
1574
1575### big big regex, stolen to check if you enter a valid address
1576{
1577 my $RFC822PAT; # RFC pattern to match for valid email address
1578
1579 sub _valid_email {
1580 my $self = shift;
1581 if (!$RFC822PAT) {
1582 my $esc = '\\\\'; my $Period = '\.'; my $space = '\040';
1583 my $tab = '\t'; my $OpenBR = '\['; my $CloseBR = '\]';
1584 my $OpenParen = '\('; my $CloseParen = '\)'; my $NonASCII = '\x80-\xff';
1585 my $ctrl = '\000-\037'; my $CRlist = '\012\015';
1586
1587 my $qtext = qq/[^$esc$NonASCII$CRlist\"]/;
1588 my $dtext = qq/[^$esc$NonASCII$CRlist$OpenBR$CloseBR]/;
1589 my $quoted_pair = qq< $esc [^$NonASCII] >; # an escaped character
1590 my $ctext = qq< [^$esc$NonASCII$CRlist()] >;
1591 my $Cnested = qq< $OpenParen $ctext* (?: $quoted_pair $ctext* )* $CloseParen >;
1592 my $comment = qq< $OpenParen $ctext* (?: (?: $quoted_pair | $Cnested ) $ctext* )* $CloseParen >;
1593 my $X = qq< [$space$tab]* (?: $comment [$space$tab]* )* >;
1594 my $atom_char = qq/[^($space)<>\@,;:\".$esc$OpenBR$CloseBR$ctrl$NonASCII]/;
1595 my $atom = qq< $atom_char+ (?!$atom_char) >;
1596 my $quoted_str = qq< \" $qtext * (?: $quoted_pair $qtext * )* \" >;
1597 my $word = qq< (?: $atom | $quoted_str ) >;
1598 my $domain_ref = $atom;
1599 my $domain_lit = qq< $OpenBR (?: $dtext | $quoted_pair )* $CloseBR >;
1600 my $sub_domain = qq< (?: $domain_ref | $domain_lit) $X >;
1601 my $domain = qq< $sub_domain (?: $Period $X $sub_domain)* >;
1602 my $route = qq< \@ $X $domain (?: , $X \@ $X $domain )* : $X >;
1603 my $local_part = qq< $word $X (?: $Period $X $word $X )* >;
1604 my $addr_spec = qq< $local_part \@ $X $domain >;
1605 my $route_addr = qq[ < $X (?: $route )? $addr_spec > ];
1606 my $phrase_ctrl = '\000-\010\012-\037'; # like ctrl, but without tab
1607 my $phrase_char = qq/[^()<>\@,;:\".$esc$OpenBR$CloseBR$NonASCII$phrase_ctrl]/;
1608 my $phrase = qq< $word $phrase_char * (?: (?: $comment | $quoted_str ) $phrase_char * )* >;
1609 $RFC822PAT = qq< $X (?: $addr_spec | $phrase $route_addr) >;
1610 }
1611
1612 return scalar ($_[0] =~ /$RFC822PAT/ox);
1613 }
1614}
1615
1616
1617
1618
1619
1620
16211;
1622
1623
1624sub _edit {
1625 my $self = shift;
1626 my $conf = $self->configure_object;
1627 my $file = shift || $conf->_config_pm_to_file( $self->config_type );
1628 my $editor = shift || $conf->get_program('editor');
1629 my $term = $self->term;
1630
1631 unless( $editor ) {
1632 print loc("
1633I'm sorry, I can't find a suitable editor, so I can't offer you
1634post-configuration editing of the config file
1635
1636");
1637 return 1;
1638 }
1639
1640 ### save the thing first, so there's something to edit
1641 $self->_save;
1642
1643 return !system("$editor $file");
1644}
1645
1646sub _save {
1647 my $self = shift;
1648 my $conf = $self->configure_object;
1649
1650 return $conf->save( $self->config_type );
1651}
1652
16531;