File Coverage

blib/lib/IO/Prompt.pm
Criterion Covered Total %
statement 54 405 13.3
branch 4 268 1.4
condition 0 129 0.0
subroutine 15 38 39.4
pod 3 3 100.0
total 76 843 9.0


line stmt bran cond sub pod time code
1             package IO::Prompt;
2              
3             our $VERSION = '0.997003';
4              
5 1     1   16706 use strict;
  1         1  
  1         36  
6 1     1   3 use Carp;
  1         1  
  1         67  
7              
8 1     1   22 use 5.008;
  1         5  
9 1     1   4 no warnings 'utf8';
  1         0  
  1         52  
10              
11             our @EXPORT = qw( prompt );
12             our @EXPORT_OK = qw( hand_print get_input );
13              
14 1     1   546 use IO::Handle;
  1         5519  
  1         47  
15 1     1   595 use Term::ReadKey;
  1         3462  
  1         71  
16 1     1   476 use POSIX qw( isprint );
  1         6162  
  1         5  
17              
18             my $clearfirst;
19             my %input;
20              
21             sub _clear {
22 0 0   0   0 return unless $_[0];
23 0 0       0 open my $OUT, ">/dev/tty" or croak "Cannot write to terminal: $!";
24 0         0 print {$OUT} "\n" x 60;
  0         0  
25 0         0 $clearfirst = 0;
26             }
27              
28             our %flags_arg = (
29             p => 'prompt',
30             s => 'speed',
31             e => 'echo',
32             r => 'require',
33             d => 'default',
34             u => 'until',
35             w => 'while',
36             nl => 'newline',
37             m => 'menu',
38             );
39              
40             our %flags_alias = (
41             '-okayif' => '-while', '-okay_if' => '-while',
42             '-failif' => '-until', '-fail_if' => '-until',
43             );
44              
45             our %flags_noarg = (
46             y => 'yes',
47             n => 'no',
48             i => 'integer',
49             num => 'number',
50             raw => 'raw_input',
51             1 => 'onechar',
52             c => 'clear',
53             f => 'clearfirst',
54             a => 'argv',
55             l => 'line',
56             t => 'tty',
57             x => 'escape',
58             );
59              
60             my $RECORD; # store filehandle for __PROMPT__ file supporting -record flag
61              
62             $flags_arg{$_} = $_ for values %flags_arg;
63             $flags_noarg{$_} = $_ for values %flags_noarg;
64              
65             my $flag_with_arg = join '|', reverse sort keys %flags_arg;
66             my $flag_no_arg = join '|', reverse sort keys %flags_noarg;
67              
68             my %yespat = (
69             'y' => qr/^\s*[yY]/,
70             'Y' => qr/^\s*Y/,
71             );
72              
73             my %nopat = (
74             'n' => qr/^\s*[nN]/,
75             'N' => qr/^\s*N/,
76             );
77              
78             my %num_pat = (
79             integer => qr{[+-]? \d+ (?:[Ee]+?\d+ )?}x,
80             number => qr{[+-]? (?:\d+[.]?\d* | [.]\d+) (?:[Ee][+-]?\d+)? }x,
81             );
82              
83             sub _get_prompt (\%@) {
84 0     0   0 my ($flags, @data) = @_;
85 0         0 my ($OUT);
86 0 0 0     0 @data = map { $flags_alias{$_} || defined($_) ? $_ : "" } @data;
  0         0  
87 0         0 for (my $i = 0 ; $i < @data ; $i++) {
88 0         0 local *_ = \$data[$i];
89 0 0 0     0 if (ref eq 'HASH') {
    0          
    0          
90 0         0 splice @data, $i + 1, 0, %$_;
91             }
92             elsif (ref eq 'GLOB' or UNIVERSAL::isa($_, 'IO::Handle')) {
93 0 0       0 croak "Can't write prompt to read-only $_" unless -w;
94 0         0 $OUT = $_;
95             }
96             elsif (/^-/) { # a flag
97 0         0 s/_//g;
98 0 0       0 if (s/^-(f|clearfirst)/-/) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
99 0 0       0 $clearfirst = 1 unless defined $clearfirst;
100             }
101             elsif (s/^-(yes|y)/-/i) {
102 0         0 $flags->{-yesno}{yes} = $yespat{ substr $1, 0, 1 };
103 0         0 $flags->{-yesno}{yesprompt} = substr $1, 0, 1;
104             }
105             elsif (s/^-(?:nl|newline)/-/i) {
106 0         0 $flags->{-nlstr} = $data[ $i + 1 ];
107 0         0 undef $data[ $i++ ];
108             }
109             elsif (s/^-escape|-x/-/i) {
110 0         0 $flags->{-escape} = 1;
111             }
112             elsif (s/^-raw_?(?:input)?/-/i) {
113 0         0 $flags->{-raw_input} = 1;
114             }
115             elsif (s/^-number|-num/-/i) {
116 0         0 $flags->{-number} = 'number';
117             }
118             elsif (s/^-integer|-i/-/i) {
119 0         0 $flags->{-number} = 'integer';
120             }
121             elsif (s/^-(no|n)/-/i) {
122 0         0 $flags->{-yesno}{no} = $nopat{ substr $1, 0, 1 };
123 0         0 $flags->{-yesno}{noprompt} = substr $1, 0, 1;
124             }
125             elsif (m/^-($flag_with_arg)/) {
126 0 0 0     0 croak "Missing argument for $_ option" if @data < $i+2
127             || !defined $data[$i+1];
128 0         0 s/^-($flag_with_arg)/-/;
129 0         0 $flags->{ -$flags_arg{$1} } = $data[$i+1];
130 0         0 undef $data[$i++];
131             }
132             elsif (s/^-($flag_no_arg)/-/) {
133 0         0 $flags->{ -$flags_noarg{$1} } = 1;
134             }
135 0         0 else { croak "Unknown flag ($_) in prompt" }
136              
137 0 0 0     0 redo if defined $_ && /^-./;
138             }
139 0         0 else { next }
140 0         0 undef $data[$i];
141             }
142             $_ =
143             !defined() ? undef
144             : ref eq 'Regexp' ? $_
145             : qr/^\Q$_\E$/
146 0 0       0 for @{$flags}{qw(-while -until -failif -okayif)};
  0 0       0  
147              
148 0         0 for (grep { defined } $flags->{ -require }) {
  0         0  
149 0 0       0 croak "Argument to -require must be hash reference"
150             unless ref eq 'HASH';
151 0         0 my %reqs = %$_;
152             $_ = sub {
153 0     0   0 my ($input) = @_;
154 0         0 for (keys %reqs) {
155 0 0       0 return $_ unless _smartmatch($input, $reqs{$_});
156             }
157 0         0 return;
158 0         0 };
159             }
160 0         0 my @prompt = grep { defined } @data;
  0         0  
161 0 0 0     0 if (@prompt && exists $flags->{-default}) {
162 0         0 my $prompt = join "", @prompt;
163 0 0       0 $prompt =~ s/(:?\s*)$/ [$flags->{-default}]$1/ if $prompt !~ /\[.*\]/;
164 0         0 @prompt = $prompt;
165             }
166 0         0 return $OUT, @prompt;
167             }
168              
169             my $prompt_req = "(The value entered is not acceptable) ";
170              
171             sub prompt {
172 0     0 1 0 my $caller = caller;
173              
174 0         0 local $\ = q{}; # Make sure no funny business on print statements
175              
176 0         0 my %flags;
177 0         0 my ($OUT, @prompt) = _get_prompt(%flags, @_);
178 0 0 0     0 open $OUT, ">/dev/tty" or croak "Cannot write to terminal: $!" if !$OUT;
179 0         0 $OUT->autoflush(1);
180 0 0 0     0 @prompt = $flags{ -prompt } if !@prompt and $flags{ -prompt };
181 0         0 my $IN;
182 0 0 0     0 if ($flags{-tty} || $flags{-argv}) {
183 0 0       0 open $IN, "
184             }
185             else {
186 1     1   2320 use Scalar::Util;
  1         2  
  1         72  
187 1     1   7 no strict 'refs';
  1         2  
  1         109  
188 0         0 my $ARGV = $caller . "::ARGV";
189 0 0       0 unless (Scalar::Util::openhandle(*$ARGV)) {
190 0   0     0 $$ARGV = shift(@$ARGV) || '-';
191 0 0       0 open $ARGV or croak "Can't open $$ARGV: $!";
192             }
193 0         0 $IN = \*$ARGV;
194 0 0       0 @prompt = () unless -t $IN;
195             }
196 0 0       0 $flags{-speed} = 0.075 unless defined $flags{-speed};
197 1     1   556 use Want qw( want );
  1         1480  
  1         519  
198 0   0     0 $flags{-set_underscore} ||= want('BOOL');
199              
200 0 0 0     0 $clearfirst = 1 if !defined($clearfirst) && $flags{-clearfirst};
201 0   0     0 _clear($flags{ -clear } || $clearfirst);
202 0         0 my $input;
203 0 0 0     0 if (-t $IN and defined $input{$caller}) {
    0          
    0          
    0          
    0          
204 0         0 $input = _fake_from_DATA($caller, $IN, $OUT, \%flags, @prompt);
205             }
206             elsif ($flags{-argv}) {
207 0 0       0 return if @ARGV;
208 0 0 0     0 @prompt = "Args for $0: " if -t $IN and !@prompt;
209 0         0 print {$OUT} @prompt;
  0         0  
210 0         0 @ARGV = map glob, split /\s+/, get_input($IN, $OUT, \%flags, @prompt);
211 0         0 return @ARGV;
212             }
213             elsif ($flags{-yesno}) {
214 0         0 return _yesno($IN, $OUT, \%flags, @prompt);
215             }
216             elsif ($flags{-number}) {
217 0         0 return _number($IN, $OUT, \%flags, @prompt);
218             }
219             elsif ($flags{-menu}) {
220 0         0 return _menu($IN, $OUT, \%flags, @prompt);
221             }
222             else {
223 0         0 print {$OUT} @prompt;
  0         0  
224 0         0 $input = get_input($IN, $OUT, \%flags, @prompt);
225             }
226 0         0 return _tidy($input, %flags);
227             }
228              
229             sub _tidy {
230 0     0   0 my ($input, %flags) = @_;
231 0         0 my $defined = defined $input;
232 0 0 0     0 chomp $input if $defined && !$flags{-line};
233             my $success = $defined
234             && (!$flags{ -while } || $input =~ $flags{ -while })
235 0   0     0 && (!$flags{ -until } || $input !~ $flags{ -until });
236 0 0 0     0 print {$RECORD} $input, "\n" if $success && $RECORD;
  0         0  
237 0 0       0 return "$input" if $flags{-raw_input};
238             return bless {
239             value => $input,
240             success => $success,
241             set_val => $flags{ -set_underscore },
242             },
243 0         0 'IO::Prompt::ReturnVal';
244             }
245              
246             sub _success {
247 0     0   0 my ($val, $no_set, $raw) = @_;
248 0 0 0     0 print {$RECORD} $val, "\n" if $val && $RECORD;
  0         0  
249 0 0       0 return "$val" if $raw;
250 0         0 return bless {
251             value => $val,
252             success => 1,
253             set_val => !$no_set,
254             },
255             'IO::Prompt::ReturnVal';
256             }
257              
258             sub _failure {
259 0     0   0 my ($val, $raw) = @_;
260 0 0       0 return "$val" if $raw;
261 0         0 return bless {
262             value => $val,
263             success => 0,
264             set_val => 0,
265             },
266             'IO::Prompt::ReturnVal';
267             }
268              
269             sub import {
270 1     1   10 my $class = shift;
271              
272             {
273 1     1   6 no strict 'refs';
  1         2  
  1         289  
  1         2  
274 1         3 *{ caller() . "::$_" } = \&{$_} for @EXPORT;
  1         8  
  1         2  
275              
276 1         2 foreach my $sym (@_) {
277 0 0       0 grep { $_ eq $sym } @EXPORT_OK or next;
  0         0  
278 0         0 *{ caller() . "::$sym" } = \&{$sym};
  0         0  
  0         0  
279             }
280             }
281              
282 1         3 @_ = grep /^-/, @_;
283 1         2 $input{ caller() } = undef;
284 1 50       5 if ("@_" eq "-clearfirst") {
285 0         0 $clearfirst = 1;
286 0         0 return;
287             }
288 1         3 for my $i (0 .. $#_) {
289 0 0       0 last if $RECORD;
290 0 0       0 if ($_[$i] eq '-record') {
291 0         0 splice @_, $i, 1;
292 0 0       0 open $RECORD, '>', '__PROMPT__'
293             or croak "Can't open __PROMPT__ recording file: $!";
294 0         0 print {$RECORD} "__DATA__\n__PROMPT__\n";
  0         0  
295             }
296             }
297 1 50       14 prompt @_ if @_;
298             }
299              
300             CHECK {
301 1     1   971 for my $pkg (keys %input) {
302 1 50       4 next if defined $input{$pkg};
303              
304 1     1   6 no strict 'refs';
  1         7  
  1         2515  
305 1 50       1 if (my $datahandle = *{"${pkg}::DATA"}{IO}) {
  1         7  
306 0         0 local $/;
307 0         0 my $data = <$datahandle>;
308 0 0       0 if ($data =~ s/(\A|\n) __PROMPT__ \s*? \n (.*)/$1/xs) {
309 0         0 $input{$pkg} = "$2";
310             }
311             else {
312 0         0 delete $input{$pkg};
313             }
314 0 0       0 open "${pkg}::DATA", "<", \$data or die "Internal error: $!";
315             }
316             else {
317 1         706 delete $input{$pkg};
318             }
319             }
320             }
321              
322             my $baseline = ord 'A';
323              
324             sub _visualize {
325 0     0     local ($_) = @_;
326 0 0         return isprint($_) ? $_
    0          
    0          
    0          
327             : $_ eq "\n" ? $_
328             : ord($_) == 0 ? ''
329             : ord($_) < $baseline ? '^' . chr($baseline + ord($_) - 1)
330             : '?'
331             }
332              
333             sub hand_print {
334 0     0 1   my $OUT = \*STDOUT;
335 0           my $echo = undef;
336 0           my $speed = 0.05;
337 0           local $| = 1;
338 0           for (@_) {
339 0 0         if (ref eq 'HASH') {
    0          
340 0 0         $speed = $_->{-speed} if exists $_->{-speed};
341 0 0         $OUT = $_->{-to} if exists $_->{-to};
342 0 0         $echo = $_->{-echo} if exists $_->{-echo};
343             }
344             elsif (!$speed) {
345 0           print {$OUT} $_;
  0            
346             }
347             else {
348 0           print {$OUT} $_ and select undef, undef, undef, rand $speed
349 0 0 0       for map { defined $echo ? $echo : _visualize($_) } split "";
  0            
350             }
351             }
352 0           return scalar @_;
353             }
354              
355             sub _fake_from_DATA {
356 0     0     my ($caller, $IN, $OUT, $flags, @prompt) = @_;
357 0     0     local $SIG{INT} = sub { ReadMode 'restore', $IN; exit };
  0            
  0            
358 0           ReadMode 'noecho', $IN;
359 0           ReadMode 'raw', $IN;
360 0           print {$OUT} @prompt;
  0            
361 0           my $input = getc $IN;
362 0 0         if ($input =~ /\cD|\cZ/) { print {$OUT} _visualize($input),"\n"; return; }
  0            
  0            
  0            
363 0 0         if ($input eq "\e") {
364 0           ReadMode 'restore', $IN;
365 0           return get_input($IN, $OUT, $flags, @prompt);
366             }
367 0           $input{$caller} =~ m/\G (?!\cD|\cZ) (.*) (\n?)/xgc;
368 0           my ($line, $nlstr) = ($1, $2);
369 0 0         unless (defined $line) {
370 0           while ($input ne "\n") { $input = getc $IN }
  0            
371 0           print {$OUT} "\n";
  0            
372 0           return;
373             }
374 0 0         delete $input{$caller} if pos $input{$caller} == length $input{$caller};
375 0 0         if ($input eq "\n") {
376 0           hand_print { -to => $OUT, %$flags }, $line;
377 0 0         unless (defined <$IN>) { print {$OUT} "\n"; return; }
  0            
  0            
  0            
378             }
379             else {
380 0           my $i = 0;
381 0           while (1) {
382 0           my $done = $i >= length $line;
383 0 0         print {$OUT} substr($line, $i++, 1) unless $done;
  0            
384 0 0         if (getc $IN eq "\n") {
385 0 0         last if $done;
386 0           hand_print { -to => $OUT, %$flags }, substr($line, $i);
387 0           $i = length $line;
388             }
389             }
390             }
391 0           ReadMode 'restore', $IN;
392 0           print {$OUT} "\n";
  0            
393 0           return $line . $nlstr;
394             }
395              
396             sub get_input {
397 0     0 1   my ($IN, $OUT, $flags, @prompt) = @_;
398             my ($onechar, $nlstr, $echo, $require) =
399 0           @{$flags}{ -onechar, -nlstr, -echo, -'require' };
  0            
400 0 0         $nlstr = "\n" unless defined $nlstr;
401 0 0         if (!-t $IN) {
402 0 0         return scalar <$IN> unless $onechar;
403 0           return getc $IN;
404             }
405 0           $OUT->autoflush(1);
406 0     0     local $SIG{INT} = sub { ReadMode 'restore', $IN; exit };
  0            
  0            
407 0           my ($input, $newlines);
408 0           my %cntl = GetControlChars $IN;
409 0           my $cntl = join '|', values %cntl;
410 0           ReadMode 'raw', $IN;
411              
412 0           INPUT: while (1) {
413 0           my $next = getc $IN;
414 0 0 0       if ($next eq $cntl{INTERRUPT}) {
    0 0        
    0          
    0          
    0          
415 0           ReadMode 'restore', $IN;
416 0           exit;
417             }
418             elsif ($next eq $cntl{ERASE}) {
419 0 0 0       if (defined $input && length $input) {
420 0           substr($input, -1) = "";
421 0           print {$OUT} "\b \b";
  0            
422             }
423 0           next;
424             }
425             elsif ($next eq $cntl{EOF}) {
426 0           ReadMode 'restore', $IN;
427 0           close $IN;
428 0           return $input;
429             }
430             elsif ($flags->{-escape} && $next eq "\e") {
431 0           ReadMode 'restore', $IN;
432 0           print {$OUT} "";
  0            
433 0           return "\e";
434             }
435             elsif ($next !~ /$cntl/ && defined $next) {
436 0           $input .= $next;
437 0 0         if ($next eq "\n") {
438 0 0 0       if ($input eq "\n" && exists $flags->{-default}) {
439 0           print {$OUT} (
440             defined $echo
441             && $flags->{-menu} ? $echo
442             : defined $echo ? $echo x length($flags->{-default})
443 0 0 0       : '['.$flags->{-default}.']'
    0          
444             );
445 0           print {$OUT} $nlstr;
  0            
446 0           ReadMode 'restore', $IN;
447             return $onechar ? substr($_, 0, 1) : $_
448 0 0         for $flags->{-default};
449             }
450 0           $newlines .= $nlstr;
451             }
452             else {
453 0 0         print {$OUT}(defined $echo ? $echo : $next);
  0            
454             }
455             }
456             else {
457 0           $input .= $next;
458             }
459 0 0 0       if ($onechar or !defined $next or $input =~ m{\Q$/\E$}) {
      0        
460 0 0         chomp $input unless $flags->{-line};
461 0 0 0       if ($require and my $mesg = $require->($input)) {
462 0           print {$OUT} "\r", " " x 79, "\r", sprintf($mesg, @prompt);
  0            
463 0           undef $input;
464 0           undef $newlines;
465             }
466             else {
467 0           ReadMode 'restore', $IN;
468 0 0         print {$OUT} $newlines if defined $newlines;
  0            
469 0 0         return $onechar ? substr($input, 0, 1) : $input;
470             }
471             }
472             }
473             }
474              
475             sub _yesno {
476 0     0     my ($IN, $OUT, $flags, @prompt) = @_;
477             my ($yes, $no, $yesprompt, $noprompt) =
478 0           @{ $flags->{ -yesno } }{qw(yes no yesprompt noprompt)};
  0            
479 0 0         $yes = qr/^([^Nn])/ unless defined $yes;
480 0 0         $no = qr/^([^Yy])/ unless defined $no;
481 0 0 0       my $prompt2 =
    0          
482             $yesprompt && $noprompt ? "'$yesprompt' or '$noprompt'"
483             : $yesprompt ? "'$yesprompt' for yes"
484             : "'$noprompt' for no";
485 0           my $raw = $flags->{-raw_input};
486 0 0         print {$OUT} @prompt if -t $IN;
  0            
487 0           while (1) {
488 0           my $response =
489             get_input($IN, $OUT, { %$flags, -nlstr => "" }, @prompt);
490 0 0         chomp $response unless $flags->{-line};
491 0 0 0       print {$OUT} "\n" and return _success($response, 'no_set', $raw)
  0   0        
492             if defined $response and $response =~ /$yes/;
493 0 0 0       print {$OUT} "\n" and return _failure($response, $raw)
  0   0        
494             if !defined $response or $response =~ /$no/;
495 0 0         print {$OUT} "\r", " " x 79, "\r", @prompt,
  0            
496             "(Please answer $prompt2) "
497             if -t $IN;
498             }
499             }
500              
501             sub _number {
502 0     0     my ($IN, $OUT, $flags, @prompt) = @_;
503 0           my $numtype = $flags->{ -number };
504 0           my $prompt_num = "(Please enter a valid $numtype) ";
505 0           my $match = $num_pat{$numtype};
506 0           my $require = $flags->{ -require };
507 0 0         print {$OUT} @prompt if -t $IN;
  0            
508 0           while (1) {
509 0           my $response =
510             get_input($IN, $OUT, { %$flags, -nlstr => "", -require => undef },
511             @prompt);
512 0 0 0       chomp $response if defined $response && !$flags->{-line};
513 0 0 0       if (-t $IN and defined $response) {
514 0 0 0       if ($response !~ /\A \s* $match \s* \Z/x) {
    0          
515 0           print {$OUT} "\r", " " x 79, "\r", @prompt, $prompt_num;
  0            
516 0           next;
517             }
518             elsif ($require and my $mesg = $require->($response)) {
519 0           print {$OUT} "\r", " " x 79, "\r", sprintf($mesg, @prompt);
  0            
520 0           next;
521             }
522             }
523 0 0         print {$OUT} "\n" and return _tidy($response);
  0            
524             }
525             }
526              
527 0     0     sub _self { $_[0] }
528              
529             sub _menu {
530 0     0     my ($IN, $OUT, $flags, @prompt) = @_;
531 0           my $datatype = ref $flags->{ -menu };
532 0           my @data = $datatype eq 'ARRAY' ? @{ $flags->{ -menu } }
533 0 0         : $datatype eq 'HASH' ? sort keys %{ $flags->{ -menu } }
  0 0          
534             : croak "Argument to -menu must be hash or array reference";
535              
536             my $val_for = $datatype eq 'ARRAY'
537             ? \&_self
538 0 0   0     : sub { $flags->{ -menu }{$_[0]} };
  0            
539              
540 0           my $count = @data;
541              
542 0 0         croak "Too many -menu items" if $count > 26;
543 0 0         croak "Too few -menu items" if $count < 1;
544              
545 0           my $max_char = chr(ord('a') + $count - 1);
546 0           my $menu = q{};
547              
548 0           my $default_key;
549 0           my $next = 'a';
550 0           for (@data) {
551 0           my $item = $_;
552 0 0 0       if (defined $flags->{ -default } && !defined $default_key && $item eq $flags->{ -default }) {
      0        
553 0           $default_key = $next;
554             }
555 0           $item =~ s/\A/qq{ }.$next++.q{. }/xmse;
  0            
556 0           $item =~ s/\n?\z/\n/xms;
557 0           $item =~ s/(?!\Z)\n/\n /gxms;
558 0           $menu .= $item;
559             }
560              
561 0           push @prompt, "\n$menu\n> ";
562              
563 0           my $prompt_range = "(Please enter a-$max_char) > ";
564 0           my $require = $flags->{ -require };
565 0 0         print {$OUT} @prompt if -t $IN;
  0            
566 0           while (1) {
567 0           my $response =
568             get_input($IN, $OUT, { %$flags, -escape => 1, -nlstr => "", -require => undef },
569             @prompt);
570 0           chomp $response;
571 0 0 0       if (-t $IN and defined $response) {
572 0 0 0       if (length $response == 1 && $response eq "\e") {
    0 0        
    0 0        
      0        
573 0           return $response;
574             }
575             elsif (length $response > 1 || ($response lt 'a' || $response gt $max_char) ) {
576 0 0         if ($response ne $flags->{-default}) {
577 0           print {$OUT} "\r", " " x 79, "\r", $prompt_range;
  0            
578 0           next;
579             }
580 0           $response = $default_key;
581             }
582             elsif ($require and my $mesg = $require->($data[ord($response)-ord('a')])) {
583 0           print {$OUT} "\r", " " x 79, "\r", sprintf($mesg, @prompt);
  0            
584 0           next;
585             }
586             }
587 0           print {$OUT} "\n";
  0            
588 0           my $selection = $data[ord($response)-ord('a')];
589 0 0         $response = defined $response ? $val_for->($selection) : $response;
590 0 0 0       if (defined $response && ref($response) =~ m/\A(?:HASH|ARRAY)\z/xms ) {
591 0           $response = _menu($IN, $OUT, {%{$flags}, -menu=>$response}, "$selection: ");
  0            
592 0 0 0       if (defined $response && $response eq "\e") {
593 0 0         print {$OUT} "\n", @prompt if -t $IN;
  0            
594 0           next;
595             }
596             }
597 0           return _tidy($response);
598             }
599             }
600              
601             sub _smartmatch {
602 0     0     my ($str, $matcher) = @_;
603 0           my $type = ref $matcher;
604             my $res = $type eq 'CODE'
605 0           ? do { local $_ = $str; $matcher->() }
  0            
606             : $type eq 'Regexp' ? ($str =~ $matcher)
607 0           : $type eq 'ARRAY' ? scalar grep({ _smartmatch($str, $_) } @$matcher)
608 0 0         : $type eq 'HASH' ? $matcher->{$str}
    0          
    0          
    0          
609             : $str eq $matcher;
610 0           return $res;
611             }
612              
613             package IO::Prompt::ReturnVal;
614              
615             use overload
616             q{bool} => sub {
617 0 0   0   0 $_ = $_[0]{value} if $_[0]{set_val};
618 0         0 $_[0]{handled} = 1;
619 0         0 $_[0]{success};
620             },
621 0     0   0 q{""} => sub { $_[0]{handled} = 1; "$_[0]{value}"; },
  0         0  
622 0     0   0 q{0+} => sub { $_[0]{handled} = 1; 0 + $_[0]{value}; },
  0         0  
623 1         10 fallback => 1,
624 1     1   1164 ;
  1         858  
625              
626             sub DESTROY {
627 0 0   0     $_ = $_[0]{value} unless $_[0]{handled};
628             }
629              
630             1; # Magic true value required at end of module
631             __END__