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