| 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__ |