File Coverage

blib/lib/CLI/Startup.pm
Criterion Covered Total %
statement 457 472 96.8
branch 167 212 78.7
condition 51 69 73.9
subroutine 62 62 100.0
pod 20 20 100.0
total 757 835 90.6


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__