# A few routines for asking the user questions. Used in --configure # and also by Makefile.PL, so this file should not depend on any # nonstandard libraries. # # package XMLTV::Ask::Term; use strict; use Carp qw(croak carp); use Term::ReadKey; # Use Log::TraceMessages if installed. BEGIN { eval { require Log::TraceMessages }; if ($@) { *t = sub {}; *d = sub { '' }; } else { *t = \&Log::TraceMessages::t; *d = \&Log::TraceMessages::d; } } sub ask( $$ ); sub ask_password( $$ ); sub ask_choice( $$$@ ); sub ask_boolean( $$$ ); sub ask_many_boolean( $$@ ); sub say( $$ ); # Ask a question with a free text answer. # Parameters: # current module # question text # Returns the text entered by the user. sub ask( $$ ) { shift; my $prompt = shift; chomp $prompt; $prompt .= ' ' if $prompt !~ /\s$/; print STDERR $prompt; my $r = ; for ($r) { return undef if not defined; s/^\s+//; s/\s+$//; return $_; } } # Ask a question with a password answer. # Parameters: # current module # question text # Returns the text entered by the user. sub ask_password( $$ ) { shift; my $prompt = shift; chomp $prompt; $prompt .= ' ' if $prompt !~ /\s$/; print STDERR $prompt; Term::ReadKey::ReadMode('noecho'); chomp( my $r = ); Term::ReadKey::ReadMode('restore'); print STDERR "\n"; return $r; } # Ask a question where the answer is one of a set of alternatives. # # Parameters: # current module # question text # default choice # Remaining arguments are the choices available. # # Returns one of the choices, or undef if input could not be read. # sub ask_choice( $$$@ ) { shift; my $question=shift(@_); die if not defined $question; chomp $question; my $default=shift(@_); die if not defined $default; my @options=@_; die if not @options; t "asking question $question, default $default"; croak "default $default not in options" if not grep { $_ eq $default } @options; my $options_size = length("@options"); t "size of options: $options_size"; my $all_digits = not ((my $tmp = join('', @options)) =~ tr/0-9//c); t "all digits? $all_digits"; if ($options_size < 20 or $all_digits) { # Simple style, one line question. my $str = "$question [".join(',',@options)." (default=$default)] "; while ( 1 ) { my $res=ask(undef, $str); return undef if not defined $res; return $default if $res eq ''; # Single character shortcut for yes/no questions return 'yes' if $res =~ /^y$/i; return 'no' if $res =~ /^n$/i; # Check for exact match, then for substring matching. foreach (@options) { return $_ if $_ eq $res; } my @poss; foreach (@options) { push @poss, $_ if /\Q$res\E/i; } if (@poss == 1) { # Unambiguous substring match. return $poss[0]; } print STDERR "invalid response, please choose one of ".join(',', @options)."\n\n"; } } else { # Long list of options, present as numbered multiple choice. print STDERR "$question\n"; my $optnum = 0; my (%num_to_choice, %choice_to_num); foreach (@options) { print STDERR "$optnum: $_\n"; $num_to_choice{$optnum} = $_; $choice_to_num{$_} = $optnum; ++ $optnum; } $optnum--; my $r=undef; my $default_num = $choice_to_num{$default}; die if not defined $default_num; until (defined $r) { $r = ask_choice(undef, 'Select one:', $default_num, 0 .. $optnum); return undef if not defined $r; for ($num_to_choice{$r}) { return $_ if defined } print STDERR "invalid response, please choose one of " .0 .. $optnum."\n\n"; undef $r; } } } # Ask a yes/no question. # # Parameters: # current module # question text # default (true or false) # # Returns true or false, or undef if input could not be read. # sub ask_boolean( $$$ ) { shift; my ($text, $default) = @_; my $r = ask_choice(undef, $text, ($default ? 'yes' : 'no'), 'yes', 'no'); return undef if not defined $r; return 1 if $r eq 'yes'; return 0 if $r eq 'no'; die; } # Ask yes/no questions with option 'default to all'. # # Parameters: # current module # default (true or false), # question texts (one per question). # # Returns: lots of booleans, one for each question. If input cannot # be read, then a partial list is returned. # sub ask_many_boolean( $$@ ) { shift; my $default = shift; # Catch a common mistake - passing the answer string as default # instead of a Boolean. # carp "default is $default, should be 0 or 1" if $default ne '0' and $default ne '1'; my @r; while (@_) { my $q = shift @_; my $r = ask_choice(undef, $q, ($default ? 'yes' : 'no'), 'yes', 'no', 'all', 'none'); last if not defined $r; if ($r eq 'yes') { push @r, 1; } elsif ($r eq 'no') { push @r, 0; } elsif ($r eq 'all' or $r eq 'none') { my $bool = ($r eq 'all'); push @r, $bool; foreach (@_) { print STDERR "$_ ", ($bool ? 'yes' : 'no'), "\n"; push @r, $bool; } last; } else { die } } return @r; } # Give some information to the user # Parameters: # current module # text to show to the user sub say( $$ ) { shift; my $question = shift; print STDERR "$question\n"; } 1;