| 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__ |