line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Smart::Options; |
2
|
21
|
|
|
21
|
|
227333
|
use strict; |
|
21
|
|
|
|
|
51
|
|
|
21
|
|
|
|
|
571
|
|
3
|
21
|
|
|
21
|
|
106
|
use warnings; |
|
21
|
|
|
|
|
48
|
|
|
21
|
|
|
|
|
525
|
|
4
|
21
|
|
|
21
|
|
539
|
use 5.010001; |
|
21
|
|
|
|
|
83
|
|
5
|
|
|
|
|
|
|
our $VERSION = '0.061'; |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
require Exporter; |
8
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
9
|
|
|
|
|
|
|
our @EXPORT = qw(argv); |
10
|
|
|
|
|
|
|
|
11
|
21
|
|
|
21
|
|
10732
|
use List::MoreUtils qw(uniq); |
|
21
|
|
|
|
|
178199
|
|
|
21
|
|
|
|
|
275
|
|
12
|
21
|
|
|
21
|
|
30244
|
use Text::Table; |
|
21
|
|
|
|
|
388744
|
|
|
21
|
|
|
|
|
824
|
|
13
|
21
|
|
|
21
|
|
12808
|
use File::Slurp; |
|
21
|
|
|
|
|
282273
|
|
|
21
|
|
|
|
|
52829
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub new { |
16
|
62
|
|
|
62
|
1
|
48048
|
my $pkg = shift; |
17
|
62
|
|
|
|
|
217
|
my %opt = @_; |
18
|
|
|
|
|
|
|
|
19
|
62
|
|
|
|
|
834
|
my $self = bless { |
20
|
|
|
|
|
|
|
alias => {}, |
21
|
|
|
|
|
|
|
default => {}, |
22
|
|
|
|
|
|
|
boolean => {}, |
23
|
|
|
|
|
|
|
demand => {}, |
24
|
|
|
|
|
|
|
usage => "Usage: $0", |
25
|
|
|
|
|
|
|
describe => {}, |
26
|
|
|
|
|
|
|
type => {}, |
27
|
|
|
|
|
|
|
subcmd => {}, |
28
|
|
|
|
|
|
|
coerce => {}, |
29
|
|
|
|
|
|
|
env => {}, |
30
|
|
|
|
|
|
|
env_prefix => '', |
31
|
|
|
|
|
|
|
}, $pkg; |
32
|
|
|
|
|
|
|
|
33
|
62
|
100
|
100
|
|
|
524
|
if ($opt{add_help} // 1) { |
34
|
59
|
|
|
|
|
327
|
$self->options(h => { |
35
|
|
|
|
|
|
|
alias => 'help', |
36
|
|
|
|
|
|
|
describe => 'show help', |
37
|
|
|
|
|
|
|
}); |
38
|
59
|
|
|
|
|
193
|
$self->{add_help} = 1; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
62
|
|
|
|
|
271
|
$self; |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub argv { |
45
|
12
|
|
|
12
|
1
|
20517
|
Smart::Options->new->parse(@_); |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
sub _set { |
49
|
229
|
|
|
229
|
|
507
|
my $self = shift; |
50
|
229
|
|
|
|
|
378
|
my $param = shift; |
51
|
|
|
|
|
|
|
|
52
|
229
|
|
|
|
|
585
|
my %args = @_; |
53
|
229
|
|
|
|
|
544
|
for my $option (keys %args) { |
54
|
230
|
|
|
|
|
673
|
$self->{$param}->{$option} = $args{$option}; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
229
|
|
|
|
|
742
|
$self; |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
89
|
|
|
89
|
1
|
270
|
sub alias { shift->_set('alias', @_) } |
61
|
7
|
|
|
7
|
1
|
40
|
sub default { shift->_set('default', @_) } |
62
|
62
|
|
|
62
|
1
|
211
|
sub describe { shift->_set('describe', @_) } |
63
|
67
|
|
|
67
|
1
|
193
|
sub type { shift->_set('type', @_) } |
64
|
4
|
|
|
4
|
1
|
9
|
sub subcmd { shift->_set('subcmd', @_) } |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub _set_flag { |
67
|
14
|
|
|
14
|
|
30
|
my $self = shift; |
68
|
14
|
|
|
|
|
25
|
my $param = shift; |
69
|
|
|
|
|
|
|
|
70
|
14
|
|
|
|
|
42
|
for my $option (@_) { |
71
|
23
|
|
|
|
|
62
|
$self->{$param}->{$option} = 1; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
14
|
|
|
|
|
47
|
$self; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
3
|
|
|
3
|
1
|
15
|
sub boolean { shift->_set_flag('boolean', @_) } |
78
|
9
|
|
|
9
|
1
|
34
|
sub demand { shift->_set_flag('demand', @_) } |
79
|
2
|
|
|
2
|
0
|
7
|
sub env { shift->_set_flag('env', @_) } |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub options { |
82
|
60
|
|
|
60
|
1
|
123
|
my $self = shift; |
83
|
|
|
|
|
|
|
|
84
|
60
|
|
|
|
|
205
|
my %args = @_; |
85
|
60
|
|
|
|
|
313
|
while (my($opt, $setting) = each %args) { |
86
|
60
|
|
|
|
|
218
|
for my $key (keys %$setting) { |
87
|
121
|
|
|
|
|
490
|
$self->$key($opt, $setting->{$key}); |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
|
91
|
60
|
|
|
|
|
159
|
$self; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub coerce { |
95
|
32
|
|
|
32
|
1
|
101
|
my ($self, $isa, $type, $generater) = @_; |
96
|
|
|
|
|
|
|
|
97
|
32
|
|
|
|
|
119
|
$self->{coerce}->{$isa} = { |
98
|
|
|
|
|
|
|
type => $type, |
99
|
|
|
|
|
|
|
generater => $generater, |
100
|
|
|
|
|
|
|
}; |
101
|
|
|
|
|
|
|
|
102
|
32
|
|
|
|
|
187
|
$self; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
4
|
|
|
4
|
1
|
31
|
sub usage { $_[0]->{usage} = $_[1]; $_[0] } |
|
4
|
|
|
|
|
20
|
|
106
|
2
|
|
|
2
|
0
|
7
|
sub env_prefix { $_[0]->{env_prefix} = $_[1]; $_[0] } |
|
2
|
|
|
|
|
6
|
|
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
sub _get_opt_desc { |
109
|
13
|
|
|
13
|
|
36
|
my ($self, $option) = @_; |
110
|
|
|
|
|
|
|
|
111
|
13
|
|
|
|
|
39
|
my @opts = ($option); |
112
|
13
|
|
|
|
|
60
|
while ( my($opt, $val) = each %{$self->{alias}} ) { |
|
30
|
|
|
|
|
102
|
|
113
|
17
|
100
|
|
|
|
50
|
push @opts, $opt if $val eq $option; |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
|
116
|
13
|
100
|
|
|
|
47
|
return join(', ', map { (length($_) == 1 ? '-' : '--') . $_ } sort @opts); |
|
20
|
|
|
|
|
142
|
|
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub _get_describe { |
120
|
13
|
|
|
13
|
|
41
|
my ($self, $option) = @_; |
121
|
|
|
|
|
|
|
|
122
|
13
|
|
|
|
|
34
|
my $desc = $self->{describe}->{$option}; |
123
|
13
|
|
|
|
|
29
|
while ( my($opt, $val) = each %{$self->{alias}} ) { |
|
30
|
|
|
|
|
117
|
|
124
|
17
|
100
|
100
|
|
|
80
|
$desc ||= $self->{describe}->{$opt} if $val eq $option; |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
13
|
100
|
|
|
|
115
|
return $desc ? ucfirst($desc) : ''; |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
sub _get_default { |
131
|
14
|
|
|
14
|
|
42
|
my ($self, $option) = @_; |
132
|
|
|
|
|
|
|
|
133
|
14
|
|
|
|
|
43
|
my $value = $self->{default}->{$option}; |
134
|
14
|
|
|
|
|
44
|
while ( my($opt, $val) = each %{$self->{alias}} ) { |
|
32
|
|
|
|
|
169
|
|
135
|
18
|
100
|
66
|
|
|
83
|
$value ||= $self->{default}->{$opt} if $val eq $option; |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
14
|
|
|
|
|
81
|
$value; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub help { |
142
|
6
|
|
|
6
|
1
|
25
|
my $self = shift; |
143
|
|
|
|
|
|
|
|
144
|
6
|
|
|
|
|
17
|
my $alias = $self->{alias}; |
145
|
6
|
|
|
|
|
18
|
my $demand = $self->{demand}; |
146
|
6
|
|
|
|
|
14
|
my $describe = $self->{describe}; |
147
|
6
|
|
|
|
|
17
|
my $default = $self->{default}; |
148
|
6
|
|
|
|
|
18
|
my $boolean = $self->{boolean}; |
149
|
6
|
|
|
|
|
27
|
my $help = $self->{usage} . "\n"; |
150
|
|
|
|
|
|
|
|
151
|
6
|
50
|
100
|
|
|
53
|
if (scalar(keys %$demand) or scalar(keys %$describe)) { |
152
|
6
|
|
|
|
|
16
|
my @opts; |
153
|
6
|
|
|
|
|
91
|
for my $opt (uniq sort keys %$demand, keys %$describe, keys %$default, keys %$boolean, values %$alias) { |
154
|
17
|
100
|
|
|
|
202
|
next if $alias->{$opt}; |
155
|
|
|
|
|
|
|
push @opts, [ |
156
|
|
|
|
|
|
|
$self->_get_opt_desc($opt), |
157
|
|
|
|
|
|
|
$self->_get_describe($opt), |
158
|
|
|
|
|
|
|
$boolean->{$opt} ? '[boolean]' : '', |
159
|
13
|
100
|
|
|
|
56
|
$demand->{$opt} ? '[required]' : '', |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
160
|
1
|
|
|
|
|
6
|
$self->_get_default($opt) ? "[default: @{[$self->_get_default($opt)]}]" : '', |
161
|
|
|
|
|
|
|
]; |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
6
|
|
|
|
|
22
|
my $sep = \' '; |
165
|
6
|
|
|
|
|
20
|
$help .= "\nOptions:\n"; |
166
|
6
|
|
|
|
|
75
|
$help .= Text::Table->new( $sep, '', $sep, '', $sep, '', $sep, '', $sep, '' ) |
167
|
|
|
|
|
|
|
->load(@opts)->stringify . "\n"; |
168
|
6
|
100
|
|
|
|
89857
|
if (keys %{$self->{subcmd}}) { |
|
6
|
|
|
|
|
100
|
|
169
|
1
|
|
|
|
|
3
|
$help .= "Implemented commands are:\n"; |
170
|
1
|
|
|
|
|
2
|
$help .= " " . join(', ', sort keys %{$self->{subcmd}}) . "\n\n"; |
|
1
|
|
|
|
|
9
|
|
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
6
|
|
|
|
|
409
|
$help; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
sub showHelp { |
178
|
4
|
|
|
4
|
1
|
18
|
my ($self, $fh) = @_; |
179
|
4
|
|
33
|
|
|
38
|
$fh //= *STDERR; |
180
|
|
|
|
|
|
|
|
181
|
4
|
|
|
|
|
26
|
print $fh $self->help; |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
sub _set_v2a { |
186
|
81
|
|
|
81
|
|
308
|
my ($argv, $key, $value, $k) = @_; |
187
|
|
|
|
|
|
|
|
188
|
81
|
100
|
|
|
|
308
|
if ($k) { |
|
|
100
|
|
|
|
|
|
189
|
5
|
|
100
|
|
|
47
|
$argv->{$key} //= {}; |
190
|
5
|
|
|
|
|
19
|
_set_v2a($argv->{$key}, $k, $value); |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
elsif (exists $argv->{$key}) { |
193
|
8
|
100
|
|
|
|
36
|
if (ref($argv->{$key})) { |
194
|
2
|
|
|
|
|
5
|
push @{$argv->{$key}}, $value; |
|
2
|
|
|
|
|
36
|
|
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
else { |
197
|
6
|
|
|
|
|
31
|
$argv->{$key} = [ $argv->{$key}, $value ]; |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
else { |
201
|
68
|
|
|
|
|
265
|
$argv->{$key} = $value; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
sub _get_real_name { |
206
|
178
|
|
|
178
|
|
432
|
my ($self, $opt) = @_; |
207
|
|
|
|
|
|
|
|
208
|
178
|
|
|
|
|
619
|
while (my $name = $self->{alias}->{$opt}) { |
209
|
12
|
|
|
|
|
57
|
$opt = $name; |
210
|
|
|
|
|
|
|
} |
211
|
178
|
|
|
|
|
437
|
return $opt; |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
sub _load_config { |
215
|
2
|
|
|
2
|
|
7
|
my ($self, $argv, $file) = @_; |
216
|
|
|
|
|
|
|
|
217
|
2
|
|
|
|
|
12
|
for my $line (read_file($file)) { |
218
|
12
|
100
|
|
|
|
334
|
next if $line =~ /^\[/; |
219
|
8
|
100
|
|
|
|
18
|
next if $line =~ /^;/; |
220
|
6
|
50
|
|
|
|
19
|
next if $line !~ /=/; |
221
|
|
|
|
|
|
|
|
222
|
6
|
|
|
|
|
14
|
chomp($line); |
223
|
6
|
50
|
|
|
|
20
|
if ($line =~ /^(.+?[^\\])=(.*)$/) { |
224
|
6
|
|
|
|
|
20
|
$argv->{$1} = $2; |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
sub parse { |
230
|
64
|
|
|
64
|
1
|
3406
|
my $self = shift; |
231
|
64
|
100
|
|
|
|
286
|
push @_, @ARGV unless @_; |
232
|
|
|
|
|
|
|
|
233
|
64
|
|
|
|
|
145
|
my $argv = {}; |
234
|
64
|
|
|
|
|
144
|
my @args; |
235
|
64
|
|
|
|
|
152
|
my $boolean = $self->{boolean}; |
236
|
|
|
|
|
|
|
|
237
|
64
|
|
|
|
|
139
|
my $key; |
238
|
|
|
|
|
|
|
my $nest_key; |
239
|
64
|
|
|
|
|
139
|
my $stop = 0; |
240
|
64
|
|
|
|
|
163
|
for my $arg (@_) { |
241
|
147
|
100
|
|
|
|
414
|
if ($stop) { |
242
|
20
|
|
|
|
|
43
|
push @args, $arg; |
243
|
20
|
|
|
|
|
38
|
next; |
244
|
|
|
|
|
|
|
} |
245
|
127
|
100
|
|
|
|
915
|
if ($arg =~ /^--((?:\w|-|\.)+)=(.+)$/) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
246
|
50
|
|
|
|
|
261
|
my ($opt, $k) = split(/\./, $1); |
247
|
50
|
|
|
|
|
176
|
my $option = $self->_get_real_name($opt); |
248
|
50
|
100
|
|
|
|
141
|
if ($k) { |
249
|
4
|
|
|
|
|
15
|
_set_v2a($argv, $option, $2, $k); |
250
|
|
|
|
|
|
|
} else { |
251
|
46
|
|
|
|
|
149
|
_set_v2a($argv, $option, $2); |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
elsif ($arg =~ /^(-(\w)|--((?:\w|-|\.)+))$/) { |
255
|
37
|
100
|
|
|
|
123
|
if ($key) { |
256
|
2
|
|
|
|
|
6
|
$argv->{$key} = 1; |
257
|
|
|
|
|
|
|
} |
258
|
37
|
|
66
|
|
|
183
|
my $opt = $2 // $3; |
259
|
37
|
100
|
|
|
|
123
|
if ($opt =~ /^no\-(.+)$/) { |
260
|
1
|
|
|
|
|
5
|
my $option = $self->_get_real_name($1); |
261
|
1
|
|
|
|
|
3
|
$argv->{$option} = 0; |
262
|
1
|
|
|
|
|
4
|
next; |
263
|
|
|
|
|
|
|
} |
264
|
36
|
|
|
|
|
167
|
($opt, my $k) = split(/\./, $opt); |
265
|
36
|
|
|
|
|
149
|
my $option = $self->_get_real_name($opt); |
266
|
36
|
100
|
|
|
|
116
|
if ($boolean->{$option}) { |
267
|
3
|
50
|
|
|
|
11
|
if ($k) { |
268
|
0
|
|
0
|
|
|
0
|
$argv->{$option} //= {}; |
269
|
0
|
|
|
|
|
0
|
$argv->{$option}->{$k} = 1; |
270
|
|
|
|
|
|
|
} else { |
271
|
3
|
|
|
|
|
12
|
$argv->{$option} = 1; |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
else { |
275
|
33
|
|
|
|
|
64
|
$key = $option; |
276
|
33
|
|
|
|
|
79
|
$nest_key = $k; |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
elsif ($arg =~ /^-(\w(?:\w|-|\.)+)$/) { |
280
|
2
|
50
|
|
|
|
10
|
if ($key) { |
281
|
0
|
|
|
|
|
0
|
$argv->{$key} = 1; |
282
|
|
|
|
|
|
|
} |
283
|
2
|
|
|
|
|
6
|
my $opt_str = $1; |
284
|
2
|
100
|
|
|
|
8
|
if ($opt_str =~ /^(.)([0-9])+$/) { |
285
|
1
|
|
|
|
|
5
|
my $option = $self->_get_real_name($1); |
286
|
1
|
|
|
|
|
3
|
$argv->{$option} = $2; |
287
|
|
|
|
|
|
|
} else { |
288
|
1
|
|
|
|
|
4
|
for (split //, $opt_str) { |
289
|
3
|
|
|
|
|
8
|
my $option = $self->_get_real_name($_); |
290
|
3
|
|
|
|
|
8
|
$argv->{$option} = 1; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
elsif ($arg =~ /^--$/) { |
295
|
|
|
|
|
|
|
|
296
|
4
|
|
|
|
|
20
|
$stop = 1; |
297
|
4
|
|
|
|
|
14
|
next; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
else { |
300
|
34
|
100
|
|
|
|
89
|
if ($key) { |
301
|
26
|
100
|
|
|
|
77
|
if ($nest_key) { |
302
|
1
|
|
|
|
|
5
|
_set_v2a($argv, $key, $arg, $nest_key); |
303
|
|
|
|
|
|
|
} else { |
304
|
25
|
|
|
|
|
82
|
_set_v2a($argv, $key, $arg); |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
|
307
|
26
|
|
|
|
|
71
|
$key = $nest_key = undef; |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
else { |
310
|
8
|
100
|
100
|
|
|
33
|
if (!scalar(@args) && keys %{$self->{subcmd}}) { |
|
4
|
|
|
|
|
21
|
|
311
|
1
|
50
|
|
|
|
3
|
if ( $self->{subcmd}->{$arg} ) { |
312
|
1
|
|
|
|
|
3
|
$argv->{command} = $arg; |
313
|
1
|
|
|
|
|
2
|
$stop = 1; |
314
|
1
|
|
|
|
|
2
|
next; |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
else { |
317
|
0
|
|
|
|
|
0
|
die "sub command '$arg' not defined."; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
|
321
|
7
|
|
|
|
|
22
|
push @args, $arg; |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
} |
325
|
64
|
100
|
|
|
|
190
|
if ($key) { |
326
|
6
|
50
|
|
|
|
20
|
if ($nest_key) { |
327
|
0
|
|
0
|
|
|
0
|
$argv->{$key} //= {}; |
328
|
0
|
|
|
|
|
0
|
$argv->{$key}->{$nest_key} = 1; |
329
|
|
|
|
|
|
|
} else { |
330
|
6
|
|
|
|
|
19
|
$argv->{$key} = 1; |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
|
334
|
64
|
100
|
100
|
|
|
456
|
if (my $parser = $self->{subcmd}->{$argv->{command}||''}) { |
335
|
1
|
|
|
|
|
9
|
$argv->{cmd_option} = $parser->parse(@args); |
336
|
|
|
|
|
|
|
} else { |
337
|
63
|
|
|
|
|
185
|
$argv->{_} = \@args; |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
|
340
|
64
|
|
|
|
|
130
|
for my $env (keys %{$self->{env}}) { |
|
64
|
|
|
|
|
242
|
|
341
|
3
|
100
|
|
|
|
14
|
if (defined($ENV{uc($self->{env_prefix}."_$env")})) { |
342
|
2
|
|
|
|
|
5
|
my $option = $self->_get_real_name($env); |
343
|
2
|
|
66
|
|
|
10
|
$argv->{$option} //= $ENV{uc($self->{env_prefix}."_$env")}; |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
|
347
|
64
|
|
|
|
|
149
|
while (my ($key, $val) = each %{$self->{default}}) { |
|
71
|
|
|
|
|
395
|
|
348
|
7
|
|
|
|
|
32
|
my $opt = $self->_get_real_name($key); |
349
|
7
|
100
|
66
|
|
|
42
|
if (ref($val) && ref($val) eq 'CODE') { |
350
|
1
|
|
33
|
|
|
7
|
$argv->{$opt} //= $val->(); |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
else { |
353
|
6
|
|
66
|
|
|
50
|
$argv->{$opt} //= $val; |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
} |
356
|
|
|
|
|
|
|
|
357
|
64
|
|
|
|
|
143
|
while (my ($key, $val) = each %{$self->{type}}) { |
|
137
|
|
|
|
|
504
|
|
358
|
73
|
100
|
|
|
|
245
|
next if $val ne 'Config'; |
359
|
30
|
100
|
66
|
|
|
174
|
next if !($argv->{$key}) || !(-f $argv->{$key}); |
360
|
2
|
|
|
|
|
11
|
$self->_load_config($argv, delete $argv->{$key}); |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
|
363
|
64
|
|
|
|
|
147
|
for my $key (keys %{$self->{demand}}) { |
|
64
|
|
|
|
|
189
|
|
364
|
9
|
|
|
|
|
31
|
my $opt = $self->_get_real_name($key); |
365
|
9
|
100
|
|
|
|
41
|
if (!$argv->{$opt}) { |
366
|
3
|
|
|
|
|
16
|
$self->showHelp; |
367
|
3
|
|
|
|
|
52
|
print STDERR "\nMissing required arguments: $opt\n"; |
368
|
3
|
|
|
|
|
89
|
die; |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
|
372
|
61
|
|
|
|
|
151
|
for my $key (keys %{$self->{type}}) { |
|
61
|
|
|
|
|
175
|
|
373
|
69
|
|
|
|
|
169
|
my $opt = $self->_get_real_name($key); |
374
|
69
|
|
|
|
|
144
|
my $type = $self->{type}->{$key}; |
375
|
69
|
100
|
|
|
|
210
|
if (my $c = $self->{coerce}->{$type}) { |
376
|
5
|
|
|
|
|
9
|
$type = $c->{type}; |
377
|
5
|
|
|
|
|
17
|
$argv->{$opt} = $c->{generater}->($argv->{$opt}); |
378
|
|
|
|
|
|
|
} |
379
|
69
|
|
|
|
|
155
|
my $check = 0; |
380
|
69
|
50
|
|
|
|
418
|
if ($type eq 'Bool') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
381
|
0
|
|
0
|
|
|
0
|
$argv->{$opt} //= 0; |
382
|
0
|
0
|
|
|
|
0
|
$check = ($argv->{$opt} =~ /^(0|1)$/) ? 1 : 0; |
383
|
|
|
|
|
|
|
} elsif ($type eq 'Str') { |
384
|
3
|
|
|
|
|
7
|
$check = 1; |
385
|
|
|
|
|
|
|
} elsif ($type eq 'Int') { |
386
|
19
|
100
|
|
|
|
45
|
if ($argv->{$opt}) { |
387
|
18
|
100
|
|
|
|
91
|
$check = ($argv->{$opt} =~ /^\-?\d+$/) ? 1 : 0; |
388
|
|
|
|
|
|
|
} else { |
389
|
1
|
|
|
|
|
2
|
$check = 1; |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
} elsif ($type eq 'Num') { |
392
|
7
|
100
|
|
|
|
24
|
if ($argv->{$opt}) { |
393
|
5
|
100
|
|
|
|
41
|
$check = ($argv->{$opt} =~ /^\-?\d+(\.\d+)$/) ? 1 : 0; |
394
|
|
|
|
|
|
|
} else { |
395
|
2
|
|
|
|
|
3
|
$check = 1; |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
} elsif ($type eq 'ArrayRef') { |
398
|
10
|
|
50
|
|
|
40
|
$argv->{$opt} //= []; |
399
|
10
|
100
|
|
|
|
33
|
unless (ref($argv->{$opt})) { |
400
|
2
|
|
|
|
|
11
|
$argv->{$opt} = [$argv->{$opt}]; |
401
|
|
|
|
|
|
|
} |
402
|
10
|
100
|
|
|
|
35
|
$check = (ref($argv->{$opt}) eq 'ARRAY') ? 1 : 0; |
403
|
|
|
|
|
|
|
} elsif ($type eq 'HashRef') { |
404
|
2
|
|
50
|
|
|
8
|
$argv->{$opt} //= {}; |
405
|
2
|
100
|
|
|
|
10
|
$check = (ref($argv->{$opt}) eq 'HASH') ? 1 : 0; |
406
|
|
|
|
|
|
|
} elsif ('Config') { |
407
|
28
|
50
|
33
|
|
|
117
|
if ($argv->{$opt} && !(-f $argv->{$opt})) { |
408
|
0
|
|
|
|
|
0
|
die "cannot load config file '@{[$argv->{$opt}]}\n"; |
|
0
|
|
|
|
|
0
|
|
409
|
|
|
|
|
|
|
} |
410
|
28
|
|
|
|
|
58
|
$check = 1; |
411
|
|
|
|
|
|
|
} else { |
412
|
|
|
|
|
|
|
die "cannot find type constraint '$type'\n"; |
413
|
|
|
|
|
|
|
} |
414
|
69
|
100
|
|
|
|
215
|
unless ($check) { |
415
|
6
|
|
|
|
|
11
|
die "Value '@{[$argv->{$opt}]}' invalid for option $opt($type)\n"; |
|
6
|
|
|
|
|
84
|
|
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
|
419
|
55
|
50
|
66
|
|
|
228
|
if ($argv->{help} && $self->{add_help}) { |
420
|
1
|
|
|
|
|
4
|
$self->showHelp; |
421
|
1
|
|
|
|
|
48
|
die; |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
|
424
|
54
|
|
|
|
|
257
|
$argv; |
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
1; |
429
|
|
|
|
|
|
|
__END__ |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
=encoding utf8 |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=head1 NAME |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
Smart::Options - smart command line options processor |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
=head1 SYNOPSIS |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
use Smart::Options; |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
my $argv = Smart::Options->new->argv; |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
if ($argv->{rif} - 5 * $argv->{xup} > 7.138) { |
444
|
|
|
|
|
|
|
say 'Buy more fiffiwobbles'; |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
else { |
447
|
|
|
|
|
|
|
say 'Sell the xupptumblers'; |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
# $ ./example.pl --rif=55 --xup=9.52 |
451
|
|
|
|
|
|
|
# Buy more fiffiwobbles |
452
|
|
|
|
|
|
|
# |
453
|
|
|
|
|
|
|
# $ ./example.pl --rif 12 --xup 8.1 |
454
|
|
|
|
|
|
|
# Sell the xupptumblers |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
=head1 DESCRIPTION |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
Smart::Options is a library for option parsing for people tired option parsing. |
459
|
|
|
|
|
|
|
This module is analyzed as people interpret an option intuitively. |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=head1 METHOD |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
=head2 new() |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
Create a parser object. |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
use Smart::Options; |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
my $argv = Smart::Options->new->parse(qw(-x 10 -y 2)); |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
=head2 parse(@args) |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
parse @args. return hashref of option values. |
474
|
|
|
|
|
|
|
if @args is empty Smart::Options use @ARGV |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
=head2 argv(@args) |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
shortcut method. this method auto export. |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
use Smart::Options; |
481
|
|
|
|
|
|
|
say argv(qw(-x 10))->{x}; |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
is the same as |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
use Smart::Options (); |
486
|
|
|
|
|
|
|
Smart::Options->new->parse(qw(-x 10))->{x}; |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
=head2 alias($alias, $option) |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
set alias for option. you can use "$option" field of argv. |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
use Smart::Options; |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
my $argv = Smart::Options->new->alias(f => 'file')->parse(qw(-f /etc/hosts)); |
495
|
|
|
|
|
|
|
$argv->{file} # => '/etc/hosts' |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
=head2 default($option, $default_value) |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
set default value for option. |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
use Smart::Options; |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
my $argv = Smart::Options->new->default(y => 5)->parse(qw(-x 10)); |
504
|
|
|
|
|
|
|
$argv->{x} + $argv->{y} # => 15 |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=head2 describe($option, $msg) |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
set option help message. |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
use Smart::Options; |
511
|
|
|
|
|
|
|
my $opt = Smart::Options->new()->alias(f => 'file')->describe('Load a file'); |
512
|
|
|
|
|
|
|
say $opt->help; |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
# Usage: ./example.pl |
515
|
|
|
|
|
|
|
# |
516
|
|
|
|
|
|
|
# Options: |
517
|
|
|
|
|
|
|
# -f, --file Load a file |
518
|
|
|
|
|
|
|
# |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=head2 boolean($option, $option2, ...) |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
interpret 'option' as a boolean. |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
use Smart::Options; |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
my $argv = Smart::Options->new->parse(qw(-x 11 -y 10)); |
527
|
|
|
|
|
|
|
$argv->{x} # => 11 |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
my $argv2 = Smart::Options->new->boolean('x')->parse(qw(-x 11 -y 10)); |
530
|
|
|
|
|
|
|
$argv2->{x} # => true (1) |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
=head2 demand($option, $option2, ...) |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
show usage (showHelp()) and exit if $option wasn't specified in args. |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
use Smart::Options; |
537
|
|
|
|
|
|
|
my $opt = Smart::Options->new()->alias(f => 'file') |
538
|
|
|
|
|
|
|
->demand('file') |
539
|
|
|
|
|
|
|
->describe('Load a file'); |
540
|
|
|
|
|
|
|
$opt->argv(); # => exit |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
# Usage: ./example.pl |
543
|
|
|
|
|
|
|
# |
544
|
|
|
|
|
|
|
# Options: |
545
|
|
|
|
|
|
|
# -f, --file Load a file [required] |
546
|
|
|
|
|
|
|
# |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
=head2 options($key => $settings, ...) |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
use Smart::Options; |
551
|
|
|
|
|
|
|
my $opt = Smart::Options->new() |
552
|
|
|
|
|
|
|
->options( f => { alias => 'file', default => '/etc/passwd' } ); |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
is the same as |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
use Smart::Options; |
557
|
|
|
|
|
|
|
my $opt = Smart::Options->new() |
558
|
|
|
|
|
|
|
->alias(f => 'file') |
559
|
|
|
|
|
|
|
->default(f => '/etc/passwd'); |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
=head2 type($option => $type) |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
set type check for option value |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
use Smart::Options; |
566
|
|
|
|
|
|
|
my $opt = Smart::Options->new()->type(foo => 'Int'); |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
$opt->parse('--foo=bar') # => fail |
569
|
|
|
|
|
|
|
$opt->parse('--foo=3.14') # => fail |
570
|
|
|
|
|
|
|
$opt->parse('--foo=1') # => ok |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
support type is here. |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
Bool |
575
|
|
|
|
|
|
|
Str |
576
|
|
|
|
|
|
|
Int |
577
|
|
|
|
|
|
|
Num |
578
|
|
|
|
|
|
|
ArrayRef |
579
|
|
|
|
|
|
|
HashRef |
580
|
|
|
|
|
|
|
Config |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
=head3 Config |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
'Config' is special type. |
585
|
|
|
|
|
|
|
The contents will be read into each option if a file name is specified as a Config type option. |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
use Smart::Options; |
588
|
|
|
|
|
|
|
my $opt = Smart::Options->new()->type(conf => 'Config'); |
589
|
|
|
|
|
|
|
$opt->parse(qw(--conf=.optrc)); |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
config file format is simple. see http://en.wikipedia.org/wiki/INI_file |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
; this is comment |
594
|
|
|
|
|
|
|
[section] |
595
|
|
|
|
|
|
|
key=value |
596
|
|
|
|
|
|
|
key2=value2 |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=head2 coerce( $newtype => $sourcetype, $generator ) |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
define new type and convert logic. |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
use Smart::Options; |
603
|
|
|
|
|
|
|
use Path::Class; # export 'file' |
604
|
|
|
|
|
|
|
my $opt = Smart::Options->new()->coerce(File => 'Str', sub { file($_[0]) }) |
605
|
|
|
|
|
|
|
->type(file => 'File'); |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
$opt->parse('--foo=/etc/passwd'); |
608
|
|
|
|
|
|
|
$argv->{file} # => Path::Class::File instance |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
=head2 usage |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
set a usage message to show which command to use. default is "Usage: $0". |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
=head2 help |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
return help message string |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
=head2 showHelp($fh) |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
print usage message. default output STDERR. |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
=head2 subcmd($cmd => $parser) |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
set a sub command. $parser is another Smart::Option object. |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
use Smart::Options; |
627
|
|
|
|
|
|
|
my $opt = Smart::Options->new() |
628
|
|
|
|
|
|
|
->subcmd(add => Smart::Options->new()) |
629
|
|
|
|
|
|
|
->subcmd(minus => Smart::Options->new()); |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
=head1 DSL |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
see also L<Smart::Options::Declare> |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
=head1 PARSING TRICKS |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
=head2 stop parsing |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
use '--' to stop parsing. |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
use Smart::Options; |
642
|
|
|
|
|
|
|
use Data::Dumper; |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
my $argv = argv(qw(-a 1 -b 2 -- -c 3 -d 4)); |
645
|
|
|
|
|
|
|
warn Dumper($argv); |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
# $VAR1 = { |
648
|
|
|
|
|
|
|
# 'a' => '1', |
649
|
|
|
|
|
|
|
# 'b' => '2', |
650
|
|
|
|
|
|
|
# '_' => [ |
651
|
|
|
|
|
|
|
# '-c', |
652
|
|
|
|
|
|
|
# '3', |
653
|
|
|
|
|
|
|
# '-d', |
654
|
|
|
|
|
|
|
# '4' |
655
|
|
|
|
|
|
|
# ] |
656
|
|
|
|
|
|
|
# }; |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
=head2 negate fields |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
'--no-key' set false to $key. |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
use Smart::Options; |
663
|
|
|
|
|
|
|
argv(qw(-a --no-b))->{b}; # => 0 |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
=head2 duplicates |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
If set flag multiple times it will get arrayref. |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
use Smart::Options; |
670
|
|
|
|
|
|
|
argv(qw(-x 1 -x 2 -x 3))->{x}; # => [1, 2, 3] |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
=head2 dot notation |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
use Smart::Optuions; |
675
|
|
|
|
|
|
|
argv(qw(--foo.x 1 --foo.y 2)); # => { foo => { x => 1, y => 2 } } |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
=head1 AUTHOR |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
Kan Fushihara E<lt>kan.fushihara@gmail.comE<gt> |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
=head1 SEE ALSO |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
https://www.npmjs.com/package/minimist |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
L<GetOpt::Casual>, L<opts>, L<GetOpt::Compat::WithCmd> |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
=head1 LICENSE |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
Copyright (C) Kan Fushihara |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
692
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
=cut |
695
|
|
|
|
|
|
|
|