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 10     10   204831 use English qw( -no_match_vars );
  10         10591  
  10         53  
4              
5 10     10   3075 use warnings;
  10         20  
  10         251  
6 10     10   50 use strict;
  10         16  
  10         195  
7              
8 10     10   47 use Carp;
  10         15  
  10         499  
9 10     10   558 use Symbol;
  10         848  
  10         499  
10 10     10   5633 use Pod::Text;
  10         461164  
  10         801  
11 10     10   7659 use Text::CSV;
  10         133298  
  10         519  
12 10     10   7012 use Class::Std;
  10         78100  
  10         82  
13 10     10   5952 use Config::Any;
  10         107201  
  10         342  
14 10     10   5532 use Data::Dumper;
  10         50602  
  10         632  
15 10     10   5135 use File::HomeDir;
  10         50888  
  10         509  
16 10     10   73 use File::Basename;
  10         19  
  10         520  
17 10     10   4018 use Clone qw{ clone };
  10         23326  
  10         535  
18 10     10   4740 use Hash::Merge qw{ merge };
  10         40854  
  10         588  
19 10     10   73 use List::Util qw{ max reduce };
  10         17  
  10         1078  
20 10         47 use Getopt::Long qw{
21             :config posix_default bundling require_order no_ignore_case
22 10     10   6881 };
  10         92901  
23              
24 10     10   2419 use Exporter 'import';
  10         18  
  10         515  
25             our @EXPORT_OK = qw/startup/;
26              
27             our $VERSION = '0.27'; # Don't forget to update the manpage version, too!
28              
29 10     10   5159 use Readonly;
  10         34968  
  10         53698  
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 39435 my $optspec = shift;
38              
39 24         105 my $app = CLI::Startup->new($optspec);
40 20         968 $app->init;
41              
42 7         18 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 30     30 1 9567 my $self = shift;
63 30 100       218 $self->die('get_config() called before init()')
64             unless $self->get_initialized;
65 29         1819 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 1347 my ( $self, $settings ) = @_;
73              
74 5 100 100     33 $self->die('set_default_settings() requires a hashref')
75             unless defined $settings and ref $settings eq 'HASH';
76 3 100       12 $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         6 return; # Needed so we don't leak a reference to the data!
82             }
83              
84             # Get the options provided on the command line. This, unlike most of
85             # the others, can ONLY be called after the app is initialized.
86             sub get_options
87             {
88 23     23 1 468 my $self = shift;
89 23 100       77 $self->die('get_options() called before init()')
90             unless $self->get_initialized;
91 22         556 return clone( $options_of{ ident $self} );
92             }
93              
94             # Returns the current specifications for the command-line options.
95             sub get_optspec
96             {
97 185     185 1 1127 my $self = shift;
98 185         3085 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 1673 my $self = shift;
105 71         98 my $spec = shift;
106              
107 71 100       205 $self->die('set_optspec() requires a hashref')
108             unless ref $spec eq 'HASH';
109 70 100       185 $self->die('set_optspec() called after init()')
110             if $self->get_initialized;
111              
112 69         501 $optspec_of{ ident $self} = clone( $self->_validate_optspec($spec) );
113              
114 64         272 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 19     19 1 402 my $self = shift;
121 19 100       59 $self->die('get_raw_options() called before init()')
122             unless $self->get_initialized;
123 18         257 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 70     70 1 3761 my ( $self, $rcfile ) = @_;
130              
131 70 100       259 $self->die('set_rcfile() called after init()')
132             if $self->get_initialized;
133 69         532 $rcfile_of{ ident $self} = "$rcfile";
134              
135 69         128 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 817 my ( $self, $usage ) = @_;
143              
144 64 100       142 $self->die('set_usage() called after init()')
145             if $self->get_initialized;
146 63         346 $usage_of{ ident $self} = "$usage";
147              
148 63         92 return;
149             }
150              
151             # Set a file writer for the rc file.
152             sub set_write_rcfile
153             {
154 7     7 1 1653 my $self = shift;
155 7   100     43 my $writer = shift || 0;
156              
157 7 100       24 $self->die('set_write_rcfile() called after init()')
158             if $self->get_initialized;
159 6 100 100     58 $self->die('set_write_rcfile() requires a coderef or false')
160             if $writer && ref($writer) ne 'CODE';
161              
162 5         15 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       13 if ($writer)
166             {
167 2         14 my $options = $self->_get_default_optspec;
168 2         36 my $aliases = $self->_option_aliases($options);
169              
170 2         7 for my $alias (qw{ rcfile write-rcfile rcfile-format })
171             {
172 6   66     44 $optspec->{$alias} ||= $options->{ $aliases->{$alias} };
173             }
174             }
175             else
176             {
177 3         15 for my $alias (qw{ rcfile write-rcfile rcfile-format })
178             {
179 9         18 delete $optspec->{$alias};
180             }
181             }
182              
183             # Save the writer
184 5         22 $write_rcfile_of{ ident $self} = $writer;
185              
186 5         17 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 133 my ( undef, $msg ) = @_;
193              
194 24         594 my $name = basename($PROGRAM_NAME);
195 24         228 CORE::die "$name: FATAL: $msg\n";
196             }
197              
198             # Die with a usage summary.
199             sub die_usage
200             {
201 8     8 1 6556 my $self = shift;
202 8         21 my $msg = shift;
203              
204 8         15 print { \*STDERR } $self->_usage_message($msg);
  8         31  
205 8         62 exit 1;
206             }
207              
208             # Return a usage message
209             sub _usage_message
210             {
211 14     14   57 my $self = shift;
212 14         22 my $msg = shift;
213 14         35 my $optspec = $self->get_optspec;
214 14         353 my $name = basename($PROGRAM_NAME);
215              
216             # The message to be returned
217 14         35 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       44 unless keys %{$optspec};
  14         74  
223              
224             #<<< Leave this alone, perltidy
225              
226             # In the usage text, show the option names, not the aliases.
227             my %options =
228 122         211 map { ( $_->{names}[0], $_ ) }
229 122         249 map { $self->_parse_spec( $_, $optspec->{$_} ) }
230 14         35 keys %{$optspec};
  14         53  
231              
232             #>>> End perltidy-free zone
233              
234             # Automatically suppress 'v' if it's an alias of 'verbose'
235 14 50 33     75 delete $options{v} if $optspec->{$V_OPTSPEC} // '' eq $V_FOR_VERBOSE;
236              
237             # Note the length of the longest option
238 14         170 my $length = max map { length() } keys %options;
  108         292  
239              
240             # Print the requested message, if any
241 14 100       45 if ( defined $msg )
242             {
243             ## no critic (ValuesAndExpressions::RequireInterpolationOfMetachars)
244 2         21 $message .= sprintf '%s: FATAL: %s\n', $name, $msg;
245             }
246              
247             # Now print the help message.
248             $message
249 14         350 .= 'usage: '
250             . basename($PROGRAM_NAME) . ' '
251             . $self->get_usage . "\n"
252             . "Options:\n";
253              
254             # Print the options, sorted in dictionary order.
255 14         153 for my $option ( sort keys %options )
256             {
257             ## no critic (ValuesAndExpressions::ProhibitMagicNumbers)
258 108         140 my $indent = $length + 8;
259 108         154 my $spec = $options{$option};
260              
261             # Print the basic help option
262 108 100       167 if ( length($option) == 1 )
263             {
264             $message .= sprintf " -%-${length}s - %s\n", $option,
265 5         19 $spec->{desc};
266             }
267             else
268             {
269             $message .= sprintf " --%-${length}s - %s\n", $option,
270 103         294 $spec->{desc};
271             }
272              
273 108         177 my @aliases = @{ $spec->{names} };
  108         185  
274 108         135 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     356 && $optspec->{$V_OPTSPEC} // '' eq $V_FOR_VERBOSE;
      33        
280              
281             # Print aliases, if any
282 108 100       258 if ( @aliases > 0 )
283             {
284              
285             # Add in the dashes
286 57 50       84 @aliases = map { length() == 1 ? "-$_" : "--$_" } @aliases;
  59         170  
287 57         174 $message .= sprintf "%${indent}s Aliases: %s\n", '',
288             join( ', ', @aliases );
289             }
290              
291             # Print negation, if any
292 108 100       237 if ( $spec->{boolean} )
293             {
294 1         5 $message .= sprintf "%${indent}s Negate this with --no-%s\n", '',
295             $option;
296             }
297             }
298              
299 14         594 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 126     126   1278 '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   221 my ( $self, $optspecs ) = @_;
323 137         424 my $parsed = { options => {}, aliases => {} };
324              
325             # Step through each option
326 137         239 for my $optspec ( keys %{$optspecs} )
  137         490  
327             {
328              
329             # Parse the spec completely
330             $parsed->{options}{$optspec}
331 712         1469 = $self->_parse_spec( $optspec, $optspecs->{$optspec} );
332              
333             # Make a reverse-lookup by option name/alias
334 712         1199 for my $alias ( @{ $parsed->{options}{$optspec}{names} } )
  712         1422  
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         2045 $parsed->{aliases}{$alias} = $optspec;
342             }
343             }
344              
345 136         388 return $parsed;
346             }
347              
348             # Parses the option specs, identifying array and hash data types
349             sub _option_data_types
350             {
351 50     50   116 my $self = shift;
352 50         166 my $optspecs = $self->get_optspec;
353 50         100 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 50         99 for my $option ( keys %{$optspecs} )
  50         237  
358             {
359 550         1144 my $spec = $self->_parse_spec( $option, $optspecs->{$option} );
360              
361 550         1085 for my $type (qw{ array hash boolean count flag })
362             {
363 2750 100       5073 next unless $spec->{$type};
364 355         450 $types{$_} = uc($type) for @{ $spec->{names} };
  355         1621  
365             }
366             }
367              
368 50         206 return \%types;
369             }
370              
371             # Breaks an option spec down into its components.
372             sub _parse_spec
373             {
374 1448     1448   2554 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 1448         16814 $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 1448         21327 my %attrs = %LAST_PAREN_MATCH;
432              
433             # If there's anything left that we failed to match, it's a fatal error
434 1448 50       5671 $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 1448 100 100     18318 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 8     8   90 my ( $self, $optspec ) = @_;
467 8         23 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 8         14 for my $option ( keys %{$optspec} )
  8         38  
473             {
474 64   50     135 $optspec->{$option} ||= 0;
475 64         140 $option = $self->_parse_spec( $option, $optspec->{$option} );
476              
477             # The spec can define aliases
478 64         112 for my $name ( @{ $option->{names} } )
  64         133  
479             {
480             $self->die("--$name option defined twice")
481 88 50       176 if exists $option_aliases{$name};
482 88         193 $option_aliases{$name} = $option->{spec};
483             }
484             }
485              
486 8         99 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   136 my ( $self, $user_optspecs ) = @_;
496 69         134 my $default_optspecs = $self->_get_default_optspec;
497              
498 69         719 my $parsed;
499              
500             # Parse the user optspecs
501 69         193 $parsed = $self->_parse_optspecs($user_optspecs);
502 68         119 my $user_options = $parsed->{options};
503 68         118 my $user_aliases = $parsed->{aliases};
504              
505             # Parse the default optspecs
506 68         144 $parsed = $self->_parse_optspecs($default_optspecs);
507 68         135 my $default_options = $parsed->{options};
508 68         102 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         98 my $default_help_optspec = $default_aliases->{'help'};
513 68         107 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         109 for my $alias ( keys %{$user_aliases} )
  68         221  
522             {
523              
524             # Only look at options that collide with default options.
525 177 100       471 next unless defined $default_aliases->{$alias};
526              
527             # If the option specifications are identical, then we can
528             # skip this option.
529 10         15 my $user_optspec = $user_aliases->{$alias};
530 10         26 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     48 if ( $user_optspecs->{$user_optspec} || 0 )
535             {
536 5 100       13 if ( $user_optspec ne $default_optspec )
537             {
538 4         16 $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         12 my $default_name = $default_options->{$default_optspec}{names}[0];
547 6 50       17 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         8 for my $name ( @{ $default_options->{$default_optspec}{names} } )
  6         14  
555             {
556 8         17 delete $default_aliases->{$name};
557             }
558 6         16 delete $default_options->{$default_optspec};
559              
560             # Special case: we use two options to cover 'verbose'
561 6 100 66     27 if ( $alias eq 'verbose'
562             and $default_optspecs->{$V_OPTSPEC} eq $V_FOR_VERBOSE )
563             {
564 2         22 delete $default_options->{ $default_aliases->{v} };
565 2         4 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         108 for my $optspec ( keys %{$user_options} )
  64         171  
572             {
573 159 100 100     390 next if $user_options->{$optspec}{desc} || 0;
574              
575 5         7 for my $alias ( @{ $user_options->{$optspec}{names} } )
  5         10  
576             {
577 5         10 delete $user_aliases->{$alias};
578             }
579 5         14 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         106 for my $name ( keys %{$user_aliases} )
  64         125  
586             {
587 166 50       318 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       158 if ( not defined $user_aliases->{'help'} )
596             {
597 64         123 $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       131 if ( not defined $user_aliases->{rcfile} )
604             {
605 64         113 for my $option (qw{ rcfile rcfile-format write-rcfile })
606             {
607 192 50       363 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       136 if ( not defined $user_aliases->{'write-rcfile'} )
618             {
619 64 50       126 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         110 my $optspecs = {};
628              
629 64         89 for my $optspec ( keys %{$default_options} )
  64         184  
630             {
631 504         839 $optspecs->{$optspec} = $default_options->{$optspec}{desc};
632             }
633              
634 64         119 for my $optspec ( keys %{$user_options} )
  64         152  
635             {
636 218         406 $optspecs->{$optspec} = $user_options->{$optspec}{desc};
637             }
638              
639 64         2211 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 20933 my $self = shift;
649              
650 61 100       184 $self->die('init() method takes no arguments') if @_;
651 60 100       156 $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     312 $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         236 my $options = $self->_process_command_line;
661 49         147 my $config = $self->_read_config_file;
662 49         423 my $default = $self->get_default_settings;
663              
664             # Save the unprocessed command-line options
665 49         885 $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   7520 $options = reduce { merge( $a, $b ) }
670 49         926 ( $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     2863 $options->{verbose} //= 0;
675              
676             # Consolidate the 'v' and 'verbose' options if the default
677             # options are in play here.
678 49 50       285 if ( $self->_get_default_optspec->{$V_OPTSPEC} eq $V_FOR_VERBOSE )
679             {
680 49 50       1463 if ( defined $options->{v} )
681             {
682 0         0 $options->{verbose} += delete $options->{v};
683             }
684             }
685              
686             # Save the fully-processed options
687 49         1161 $options_of{ ident $self} = clone($options);
688              
689             # Mark the object as initialized
690 49         232 $initialized_of{ ident $self} = 1;
691              
692             #
693             # Automatically processed options:
694             #
695              
696             # Print the version information, if requested
697 49 100       129 $self->print_version if $options->{version};
698              
699             # Print the POD manpage from the script, if requested
700 47 100       124 $self->print_manpage if $options->{manpage};
701              
702             # Write back the config if requested
703 45 100       108 $self->write_rcfile() if $options->{'write-rcfile'};
704              
705 40         335 return;
706             }
707              
708             sub _process_command_line
709             {
710 59     59   101 my $self = shift;
711 59         109 my $optspec = $self->get_optspec;
712 59         120 my %options;
713              
714             # Parse the command line and die if anything is wrong.
715 59         98 my $opts_ok = GetOptions( \%options, keys %{$optspec} );
  59         417  
716              
717 59 100       44001 if ( $options{help} )
    100          
718             {
719 5         15 print $self->_usage_message();
720 5         35 exit 0;
721             }
722             elsif ( !$opts_ok )
723             {
724 4         15 $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         669 my $csv = Text::CSV->new( { allow_loose_quotes => 1 } );
730              
731             # Further process the array and hash options
732 50         10096 for my $option ( keys %options )
733             {
734 26 100       134 if ( ref $options{$option} eq 'ARRAY' )
    100          
735             {
736 2         4 my @values;
737 2         3 for my $value ( @{ $options{$option} } )
  2         4  
738             {
739 5 100       36 $csv->parse($value)
740             or $self->die_usage(
741             "Can't parse --$option option \"$value\": "
742             . $csv->error_diag );
743 4         110 push @values, $csv->fields;
744             }
745              
746 1         9 $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         17 my $value = $hash->{$key};
756 4 50       10 $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       88 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         13 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         7 $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       176 $self->set_rcfile( $options{rcfile} ) if defined $options{rcfile};
789              
790             # That's it!
791 49         400 return \%options;
792             }
793              
794             sub _read_config_file
795             {
796 49     49   75 my $self = shift;
797 49         236 my $types = $self->_option_data_types;
798 49   100     184 my $rcfile = $self->get_rcfile || '';
799 49         482 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         72 my $raw_config;
810              
811             # Attempt to parse the file, if any
812 49 100 100     1316 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 18     18   187978 my @args = @_;
819              
820 18         54 for my $arg (@args)
821             {
822 18 50       71 next if ref $arg;
823 18 50       247 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         299 };
829              
830             # OK, NOW load the files.
831 26         545 my $files = Config::Any->load_files($options);
832 26   50     37651 $files = shift @{$files} || {};
833 26   50     963 $raw_config = $files->{$rcfile} || {};
834             }
835             else
836             {
837 23         64 $raw_config = {};
838             }
839              
840             # Initialize an empty config
841 49         234 my $config = { default => {} };
842              
843             # Copy in the default section, if there is one.
844 49 100       243 if ( defined $raw_config->{default} )
845             {
846 20 50       225 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         147 $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         109 for my $key ( keys %{$raw_config} )
  49         283  
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       255 if ( not ref $raw_config->{$key} )
869             {
870 8         20 $config->{default}{$key} = delete $raw_config->{$key};
871 8         14 next;
872             }
873             else
874             {
875 17         130 $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         151 for my $option ( keys %{ $config->{default} } )
  49         254  
882             {
883 123         235 my $value = $config->{default}{$option};
884 123         490 $value = $self->_parse_setting( $value, $option, $types );
885              
886 123         312 $config->{default}{$option} = $value;
887             }
888              
889             # Save the cleaned-up config for reference
890 49         339 $config_of{ ident $self} = $config;
891              
892 49         1155 return $config;
893             }
894              
895             # Convert string values into an arrayref or hashref as needed
896             sub _parse_setting
897             {
898 123     123   334 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     545 my $type = $types->{$option} || 'NONE';
902 123 100 100     778 return $value if ref $value eq $type or $type eq 'NONE';
903              
904             # All other data types we support are scalars.
905 43 50       118 $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 43 100 100     194 if ( $type eq 'BOOLEAN' or $type eq 'FLAG' )
911             {
912 35 100 50     153 return $value // 0 ? 1 : 0;
913             }
914              
915             # Counters are integer-valued
916 8 50       20 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 8         36 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 8         1176 $csv->parse($value);
936 8         312 $value = [ $csv->fields ];
937 8 100       98 return $value if $type eq 'ARRAY';
938              
939 5         8 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 5         7 for ( @{$value} )
  5         12  
945             {
946 9         85 my ( $key, $val ) = m/^([^=:]+)(?:\s*[:=]\s*)?(.*)$/xms;
947 9         38 $hash{$key} = $val;
948             }
949              
950 5         45 return \%hash;
951             }
952              
953             # Constructor for this object.
954             sub BUILD
955             {
956 67     67 1 40875 my ( $self, undef, $argref ) = @_;
957              
958             # Shorthand: { options => \%options } can be
959             # abbreviated \%options.
960 67 100       251 if ( not exists $argref->{options} )
961             {
962 30         62 $argref = { options => $argref };
963             }
964 67   100     333 $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       156 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       442 : 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       167 if ( exists $argref->{write_rcfile} )
984             {
985 2         24 $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       312 : '[options]'
993             );
994              
995 62         137 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 10 my $self = shift;
1009 2         25 my $parser = Pod::Text->new;
1010              
1011             # Print SOMETHING...
1012 2         411 $parser->output_fh(*STDOUT);
1013 2         18 $parser->parse_file($PROGRAM_NAME);
1014 2 100       10316 print $self->_usage_message() unless $parser->content_seen;
1015              
1016 2         26 exit 0;
1017             }
1018              
1019             # Prints the version of the script.
1020             sub print_version
1021             {
1022 2   100 2 1 6 my $version = $::VERSION || 'UNKNOWN';
1023 2         48 my $name = basename($PROGRAM_NAME);
1024              
1025 2         5 print { \*STDERR } <<"EOF";
  2         128  
1026             This is $name, version $version
1027             path: $PROGRAM_NAME
1028             perl: $PERL_VERSION
1029             EOF
1030 2         13 exit 0;
1031             }
1032              
1033             # Print a nicely-formatted warning message.
1034             sub warn ## no critic ( Subroutines::RequireFinalReturn )
1035             {
1036 1     1 1 1570 my ( undef, $msg ) = @_;
1037              
1038 1         27 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 7     7 1 360 my $self = shift;
1046 7   66     46 my $file = shift || $self->get_rcfile;
1047              
1048             # It's a fatal error to call write_rcfile() before init()
1049 7 100       48 $self->die('write_rcfile() called before init()')
1050             unless $self->get_initialized;
1051              
1052             # If there's no file to write, abort.
1053 6 100       44 $self->die('can\'t write rcfile: no file specified') unless $file;
1054              
1055             # Check whether a writer has been set
1056 3         16 my $writer = $self->_choose_rcfile_writer;
1057              
1058             # If there's a writer, call it.
1059 3 100       9 if ( ref $writer eq 'CODE' )
1060             {
1061 2         7 $writer->( $self, $file );
1062             }
1063             else
1064             {
1065 1         4 $self->die('write_rcfile() disabled, but called anyway');
1066             }
1067              
1068 2         65 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 6     6 1 17 my $self = shift;
1077              
1078             # Collate the settings for writing
1079 6         18 my $settings = $self->get_config;
1080 6         29 my $options = $self->get_raw_options;
1081 6         21 my $default = $self->get_default_settings;
1082 6         30 my $default_aliases
1083             = $self->_option_aliases( $self->_get_default_optspec );
1084              
1085             # Copy the current options back into the "default" group
1086 12     12   551 $settings->{default} = reduce { merge( $a, $b ) }
1087 6         96 ( $options, $settings->{default}, $default );
1088              
1089             # Delete settings for the automatically-generated options; none of them
1090             # belong in the rcfile.
1091 6         311 for my $option ( keys %{$default_aliases} )
  6         30  
1092             {
1093 66         92 delete $settings->{default}{$option};
1094             }
1095              
1096 6         96 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 3     3   6 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 3 100       14 if ( exists $write_rcfile_of{ ident $self} )
1109             {
1110 2         8 return $write_rcfile_of{ ident $self};
1111             }
1112              
1113 1         9 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 1     1   113 eval 'use Config::INI::Writer';
  1         10  
  1         2  
  1         19  
1123 1 50       5 my $default = $EVAL_ERROR ? 'PERL' : 'INI';
1124              
1125             # Check whether a file format was specified; if not, use the default.
1126 1         3 my $options = $self->get_options;
1127 1   33     5 my $format = uc( $options->{'rcfile-format'} || $default );
1128              
1129             $self->die("Unknown --rcfile-format option specified: \"$format\"")
1130 1 50       4 unless defined $writer->{$format};
1131              
1132 1         5 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 1     1   19660 my ( $self, $file ) = @_;
1140              
1141             # Installing the INI module is optional
1142 1     1   83 eval 'use Config::INI::Writer';
  1         10  
  1         6  
  1         20  
1143 1 50       7 $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 1         5 my $settings = $self->get_options_as_defaults;
1149 1         4 my $types = $self->_option_data_types;
1150              
1151 1         2 for my $setting ( keys %{ $settings->{default} } )
  1         11  
1152             {
1153 5         12 my $value = $settings->{default}{$setting};
1154              
1155             # String data doesn't need anything done to it.
1156 5 100       14 next unless ref $value;
1157              
1158             # We produce compliant CSV; no options needed.
1159 2         34 my $csv = Text::CSV->new( {} );
1160              
1161             # Serialize the two structures we know about.
1162 2 100       443 if ( ref $value eq 'ARRAY' )
    50          
1163             {
1164              
1165             # Just stringify. Deep structure will be silently lost.
1166 1         3 $csv->combine( map {"$_"} @{$value} );
  4         12  
  1         3  
1167 1         21 $value = $csv->string;
1168              
1169             # Warn if the type is wrong, but proceed anyway.
1170             $self->warn("Option \"$setting\" is unexpectedly an array")
1171 1 50 50     11 if ( $types->{$setting} || '' ) ne 'ARRAY';
1172             }
1173             elsif ( ref $value eq 'HASH' )
1174             {
1175              
1176             # Just stringify. Deep structure will be silently lost.
1177 1         2 $csv->combine( map {"$_=$value->{$_}"} keys %{$value} );
  3         43  
  1         5  
1178 1         80 $value = $csv->string;
1179              
1180             # Warn if the type is wrong, but proceed anyway.
1181             $self->warn("Option \"$setting\" is unexpectedly a hash")
1182 1 50 50     15 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 2         15 $settings->{default}{$setting} = $value;
1195             }
1196              
1197             # Write settings to the file.
1198 1         14 Config::INI::Writer->write_file( $settings, $file );
1199              
1200 1         1066 return 1;
1201             }
1202              
1203             # Write the current settings to an XML file.
1204             sub _write_rcfile_xml
1205             {
1206 1     1   2283 my ( $self, $file ) = @_;
1207              
1208             # Installing a XML module is optional.
1209 1     1   80 eval 'use XML::Simple';
  1         9  
  1         3  
  1         6  
1210 1 50       66 $self->die('Can\'t write rcfile: XML::Simple is not installed.')
1211             if $EVAL_ERROR;
1212              
1213 1 50       101 open my $RCFILE, '>', $file
1214             or $self->die("Couldn't open file \"$file\": $OS_ERROR");
1215 1 50       14 print {$RCFILE} XMLout( $self->get_options_as_defaults )
  1         6  
1216             or $self->die("Couldn't write to file \"$file\": $OS_ERROR");
1217 1 50       1268 close $RCFILE
1218             or $self->die("Couldn't close file \"$file\": $OS_ERROR");
1219              
1220 1         10 return 1;
1221             }
1222              
1223             # Write the current settings to a JSON file.
1224             sub _write_rcfile_json
1225             {
1226 1     1   2164 my ( $self, $file ) = @_;
1227              
1228             # Installing a JSON module is optional.
1229 1     1   76 eval 'use JSON::MaybeXS';
  1         8  
  1         2  
  1         39  
1230 1 50       5 $self->die('Can\'t write rcfile: JSON::MaybeXS is not installed.')
1231             if $EVAL_ERROR;
1232              
1233 1         18 my $json = JSON::MaybeXS->new();
1234              
1235 1 50       115 open my $RCFILE, '>', $file
1236             or $self->die("Couldn't open file \"$file\": $OS_ERROR");
1237 1 50       4 print {$RCFILE} $json->encode( $self->get_options_as_defaults )
  1         6  
1238             or $self->die("Couldn't write to file \"$file\": $OS_ERROR");
1239 1 50       74 close $RCFILE
1240             or $self->die("Couldn't close file \"$file\": $OS_ERROR");
1241              
1242 1         12 return 1;
1243             }
1244              
1245             # Write the current settings to a YAML file.
1246             sub _write_rcfile_yaml
1247             {
1248 1     1   4705 my ( $self, $file ) = @_;
1249              
1250             # Installing a YAML module is optional.
1251 1     1   67 eval 'use YAML::Any qw{DumpFile}';
  1         9  
  1         2  
  1         6  
1252 1 50       757 $self->die('Can\'t write rcfile: YAML::Any is not installed.')
1253             if $EVAL_ERROR;
1254              
1255 1         5 DumpFile( $file, $self->get_options_as_defaults );
1256              
1257 1         15073 return 1;
1258             }
1259              
1260             # Write the current settings to a Perl file.
1261             sub _write_rcfile_perl
1262             {
1263 2     2   2385 my ( $self, $file ) = @_;
1264              
1265 2         24 local $Data::Dumper::Terse = 1;
1266              
1267 2 50       190 open my $RCFILE, '>', $file
1268             or $self->die("Couldn't open file \"$file\": $OS_ERROR");
1269 2 50       7 print {$RCFILE} Dumper( $self->get_options_as_defaults )
  2         16  
1270             or $self->die("Couldn't write to file \"$file\": $OS_ERROR");
1271 2 50       652 close $RCFILE
1272             or $self->die("Couldn't close file \"$file\": $OS_ERROR");
1273              
1274 2         15 return 1;
1275             }
1276              
1277             1; # End of CLI::Startup
1278              
1279             __END__