line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package CLI::Startup; |
2
|
|
|
|
|
|
|
|
3
|
11
|
|
|
11
|
|
163747
|
use English qw( -no_match_vars ); |
|
11
|
|
|
|
|
9019
|
|
|
11
|
|
|
|
|
73
|
|
4
|
|
|
|
|
|
|
|
5
|
10
|
|
|
10
|
|
2773
|
use warnings; |
|
10
|
|
|
|
|
18
|
|
|
10
|
|
|
|
|
225
|
|
6
|
10
|
|
|
10
|
|
40
|
use strict; |
|
10
|
|
|
|
|
16
|
|
|
10
|
|
|
|
|
190
|
|
7
|
|
|
|
|
|
|
|
8
|
10
|
|
|
10
|
|
42
|
use Carp; |
|
10
|
|
|
|
|
13
|
|
|
10
|
|
|
|
|
458
|
|
9
|
10
|
|
|
10
|
|
445
|
use Symbol; |
|
10
|
|
|
|
|
658
|
|
|
10
|
|
|
|
|
431
|
|
10
|
10
|
|
|
10
|
|
5091
|
use Pod::Text; |
|
10
|
|
|
|
|
415226
|
|
|
10
|
|
|
|
|
704
|
|
11
|
10
|
|
|
10
|
|
6712
|
use Text::CSV; |
|
10
|
|
|
|
|
119763
|
|
|
10
|
|
|
|
|
446
|
|
12
|
10
|
|
|
10
|
|
6048
|
use Class::Std; |
|
10
|
|
|
|
|
69677
|
|
|
10
|
|
|
|
|
51
|
|
13
|
10
|
|
|
10
|
|
5203
|
use Config::Any; |
|
10
|
|
|
|
|
96389
|
|
|
10
|
|
|
|
|
277
|
|
14
|
10
|
|
|
10
|
|
4869
|
use Data::Dumper; |
|
10
|
|
|
|
|
46683
|
|
|
10
|
|
|
|
|
505
|
|
15
|
10
|
|
|
10
|
|
4539
|
use File::HomeDir; |
|
10
|
|
|
|
|
46206
|
|
|
10
|
|
|
|
|
453
|
|
16
|
10
|
|
|
10
|
|
59
|
use File::Basename; |
|
10
|
|
|
|
|
21
|
|
|
10
|
|
|
|
|
541
|
|
17
|
10
|
|
|
10
|
|
3720
|
use Clone qw{ clone }; |
|
10
|
|
|
|
|
20842
|
|
|
10
|
|
|
|
|
478
|
|
18
|
10
|
|
|
10
|
|
4266
|
use Hash::Merge qw{ merge }; |
|
10
|
|
|
|
|
37255
|
|
|
10
|
|
|
|
|
536
|
|
19
|
10
|
|
|
10
|
|
70
|
use List::Util qw{ max reduce }; |
|
10
|
|
|
|
|
26
|
|
|
10
|
|
|
|
|
949
|
|
20
|
10
|
|
|
|
|
37
|
use Getopt::Long qw{ |
21
|
|
|
|
|
|
|
GetOptionsFromArray :config posix_default bundling require_order no_ignore_case |
22
|
10
|
|
|
10
|
|
6305
|
}; |
|
10
|
|
|
|
|
85908
|
|
23
|
|
|
|
|
|
|
|
24
|
10
|
|
|
10
|
|
2522
|
use Exporter 'import'; |
|
10
|
|
|
|
|
18
|
|
|
10
|
|
|
|
|
453
|
|
25
|
|
|
|
|
|
|
our @EXPORT_OK = qw/startup/; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
our $VERSION = '0.29'; # Don't forget to update the manpage version, too! |
28
|
|
|
|
|
|
|
|
29
|
10
|
|
|
10
|
|
4681
|
use Readonly; |
|
10
|
|
|
|
|
32193
|
|
|
10
|
|
|
|
|
49916
|
|
30
|
|
|
|
|
|
|
Readonly my $V_FOR_VERBOSE => 'ALIAS OF VERBOSE'; |
31
|
|
|
|
|
|
|
Readonly my $V_OPTSPEC => 'v+'; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# Simple command-line processing with transparent |
34
|
|
|
|
|
|
|
# support for config files. |
35
|
|
|
|
|
|
|
sub startup |
36
|
|
|
|
|
|
|
{ |
37
|
24
|
|
|
24
|
1
|
38212
|
my $optspec = shift; |
38
|
|
|
|
|
|
|
|
39
|
24
|
|
|
|
|
99
|
my $app = CLI::Startup->new($optspec); |
40
|
20
|
|
|
|
|
1002
|
$app->init; |
41
|
|
|
|
|
|
|
|
42
|
7
|
|
|
|
|
15
|
return $app->get_options; |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
#<<< Leave this alone, perltidy |
46
|
|
|
|
|
|
|
# Attributes of our inside-out objects. |
47
|
|
|
|
|
|
|
my %config_of : ATTR(); |
48
|
|
|
|
|
|
|
my %initialized_of : ATTR( :get ); |
49
|
|
|
|
|
|
|
my %options_of : ATTR(); |
50
|
|
|
|
|
|
|
my %optspec_of : ATTR( :initarg ); |
51
|
|
|
|
|
|
|
my %raw_options_of : ATTR(); |
52
|
|
|
|
|
|
|
my %rcfile_of : ATTR( :get :initarg ); |
53
|
|
|
|
|
|
|
my %usage_of : ATTR( :get :initarg ); |
54
|
|
|
|
|
|
|
my %write_rcfile_of : ATTR( :get :initarg ); |
55
|
|
|
|
|
|
|
my %default_settings_of : |
56
|
|
|
|
|
|
|
ATTR( :get :initarg ); |
57
|
|
|
|
|
|
|
#>>> |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# Returns a clone of the config object. |
60
|
|
|
|
|
|
|
sub get_config |
61
|
|
|
|
|
|
|
{ |
62
|
35
|
|
|
35
|
1
|
9397
|
my $self = shift; |
63
|
35
|
100
|
|
|
|
138
|
$self->die('get_config() called before init()') |
64
|
|
|
|
|
|
|
unless $self->get_initialized; |
65
|
34
|
|
|
|
|
1381
|
return clone( $config_of{ ident $self} ); |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# Set defaults for the command-line options. Can be done as much as |
69
|
|
|
|
|
|
|
# desired until the app is initialized. |
70
|
|
|
|
|
|
|
sub set_default_settings |
71
|
|
|
|
|
|
|
{ |
72
|
5
|
|
|
5
|
1
|
1076
|
my ( $self, $settings ) = @_; |
73
|
|
|
|
|
|
|
|
74
|
5
|
100
|
100
|
|
|
25
|
$self->die('set_default_settings() requires a hashref') |
75
|
|
|
|
|
|
|
unless defined $settings and ref $settings eq 'HASH'; |
76
|
3
|
100
|
|
|
|
9
|
$self->die('set_default_settings() called after init()') |
77
|
|
|
|
|
|
|
if $self->get_initialized; |
78
|
|
|
|
|
|
|
|
79
|
2
|
|
|
|
|
19
|
$default_settings_of{ ident $self} = clone($settings); |
80
|
|
|
|
|
|
|
|
81
|
2
|
|
|
|
|
6
|
return; # Needed so we don't leak a reference to the data! |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# Get the options provided on the command line. This, unlike most of |
85
|
|
|
|
|
|
|
# the others, can ONLY be called after the app is initialized. |
86
|
|
|
|
|
|
|
sub get_options |
87
|
|
|
|
|
|
|
{ |
88
|
28
|
|
|
28
|
1
|
378
|
my $self = shift; |
89
|
28
|
100
|
|
|
|
88
|
$self->die('get_options() called before init()') |
90
|
|
|
|
|
|
|
unless $self->get_initialized; |
91
|
27
|
|
|
|
|
582
|
return clone( $options_of{ ident $self} ); |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
# Returns the current specifications for the command-line options. |
95
|
|
|
|
|
|
|
sub get_optspec |
96
|
|
|
|
|
|
|
{ |
97
|
186
|
|
|
186
|
1
|
1011
|
my $self = shift; |
98
|
186
|
|
|
|
|
2644
|
return clone( $optspec_of{ ident $self} ); |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# Set the specifications of the current command-line options. |
102
|
|
|
|
|
|
|
sub set_optspec |
103
|
|
|
|
|
|
|
{ |
104
|
71
|
|
|
71
|
1
|
1486
|
my $self = shift; |
105
|
71
|
|
|
|
|
84
|
my $spec = shift; |
106
|
|
|
|
|
|
|
|
107
|
71
|
100
|
|
|
|
218
|
$self->die('set_optspec() requires a hashref') |
108
|
|
|
|
|
|
|
unless ref $spec eq 'HASH'; |
109
|
70
|
100
|
|
|
|
167
|
$self->die('set_optspec() called after init()') |
110
|
|
|
|
|
|
|
if $self->get_initialized; |
111
|
|
|
|
|
|
|
|
112
|
69
|
|
|
|
|
411
|
$optspec_of{ ident $self} = clone( $self->_validate_optspec($spec) ); |
113
|
|
|
|
|
|
|
|
114
|
64
|
|
|
|
|
223
|
return; # Needed so we don't leak a reference to the data! |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# Returns a clone of the actual command-line options. |
118
|
|
|
|
|
|
|
sub get_raw_options |
119
|
|
|
|
|
|
|
{ |
120
|
24
|
|
|
24
|
1
|
321
|
my $self = shift; |
121
|
24
|
100
|
|
|
|
72
|
$self->die('get_raw_options() called before init()') |
122
|
|
|
|
|
|
|
unless $self->get_initialized; |
123
|
23
|
|
|
|
|
276
|
return clone( $raw_options_of{ ident $self} ); |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
# Set the filename of the rcfile for the app. |
127
|
|
|
|
|
|
|
sub set_rcfile |
128
|
|
|
|
|
|
|
{ |
129
|
75
|
|
|
75
|
1
|
3312
|
my ( $self, $rcfile ) = @_; |
130
|
|
|
|
|
|
|
|
131
|
75
|
100
|
|
|
|
230
|
$self->die('set_rcfile() called after init()') |
132
|
|
|
|
|
|
|
if $self->get_initialized; |
133
|
74
|
|
|
|
|
517
|
$rcfile_of{ ident $self} = "$rcfile"; |
134
|
|
|
|
|
|
|
|
135
|
74
|
|
|
|
|
125
|
return; |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# Set the usage string for the app. Only needed if there are |
139
|
|
|
|
|
|
|
# arguments other than command-line options. |
140
|
|
|
|
|
|
|
sub set_usage |
141
|
|
|
|
|
|
|
{ |
142
|
64
|
|
|
64
|
1
|
675
|
my ( $self, $usage ) = @_; |
143
|
|
|
|
|
|
|
|
144
|
64
|
100
|
|
|
|
128
|
$self->die('set_usage() called after init()') |
145
|
|
|
|
|
|
|
if $self->get_initialized; |
146
|
63
|
|
|
|
|
288
|
$usage_of{ ident $self} = "$usage"; |
147
|
|
|
|
|
|
|
|
148
|
63
|
|
|
|
|
85
|
return; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# Set a file writer for the rc file. |
152
|
|
|
|
|
|
|
sub set_write_rcfile |
153
|
|
|
|
|
|
|
{ |
154
|
7
|
|
|
7
|
1
|
1330
|
my $self = shift; |
155
|
7
|
|
100
|
|
|
33
|
my $writer = shift || 0; |
156
|
|
|
|
|
|
|
|
157
|
7
|
100
|
|
|
|
19
|
$self->die('set_write_rcfile() called after init()') |
158
|
|
|
|
|
|
|
if $self->get_initialized; |
159
|
6
|
100
|
100
|
|
|
48
|
$self->die('set_write_rcfile() requires a coderef or false') |
160
|
|
|
|
|
|
|
if $writer && ref($writer) ne 'CODE'; |
161
|
|
|
|
|
|
|
|
162
|
5
|
|
|
|
|
17
|
my $optspec = $optspec_of{ ident $self}; # Need a reference, not a copy |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
# Toggle the various rcfile options if writing is turned on or off |
165
|
5
|
100
|
|
|
|
16
|
if ($writer) |
166
|
|
|
|
|
|
|
{ |
167
|
2
|
|
|
|
|
7
|
my $options = $self->_get_default_optspec; |
168
|
2
|
|
|
|
|
20
|
my $aliases = $self->_option_aliases($options); |
169
|
|
|
|
|
|
|
|
170
|
2
|
|
|
|
|
10
|
for my $alias (qw{ rcfile write-rcfile rcfile-format }) |
171
|
|
|
|
|
|
|
{ |
172
|
6
|
|
66
|
|
|
28
|
$optspec->{$alias} ||= $options->{ $aliases->{$alias} }; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
else |
176
|
|
|
|
|
|
|
{ |
177
|
3
|
|
|
|
|
8
|
for my $alias (qw{ rcfile write-rcfile rcfile-format }) |
178
|
|
|
|
|
|
|
{ |
179
|
9
|
|
|
|
|
14
|
delete $optspec->{$alias}; |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# Save the writer |
184
|
5
|
|
|
|
|
20
|
$write_rcfile_of{ ident $self} = $writer; |
185
|
|
|
|
|
|
|
|
186
|
5
|
|
|
|
|
15
|
return; # Needed so we don't leak a reference to the data! |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
# Die with a standardized message format. |
190
|
|
|
|
|
|
|
sub die ## no critic ( Subroutines::RequireFinalReturn ) |
191
|
|
|
|
|
|
|
{ |
192
|
24
|
|
|
24
|
1
|
110
|
my ( undef, $msg ) = @_; |
193
|
|
|
|
|
|
|
|
194
|
24
|
|
|
|
|
543
|
my $name = basename($PROGRAM_NAME); |
195
|
24
|
|
|
|
|
208
|
CORE::die "$name: FATAL: $msg\n"; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
# Die with a usage summary. |
199
|
|
|
|
|
|
|
sub die_usage |
200
|
|
|
|
|
|
|
{ |
201
|
8
|
|
|
8
|
1
|
5289
|
my $self = shift; |
202
|
8
|
|
|
|
|
8
|
my $msg = shift; |
203
|
|
|
|
|
|
|
|
204
|
8
|
|
|
|
|
13
|
print { \*STDERR } $self->_usage_message($msg); |
|
8
|
|
|
|
|
22
|
|
205
|
8
|
|
|
|
|
49
|
exit 1; |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# Return a usage message |
209
|
|
|
|
|
|
|
sub _usage_message |
210
|
|
|
|
|
|
|
{ |
211
|
14
|
|
|
14
|
|
54
|
my $self = shift; |
212
|
14
|
|
|
|
|
19
|
my $msg = shift; |
213
|
14
|
|
|
|
|
33
|
my $optspec = $self->get_optspec; |
214
|
14
|
|
|
|
|
313
|
my $name = basename($PROGRAM_NAME); |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
# The message to be returned |
217
|
14
|
|
|
|
|
29
|
my $message = ''; |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
# This happens if options aren't defined in the constructor |
220
|
|
|
|
|
|
|
# and then die_usage() is called directly or indirectly. |
221
|
|
|
|
|
|
|
$self->die('_usage_message() called without defining any options') |
222
|
14
|
50
|
|
|
|
45
|
unless keys %{$optspec}; |
|
14
|
|
|
|
|
69
|
|
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
#<<< Leave this alone, perltidy |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
# In the usage text, show the option names, not the aliases. |
227
|
|
|
|
|
|
|
my %options = |
228
|
122
|
|
|
|
|
196
|
map { ( $_->{names}[0], $_ ) } |
229
|
122
|
|
|
|
|
226
|
map { $self->_parse_spec( $_, $optspec->{$_} ) } |
230
|
14
|
|
|
|
|
25
|
keys %{$optspec}; |
|
14
|
|
|
|
|
60
|
|
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
#>>> End perltidy-free zone |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# Automatically suppress 'v' if it's an alias of 'verbose' |
235
|
14
|
50
|
33
|
|
|
63
|
delete $options{v} if $optspec->{$V_OPTSPEC} // '' eq $V_FOR_VERBOSE; |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# Note the length of the longest option |
238
|
14
|
|
|
|
|
145
|
my $length = max map { length() } keys %options; |
|
108
|
|
|
|
|
267
|
|
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
# Print the requested message, if any |
241
|
14
|
100
|
|
|
|
52
|
if ( defined $msg ) |
242
|
|
|
|
|
|
|
{ |
243
|
2
|
|
|
|
|
10
|
$message .= sprintf "\%s: FATAL: \%s\n", $name, $msg; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# Now print the help message. |
247
|
|
|
|
|
|
|
$message |
248
|
14
|
|
|
|
|
309
|
.= 'usage: ' |
249
|
|
|
|
|
|
|
. basename($PROGRAM_NAME) . ' ' |
250
|
|
|
|
|
|
|
. $self->get_usage . "\n" |
251
|
|
|
|
|
|
|
. "Options:\n"; |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
# Print the options, sorted in dictionary order. |
254
|
14
|
|
|
|
|
140
|
for my $option ( sort keys %options ) |
255
|
|
|
|
|
|
|
{ |
256
|
|
|
|
|
|
|
## no critic (ValuesAndExpressions::ProhibitMagicNumbers) |
257
|
108
|
|
|
|
|
127
|
my $indent = $length + 8; |
258
|
108
|
|
|
|
|
121
|
my $spec = $options{$option}; |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# Print the basic help option |
261
|
108
|
100
|
|
|
|
140
|
if ( length($option) == 1 ) |
262
|
|
|
|
|
|
|
{ |
263
|
|
|
|
|
|
|
$message .= sprintf " -%-${length}s - %s\n", $option, |
264
|
5
|
|
|
|
|
17
|
$spec->{desc}; |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
else |
267
|
|
|
|
|
|
|
{ |
268
|
|
|
|
|
|
|
$message .= sprintf " --%-${length}s - %s\n", $option, |
269
|
103
|
|
|
|
|
268
|
$spec->{desc}; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
108
|
|
|
|
|
161
|
my @aliases = @{ $spec->{names} }; |
|
108
|
|
|
|
|
181
|
|
273
|
108
|
|
|
|
|
138
|
shift @aliases; |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
# Insert 'v' as an alias of 'verbose' if it is |
276
|
|
|
|
|
|
|
push @aliases, 'v' |
277
|
|
|
|
|
|
|
if $option eq 'verbose' |
278
|
108
|
100
|
66
|
|
|
286
|
&& $optspec->{$V_OPTSPEC} // '' eq $V_FOR_VERBOSE; |
|
|
|
33
|
|
|
|
|
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# Print aliases, if any |
281
|
108
|
100
|
|
|
|
240
|
if ( @aliases > 0 ) |
282
|
|
|
|
|
|
|
{ |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
# Add in the dashes |
285
|
57
|
50
|
|
|
|
73
|
@aliases = map { length() == 1 ? "-$_" : "--$_" } @aliases; |
|
59
|
|
|
|
|
160
|
|
286
|
57
|
|
|
|
|
154
|
$message .= sprintf "%${indent}s Aliases: %s\n", '', |
287
|
|
|
|
|
|
|
join( ', ', @aliases ); |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
# Print negation, if any |
291
|
108
|
100
|
|
|
|
226
|
if ( $spec->{boolean} ) |
292
|
|
|
|
|
|
|
{ |
293
|
1
|
|
|
|
|
6
|
$message .= sprintf "%${indent}s Negate this with --no-%s\n", '', |
294
|
|
|
|
|
|
|
$option; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
14
|
|
|
|
|
598
|
return $message; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
# Returns the "default" optspec, consisting of options |
302
|
|
|
|
|
|
|
# that CLI::Startup normally creates automatically. |
303
|
|
|
|
|
|
|
sub _get_default_optspec |
304
|
|
|
|
|
|
|
{ |
305
|
|
|
|
|
|
|
return { |
306
|
131
|
|
|
131
|
|
996
|
'help|h' => 'Print this help message', |
307
|
|
|
|
|
|
|
'rcfile:s' => 'Config file to load', |
308
|
|
|
|
|
|
|
'write-rcfile' => 'Write the current options to config file', |
309
|
|
|
|
|
|
|
'rcfile-format=s' => 'Format to write the config file', |
310
|
|
|
|
|
|
|
'version|V' => 'Print version information and exit', |
311
|
|
|
|
|
|
|
'verbose:1' => |
312
|
|
|
|
|
|
|
'Print verbose messages', # Supports --verbose or --verbose=9 |
313
|
|
|
|
|
|
|
$V_OPTSPEC => $V_FOR_VERBOSE, # 'v+' Supports -vvv |
314
|
|
|
|
|
|
|
'manpage|H' => 'Print the manpage for this script', |
315
|
|
|
|
|
|
|
}; |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
# Parse the optspecs, returning a complete description of each. |
319
|
|
|
|
|
|
|
sub _parse_optspecs |
320
|
|
|
|
|
|
|
{ |
321
|
137
|
|
|
137
|
|
210
|
my ( $self, $optspecs ) = @_; |
322
|
137
|
|
|
|
|
321
|
my $parsed = { options => {}, aliases => {} }; |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# Step through each option |
325
|
137
|
|
|
|
|
188
|
for my $optspec ( keys %{$optspecs} ) |
|
137
|
|
|
|
|
358
|
|
326
|
|
|
|
|
|
|
{ |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
# Parse the spec completely |
329
|
|
|
|
|
|
|
$parsed->{options}{$optspec} |
330
|
712
|
|
|
|
|
1177
|
= $self->_parse_spec( $optspec, $optspecs->{$optspec} ); |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
# Make a reverse-lookup by option name/alias |
333
|
712
|
|
|
|
|
984
|
for my $alias ( @{ $parsed->{options}{$optspec}{names} } ) |
|
712
|
|
|
|
|
1305
|
|
334
|
|
|
|
|
|
|
{ |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
# It's a fatal error to use the same alias twice |
337
|
|
|
|
|
|
|
$self->die("--$alias option defined twice") |
338
|
928
|
100
|
|
|
|
1533
|
if defined $parsed->{aliases}{$alias}; |
339
|
|
|
|
|
|
|
|
340
|
927
|
|
|
|
|
1732
|
$parsed->{aliases}{$alias} = $optspec; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
|
344
|
136
|
|
|
|
|
294
|
return $parsed; |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
# Parses the option specs, identifying array and hash data types |
348
|
|
|
|
|
|
|
sub _option_data_types |
349
|
|
|
|
|
|
|
{ |
350
|
51
|
|
|
51
|
|
89
|
my $self = shift; |
351
|
51
|
|
|
|
|
132
|
my $optspecs = $self->get_optspec; |
352
|
51
|
|
|
|
|
92
|
my %types; |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
# Build a list of the array and hash configs, so we can |
355
|
|
|
|
|
|
|
# unflatten them from the config file if necessary. |
356
|
51
|
|
|
|
|
85
|
for my $option ( keys %{$optspecs} ) |
|
51
|
|
|
|
|
211
|
|
357
|
|
|
|
|
|
|
{ |
358
|
565
|
|
|
|
|
943
|
my $spec = $self->_parse_spec( $option, $optspecs->{$option} ); |
359
|
|
|
|
|
|
|
|
360
|
565
|
|
|
|
|
940
|
for my $type (qw{ array hash boolean count flag }) |
361
|
|
|
|
|
|
|
{ |
362
|
2825
|
100
|
|
|
|
4484
|
next unless $spec->{$type}; |
363
|
364
|
|
|
|
|
363
|
$types{$_} = uc($type) for @{ $spec->{names} }; |
|
364
|
|
|
|
|
1367
|
|
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
|
367
|
51
|
|
|
|
|
190
|
return \%types; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
# Breaks an option spec down into its components. |
371
|
|
|
|
|
|
|
sub _parse_spec |
372
|
|
|
|
|
|
|
{ |
373
|
1503
|
|
|
1503
|
|
2300
|
my ( $self, $spec, $help_text ) = @_; |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
## no critic ( Perl::Critic::Policy::RegularExpressions::ProhibitComplexRegexes ) |
376
|
|
|
|
|
|
|
## no critic ( Perl::Critic::Policy::RegularExpressions::ProhibitUnusedCapture ) |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
# We really want the "name(s)" portion |
379
|
1503
|
|
|
|
|
14860
|
$spec =~ m{ |
380
|
|
|
|
|
|
|
(?: |
381
|
|
|
|
|
|
|
(?&start) |
382
|
|
|
|
|
|
|
(? (?&word_list) ) |
383
|
|
|
|
|
|
|
(?: |
384
|
|
|
|
|
|
|
(?: # Boolean |
385
|
|
|
|
|
|
|
(? (?&bang) (?&end) ) ) |
386
|
|
|
|
|
|
|
| (?: # Counter |
387
|
|
|
|
|
|
|
(? (?&optional)? ) |
388
|
|
|
|
|
|
|
(? (?&plus) (?&end) ) ) |
389
|
|
|
|
|
|
|
| (?: # Scalar types - number, integer, string |
390
|
|
|
|
|
|
|
(? (?&arg) ) |
391
|
|
|
|
|
|
|
(? (?&scalar_type) ) |
392
|
|
|
|
|
|
|
(? (?&non_scalar)? ) ) |
393
|
|
|
|
|
|
|
| (?: # Int with default argument |
394
|
|
|
|
|
|
|
(? (?&optional) ) |
395
|
|
|
|
|
|
|
(? (?&integer) ) ) |
396
|
|
|
|
|
|
|
| (?: # Flag |
397
|
|
|
|
|
|
|
(?&end) ) # Nothing to capture |
398
|
|
|
|
|
|
|
)? |
399
|
|
|
|
|
|
|
(? (?&unmatched)? ) |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
# This ensures that every token is defined, even if only |
402
|
|
|
|
|
|
|
# to the empty string. |
403
|
|
|
|
|
|
|
(? (?()) ) |
404
|
|
|
|
|
|
|
(? (?()) ) |
405
|
|
|
|
|
|
|
(? (?()) ) |
406
|
|
|
|
|
|
|
(? (?()) ) |
407
|
|
|
|
|
|
|
) |
408
|
|
|
|
|
|
|
(?(DEFINE) |
409
|
|
|
|
|
|
|
(? ^ ) |
410
|
|
|
|
|
|
|
(? (?: (?&word) (?: (?&separator) (?&alias) )* ) ) |
411
|
|
|
|
|
|
|
(? \w[-\w]* ) |
412
|
|
|
|
|
|
|
(? (?: [?] | (?&word) ) ) |
413
|
|
|
|
|
|
|
(? [|] ) |
414
|
|
|
|
|
|
|
(? (?: [fions] ) ) |
415
|
|
|
|
|
|
|
(? (?: -? \d+ ) ) |
416
|
|
|
|
|
|
|
(? [:=] ) |
417
|
|
|
|
|
|
|
(? [:] ) |
418
|
|
|
|
|
|
|
(? [=] ) |
419
|
|
|
|
|
|
|
(? [@%] ) |
420
|
|
|
|
|
|
|
(? [%] ) |
421
|
|
|
|
|
|
|
(? [@] ) |
422
|
|
|
|
|
|
|
(? [!] ) |
423
|
|
|
|
|
|
|
(? [+] ) |
424
|
|
|
|
|
|
|
(? (?! . ) ) |
425
|
|
|
|
|
|
|
(? (?: .* $ ) ) # This will be the last thing in an invalid spec |
426
|
|
|
|
|
|
|
) |
427
|
|
|
|
|
|
|
}xms; |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
# Capture the pieces of the optspec that we found |
430
|
1503
|
|
|
|
|
19756
|
my %attrs = %LAST_PAREN_MATCH; |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
# If there's anything left that we failed to match, it's a fatal error |
433
|
1503
|
50
|
|
|
|
5130
|
$self->die("Invalid optspec: $spec") if $attrs{garbage}; |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
## no critic ( ValuesAndExpressions::ProhibitNoisyQuotes Perl::Critic::Policy::ValuesAndExpressions::ProhibitMagicNumbers) |
436
|
|
|
|
|
|
|
#<< Leave this alone, perltidy |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
# Note: doesn't identify string, int, float options |
439
|
|
|
|
|
|
|
return { |
440
|
|
|
|
|
|
|
spec => $spec, |
441
|
|
|
|
|
|
|
names => [ split /[|]/xms, $attrs{names} ], |
442
|
|
|
|
|
|
|
desc => $help_text, |
443
|
|
|
|
|
|
|
default => $attrs{default}, |
444
|
|
|
|
|
|
|
required => ( $attrs{argument} eq '=' ? 1 : 0 ), |
445
|
|
|
|
|
|
|
type => ( |
446
|
|
|
|
|
|
|
$attrs{subtype} eq '' ? 'i' |
447
|
|
|
|
|
|
|
: $attrs{subtype} eq 'n' ? 'i' |
448
|
|
|
|
|
|
|
: $attrs{subtype} |
449
|
|
|
|
|
|
|
), |
450
|
|
|
|
|
|
|
array => ( $attrs{type} eq '@' ? 1 : 0 ), |
451
|
|
|
|
|
|
|
hash => ( $attrs{type} eq '%' ? 1 : 0 ), |
452
|
|
|
|
|
|
|
scalar => ( $attrs{type} !~ m{[@%]}xms ? 1 : 0 ), |
453
|
|
|
|
|
|
|
boolean => ( $attrs{type} eq '!' ? 1 : 0 ), |
454
|
|
|
|
|
|
|
count => ( $attrs{type} eq '+' ? 1 : 0 ), |
455
|
1503
|
100
|
100
|
|
|
16328
|
flag => ( $attrs{type} eq '' && $attrs{argument} eq '' ? 1 : 0 ), |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
456
|
|
|
|
|
|
|
}; |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
#>> End perltidy free zone |
459
|
|
|
|
|
|
|
} |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
# Returns a hash of option aliases and specifications from the |
462
|
|
|
|
|
|
|
# supplied hash. Also converts undef to 0 in $optspec. |
463
|
|
|
|
|
|
|
sub _option_aliases |
464
|
|
|
|
|
|
|
{ |
465
|
13
|
|
|
13
|
|
158
|
my ( $self, $optspec ) = @_; |
466
|
13
|
|
|
|
|
22
|
my %option_aliases; |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
# Make sure that there are no duplicated option names, |
469
|
|
|
|
|
|
|
# and that options with undefined help text are defined |
470
|
|
|
|
|
|
|
# to false. |
471
|
13
|
|
|
|
|
21
|
for my $option ( keys %{$optspec} ) |
|
13
|
|
|
|
|
76
|
|
472
|
|
|
|
|
|
|
{ |
473
|
104
|
|
50
|
|
|
201
|
$optspec->{$option} ||= 0; |
474
|
104
|
|
|
|
|
196
|
$option = $self->_parse_spec( $option, $optspec->{$option} ); |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
# The spec can define aliases |
477
|
104
|
|
|
|
|
161
|
for my $name ( @{ $option->{names} } ) |
|
104
|
|
|
|
|
173
|
|
478
|
|
|
|
|
|
|
{ |
479
|
|
|
|
|
|
|
$self->die("--$name option defined twice") |
480
|
143
|
50
|
|
|
|
230
|
if exists $option_aliases{$name}; |
481
|
143
|
|
|
|
|
265
|
$option_aliases{$name} = $option->{spec}; |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
|
485
|
13
|
|
|
|
|
160
|
return \%option_aliases; |
486
|
|
|
|
|
|
|
} |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
# Returns an options spec hashref, with automatic options |
489
|
|
|
|
|
|
|
# added in. |
490
|
|
|
|
|
|
|
sub _validate_optspec |
491
|
|
|
|
|
|
|
{ |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
# no critic ( Perl::Critic::Policy::Modules::ProhibitExcessMainComplexity ) |
494
|
69
|
|
|
69
|
|
129
|
my ( $self, $user_optspecs ) = @_; |
495
|
69
|
|
|
|
|
122
|
my $default_optspecs = $self->_get_default_optspec; |
496
|
|
|
|
|
|
|
|
497
|
69
|
|
|
|
|
650
|
my $parsed; |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
# Parse the user optspecs |
500
|
69
|
|
|
|
|
157
|
$parsed = $self->_parse_optspecs($user_optspecs); |
501
|
68
|
|
|
|
|
112
|
my $user_options = $parsed->{options}; |
502
|
68
|
|
|
|
|
82
|
my $user_aliases = $parsed->{aliases}; |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
# Parse the default optspecs |
505
|
68
|
|
|
|
|
118
|
$parsed = $self->_parse_optspecs($default_optspecs); |
506
|
68
|
|
|
|
|
97
|
my $default_options = $parsed->{options}; |
507
|
68
|
|
|
|
|
82
|
my $default_aliases = $parsed->{aliases}; |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
# While we're here, remember the "help" option settings for later. |
510
|
|
|
|
|
|
|
# If a tricksy user deletes it, we'll put it back. |
511
|
68
|
|
|
|
|
132
|
my $default_help_optspec = $default_aliases->{'help'}; |
512
|
68
|
|
|
|
|
104
|
my $default_help_parsed = $default_options->{$default_help_optspec}; |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
# At this point we also know that there are no conflicting aliases |
515
|
|
|
|
|
|
|
# in either the user or default optspecs. So the only thing to check |
516
|
|
|
|
|
|
|
# is whether the user invokes any of the default optspecs. |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
# Step through each user alias. Check for collisions, and also delete |
519
|
|
|
|
|
|
|
# any default options for which this was requested. |
520
|
68
|
|
|
|
|
84
|
for my $alias ( keys %{$user_aliases} ) |
|
68
|
|
|
|
|
171
|
|
521
|
|
|
|
|
|
|
{ |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
# Only look at options that collide with default options. |
524
|
177
|
100
|
|
|
|
373
|
next unless defined $default_aliases->{$alias}; |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
# If the option specifications are identical, then we can |
527
|
|
|
|
|
|
|
# skip this option. |
528
|
10
|
|
|
|
|
43
|
my $user_optspec = $user_aliases->{$alias}; |
529
|
10
|
|
|
|
|
14
|
my $default_optspec = $default_aliases->{$alias}; |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
# If the option evaluates to true, it MAY be changing something, |
532
|
|
|
|
|
|
|
# which is an error. |
533
|
10
|
100
|
100
|
|
|
44
|
if ( $user_optspecs->{$user_optspec} || 0 ) |
534
|
|
|
|
|
|
|
{ |
535
|
5
|
100
|
|
|
|
10
|
if ( $user_optspec ne $default_optspec ) |
536
|
|
|
|
|
|
|
{ |
537
|
4
|
|
|
|
|
13
|
$self->die("Multiple definitions for --$alias option"); |
538
|
|
|
|
|
|
|
} |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
# OK, this option is being deleted. |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
# If the alias was not the primary name of the default option, |
544
|
|
|
|
|
|
|
# then we delete only the specific alias requested. |
545
|
6
|
|
|
|
|
12
|
my $default_name = $default_options->{$default_optspec}{names}[0]; |
546
|
6
|
50
|
|
|
|
13
|
if ( $alias ne $default_name ) |
547
|
|
|
|
|
|
|
{ |
548
|
0
|
|
|
|
|
0
|
delete $default_aliases->{$alias}; |
549
|
0
|
|
|
|
|
0
|
next; |
550
|
|
|
|
|
|
|
} |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
# Completely delete the default options corresponding to this alias. |
553
|
6
|
|
|
|
|
9
|
for my $name ( @{ $default_options->{$default_optspec}{names} } ) |
|
6
|
|
|
|
|
12
|
|
554
|
|
|
|
|
|
|
{ |
555
|
8
|
|
|
|
|
15
|
delete $default_aliases->{$name}; |
556
|
|
|
|
|
|
|
} |
557
|
6
|
|
|
|
|
16
|
delete $default_options->{$default_optspec}; |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
# Special case: we use two options to cover 'verbose' |
560
|
6
|
100
|
66
|
|
|
22
|
if ( $alias eq 'verbose' |
561
|
|
|
|
|
|
|
and $default_optspecs->{$V_OPTSPEC} eq $V_FOR_VERBOSE ) |
562
|
|
|
|
|
|
|
{ |
563
|
2
|
|
|
|
|
21
|
delete $default_options->{ $default_aliases->{v} }; |
564
|
2
|
|
|
|
|
4
|
delete $default_aliases->{v}; |
565
|
|
|
|
|
|
|
} |
566
|
|
|
|
|
|
|
} |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
# Remove any disabled user options. Options are disabled by |
569
|
|
|
|
|
|
|
# setting them to anything that evaluates to false. |
570
|
64
|
|
|
|
|
101
|
for my $optspec ( keys %{$user_options} ) |
|
64
|
|
|
|
|
147
|
|
571
|
|
|
|
|
|
|
{ |
572
|
159
|
100
|
100
|
|
|
314
|
next if $user_options->{$optspec}{desc} || 0; |
573
|
|
|
|
|
|
|
|
574
|
5
|
|
|
|
|
6
|
for my $alias ( @{ $user_options->{$optspec}{names} } ) |
|
5
|
|
|
|
|
8
|
|
575
|
|
|
|
|
|
|
{ |
576
|
5
|
|
|
|
|
16
|
delete $user_aliases->{$alias}; |
577
|
|
|
|
|
|
|
} |
578
|
5
|
|
|
|
|
14
|
delete $user_options->{$optspec}; |
579
|
|
|
|
|
|
|
} |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
# Now we just check for ordinary collisions. Since we've performed any |
582
|
|
|
|
|
|
|
# requested deletions, any collisions between user and default aliases |
583
|
|
|
|
|
|
|
# means that an alias is defined twice. |
584
|
64
|
|
|
|
|
85
|
for my $name ( keys %{$user_aliases} ) |
|
64
|
|
|
|
|
119
|
|
585
|
|
|
|
|
|
|
{ |
586
|
166
|
50
|
|
|
|
259
|
next unless defined $default_aliases->{$name}; |
587
|
|
|
|
|
|
|
|
588
|
0
|
|
|
|
|
0
|
$self->die("Multiple definitions for --$name option"); |
589
|
|
|
|
|
|
|
} |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
# The --help option is NOT optional, so we override it if it evaluates |
592
|
|
|
|
|
|
|
# to false. It must be present, because if we didn't find it above we |
593
|
|
|
|
|
|
|
# would have inserted it. |
594
|
64
|
50
|
|
|
|
125
|
if ( not defined $user_aliases->{'help'} ) |
595
|
|
|
|
|
|
|
{ |
596
|
64
|
|
|
|
|
127
|
$user_options->{$default_help_optspec} = $default_help_parsed; |
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
# If the --rcfile option is disabled, then we must also delete the |
600
|
|
|
|
|
|
|
# --rcfile-format and --write-rcfile options, since they make no |
601
|
|
|
|
|
|
|
# sense in scripts that don't support config files. |
602
|
64
|
50
|
|
|
|
112
|
if ( not defined $user_aliases->{rcfile} ) |
603
|
|
|
|
|
|
|
{ |
604
|
64
|
|
|
|
|
121
|
for my $option (qw{ rcfile rcfile-format write-rcfile }) |
605
|
|
|
|
|
|
|
{ |
606
|
192
|
50
|
|
|
|
314
|
if ( defined $user_aliases->{$option} ) |
607
|
|
|
|
|
|
|
{ |
608
|
0
|
|
|
|
|
0
|
delete $user_options->{ $user_aliases->{$option} }; |
609
|
0
|
|
|
|
|
0
|
delete $user_aliases->{$option}; |
610
|
|
|
|
|
|
|
} |
611
|
|
|
|
|
|
|
} |
612
|
|
|
|
|
|
|
} |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
# If rcfile writing is disabled, then we must delete the --rcfile-format |
615
|
|
|
|
|
|
|
# option, which is meaningless when we don't write config files. |
616
|
64
|
50
|
|
|
|
119
|
if ( not defined $user_aliases->{'write-rcfile'} ) |
617
|
|
|
|
|
|
|
{ |
618
|
64
|
50
|
|
|
|
108
|
if ( defined $user_aliases->{'rcfile-format'} ) |
619
|
|
|
|
|
|
|
{ |
620
|
0
|
|
|
|
|
0
|
delete $user_options->{ $user_aliases->{'rcfile-format'} }; |
621
|
0
|
|
|
|
|
0
|
delete $user_aliases->{'rcfile-format'}; |
622
|
|
|
|
|
|
|
} |
623
|
|
|
|
|
|
|
} |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
# Create a new optspec which includes both the user and default options. |
626
|
64
|
|
|
|
|
105
|
my $optspecs = {}; |
627
|
|
|
|
|
|
|
|
628
|
64
|
|
|
|
|
81
|
for my $optspec ( keys %{$default_options} ) |
|
64
|
|
|
|
|
169
|
|
629
|
|
|
|
|
|
|
{ |
630
|
504
|
|
|
|
|
791
|
$optspecs->{$optspec} = $default_options->{$optspec}{desc}; |
631
|
|
|
|
|
|
|
} |
632
|
|
|
|
|
|
|
|
633
|
64
|
|
|
|
|
98
|
for my $optspec ( keys %{$user_options} ) |
|
64
|
|
|
|
|
133
|
|
634
|
|
|
|
|
|
|
{ |
635
|
218
|
|
|
|
|
323
|
$optspecs->{$optspec} = $user_options->{$optspec}{desc}; |
636
|
|
|
|
|
|
|
} |
637
|
|
|
|
|
|
|
|
638
|
64
|
|
|
|
|
1891
|
return $optspecs; |
639
|
|
|
|
|
|
|
} |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
# This is the core method of the whole module: it actually does the |
642
|
|
|
|
|
|
|
# command-line processing, config-file reading, etc.. Once it |
643
|
|
|
|
|
|
|
# completes, most of the write accesors are disabled, and this |
644
|
|
|
|
|
|
|
# object becomes a reference for looking up configuration info. |
645
|
|
|
|
|
|
|
sub init |
646
|
|
|
|
|
|
|
{ |
647
|
61
|
|
|
61
|
1
|
17456
|
my $self = shift; |
648
|
|
|
|
|
|
|
|
649
|
61
|
100
|
|
|
|
139
|
$self->die('init() method takes no arguments') if @_; |
650
|
60
|
100
|
|
|
|
138
|
$self->die('init() called a second time') |
651
|
|
|
|
|
|
|
if $self->get_initialized; |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
# It's a fatal error to call init() without defining any |
654
|
|
|
|
|
|
|
# command-line options |
655
|
59
|
50
|
50
|
|
|
299
|
$self->die('init() called without defining any command-line options') |
656
|
|
|
|
|
|
|
unless $self->get_optspec || 0; |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
# Parse command-line options, then read the config file if any. |
659
|
59
|
|
|
|
|
214
|
my $options = $self->_process_command_line; |
660
|
49
|
|
|
|
|
135
|
my $config = $self->_read_config_file; |
661
|
49
|
|
|
|
|
239
|
my $default = $self->get_default_settings; |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
# Save the unprocessed command-line options |
664
|
49
|
|
|
|
|
670
|
$raw_options_of{ ident $self} = clone($options); |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
# Now, combine the command options, the config-file defaults, |
667
|
|
|
|
|
|
|
# and the wired-in app defaults, in that order of precedence. |
668
|
98
|
|
|
98
|
|
5331
|
$options = reduce { merge( $a, $b ) } |
669
|
49
|
|
|
|
|
554
|
( $options, $config->{default}, $default ); |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
# Add a 'verbose' option that evaluates to false if there isn't |
672
|
|
|
|
|
|
|
# already one in $options. |
673
|
49
|
|
50
|
|
|
2171
|
$options->{verbose} //= 0; |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
# Consolidate the 'v' and 'verbose' options if the default |
676
|
|
|
|
|
|
|
# options are in play here. |
677
|
49
|
50
|
|
|
|
211
|
if ( $self->_get_default_optspec->{$V_OPTSPEC} eq $V_FOR_VERBOSE ) |
678
|
|
|
|
|
|
|
{ |
679
|
49
|
50
|
|
|
|
1000
|
if ( defined $options->{v} ) |
680
|
|
|
|
|
|
|
{ |
681
|
0
|
|
|
|
|
0
|
$options->{verbose} += delete $options->{v}; |
682
|
|
|
|
|
|
|
} |
683
|
|
|
|
|
|
|
} |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
# Save the fully-processed options |
686
|
49
|
|
|
|
|
835
|
$options_of{ ident $self} = clone($options); |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
# Mark the object as initialized |
689
|
49
|
|
|
|
|
193
|
$initialized_of{ ident $self} = 1; |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
# |
692
|
|
|
|
|
|
|
# Automatically processed options: |
693
|
|
|
|
|
|
|
# |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
# Print the version information, if requested |
696
|
49
|
100
|
|
|
|
130
|
$self->print_version if $options->{version}; |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
# Print the POD manpage from the script, if requested |
699
|
47
|
100
|
|
|
|
99
|
$self->print_manpage if $options->{manpage}; |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
# Write back the config if requested |
702
|
45
|
100
|
|
|
|
134
|
$self->write_rcfile() if $options->{'write-rcfile'}; |
703
|
|
|
|
|
|
|
|
704
|
35
|
|
|
|
|
178
|
return; |
705
|
|
|
|
|
|
|
} |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
sub _process_command_line |
708
|
|
|
|
|
|
|
{ |
709
|
59
|
|
|
59
|
|
85
|
my $self = shift; |
710
|
59
|
|
|
|
|
90
|
my $optspec = $self->get_optspec; |
711
|
59
|
|
|
|
|
95
|
my %options; |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
# Parse the command line and die if anything is wrong. |
714
|
59
|
|
|
|
|
107
|
my $opts_ok = GetOptionsFromArray( \@ARGV, \%options, keys %{$optspec} ); |
|
59
|
|
|
|
|
379
|
|
715
|
|
|
|
|
|
|
|
716
|
59
|
100
|
|
|
|
37426
|
if ( $options{help} ) |
|
|
100
|
|
|
|
|
|
717
|
|
|
|
|
|
|
{ |
718
|
5
|
|
|
|
|
12
|
print $self->_usage_message(); |
719
|
5
|
|
|
|
|
31
|
exit 0; |
720
|
|
|
|
|
|
|
} |
721
|
|
|
|
|
|
|
elsif ( !$opts_ok ) |
722
|
|
|
|
|
|
|
{ |
723
|
4
|
|
|
|
|
16
|
$self->die_usage(); |
724
|
|
|
|
|
|
|
} |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
# Treat array and hash options as CSV records, so we can |
727
|
|
|
|
|
|
|
# cope with quoting and values containing commas. |
728
|
50
|
|
|
|
|
494
|
my $csv = Text::CSV->new( { allow_loose_quotes => 1 } ); |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
# Further process the array and hash options |
731
|
50
|
|
|
|
|
7418
|
for my $option ( keys %options ) |
732
|
|
|
|
|
|
|
{ |
733
|
41
|
100
|
|
|
|
164
|
if ( ref $options{$option} eq 'ARRAY' ) |
|
|
100
|
|
|
|
|
|
734
|
|
|
|
|
|
|
{ |
735
|
2
|
|
|
|
|
3
|
my @values; |
736
|
2
|
|
|
|
|
3
|
for my $value ( @{ $options{$option} } ) |
|
2
|
|
|
|
|
4
|
|
737
|
|
|
|
|
|
|
{ |
738
|
5
|
100
|
|
|
|
32
|
$csv->parse($value) |
739
|
|
|
|
|
|
|
or $self->die_usage( |
740
|
|
|
|
|
|
|
"Can't parse --$option option \"$value\": " |
741
|
|
|
|
|
|
|
. $csv->error_diag ); |
742
|
4
|
|
|
|
|
126
|
push @values, $csv->fields; |
743
|
|
|
|
|
|
|
} |
744
|
|
|
|
|
|
|
|
745
|
1
|
|
|
|
|
9
|
$options{$option} = \@values; |
746
|
|
|
|
|
|
|
} |
747
|
|
|
|
|
|
|
elsif ( ref $options{$option} eq 'HASH' ) |
748
|
|
|
|
|
|
|
{ |
749
|
2
|
|
|
|
|
3
|
my $hash = $options{$option}; |
750
|
2
|
|
|
|
|
4
|
for my $key ( keys %{$hash} ) |
|
2
|
|
|
|
|
3
|
|
751
|
|
|
|
|
|
|
{ |
752
|
|
|
|
|
|
|
# Extract each value and tech for embedded name/value |
753
|
|
|
|
|
|
|
# pairs. We only go one level deep. |
754
|
4
|
|
|
|
|
18
|
my $value = $hash->{$key}; |
755
|
4
|
50
|
|
|
|
9
|
$csv->parse($value) |
756
|
|
|
|
|
|
|
or $self->die_usage( |
757
|
|
|
|
|
|
|
"Can't parse --$option option \"$value\": " |
758
|
|
|
|
|
|
|
. $csv->error_diag ); |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
# If there's only one field, nothing to do |
761
|
4
|
100
|
|
|
|
78
|
next if ( $csv->fields == 1 ); |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
# Pick off the first value |
764
|
1
|
|
|
|
|
8
|
my @values = $csv->fields; |
765
|
1
|
|
|
|
|
7
|
$hash->{$key} = shift @values; |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
# Now parse the rest |
768
|
1
|
|
|
|
|
2
|
for my $value (@values) |
769
|
|
|
|
|
|
|
{ |
770
|
3
|
|
|
|
|
11
|
my ( $k, $v ) = $value =~ m/^ ([^=]+) = (.*) $/xmsg; |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
# Check for collision |
773
|
|
|
|
|
|
|
carp "Redefined option value: $k" |
774
|
3
|
50
|
|
|
|
6
|
if defined $hash->{$k}; |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
# Set the value |
777
|
3
|
|
|
|
|
7
|
$hash->{$k} = $v; |
778
|
|
|
|
|
|
|
} |
779
|
|
|
|
|
|
|
} |
780
|
|
|
|
|
|
|
} |
781
|
|
|
|
|
|
|
} |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
# Process the rcfile option immediately, to override any settings |
784
|
|
|
|
|
|
|
# hard-wired in the app, as well as this module's defaults. If the |
785
|
|
|
|
|
|
|
# rcfile has already been set to a false value, however, then this |
786
|
|
|
|
|
|
|
# option is disallowed. |
787
|
49
|
100
|
|
|
|
142
|
$self->set_rcfile( $options{rcfile} ) if defined $options{rcfile}; |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
# That's it! |
790
|
49
|
|
|
|
|
348
|
return \%options; |
791
|
|
|
|
|
|
|
} |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
sub _read_config_file |
794
|
|
|
|
|
|
|
{ |
795
|
49
|
|
|
49
|
|
76
|
my $self = shift; |
796
|
49
|
|
|
|
|
144
|
my $types = $self->_option_data_types; |
797
|
49
|
|
100
|
|
|
150
|
my $rcfile = $self->get_rcfile || ''; |
798
|
49
|
|
|
|
|
375
|
my $options = { |
799
|
|
|
|
|
|
|
files => [$rcfile], |
800
|
|
|
|
|
|
|
use_ext => 0, |
801
|
|
|
|
|
|
|
force_plugins => [ qw{ |
802
|
|
|
|
|
|
|
Config::Any::INI Config::Any::XML Config::Any::YAML |
803
|
|
|
|
|
|
|
Config::Any::JSON Config::Any::Perl |
804
|
|
|
|
|
|
|
} |
805
|
|
|
|
|
|
|
], |
806
|
|
|
|
|
|
|
}; |
807
|
|
|
|
|
|
|
|
808
|
49
|
|
|
|
|
63
|
my $raw_config; |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
# Attempt to parse the file, if any |
811
|
49
|
100
|
100
|
|
|
972
|
if ( $rcfile && -r $rcfile ) |
812
|
|
|
|
|
|
|
{ |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
# Defend against badly configured parsers. I'm looking |
815
|
|
|
|
|
|
|
# at YOU, XML::SAX! |
816
|
|
|
|
|
|
|
local $SIG{__WARN__} = sub { |
817
|
16
|
|
|
16
|
|
155760
|
my @args = @_; |
818
|
|
|
|
|
|
|
|
819
|
16
|
|
|
|
|
49
|
for my $arg (@args) |
820
|
|
|
|
|
|
|
{ |
821
|
16
|
50
|
|
|
|
76
|
next if ref $arg; |
822
|
16
|
50
|
|
|
|
183
|
return if $arg =~ /Unable to recognise encoding/ms; |
823
|
0
|
0
|
|
|
|
0
|
return if $arg =~ /ParserDetails[.]ini/xms; |
824
|
|
|
|
|
|
|
} |
825
|
|
|
|
|
|
|
|
826
|
0
|
|
|
|
|
0
|
CORE::warn(@args); |
827
|
26
|
|
|
|
|
239
|
}; |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
# OK, NOW load the files. |
830
|
26
|
|
|
|
|
204
|
my $files = Config::Any->load_files($options); |
831
|
26
|
|
50
|
|
|
64753
|
$files = shift @{$files} || {}; |
832
|
26
|
|
50
|
|
|
605
|
$raw_config = $files->{$rcfile} || {}; |
833
|
|
|
|
|
|
|
} |
834
|
|
|
|
|
|
|
else |
835
|
|
|
|
|
|
|
{ |
836
|
23
|
|
|
|
|
57
|
$raw_config = {}; |
837
|
|
|
|
|
|
|
} |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
# Initialize an empty config |
840
|
49
|
|
|
|
|
170
|
my $config = { default => {} }; |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
# Copy in the default section, if there is one. |
843
|
49
|
100
|
|
|
|
168
|
if ( defined $raw_config->{default} ) |
844
|
|
|
|
|
|
|
{ |
845
|
20
|
50
|
|
|
|
105
|
if ( ref $raw_config->{default} ne 'HASH' ) |
846
|
|
|
|
|
|
|
{ |
847
|
0
|
|
|
|
|
0
|
$self->die('Config file\'s "default" setting isn\'t a hash!'); |
848
|
|
|
|
|
|
|
} |
849
|
|
|
|
|
|
|
else |
850
|
|
|
|
|
|
|
{ |
851
|
20
|
|
|
|
|
103
|
$config->{default} = delete $raw_config->{default}; |
852
|
|
|
|
|
|
|
} |
853
|
|
|
|
|
|
|
} |
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
# Now parse strings if they're supposed to be hashes or arrays. |
856
|
|
|
|
|
|
|
# This is basically a fix for file formats like INI, that can't |
857
|
|
|
|
|
|
|
# encode data structures. |
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
# Step through the config, moving any scalars we see into the |
860
|
|
|
|
|
|
|
# default section. |
861
|
49
|
|
|
|
|
104
|
for my $key ( keys %{$raw_config} ) |
|
49
|
|
|
|
|
195
|
|
862
|
|
|
|
|
|
|
{ |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
# We expect a hash, with a "default" section, but if there |
865
|
|
|
|
|
|
|
# isn't one, or there are naked options, then we treat them |
866
|
|
|
|
|
|
|
# as defaults. |
867
|
25
|
100
|
|
|
|
114
|
if ( not ref $raw_config->{$key} ) |
868
|
|
|
|
|
|
|
{ |
869
|
8
|
|
|
|
|
20
|
$config->{default}{$key} = delete $raw_config->{$key}; |
870
|
8
|
|
|
|
|
19
|
next; |
871
|
|
|
|
|
|
|
} |
872
|
|
|
|
|
|
|
else |
873
|
|
|
|
|
|
|
{ |
874
|
17
|
|
|
|
|
50
|
$config->{$key} = delete $raw_config->{$key}; |
875
|
|
|
|
|
|
|
} |
876
|
|
|
|
|
|
|
} |
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
# Now step through the default section, turning scalars into |
879
|
|
|
|
|
|
|
# arrays and hashes as necessary. |
880
|
49
|
|
|
|
|
68
|
for my $option ( keys %{ $config->{default} } ) |
|
49
|
|
|
|
|
133
|
|
881
|
|
|
|
|
|
|
{ |
882
|
123
|
|
|
|
|
224
|
my $value = $config->{default}{$option}; |
883
|
123
|
|
|
|
|
268
|
$value = $self->_parse_setting( $value, $option, $types ); |
884
|
|
|
|
|
|
|
|
885
|
123
|
|
|
|
|
239
|
$config->{default}{$option} = $value; |
886
|
|
|
|
|
|
|
} |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
# Save the cleaned-up config for reference |
889
|
49
|
|
|
|
|
262
|
$config_of{ ident $self} = $config; |
890
|
|
|
|
|
|
|
|
891
|
49
|
|
|
|
|
707
|
return $config; |
892
|
|
|
|
|
|
|
} |
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
# Convert string values into an arrayref or hashref as needed |
895
|
|
|
|
|
|
|
sub _parse_setting |
896
|
|
|
|
|
|
|
{ |
897
|
123
|
|
|
123
|
|
260
|
my ( $self, $value, $option, $types ) = @_; |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
# If the data is the right type, or we have no spec, nothing to do. |
900
|
123
|
|
100
|
|
|
412
|
my $type = $types->{$option} || 'NONE'; |
901
|
123
|
100
|
100
|
|
|
609
|
return $value if ref $value eq $type or $type eq 'NONE'; |
902
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
# All other data types we support are scalars. |
904
|
45
|
50
|
|
|
|
93
|
$self->die("Bad data type for \"$option\" option in config file.") |
905
|
|
|
|
|
|
|
if ref $value; |
906
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
# Boolean or flags are converted to boolean. Booleans are just |
908
|
|
|
|
|
|
|
# negatable flags. |
909
|
45
|
100
|
100
|
|
|
203
|
if ( $type eq 'BOOLEAN' or $type eq 'FLAG' ) |
910
|
|
|
|
|
|
|
{ |
911
|
35
|
100
|
50
|
|
|
121
|
return $value // 0 ? 1 : 0; |
912
|
|
|
|
|
|
|
} |
913
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
# Counters are integer-valued |
915
|
10
|
50
|
|
|
|
22
|
if ( $type eq 'COUNT' ) |
916
|
|
|
|
|
|
|
{ |
917
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
# All other data types we support are scalars. |
919
|
0
|
0
|
|
|
|
0
|
$self->die( |
920
|
|
|
|
|
|
|
"Invalid value \"$value\" for option \"$option\" in config file.") |
921
|
|
|
|
|
|
|
if $value !~ /^ \d+ $/xms; |
922
|
|
|
|
|
|
|
|
923
|
0
|
|
|
|
|
0
|
return $value; |
924
|
|
|
|
|
|
|
} |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
# The only fix we implement is to parse CSV and primitive name/value |
927
|
|
|
|
|
|
|
# pairs. |
928
|
10
|
|
|
|
|
44
|
my $csv = Text::CSV->new( { |
929
|
|
|
|
|
|
|
allow_loose_quotes => 1, |
930
|
|
|
|
|
|
|
allow_whitespace => 1, |
931
|
|
|
|
|
|
|
} ); |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
# Start by turning the string to an array |
934
|
10
|
|
|
|
|
1189
|
$csv->parse($value); |
935
|
10
|
|
|
|
|
357
|
$value = [ $csv->fields ]; |
936
|
10
|
100
|
|
|
|
114
|
return $value if $type eq 'ARRAY'; |
937
|
|
|
|
|
|
|
|
938
|
6
|
|
|
|
|
9
|
my %hash; |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
# Now it has to be a hash, so we need to split the values |
941
|
|
|
|
|
|
|
# on equal signs or colons. |
942
|
|
|
|
|
|
|
|
943
|
6
|
|
|
|
|
9
|
for ( @{$value} ) |
|
6
|
|
|
|
|
12
|
|
944
|
|
|
|
|
|
|
{ |
945
|
12
|
|
|
|
|
79
|
my ( $key, $val ) = m/^([^=:]+)(?:\s*[:=]\s*)?(.*)$/xms; |
946
|
12
|
|
|
|
|
47
|
$hash{$key} = $val; |
947
|
|
|
|
|
|
|
} |
948
|
|
|
|
|
|
|
|
949
|
6
|
|
|
|
|
36
|
return \%hash; |
950
|
|
|
|
|
|
|
} |
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
# Constructor for this object. |
953
|
|
|
|
|
|
|
sub BUILD |
954
|
|
|
|
|
|
|
{ |
955
|
67
|
|
|
67
|
1
|
42302
|
my ( $self, undef, $argref ) = @_; |
956
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
# Shorthand: { options => \%options } can be |
958
|
|
|
|
|
|
|
# abbreviated \%options. |
959
|
67
|
100
|
|
|
|
204
|
if ( not exists $argref->{options} ) |
960
|
|
|
|
|
|
|
{ |
961
|
30
|
|
|
|
|
65
|
$argref = { options => $argref }; |
962
|
|
|
|
|
|
|
} |
963
|
67
|
|
100
|
|
|
247
|
$self->set_optspec( $argref->{options} || {} ); |
964
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
# Caller can specify default settings for all options. |
966
|
|
|
|
|
|
|
$self->set_default_settings( $argref->{default_settings} ) |
967
|
62
|
100
|
|
|
|
132
|
if exists $argref->{default_settings}; |
968
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
## no critic ( ValuesAndExpressions::ProhibitNoisyQuotes ) |
970
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
# Setting rcfile to undef in the constructor disables rcfile reading |
972
|
|
|
|
|
|
|
# for the script. |
973
|
|
|
|
|
|
|
$self->set_rcfile( |
974
|
|
|
|
|
|
|
exists $argref->{rcfile} |
975
|
|
|
|
|
|
|
? $argref->{rcfile} |
976
|
62
|
100
|
|
|
|
329
|
: File::HomeDir->my_home . '/.' . basename($PROGRAM_NAME) . 'rc' |
977
|
|
|
|
|
|
|
); |
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
# Caller can forbid writing of rcfiles by setting |
980
|
|
|
|
|
|
|
# the write_rcfile option to undef, or can supply |
981
|
|
|
|
|
|
|
# a coderef to do the writing. |
982
|
62
|
100
|
|
|
|
144
|
if ( exists $argref->{write_rcfile} ) |
983
|
|
|
|
|
|
|
{ |
984
|
2
|
|
|
|
|
10
|
$self->set_write_rcfile( $argref->{write_rcfile} ); |
985
|
|
|
|
|
|
|
} |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
# Set an optional usage message for the script. |
988
|
|
|
|
|
|
|
$self->set_usage( |
989
|
|
|
|
|
|
|
exists $argref->{usage} |
990
|
|
|
|
|
|
|
? $argref->{usage} |
991
|
62
|
100
|
|
|
|
172
|
: '[options]' |
992
|
|
|
|
|
|
|
); |
993
|
|
|
|
|
|
|
|
994
|
62
|
|
|
|
|
123
|
return; |
995
|
|
|
|
|
|
|
} |
996
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
# Destructor. Nothing much to do, but without it we get |
998
|
|
|
|
|
|
|
# a warning about CLI::Startup::DEMOLISH only being used |
999
|
|
|
|
|
|
|
# once by Class::Std. |
1000
|
|
|
|
|
|
|
sub DEMOLISH |
1001
|
|
|
|
66
|
1
|
|
{ |
1002
|
|
|
|
|
|
|
} |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
# Prints out the POD contained in the script file, if any. |
1005
|
|
|
|
|
|
|
sub print_manpage |
1006
|
|
|
|
|
|
|
{ |
1007
|
2
|
|
|
2
|
1
|
3
|
my $self = shift; |
1008
|
2
|
|
|
|
|
18
|
my $parser = Pod::Text->new; |
1009
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
# Print SOMETHING... |
1011
|
2
|
|
|
|
|
365
|
$parser->output_fh(*STDOUT); |
1012
|
2
|
|
|
|
|
36
|
$parser->parse_file($PROGRAM_NAME); |
1013
|
2
|
100
|
|
|
|
10252
|
print $self->_usage_message() unless $parser->content_seen; |
1014
|
|
|
|
|
|
|
|
1015
|
2
|
|
|
|
|
20
|
exit 0; |
1016
|
|
|
|
|
|
|
} |
1017
|
|
|
|
|
|
|
|
1018
|
|
|
|
|
|
|
# Prints the version of the script. |
1019
|
|
|
|
|
|
|
sub print_version |
1020
|
|
|
|
|
|
|
{ |
1021
|
2
|
|
100
|
2
|
1
|
6
|
my $version = $::VERSION || 'UNKNOWN'; |
1022
|
2
|
|
|
|
|
48
|
my $name = basename($PROGRAM_NAME); |
1023
|
|
|
|
|
|
|
|
1024
|
2
|
|
|
|
|
4
|
print { \*STDERR } <<"EOF"; |
|
2
|
|
|
|
|
155
|
|
1025
|
|
|
|
|
|
|
This is $name, version $version |
1026
|
|
|
|
|
|
|
path: $PROGRAM_NAME |
1027
|
|
|
|
|
|
|
perl: $PERL_VERSION |
1028
|
|
|
|
|
|
|
EOF |
1029
|
2
|
|
|
|
|
11
|
exit 0; |
1030
|
|
|
|
|
|
|
} |
1031
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
# Print a nicely-formatted warning message. |
1033
|
|
|
|
|
|
|
sub warn ## no critic ( Subroutines::RequireFinalReturn ) |
1034
|
|
|
|
|
|
|
{ |
1035
|
1
|
|
|
1
|
1
|
1294
|
my ( undef, $msg ) = @_; |
1036
|
|
|
|
|
|
|
|
1037
|
1
|
|
|
|
|
22
|
my $name = basename($PROGRAM_NAME); |
1038
|
1
|
|
|
|
|
11
|
CORE::warn "$name: WARNING: $msg\n"; |
1039
|
|
|
|
|
|
|
} |
1040
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
# Writes the config file in the specified format. |
1042
|
|
|
|
|
|
|
sub write_rcfile |
1043
|
|
|
|
|
|
|
{ |
1044
|
12
|
|
|
12
|
1
|
312
|
my $self = shift; |
1045
|
12
|
|
66
|
|
|
93
|
my $file = shift || $self->get_rcfile; |
1046
|
|
|
|
|
|
|
|
1047
|
|
|
|
|
|
|
# It's a fatal error to call write_rcfile() before init() |
1048
|
12
|
100
|
|
|
|
94
|
$self->die('write_rcfile() called before init()') |
1049
|
|
|
|
|
|
|
unless $self->get_initialized; |
1050
|
|
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
# If there's no file to write, abort. |
1052
|
11
|
100
|
|
|
|
148
|
$self->die('can\'t write rcfile: no file specified') unless $file; |
1053
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
# Check whether a writer has been set |
1055
|
8
|
|
|
|
|
44
|
my $writer = $self->_choose_rcfile_writer; |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
# If there's a writer, call it. |
1058
|
8
|
100
|
|
|
|
30
|
if ( ref $writer eq 'CODE' ) |
1059
|
|
|
|
|
|
|
{ |
1060
|
7
|
|
|
|
|
27
|
$writer->( $self, $file ); |
1061
|
|
|
|
|
|
|
} |
1062
|
|
|
|
|
|
|
else |
1063
|
|
|
|
|
|
|
{ |
1064
|
1
|
|
|
|
|
4
|
$self->die('write_rcfile() disabled, but called anyway'); |
1065
|
|
|
|
|
|
|
} |
1066
|
|
|
|
|
|
|
|
1067
|
7
|
|
|
|
|
118
|
exit 0; |
1068
|
|
|
|
|
|
|
} |
1069
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
# Returns a hashref that looks like a config file's contents, with |
1071
|
|
|
|
|
|
|
# the defaults overwritten by the options used for the current |
1072
|
|
|
|
|
|
|
# invocation of the script. |
1073
|
|
|
|
|
|
|
sub get_options_as_defaults |
1074
|
|
|
|
|
|
|
{ |
1075
|
11
|
|
|
11
|
1
|
27
|
my $self = shift; |
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
# Collate the settings for writing |
1078
|
11
|
|
|
|
|
66
|
my $settings = $self->get_config; |
1079
|
11
|
|
|
|
|
53
|
my $options = $self->get_raw_options; |
1080
|
11
|
|
|
|
|
61
|
my $default = $self->get_default_settings; |
1081
|
11
|
|
|
|
|
50
|
my $default_aliases |
1082
|
|
|
|
|
|
|
= $self->_option_aliases( $self->_get_default_optspec ); |
1083
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
# Copy the current options back into the "default" group |
1085
|
22
|
|
|
22
|
|
894
|
$settings->{default} = reduce { merge( $a, $b ) } |
1086
|
11
|
|
|
|
|
160
|
( $options, $settings->{default}, $default ); |
1087
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
# Delete settings for the automatically-generated options; none of them |
1089
|
|
|
|
|
|
|
# belong in the rcfile. |
1090
|
11
|
|
|
|
|
473
|
for my $option ( keys %{$default_aliases} ) |
|
11
|
|
|
|
|
69
|
|
1091
|
|
|
|
|
|
|
{ |
1092
|
121
|
|
|
|
|
149
|
delete $settings->{default}{$option}; |
1093
|
|
|
|
|
|
|
} |
1094
|
|
|
|
|
|
|
|
1095
|
11
|
|
|
|
|
179
|
return $settings; |
1096
|
|
|
|
|
|
|
} |
1097
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
# Choose the correct built-in config writer based on the current |
1099
|
|
|
|
|
|
|
# value of --rcfile-format. |
1100
|
|
|
|
|
|
|
sub _choose_rcfile_writer |
1101
|
|
|
|
|
|
|
{ |
1102
|
8
|
|
|
8
|
|
18
|
my $self = shift; |
1103
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
# If a writer was specified by the user, we don't have to think. |
1105
|
|
|
|
|
|
|
# If it evaluates to false, or isn't a coderef, write_rcfile() |
1106
|
|
|
|
|
|
|
# will abort with an error. |
1107
|
8
|
100
|
|
|
|
52
|
if ( exists $write_rcfile_of{ ident $self} ) |
1108
|
|
|
|
|
|
|
{ |
1109
|
2
|
|
|
|
|
6
|
return $write_rcfile_of{ ident $self}; |
1110
|
|
|
|
|
|
|
} |
1111
|
|
|
|
|
|
|
|
1112
|
6
|
|
|
|
|
141
|
my $writer = { |
1113
|
|
|
|
|
|
|
INI => \&_write_rcfile_ini, |
1114
|
|
|
|
|
|
|
XML => \&_write_rcfile_xml, |
1115
|
|
|
|
|
|
|
JSON => \&_write_rcfile_json, |
1116
|
|
|
|
|
|
|
YAML => \&_write_rcfile_yaml, |
1117
|
|
|
|
|
|
|
PERL => \&_write_rcfile_perl, |
1118
|
|
|
|
|
|
|
}; |
1119
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
# Decide what the default should be: INI falling back on Perl |
1121
|
6
|
|
|
4
|
|
748
|
eval 'use Config::INI::Writer'; |
|
4
|
|
|
|
|
46
|
|
|
4
|
|
|
|
|
16
|
|
|
4
|
|
|
|
|
79
|
|
1122
|
6
|
50
|
|
|
|
56
|
my $default = $EVAL_ERROR ? 'PERL' : 'INI'; |
1123
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
# Check whether a file format was specified; if not, use the default. |
1125
|
6
|
|
|
|
|
50
|
my $options = $self->get_options; |
1126
|
6
|
|
33
|
|
|
65
|
my $format = uc( $options->{'rcfile-format'} || $default ); |
1127
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
$self->die("Unknown --rcfile-format option specified: \"$format\"") |
1129
|
6
|
50
|
|
|
|
18
|
unless defined $writer->{$format}; |
1130
|
|
|
|
|
|
|
|
1131
|
6
|
|
|
|
|
28
|
return $writer->{$format}; |
1132
|
|
|
|
|
|
|
} |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
# Write the current settings to an INI file. Serialize hash and array |
1135
|
|
|
|
|
|
|
# values for known command-line options. Leave everything else alone. |
1136
|
|
|
|
|
|
|
sub _write_rcfile_ini |
1137
|
|
|
|
|
|
|
{ |
1138
|
2
|
|
|
2
|
|
16278
|
my ( $self, $file ) = @_; |
1139
|
|
|
|
|
|
|
|
1140
|
|
|
|
|
|
|
# Installing the INI module is optional |
1141
|
2
|
|
|
2
|
|
122
|
eval 'use Config::INI::Writer'; |
|
2
|
|
|
|
|
20
|
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
25
|
|
1142
|
2
|
50
|
|
|
|
11
|
$self->die('Can\'t write rcfile: Config::INI::Writer is not installed.') |
1143
|
|
|
|
|
|
|
if $EVAL_ERROR; |
1144
|
|
|
|
|
|
|
|
1145
|
|
|
|
|
|
|
# Get out current settings, and then fix the formats of array and |
1146
|
|
|
|
|
|
|
# hash values. |
1147
|
2
|
|
|
|
|
13
|
my $settings = $self->get_options_as_defaults; |
1148
|
2
|
|
|
|
|
14
|
my $types = $self->_option_data_types; |
1149
|
|
|
|
|
|
|
|
1150
|
2
|
|
|
|
|
10
|
for my $setting ( keys %{ $settings->{default} } ) |
|
2
|
|
|
|
|
21
|
|
1151
|
|
|
|
|
|
|
{ |
1152
|
12
|
|
|
|
|
23
|
my $value = $settings->{default}{$setting}; |
1153
|
|
|
|
|
|
|
|
1154
|
|
|
|
|
|
|
# String data doesn't need anything done to it. |
1155
|
12
|
100
|
|
|
|
24
|
next unless ref $value; |
1156
|
|
|
|
|
|
|
|
1157
|
|
|
|
|
|
|
# We produce compliant CSV; no options needed. |
1158
|
4
|
|
|
|
|
54
|
my $csv = Text::CSV->new( {} ); |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
# Serialize the two structures we know about. |
1161
|
4
|
100
|
|
|
|
694
|
if ( ref $value eq 'ARRAY' ) |
|
|
50
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
{ |
1163
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
# Just stringify. Deep structure will be silently lost. |
1165
|
2
|
|
|
|
|
5
|
$csv->combine( map {"$_"} @{$value} ); |
|
11
|
|
|
|
|
23
|
|
|
2
|
|
|
|
|
4
|
|
1166
|
2
|
|
|
|
|
62
|
$value = $csv->string; |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
# Warn if the type is wrong, but proceed anyway. |
1169
|
|
|
|
|
|
|
$self->warn("Option \"$setting\" is unexpectedly an array") |
1170
|
2
|
50
|
50
|
|
|
21
|
if ( $types->{$setting} || '' ) ne 'ARRAY'; |
1171
|
|
|
|
|
|
|
} |
1172
|
|
|
|
|
|
|
elsif ( ref $value eq 'HASH' ) |
1173
|
|
|
|
|
|
|
{ |
1174
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
# Just stringify. Deep structure will be silently lost. |
1176
|
2
|
|
|
|
|
4
|
$csv->combine( map {"$_=$value->{$_}"} keys %{$value} ); |
|
6
|
|
|
|
|
94
|
|
|
2
|
|
|
|
|
5
|
|
1177
|
2
|
|
|
|
|
144
|
$value = $csv->string; |
1178
|
|
|
|
|
|
|
|
1179
|
|
|
|
|
|
|
# Warn if the type is wrong, but proceed anyway. |
1180
|
|
|
|
|
|
|
$self->warn("Option \"$setting\" is unexpectedly a hash") |
1181
|
2
|
50
|
50
|
|
|
28
|
if ( $types->{$setting} || '' ) ne 'HASH'; |
1182
|
|
|
|
|
|
|
} |
1183
|
|
|
|
|
|
|
else |
1184
|
|
|
|
|
|
|
{ |
1185
|
|
|
|
|
|
|
# Just stringify. We know this is wrong, but the user |
1186
|
|
|
|
|
|
|
# shouldn't be using an INI file for structured data. |
1187
|
0
|
|
|
|
|
0
|
$value = "$value"; |
1188
|
|
|
|
|
|
|
|
1189
|
|
|
|
|
|
|
# Don't know what to do; can't do anything about it. |
1190
|
0
|
|
|
|
|
0
|
$self->warn("Option \"$setting\" will be corrupt in config file"); |
1191
|
|
|
|
|
|
|
} |
1192
|
|
|
|
|
|
|
|
1193
|
4
|
|
|
|
|
30
|
$settings->{default}{$setting} = $value; |
1194
|
|
|
|
|
|
|
} |
1195
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
# Write settings to the file. |
1197
|
2
|
|
|
|
|
19
|
Config::INI::Writer->write_file( $settings, $file ); |
1198
|
|
|
|
|
|
|
|
1199
|
2
|
|
|
|
|
2112
|
return 1; |
1200
|
|
|
|
|
|
|
} |
1201
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
# Write the current settings to an XML file. |
1203
|
|
|
|
|
|
|
sub _write_rcfile_xml |
1204
|
|
|
|
|
|
|
{ |
1205
|
2
|
|
|
2
|
|
1361
|
my ( $self, $file ) = @_; |
1206
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
# Installing a XML module is optional. |
1208
|
2
|
|
|
3
|
|
111
|
eval 'use XML::Simple'; |
|
3
|
|
|
|
|
23
|
|
|
3
|
|
|
|
|
12
|
|
|
3
|
|
|
|
|
52
|
|
1209
|
2
|
50
|
|
|
|
205
|
$self->die('Can\'t write rcfile: XML::Simple is not installed.') |
1210
|
|
|
|
|
|
|
if $EVAL_ERROR; |
1211
|
|
|
|
|
|
|
|
1212
|
2
|
50
|
|
|
|
154
|
open my $RCFILE, '>', $file |
1213
|
|
|
|
|
|
|
or $self->die("Couldn't open file \"$file\": $OS_ERROR"); |
1214
|
2
|
50
|
|
|
|
7
|
print {$RCFILE} XMLout( $self->get_options_as_defaults ) |
|
2
|
|
|
|
|
14
|
|
1215
|
|
|
|
|
|
|
or $self->die("Couldn't write to file \"$file\": $OS_ERROR"); |
1216
|
2
|
50
|
|
|
|
2615
|
close $RCFILE |
1217
|
|
|
|
|
|
|
or $self->die("Couldn't close file \"$file\": $OS_ERROR"); |
1218
|
|
|
|
|
|
|
|
1219
|
2
|
|
|
|
|
14
|
return 1; |
1220
|
|
|
|
|
|
|
} |
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
# Write the current settings to a JSON file. |
1223
|
|
|
|
|
|
|
sub _write_rcfile_json |
1224
|
|
|
|
|
|
|
{ |
1225
|
2
|
|
|
2
|
|
1643
|
my ( $self, $file ) = @_; |
1226
|
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
# Installing a JSON module is optional. |
1228
|
2
|
|
|
2
|
|
117
|
eval 'use JSON::MaybeXS'; |
|
2
|
|
|
|
|
19
|
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
147
|
|
1229
|
2
|
50
|
|
|
|
15
|
$self->die('Can\'t write rcfile: JSON::MaybeXS is not installed.') |
1230
|
|
|
|
|
|
|
if $EVAL_ERROR; |
1231
|
|
|
|
|
|
|
|
1232
|
2
|
|
|
|
|
30
|
my $json = JSON::MaybeXS->new(); |
1233
|
|
|
|
|
|
|
|
1234
|
2
|
50
|
|
|
|
224
|
open my $RCFILE, '>', $file |
1235
|
|
|
|
|
|
|
or $self->die("Couldn't open file \"$file\": $OS_ERROR"); |
1236
|
2
|
50
|
|
|
|
13
|
print {$RCFILE} $json->encode( $self->get_options_as_defaults ) |
|
2
|
|
|
|
|
11
|
|
1237
|
|
|
|
|
|
|
or $self->die("Couldn't write to file \"$file\": $OS_ERROR"); |
1238
|
2
|
50
|
|
|
|
189
|
close $RCFILE |
1239
|
|
|
|
|
|
|
or $self->die("Couldn't close file \"$file\": $OS_ERROR"); |
1240
|
|
|
|
|
|
|
|
1241
|
2
|
|
|
|
|
26
|
return 1; |
1242
|
|
|
|
|
|
|
} |
1243
|
|
|
|
|
|
|
|
1244
|
|
|
|
|
|
|
# Write the current settings to a YAML file. |
1245
|
|
|
|
|
|
|
sub _write_rcfile_yaml |
1246
|
|
|
|
|
|
|
{ |
1247
|
2
|
|
|
2
|
|
3949
|
my ( $self, $file ) = @_; |
1248
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
# Installing a YAML module is optional. |
1250
|
2
|
|
|
2
|
|
118
|
eval 'use YAML::Any qw{DumpFile}'; |
|
2
|
|
|
|
|
18
|
|
|
2
|
|
|
|
|
11
|
|
|
2
|
|
|
|
|
24
|
|
1251
|
2
|
50
|
|
|
|
1288
|
$self->die('Can\'t write rcfile: YAML::Any is not installed.') |
1252
|
|
|
|
|
|
|
if $EVAL_ERROR; |
1253
|
|
|
|
|
|
|
|
1254
|
2
|
|
|
|
|
10
|
DumpFile( $file, $self->get_options_as_defaults ); |
1255
|
|
|
|
|
|
|
|
1256
|
2
|
|
|
|
|
28755
|
return 1; |
1257
|
|
|
|
|
|
|
} |
1258
|
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
# Write the current settings to a Perl file. |
1260
|
|
|
|
|
|
|
sub _write_rcfile_perl |
1261
|
|
|
|
|
|
|
{ |
1262
|
3
|
|
|
3
|
|
1783
|
my ( $self, $file ) = @_; |
1263
|
|
|
|
|
|
|
|
1264
|
3
|
|
|
|
|
31
|
local $Data::Dumper::Terse = 1; |
1265
|
|
|
|
|
|
|
|
1266
|
3
|
50
|
|
|
|
222
|
open my $RCFILE, '>', $file |
1267
|
|
|
|
|
|
|
or $self->die("Couldn't open file \"$file\": $OS_ERROR"); |
1268
|
3
|
50
|
|
|
|
14
|
print {$RCFILE} Dumper( $self->get_options_as_defaults ) |
|
3
|
|
|
|
|
23
|
|
1269
|
|
|
|
|
|
|
or $self->die("Couldn't write to file \"$file\": $OS_ERROR"); |
1270
|
3
|
50
|
|
|
|
864
|
close $RCFILE |
1271
|
|
|
|
|
|
|
or $self->die("Couldn't close file \"$file\": $OS_ERROR"); |
1272
|
|
|
|
|
|
|
|
1273
|
3
|
|
|
|
|
20
|
return 1; |
1274
|
|
|
|
|
|
|
} |
1275
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
1; # End of CLI::Startup |
1277
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
__END__ |