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