line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package IO::Prompt::Simple; |
2
|
|
|
|
|
|
|
|
3
|
12
|
|
|
12
|
|
361703
|
use strict; |
|
12
|
|
|
|
|
31
|
|
|
12
|
|
|
|
|
436
|
|
4
|
12
|
|
|
12
|
|
66
|
use warnings; |
|
12
|
|
|
|
|
25
|
|
|
12
|
|
|
|
|
347
|
|
5
|
12
|
|
|
12
|
|
323
|
use 5.006001; |
|
12
|
|
|
|
|
51
|
|
|
12
|
|
|
|
|
963
|
|
6
|
12
|
|
|
12
|
|
67
|
use base 'Exporter'; |
|
12
|
|
|
|
|
24
|
|
|
12
|
|
|
|
|
1236
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
BEGIN { |
9
|
12
|
50
|
|
12
|
|
55362
|
$ENV{ANSI_COLORS_DISABLED} = 1 if $^O eq 'MSWin32'; |
10
|
|
|
|
|
|
|
} |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $VERSION = '0.06'; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our @EXPORT = 'prompt'; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
sub prompt { |
17
|
43
|
|
|
43
|
1
|
52757
|
my ($message, $opts) = @_; |
18
|
43
|
100
|
|
|
|
146
|
_croak('Usage: prompt($message, [$default_or_opts])') unless defined $message; |
19
|
|
|
|
|
|
|
|
20
|
42
|
|
|
|
|
60
|
my $default; |
21
|
42
|
100
|
|
|
|
132
|
if (ref $opts eq 'HASH') { |
22
|
32
|
|
|
|
|
75
|
$default = $opts->{default}; |
23
|
|
|
|
|
|
|
} |
24
|
|
|
|
|
|
|
else { |
25
|
10
|
|
|
|
|
22
|
($default, $opts) = ($opts, {}); |
26
|
|
|
|
|
|
|
} |
27
|
42
|
100
|
|
|
|
112
|
my $display_default = defined $default ? "[$default]" : ''; |
28
|
42
|
100
|
|
|
|
101
|
$default = defined $default ? $default : ''; |
29
|
|
|
|
|
|
|
|
30
|
42
|
|
|
|
|
111
|
my $stash = { message => $message }; |
31
|
42
|
|
|
|
|
104
|
_parse_option($opts, $stash); |
32
|
|
|
|
|
|
|
|
33
|
42
|
|
|
|
|
42933
|
$stash->{message} .= " $display_default"; |
34
|
42
|
100
|
|
|
|
117
|
if (my $color = $opts->{color}) { |
35
|
3
|
|
|
|
|
17
|
require Term::ANSIColor; |
36
|
3
|
100
|
|
|
|
10
|
$color = [$color] unless ref $color eq 'ARRAY'; |
37
|
3
|
|
|
|
|
12
|
$stash->{message} = Term::ANSIColor::colored($color, $stash->{message}); |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
|
40
|
42
|
|
|
|
|
222
|
my ($in, $out) = @$stash{qw/in out/}; |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# autoflush and reset format for output |
43
|
42
|
|
|
|
|
614
|
my $org_out = select $out; |
44
|
42
|
|
|
|
|
152
|
local $| = 1; |
45
|
42
|
|
|
|
|
85
|
local $\; |
46
|
42
|
|
|
|
|
105
|
select $org_out; |
47
|
|
|
|
|
|
|
|
48
|
42
|
100
|
|
|
|
108
|
my $ignore_case = $opts->{ignore_case} ? 1 : 0; |
49
|
42
|
|
|
|
|
107
|
my $isa_tty = _isa_tty($in, $out); |
50
|
42
|
|
|
|
|
124
|
my $answer; |
51
|
|
|
|
|
|
|
my @answers; # for multi |
52
|
42
|
|
|
|
|
47
|
while (1) { |
53
|
52
|
100
|
|
|
|
137
|
print {$out} $stash->{choices}, "\n" if defined $stash->{choices}; |
|
8
|
|
|
|
|
19
|
|
54
|
52
|
|
|
|
|
62
|
print {$out} $stash->{message}, ': '; |
|
52
|
|
|
|
|
172
|
|
55
|
52
|
100
|
100
|
|
|
389
|
if ($ENV{PERL_IOPS_USE_DEFAULT} || $opts->{use_default} || (!$isa_tty && eof $in)) { |
|
|
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
56
|
4
|
|
|
|
|
25
|
print {$out} "$default\n"; |
|
4
|
|
|
|
|
12
|
|
57
|
4
|
|
|
|
|
9
|
$answer = $default; |
58
|
4
|
|
|
|
|
8
|
last; |
59
|
|
|
|
|
|
|
} |
60
|
48
|
|
|
|
|
728
|
$answer = <$in>; |
61
|
48
|
100
|
|
|
|
238
|
if (defined $answer) { |
62
|
45
|
|
|
|
|
82
|
chomp $answer; |
63
|
45
|
100
|
|
|
|
110
|
print {$out} "$answer\n" unless $isa_tty; |
|
1
|
|
|
|
|
3
|
|
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
else { |
66
|
3
|
|
|
|
|
4
|
print {$out} "\n"; |
|
3
|
|
|
|
|
8
|
|
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
48
|
100
|
100
|
|
|
218
|
$answer = $default if !defined $answer || $answer eq ''; |
70
|
48
|
100
|
|
|
|
1478
|
$answer = $stash->{encoder}->decode($answer) if defined $stash->{encoder}; |
71
|
48
|
100
|
|
|
|
371
|
if (my $exclusive_map = $stash->{exclusive_map}) { |
|
|
100
|
|
|
|
|
|
72
|
31
|
100
|
|
|
|
56
|
if ($stash->{want_multi}) { |
73
|
3
|
50
|
|
|
|
7
|
$answer = $ignore_case ? lc $answer : $answer; |
74
|
3
|
|
|
|
|
3
|
my $has_error; |
75
|
3
|
|
|
|
|
9
|
for my $ans (split /\s+/, $answer) { |
76
|
5
|
100
|
|
|
|
12
|
unless (exists $exclusive_map->{$ans}) { |
77
|
1
|
|
|
|
|
1
|
$has_error = 1; |
78
|
1
|
|
|
|
|
2
|
last; |
79
|
|
|
|
|
|
|
} |
80
|
4
|
|
|
|
|
10
|
push @answers, $exclusive_map->{$ans}; |
81
|
|
|
|
|
|
|
} |
82
|
3
|
100
|
|
|
|
10
|
$has_error = 1 unless @answers; |
83
|
3
|
100
|
|
|
|
9
|
last unless $has_error; |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
else { |
86
|
28
|
100
|
|
|
|
85
|
if (exists $exclusive_map->{$ignore_case ? lc $answer : $answer}) { |
|
|
100
|
|
|
|
|
|
87
|
20
|
100
|
|
|
|
42
|
$answer = $exclusive_map->{$ignore_case ? lc $answer : $answer}; |
88
|
20
|
|
|
|
|
28
|
last; |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
} |
91
|
9
|
|
|
|
|
17
|
@answers = (); |
92
|
9
|
|
|
|
|
12
|
$answer = undef; |
93
|
9
|
|
|
|
|
12
|
print {$out} $stash->{hint}; |
|
9
|
|
|
|
|
26
|
|
94
|
9
|
|
|
|
|
18
|
next; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
elsif (my $regexp = $stash->{regexp}) { |
97
|
5
|
100
|
|
|
|
37
|
last if $answer =~ $regexp; |
98
|
1
|
|
|
|
|
1
|
$answer = undef; |
99
|
1
|
|
|
|
|
3
|
print {$out} $stash->{hint}; |
|
1
|
|
|
|
|
3
|
|
100
|
1
|
|
|
|
|
2
|
next; |
101
|
|
|
|
|
|
|
} |
102
|
12
|
|
|
|
|
21
|
last; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
42
|
100
|
|
|
|
891
|
return $stash->{want_multi} ? @answers : $answer; |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
sub _parse_option { |
109
|
42
|
|
|
42
|
|
66
|
my ($opts, $stash) = @_; |
110
|
|
|
|
|
|
|
|
111
|
42
|
100
|
|
|
|
134
|
$stash->{in} = _is_fh($opts->{input}) ? $opts->{input} : *STDIN; |
112
|
42
|
100
|
|
|
|
129
|
$stash->{out} = _is_fh($opts->{output}) ? $opts->{output} : *STDOUT; |
113
|
|
|
|
|
|
|
|
114
|
42
|
100
|
|
|
|
117
|
if ($opts->{yn}) { |
115
|
5
|
|
|
|
|
16
|
$opts->{anyone} = \[y => 1, n => 0]; |
116
|
5
|
100
|
|
|
|
17
|
$opts->{ignore_case} = 1 unless exists $opts->{ignore_case}; |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
42
|
|
100
|
|
|
165
|
$opts->{anyone} ||= $opts->{choices}; |
120
|
42
|
100
|
|
|
|
114
|
if ($opts->{anyone}) { |
|
|
100
|
|
|
|
|
|
121
|
24
|
|
|
|
|
63
|
$stash->{exclusive_map} = _make_exclusive_map($opts, $stash); |
122
|
24
|
100
|
|
|
|
91
|
$stash->{want_multi} = $opts->{multi} ? 1 : 0; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
elsif ($opts->{regexp}) { |
125
|
5
|
|
|
|
|
14
|
$stash->{regexp} = _make_regexp($opts, $stash); |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
42
|
100
|
|
|
|
122
|
if ($opts->{encode}) { |
129
|
1
|
|
|
|
|
9
|
require Encode; |
130
|
1
|
|
|
|
|
12
|
$stash->{encoder} = Encode::find_encoding($opts->{encode}); |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub _make_exclusive_map { |
135
|
24
|
|
|
24
|
|
32
|
my ($opts, $stash) = @_; |
136
|
24
|
|
|
|
|
39
|
my $anyone = $opts->{anyone}; |
137
|
24
|
|
|
|
|
37
|
my $exclusive_map = {}; |
138
|
|
|
|
|
|
|
|
139
|
24
|
100
|
|
|
|
63
|
my $ignore_case = $opts->{ignore_case} ? 1 : 0; |
140
|
24
|
|
|
|
|
62
|
my ($message, $hint, $choices) = @$stash{qw/message hint choices/}; |
141
|
24
|
|
50
|
|
|
52
|
my $type = _anyone_type($anyone) || return; |
142
|
24
|
100
|
66
|
|
|
139
|
if ($type eq 'ARRAY') { |
|
|
50
|
33
|
|
|
|
|
143
|
6
|
|
|
|
|
16
|
my @stuffs = _uniq(@$anyone); |
144
|
6
|
|
|
|
|
12
|
for my $stuff (@stuffs) { |
145
|
12
|
100
|
|
|
|
585
|
$exclusive_map->{$ignore_case ? lc $stuff : $stuff} = $stuff; |
146
|
|
|
|
|
|
|
} |
147
|
6
|
|
|
|
|
42
|
$hint = sprintf "# Please answer %s\n", join ' or ', map qq{`$_`}, @stuffs; |
148
|
6
|
|
|
|
|
56
|
$message .= sprintf ' (%s)', join '/', @stuffs; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
elsif ($type eq 'HASH' || $type eq 'REFARRAY' || $type eq 'Hash::MultiValue') { |
151
|
9
|
|
|
|
|
26
|
my @keys = |
152
|
|
|
|
|
|
|
$type eq 'HASH' ? sort { $a cmp $b } keys %$anyone : |
153
|
18
|
0
|
|
|
|
590
|
$type eq 'REFARRAY' ? do { my $i = 0; grep { ++$i % 2 == 1 } @{$$anyone} } : |
|
8
|
50
|
|
|
|
11
|
|
|
8
|
100
|
|
|
|
11
|
|
|
32
|
|
|
|
|
79
|
|
|
8
|
|
|
|
|
15
|
|
154
|
|
|
|
|
|
|
$type eq 'Hash::MultiValue' ? $anyone->keys : (); |
155
|
18
|
|
|
|
|
30
|
my $max = 0; |
156
|
18
|
|
|
|
|
23
|
my $idx = 1; |
157
|
18
|
|
|
|
|
35
|
for my $key (@keys) { |
158
|
35
|
100
|
|
|
|
77
|
$max = length $key > $max ? length $key : $max; |
159
|
35
|
100
|
|
|
|
125
|
$exclusive_map->{$ignore_case ? lc $key : $key} = |
|
|
100
|
|
|
|
|
|
160
|
|
|
|
|
|
|
$type eq 'REFARRAY' ? $$anyone->[$idx] : $anyone->{$key}; |
161
|
35
|
|
|
|
|
78
|
$idx += 2; |
162
|
|
|
|
|
|
|
} |
163
|
18
|
|
|
|
|
115
|
$hint = sprintf "# Please answer %s\n", join ' or ',map qq{`$_`}, @keys; |
164
|
18
|
100
|
|
|
|
63
|
if ($opts->{verbose}) { |
165
|
6
|
|
|
|
|
7
|
my $idx = -1; |
166
|
11
|
|
|
|
|
12
|
$choices = join "\n", map { |
167
|
6
|
|
|
|
|
12
|
$idx += 2; |
168
|
11
|
100
|
|
|
|
54
|
sprintf "# %-*s => %s", $max, $_, |
169
|
|
|
|
|
|
|
$type eq 'REFARRAY' ? $$anyone->[$idx] : $anyone->{$_}; |
170
|
|
|
|
|
|
|
} @keys; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
else { |
173
|
12
|
|
|
|
|
45
|
$message .= sprintf ' (%s)', join '/', @keys; |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
24
|
|
|
|
|
75
|
@$stash{qw/message hint choices/} = ($message, $hint, $choices); |
178
|
24
|
|
|
|
|
75
|
return $exclusive_map; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub _anyone_type { |
182
|
24
|
|
|
24
|
|
35
|
my $anyone = shift; |
183
|
|
|
|
|
|
|
my $type = |
184
|
|
|
|
|
|
|
ref $anyone eq 'ARRAY' && @$anyone ? 'ARRAY' : |
185
|
|
|
|
|
|
|
ref $anyone eq 'HASH' && %$anyone ? 'HASH' : |
186
|
|
|
|
|
|
|
ref $anyone eq 'REF' && ref $$anyone eq 'ARRAY' && @{$$anyone} |
187
|
|
|
|
|
|
|
? 'REFARRAY' : |
188
|
24
|
0
|
66
|
|
|
750
|
do { |
|
|
50
|
66
|
|
|
|
|
|
|
100
|
33
|
|
|
|
|
|
|
100
|
0
|
|
|
|
|
189
|
|
|
|
|
|
|
require Scalar::Util; |
190
|
|
|
|
|
|
|
Scalar::Util::blessed($anyone) || '' |
191
|
|
|
|
|
|
|
} eq 'Hash::MultiValue' && %$anyone |
192
|
|
|
|
|
|
|
? 'Hash::MultiValue' : ''; |
193
|
24
|
|
|
|
|
575
|
return $type; |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub _make_regexp { |
197
|
5
|
|
|
5
|
|
6
|
my ($opts, $stash) = @_; |
198
|
5
|
100
|
|
|
|
58
|
my $regexp = ref $opts->{regexp} eq 'Regexp' ? $opts->{regexp} |
|
|
100
|
|
|
|
|
|
199
|
|
|
|
|
|
|
: $opts->{ignore_case} ? qr/$opts->{regexp}/i : qr/$opts->{regexp}/; |
200
|
5
|
|
|
|
|
24
|
$stash->{hint} = sprintf "# Please answer pattern %s\n", $regexp; |
201
|
5
|
|
|
|
|
71
|
$regexp = qr/\A $regexp \Z/x; |
202
|
5
|
|
|
|
|
23
|
return $regexp; |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# using IO::Interactive::is_interactive() ? |
206
|
|
|
|
|
|
|
sub _isa_tty { |
207
|
0
|
|
|
0
|
|
0
|
my ($in, $out) = @_; |
208
|
0
|
0
|
0
|
|
|
0
|
return -t $in && (-t $out || !(-f $out || -c $out)) ? 1 : 0; ## no critic |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
# taken from Test::Builder |
212
|
|
|
|
|
|
|
sub _is_fh { |
213
|
84
|
|
|
84
|
|
108
|
my $maybe_fh = shift; |
214
|
84
|
100
|
|
|
|
248
|
return 0 unless defined $maybe_fh; |
215
|
|
|
|
|
|
|
|
216
|
64
|
50
|
|
|
|
338
|
return 1 if ref $maybe_fh eq 'GLOB'; # its a glob ref |
217
|
0
|
0
|
|
|
|
0
|
return 1 if ref \$maybe_fh eq 'GLOB'; # its a glob |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
return eval { $maybe_fh->isa('IO::Handle') } |
220
|
0
|
|
0
|
|
|
0
|
|| eval { tied($maybe_fh)->can('TIEHANDLE') }; |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub _uniq { |
224
|
6
|
|
|
6
|
|
7
|
my %h; |
225
|
6
|
|
|
|
|
37
|
grep !$h{$_}++, @_; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
sub _croak { |
229
|
1
|
|
|
1
|
|
12
|
require Carp; |
230
|
1
|
|
|
|
|
405
|
Carp::croak(@_); |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
1; |
234
|
|
|
|
|
|
|
__END__ |