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