File Coverage

blib/lib/Getopt/Lucid.pm
Criterion Covered Total %
statement 417 419 99.5
branch 285 302 94.3
condition 40 50 80.0
subroutine 44 44 100.0
pod 15 15 100.0
total 801 830 96.5


line stmt bran cond sub pod time code
1 11     11   311915 use 5.006;
  11         36  
2 11     11   38 use strict;
  11         16  
  11         206  
3 11     11   31 use warnings;
  11         21  
  11         1775  
4             package Getopt::Lucid;
5             # ABSTRACT: Clear, readable syntax for command line processing
6              
7             our $VERSION = '1.08';
8              
9             our @EXPORT_OK = qw(Switch Counter Param List Keypair);
10             our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
11             our @ISA = qw( Exporter );
12              
13 11     11   660 use Carp;
  11         11  
  11         2087  
14 11     11   1381 use Exporter ();
  11         1430  
  11         865  
15 11     11   3008 use Getopt::Lucid::Exception;
  11         15  
  11         551  
16 11     11   6068 use Storable 2.16 qw(dclone);
  11         30390  
  11         14574  
17              
18             # Definitions
19             my $VALID_STARTCHAR = "a-zA-Z0-9";
20             my $VALID_CHAR = "a-zA-Z0-9_-";
21             my $VALID_LONG = qr/--[$VALID_STARTCHAR][$VALID_CHAR]*/;
22             my $VALID_SHORT = qr/-[$VALID_STARTCHAR]/;
23             my $VALID_BARE = qr/[$VALID_STARTCHAR][$VALID_CHAR]*/;
24             my $VALID_NAME = qr/$VALID_LONG|$VALID_SHORT|$VALID_BARE/;
25             my $SHORT_BUNDLE = qr/-[$VALID_STARTCHAR]{2,}/;
26             my $NEGATIVE = qr/(?:--)?no-/;
27              
28             my @valid_keys = qw( name type default nocase valid needs canon );
29             my @valid_types = qw( switch counter parameter list keypair);
30              
31             sub Switch {
32 47     47 1 8383 return bless { name => shift, type => 'switch' },
33             "Getopt::Lucid::Spec";
34             }
35             sub Counter {
36 23     23 1 4875 return bless { name => shift, type => 'counter' },
37             "Getopt::Lucid::Spec";
38             }
39             sub Param {
40 34     34 1 128 my $self = { name => shift, type => 'parameter' };
41 34 100       68 $self->{valid} = shift if @_;
42 34         132 return bless $self, "Getopt::Lucid::Spec";
43             }
44             sub List {
45 9     9 1 41 my $self = { name => shift, type => 'list' };
46 9 100       20 $self->{valid} = shift if @_;
47 9         30 return bless $self, "Getopt::Lucid::Spec";
48             }
49             sub Keypair {
50 12     12 1 54 my $self = { name => shift, type => 'keypair' };
51 12 100       31 $self->{valid} = [ @_ ] if scalar @_;
52 12         56 return bless $self, "Getopt::Lucid::Spec";
53             }
54              
55             package
56             Getopt::Lucid::Spec;
57             $Getopt::Lucid::Spec::VERSION = $Getopt::Lucid::VERSION;
58              
59             # alternate way to specify validation
60             sub valid {
61 2     2   1 my $self = shift;
62             Getopt::Lucid::throw_spec("valid() is not supported for '$self->{type}' options")
63 2 50       2 unless grep { $self->{type} eq $_ } qw/parameter list keypair/;
  6         10  
64 2 50       4 $self->{valid} = $self->{type} eq 'keypair' ? [ @_ ] : shift;
65 2         5 return $self;
66             }
67              
68             sub default {
69 33     33   23 my $self = shift;
70 33         45 my $type = $self->{type};
71 33 100       58 if ($self->{type} eq 'keypair') {
    100          
72 5 100       19 if (ref($_[0]) eq 'HASH') {
    100          
73 2         2 $self->{default} = shift;
74             }
75             elsif ( @_ % 2 == 0 ) {
76 2         4 $self->{default} = { @_ };
77             }
78             else {
79 1         1 $self->{default} = []; # will cause an exception later
80             }
81             }
82             elsif ( $self->{type} eq 'list' ) {
83 4         9 $self->{default} = [ @_ ];
84             }
85             else {
86 24         38 $self->{default} = shift;
87             }
88 33         172 return $self
89             };
90              
91 2     2   3 sub anycase { my $self = shift; $self->{nocase}=1; return $self };
  2         8  
  2         1608  
92              
93 5     5   5 sub needs { my $self = shift; $self->{needs}=[@_]; return $self };
  5         11  
  5         8  
94              
95             package Getopt::Lucid;
96              
97             #--------------------------------------------------------------------------#
98             # new()
99             #--------------------------------------------------------------------------#
100              
101             my @params = qw/strict target/;
102              
103             sub new {
104 189     189 1 16123 my ($class, $spec, $target) = @_;
105 189 100       488 my $args = ref($_[-1]) eq 'HASH' ? pop(@_) : {};
106 189 100       435 $args->{target} = ref($target) eq 'ARRAY' ? $target : \@ARGV;
107 189         179 my $self = {};
108 189         542 $self->{$_} = $args->{$_} for @params;
109 189         234 $self->{raw_spec} = $spec;
110 189 50       306 bless ($self, ref($class) ? ref($class) : $class);
111             throw_usage("Getopt::Lucid->new() requires an option specification array reference")
112 189 100       452 unless ref($self->{raw_spec}) eq 'ARRAY';
113 188         308 _parse_spec($self);
114 179         297 _set_defaults($self);
115 177         317 $self->{options} = {};
116 177         214 $self->{parsed} = [];
117 177         140 $self->{seen}{$_} = 0 for keys %{$self->{spec}};
  177         584  
118 177         518 return $self;
119             }
120              
121             #--------------------------------------------------------------------------#
122             # append_defaults()
123             #--------------------------------------------------------------------------#
124              
125             sub append_defaults {
126 8     8 1 2353 my $self = shift;
127             my %append =
128 8 100       32 ref $_[0] eq 'HASH' ? %{+shift} :
  4 100       15  
129             (@_ % 2 == 0) ? @_ :
130             throw_usage("Argument to append_defaults() must be a hash or hash reference");
131 7         9 for my $name ( keys %{$self->{spec}} ) {
  7         18  
132 46         58 my $spec = $self->{spec}{$name};
133 46         36 my $strip = $self->{strip}{$name};
134 46 100       64 next unless exists $append{$strip};
135 29         49 for ( $spec->{type} ) {
136 29 100       77 /switch|parameter/ && do {
137 15         18 $self->{default}{$strip} = $append{$strip};
138 15         13 last;
139             };
140 14 100       25 /counter/ && do {
141 4         7 $self->{default}{$strip} += $append{$strip};
142 4         4 last;
143             };
144 10 100       22 /list/ && do {
145             throw_usage("Option '$strip' in append_defaults() must be scalar or array reference")
146 5 100 100     25 if ref($append{$strip}) && ref($append{$strip}) ne 'ARRAY';
147             $append{$strip} = ref($append{$strip}) eq 'ARRAY'
148             ? dclone( $append{$strip} )
149 4 100       37 : [ $append{$strip} ] ;
150 4         5 push @{$self->{default}{$strip}}, @{$append{$strip}};
  4         6  
  4         7  
151 4         4 last;
152             };
153 5 50       12 /keypair/ && do {
154             throw_usage("Option '$strip' in append_defaults() must be scalar or hash reference")
155 5 100 66     32 if ref($append{$strip}) && ref($append{$strip}) ne 'HASH';
156             $self->{default}{$strip} = {
157 4         6 %{$self->{default}{$strip}},
158 4         4 %{$append{$strip}},
  4         12  
159             };
160 4         7 last;
161             };
162             }
163             throw_spec("Default '$spec->{canon}' = '$self->{default}{$strip}' fails to validate")
164 27 100       42 unless _validate_value($self, $self->{default}{$strip}, $spec->{valid} );
165             }
166 4         7 _recalculate_options($self);
167 4         14 return $self->options;
168             }
169              
170             #--------------------------------------------------------------------------#
171             # defaults()
172             #--------------------------------------------------------------------------#
173              
174             sub defaults {
175 7     7 1 7 my ($self) = @_;
176 7         7 return %{dclone($self->{default})};
  7         211  
177             }
178              
179              
180             #--------------------------------------------------------------------------#
181             # getopt()
182             #--------------------------------------------------------------------------#
183              
184             sub getopt {
185 141     141 1 2302 my ($self,$spec,$target) = @_;
186 141 100       324 if ( $self eq 'Getopt::Lucid' ) {
187 2 100       7 throw_usage("Getopt::Lucid->getopt() requires an option specification array reference")
188             unless ref($spec) eq 'ARRAY';
189 1         3 $self = new(@_)
190             }
191 140         118 my (@passthrough);
192 140         112 while (@{$self->{target}}) {
  375         599  
193 287         207 my $raw = shift @{$self->{target}};
  287         314  
194 287 100       476 last if $raw =~ /^--$/;
195 272         298 my ($orig, $val) = _split_equals($self, $raw);
196 272 100       342 next if _unbundle($self, $orig, $val);
197 248 100       685 my $neg = $orig =~ s/^$NEGATIVE(.*)$/$1/ ? 1 : 0;
198 248         300 my $arg = _find_arg($self, $orig);
199 248 100       288 if ( $arg ) {
200             $neg ?
201             $self->{seen}{$arg} = 0 :
202 232 100       350 $self->{seen}{$arg}++;
203 232         297 for ($self->{spec}{$arg}{type}) {
204 232 50       751 /switch/ ? _switch ($self, $arg, $val, $neg) :
    100          
    100          
    100          
    100          
205             /counter/ ? _counter ($self, $arg, $val, $neg) :
206             /parameter/ ? _parameter($self, $arg, $val, $neg) :
207             /list/ ? _list ($self, $arg, $val, $neg) :
208             /keypair/ ? _keypair ($self, $arg, $val, $neg) :
209             throw_usage("can't handle type '$_'");
210             }
211             } else {
212 16 100       72 throw_argv("Invalid argument: $orig")
213             if $orig =~ /^-./; # invalid if looks like it could be an arg;
214 5         8 push @passthrough, $orig;
215             }
216             }
217 103         154 _recalculate_options($self);
218 103         128 @{$self->{target}} = (@passthrough, @{$self->{target}});
  103         120  
  103         94  
219 103         205 return $self;
220             }
221              
222 11     11   36375 BEGIN { *getopts = \&getopt }; # handy alias
223              
224             #--------------------------------------------------------------------------#
225             # validate
226             #--------------------------------------------------------------------------#
227              
228             sub validate {
229 83     83 1 80 my ($self, $arg) = @_;
230 83 50 33     334 throw_usage("Getopt::Lucid->validate() takes a hashref argument")
231             if $arg && ref($arg) ne 'HASH';
232              
233 83 100 33     194 if ( $arg && exists $arg->{requires} ) {
234 7         9 my $requires = $arg->{requires};
235 7 50 33     30 throw_usage("'validate' argument 'requires' must be an array reference")
236             if $requires && ref($requires) ne 'ARRAY';
237 7         10 for my $p ( @$requires ) {
238             throw_argv("Required option '$self->{spec}{$p}{canon}' not found")
239 7 100       30 if ( ! $self->{seen}{$p} );
240             }
241             }
242              
243 81         109 _check_prereqs($self);
244              
245 76         130 return $self;
246             }
247              
248             #--------------------------------------------------------------------------#
249             # merge_defaults()
250             #--------------------------------------------------------------------------#
251              
252             sub merge_defaults {
253 8     8 1 1923 my $self = shift;
254             my %merge =
255 8 100       34 ref $_[0] eq 'HASH' ? %{+shift} :
  4 100       13  
256             (@_ % 2 == 0) ? @_ :
257             throw_usage("Argument to merge_defaults() must be a hash or hash reference");
258 7         8 for my $name ( keys %{$self->{spec}} ) {
  7         17  
259 47         36 my $spec = $self->{spec}{$name};
260 47         38 my $strip = $self->{strip}{$name};
261 47 100       66 next unless exists $merge{$strip};
262 29         31 for ( $self->{spec}{$name}{type} ) {
263 29 100       89 /switch|counter|parameter/ && do {
264 19         22 $self->{default}{$strip} = $merge{$strip};
265 19         15 last;
266             };
267 10 100       21 /list/ && do {
268             throw_usage("Option '$strip' in merge_defaults() must be scalar or array reference")
269 5 100 100     30 if ref($merge{$strip}) && ref($merge{$strip}) ne 'ARRAY';
270             $merge{$strip} = ref($merge{$strip}) eq 'ARRAY'
271             ? dclone( $merge{$strip} )
272 4 100       51 : [ $merge{$strip} ] ;
273 4         5 $self->{default}{$strip} = $merge{$strip};
274 4         17 last;
275             };
276 5 50       10 /keypair/ && do {
277             throw_usage("Option '$strip' in merge_defaults() must be scalar or hash reference")
278 5 100 66     32 if ref($merge{$strip}) && ref($merge{$strip}) ne 'HASH';
279 4         42 $self->{default}{$strip} = dclone($merge{$strip});
280 4         6 last;
281             };
282             }
283             throw_spec("Default '$spec->{canon}' = '$self->{default}{$strip}' fails to validate")
284 27 100       47 unless _validate_value($self, $self->{default}{$strip}, $spec->{valid} );
285             }
286 4         8 _recalculate_options($self);
287 4         15 return $self->options;
288             }
289              
290             #--------------------------------------------------------------------------#
291             # names()
292             #--------------------------------------------------------------------------#
293              
294             sub names {
295 1     1 1 1 my ($self) = @_;
296 1         2 return values %{$self->{strip}};
  1         6  
297             }
298              
299              
300             #--------------------------------------------------------------------------#
301             # options()
302             #--------------------------------------------------------------------------#
303              
304             sub options {
305 122     122 1 156 my ($self) = @_;
306 122         80 return %{dclone($self->{options})};
  122         2967  
307             }
308              
309             #--------------------------------------------------------------------------#
310             # replace_defaults()
311             #--------------------------------------------------------------------------#
312              
313             sub replace_defaults {
314 8     8 1 2343 my $self = shift;
315             my %replace =
316 8 100       33 ref $_[0] eq 'HASH' ? %{+shift} :
  4 100       13  
317             (@_ % 2 == 0) ? @_ :
318             throw_usage("Argument to replace_defaults() must be a hash or hash reference");
319 7         7 for my $name ( keys %{$self->{spec}} ) {
  7         21  
320 48         39 my $spec = $self->{spec}{$name};
321 48         43 my $strip = $self->{strip}{$name};
322 48         50 for ( $self->{spec}{$name}{type} ) {
323 48 100       116 /switch|counter/ && do {
324 17   100     50 $self->{default}{$strip} = $replace{$strip} || 0;
325 17         16 last;
326             };
327 31 100       44 /parameter/ && do {
328 18         24 $self->{default}{$strip} = $replace{$strip};
329 18         14 last;
330             };
331 13 100       26 /list/ && do {
332             throw_usage("Option '$strip' in replace_defaults() must be scalar or array reference")
333 7 100 100     30 if ref($replace{$strip}) && ref($replace{$strip}) ne 'ARRAY';
334 6 100       13 if ( exists $replace{$strip} ) {
335             $replace{$strip} = ref($replace{$strip}) eq 'ARRAY' ?
336 4 100       13 $replace{$strip} : [ $replace{$strip} ];
337             } else {
338 2         5 $replace{$strip} = [];
339             }
340 6         94 $self->{default}{$strip} = dclone($replace{$strip});
341 6         9 last;
342             };
343 6 50       14 /keypair/ && do {
344             throw_usage("Option '$strip' in replace_defaults() must be scalar or hash reference")
345 6 100 100     30 if ref($replace{$strip}) && ref($replace{$strip}) ne 'HASH';
346 5 100       11 $replace{$strip} = {} unless exists $replace{$strip};
347 5         50 $self->{default}{$strip} = dclone($replace{$strip});
348 5         7 last;
349             };
350             }
351             throw_spec("Default '$spec->{canon}' = '$self->{default}{$strip}' fails to validate")
352 46 100       74 unless _validate_value($self, $self->{default}{$strip}, $spec->{valid} );
353             }
354 4         8 _recalculate_options($self);
355 4         15 return $self->options;
356             }
357              
358             #--------------------------------------------------------------------------#
359             # reset_defaults()
360             #--------------------------------------------------------------------------#
361              
362             sub reset_defaults {
363 6     6 1 7 my ($self) = @_;
364 6         11 _set_defaults($self);
365 6         20 _recalculate_options($self);
366 6         21 return $self->options;
367             }
368              
369             #--------------------------------------------------------------------------#
370             # _check_prereqs()
371             #--------------------------------------------------------------------------#
372              
373             sub _check_prereqs {
374 81     81   66 my ($self) = @_;
375 81         59 for my $key ( keys %{$self->{seen}} ) {
  81         144  
376 205 100       284 next unless $self->{seen}{$key};
377 105 100       183 next unless exists $self->{spec}{$key}{needs};
378 10         10 for (@{$self->{spec}{$key}{needs}}) {
  10         19  
379             throw_argv("Option '$self->{spec}{$key}{canon}' ".
380             "requires option '$self->{spec}{$_}{canon}'")
381 12 100       46 unless $self->{seen}{$_};
382             }
383             }
384             }
385              
386             #--------------------------------------------------------------------------#
387             # _counter()
388             #--------------------------------------------------------------------------#
389              
390             sub _counter {
391 82     82   85 my ($self, $arg, $val, $neg) = @_;
392 82 100       122 throw_argv("Counter option can't take a value: $self->{spec}{$arg}{canon}=$val")
393             if defined $val;
394 80         66 push @{$self->{parsed}}, [ $arg, 1, $neg ];
  80         200  
395             }
396              
397             #--------------------------------------------------------------------------#
398             # _find_arg()
399             #--------------------------------------------------------------------------#
400              
401             sub _find_arg {
402 344     344   286 my ($self, $arg) = @_;
403              
404 344 100       860 $arg =~ s/^-*// unless $self->{strict};
405 344 100       791 return $self->{alias_hr}{$arg} if exists $self->{alias_hr}{$arg};
406              
407 66         54 for ( keys %{$self->{alias_nocase}} ) {
  66         152  
408 14 100       110 return $self->{alias_nocase}{$_} if $arg =~ /^$_$/i;
409             }
410              
411 62         165 return;
412             }
413              
414             #--------------------------------------------------------------------------#
415             # _keypair()
416             #--------------------------------------------------------------------------#
417              
418             sub _keypair {
419 24     24   32 my ($self, $arg, $val, $neg) = @_;
420 24         21 my ($key, $data);
421 24 100       36 if ($neg) {
422 4         3 $key = $val;
423             }
424             else {
425 20 100       31 my $value = defined $val ? $val : shift @{$self->{target}};
  17         26  
426 20 100 66     64 if (! defined $val && ! defined $value) {
427 1         6 throw_argv("Option '$self->{spec}{$arg}{canon}' requires a value");
428             }
429              
430 19 100       71 throw_argv("Badly formed keypair for '$self->{spec}{$arg}{canon}'")
431             unless $value =~ /[^=]+=.+/;
432 16         61 ($key, $data) = ( $value =~ /^([^=]*)=(.*)$/ ) ;
433             throw_argv("Invalid keypair '$self->{spec}{$arg}{canon}': $key => $data")
434             unless _validate_value($self, { $key => $data },
435 16 100       53 $self->{spec}{$arg}{valid});
436             }
437 16         25 push @{$self->{parsed}}, [ $arg, [ $key, $data ], $neg ];
  16         57  
438             }
439              
440             #--------------------------------------------------------------------------#
441             # _list()
442             #--------------------------------------------------------------------------#
443              
444             sub _list {
445 20     20   25 my ($self, $arg, $val, $neg) = @_;
446 20         20 my $value;
447 20 100       27 if ($neg) {
448 4         4 $value = $val;
449             }
450             else {
451 16 100       27 $value = defined $val ? $val : shift @{$self->{target}};
  13         18  
452 16 100       27 if (! defined $val) {
453 13 100       26 if (! defined $value) {
454 1         5 throw_argv("Option '$self->{spec}{$arg}{canon}' requires a value");
455             }
456 12         83 $value =~ s/^$NEGATIVE(.*)$/$1/;
457             }
458              
459 15 100 100     39 throw_argv("Ambiguous value for $self->{spec}{$arg}{canon} could be option: $value")
460             if ! defined $val and _find_arg($self, $value);
461             throw_argv("Invalid list option $self->{spec}{$arg}{canon} = $value")
462 14 100       34 unless _validate_value($self, $value, $self->{spec}{$arg}{valid});
463             }
464 17         21 push @{$self->{parsed}}, [ $arg, $value, $neg ];
  17         52  
465             }
466              
467             #--------------------------------------------------------------------------#
468             # _parameter()
469             #--------------------------------------------------------------------------#
470              
471             sub _parameter {
472 52     52   76 my ($self, $arg, $val, $neg) = @_;
473 52         45 my $value;
474 52 100       90 if ($neg) {
475 6 100       13 throw_argv("Negated parameter option can't take a value: $self->{spec}{$arg}{canon}=$val")
476             if defined $val;
477             }
478             else {
479 46 100       62 $value = defined $val ? $val : shift @{$self->{target}};
  39         58  
480 46 100       88 if (! defined $val) {
481 39 100       67 if (! defined $value) {
482 2         11 throw_argv("Option '$self->{spec}{$arg}{canon}' requires a value");
483             }
484 37         160 $value =~ s/^$NEGATIVE(.*)$/$1/;
485             }
486 44 100 100     111 throw_argv("Ambiguous value for $self->{spec}{$arg}{canon} could be option: $value")
487             if ! defined $val and _find_arg($self, $value);
488             throw_argv("Invalid parameter $self->{spec}{$arg}{canon} = $value")
489 41 100       109 unless _validate_value($self, $value, $self->{spec}{$arg}{valid});
490             }
491 42         58 push @{$self->{parsed}}, [ $arg, $value, $neg ];
  42         134  
492             }
493              
494             #--------------------------------------------------------------------------#
495             # _parse_spec()
496             #--------------------------------------------------------------------------#
497              
498             sub _parse_spec {
499 188     188   152 my ($self) = @_;
500 188         173 my $spec = $self->{raw_spec};
501 188         210 for my $opt ( @$spec ) {
502 534         539 my $name = $opt->{name};
503 534         907 my @names = split( /\|/, $name );
504 534         532 $opt->{canon} = $names[0];
505 534         702 _validate_spec($self,\@names,$opt);
506 526 100       831 @names = map { s/^-*//; $_ } @names unless $self->{strict}; ## no critic
  792         1211  
  792         1263  
507 526         570 for (@names) {
508 840         1041 $self->{alias_hr}{$_} = $names[0];
509 840 100       1233 $self->{alias_nocase}{$_} = $names[0] if $opt->{nocase};
510             }
511 526         586 $self->{spec}{$names[0]} = $opt;
512 526         1029 ($self->{strip}{$names[0]} = $names[0]) =~ s/^-+//;
513             }
514 180         269 _validate_prereqs($self);
515             }
516              
517             #--------------------------------------------------------------------------#
518             # _recalculate_options()
519             #--------------------------------------------------------------------------#
520              
521             sub _recalculate_options {
522 121     121   105 my ($self) = @_;
523 121         99 my %result;
524 121         81 for my $k ( keys %{$self->{default}} ) {
  121         235  
525 463         378 my $d = $self->{default}{$k};
526 463 100       957 $result{$k} = ref($d) eq 'ARRAY' ? [ @$d ] :
    100          
527             ref($d) eq 'HASH' ? { %$d } : $d;
528             }
529 121         116 for my $opt ( @{$self->{parsed}} ) {
  121         141  
530 274         296 my ($name, $value, $neg) = @$opt;
531 274         288 for ($self->{spec}{$name}{type}) {
532 274         224 my $strip = $self->{strip}{$name};
533 274 100       397 /switch/ && do {
534 59 100       87 $result{$strip} = $neg ? 0 : $value;
535 59         70 last;
536             };
537 215 100       317 /counter/ && do {
538 102 100       137 $result{$strip} = $neg ? 0 : $result{$strip} + $value;
539 102         94 last;
540             };
541 113 100       160 /parameter/ && do {
542 42 100       68 $result{$strip} = $neg ? "" : $value;
543 42         43 last;
544             };
545 71 100       99 /list/ && do {
546 40 100       45 if ($neg) {
547             $result{$strip} = $value ?
548 4 100       7 [ grep { $_ ne $value } @{$result{$strip}} ] :
  4         7  
  2         4  
549             [];
550             }
551 36         24 else { push @{$result{$strip}}, $value }
  36         46  
552 40         36 last;
553             };
554 31 50       57 /keypair/ && do {
555 31 100       39 if ($neg) {
556 4 100       6 if ($value->[0]) { delete $result{$strip}{$value->[0]} }
  2         4  
557 2         2 else { $result{$strip} = {} }
558             }
559 27         49 else { $result{$strip}{$value->[0]} = $value->[1]};
560 31         40 last;
561             };
562             }
563             }
564 121         187 return $self->{options} = \%result;
565             }
566              
567             #--------------------------------------------------------------------------#
568             # _regex_or_code
569             #--------------------------------------------------------------------------#
570              
571             sub _regex_or_code {
572 174     174   147 my ($value,$valid) = @_;
573 174 100       247 return 1 unless defined $valid;
574 171 100       196 if ( ref($valid) eq 'CODE' ) {
575 10         11 local $_ = $value;
576 10         16 return $valid->($value);
577             } else {
578 161         2732 return $value =~ /^$valid$/;
579             }
580             }
581              
582             #--------------------------------------------------------------------------#
583             # _set_defaults()
584             #--------------------------------------------------------------------------#
585              
586             sub _set_defaults {
587 185     185   147 my ($self) = @_;
588 185         148 my %default;
589 185         133 for my $k ( keys %{$self->{spec}} ) {
  185         283  
590 581         513 my $spec = $self->{spec}{$k};
591 581 100       685 my $d = exists ($spec->{default}) ? $spec->{default} : undef;
592 581         467 my $type = $self->{spec}{$k}{type};
593 581         473 my $strip = $self->{strip}{$k};
594 581 50 100     1259 throw_spec("Default for list '$spec->{canon}' must be array reference")
      66        
595             if ( $type eq "list" && defined $d && ref($d) ne "ARRAY" );
596 581 100 100     1076 throw_spec("Default for keypair '$spec->{canon}' must be hash reference")
      100        
597             if ( $type eq "keypair" && defined $d && ref($d) ne "HASH" );
598 580 100       725 if (defined $d) {
599             throw_spec("Default '$spec->{canon}' = '$d' fails to validate")
600 217 100       381 unless _validate_value($self, $d, $spec->{valid});
601             }
602 579         442 $default{$strip} = do {
603 579         516 local $_ = $type;
604 579 100       3069 /switch/ ? (defined $d ? $d: 0) :
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    100          
605             /counter/ ? (defined $d ? $d: 0) :
606             /parameter/ ? $d :
607             /list/ ? (defined $d ? dclone($d): []) :
608             /keypair/ ? (defined $d ? dclone($d): {}) :
609             undef;
610             };
611             }
612 183         360 $self->{default} = \%default;
613             }
614              
615             #--------------------------------------------------------------------------#
616             # _split_equals()
617             #--------------------------------------------------------------------------#
618              
619             sub _split_equals {
620 272     272   248 my ($self,$raw) = @_;
621 272         188 my ($arg,$val);
622 272 100       1347 if ( $raw =~ /^($NEGATIVE?$VALID_NAME|$SHORT_BUNDLE)=(.*)/ ) {
623 27         48 $arg = $1;
624 27         30 $val = $2;
625             } else {
626 245         210 $arg = $raw;
627             }
628 272         472 return ($arg, $val);
629             }
630              
631             #--------------------------------------------------------------------------#
632             # _switch()
633             #--------------------------------------------------------------------------#
634              
635             sub _switch {
636 54     54   55 my ($self, $arg, $val, $neg) = @_;
637 54 100       91 throw_argv("Switch can't take a value: $self->{spec}{$arg}{canon}=$val")
638             if defined $val;
639 52 100       97 if (! $neg ) {
640             throw_argv("Switch used twice: $self->{spec}{$arg}{canon}")
641 48 100       89 if $self->{seen}{$arg} > 1;
642             }
643 51         34 push @{$self->{parsed}}, [ $arg, 1, $neg ];
  51         142  
644             }
645              
646             #--------------------------------------------------------------------------#
647             # _unbundle()
648             #--------------------------------------------------------------------------#
649              
650             sub _unbundle {
651 272     272   239 my ($self,$arg, $val) = @_;
652 272 100       799 if ( $arg =~ /^$SHORT_BUNDLE$/ ) {
653 24         67 my @flags = split(//,substr($arg,1));
654 24 100       41 unshift @{$self->{target}}, ("-" . pop(@flags) . "=" . $val)
  4         38  
655             if defined $val;
656 24         31 for ( reverse @flags ) {
657 61         44 unshift @{$self->{target}}, "-$_";
  61         89  
658             }
659 24         91 return 1;
660             }
661 248         424 return 0;
662             }
663              
664             #--------------------------------------------------------------------------#
665             # _validate_prereqs()
666             #--------------------------------------------------------------------------#
667              
668             sub _validate_prereqs {
669 180     180   158 my ($self) = @_;
670 180         147 for my $key ( keys %{$self->{spec}} ) {
  180         329  
671 523 100       818 next unless exists $self->{spec}{$key}{needs};
672 18         20 my $needs = $self->{spec}{$key}{needs};
673 18 50       46 my @prereq = ref($needs) eq 'ARRAY' ? @$needs : ( $needs );
674 18         26 for (@prereq) {
675 24 100       33 throw_spec("Prerequisite '$_' for '$self->{spec}{$key}{canon}' is not recognized")
676             unless _find_arg($self,$_);
677 23         30 $_ = _find_arg($self,$_);
678             }
679 17         32 $self->{spec}{$key}{needs} = \@prereq;
680             }
681             }
682              
683              
684             #--------------------------------------------------------------------------#
685             # _validate_spec()
686             #--------------------------------------------------------------------------#
687              
688             sub _validate_spec {
689 534     534   466 my ($self,$names,$details) = @_;
690 534         448 for my $name ( @$names ) {
691 849         667 my $alt_name = $name;
692 849 100       2124 $alt_name =~ s/^-*// unless $self->{strict};
693 849 100       3627 throw_spec(
694             "'$name' is not a valid option name/alias"
695             ) unless $name =~ /^$VALID_NAME$/;
696             throw_spec(
697             "'$name' is not unique"
698 847 100       1388 ) if exists $self->{alias_hr}{$alt_name};
699 844         536 my $strip;
700 844         1171 ($strip = $name) =~ s/^-+//;
701             throw_spec(
702             "'$strip' conflicts with other options"
703 844 50       627 ) if grep { $strip eq $_ } values %{$self->{strip}};
  1023         1668  
  844         1707  
704             }
705 529         905 for my $key ( keys %$details ) {
706             throw_spec(
707             "'$key' is not a valid option specification key"
708 1880 100       1435 ) unless grep { $key eq $_ } @valid_keys;
  13160         12331  
709             }
710 528         605 my $type = $details->{type};
711             throw_spec(
712             "'$type' is not a valid option type"
713 528 100       431 ) unless grep { $type eq $_ } @valid_types;
  2640         2820  
714             }
715              
716             #--------------------------------------------------------------------------#
717             # _validate_value()
718             #--------------------------------------------------------------------------#
719              
720             sub _validate_value {
721 388     388   503 my ($self, $value, $valid) = @_;
722 388 100       859 return 1 unless defined $valid;
723 102 100       199 if ( ref($value) eq 'HASH' ) {
    100          
724 30         29 my $valid_key = $valid->[0];
725 30         26 my $valid_val = $valid->[1];
726 30         74 while (my ($k,$v) = each %$value) {
727 44 100       54 _regex_or_code($k, $valid_key) or return 0;
728 42 100       53 _regex_or_code($v, $valid_val) or return 0;
729             }
730 26         79 return 1;
731             } elsif ( ref($value) eq 'ARRAY' ) {
732 20         23 for (@$value) {
733 36 50       42 _regex_or_code($_, $valid) or return 0;
734             }
735 20         34 return 1;
736             } else {
737 52         65 return _regex_or_code($value, $valid);
738             }
739             }
740              
741             #--------------------------------------------------------------------------#
742             # AUTOLOAD()
743             #--------------------------------------------------------------------------#
744              
745             sub AUTOLOAD {
746 204     204   96346 my $self = shift;
747 204         226 my $name = $Getopt::Lucid::AUTOLOAD;
748 204         804 $name =~ s/.*:://; # strip fully-qualified portion
749 204 100       1658 return if $name eq "DESTROY";
750 15         39 my ($action, $maybe_opt) = $name =~ /^(get|set)_(.+)/ ;
751 15 50       23 if ($action) {
752             # look for a match
753 15         11 my $opt;
754             SEARCH:
755 15         9 for my $known_opt ( values %{ $self->{strip} } ) {
  15         43  
756 45 100       51 if ( $maybe_opt eq $known_opt ) {
757 12         8 $opt = $known_opt;
758 12         15 last SEARCH;
759             }
760             # try without dashes
761 33         32 (my $fuzzy_opt = $known_opt) =~ s/-/_/g;
762 33 100       49 if ( $maybe_opt eq $fuzzy_opt ) {
763 3         2 $opt = $known_opt;
764 3         4 last SEARCH;
765             }
766             }
767              
768             # throw if no valid option was found
769 15 50       23 throw_usage("Can't $action unknown option '$maybe_opt'")
770             if ! $opt;
771              
772             # handle the accessor if an option was found
773 15 100       19 if ($action eq "set") {
774             $self->{options}{$opt} =
775             ref($self->{options}{$opt}) eq 'ARRAY' ? [@_] :
776 5 100       19 ref($self->{options}{$opt}) eq 'HASH' ? {@_} : shift;
    100          
777              
778             }
779 15         17 my $ans = $self->{options}{$opt};
780 15 100       107 return ref($ans) eq 'ARRAY' ? @$ans :
    100          
781             ref($ans) eq 'HASH' ? %$ans : $ans;
782             }
783 0           my $super = "SUPER::$name";
784 0           $self->$super(@_);
785             }
786              
787             1; # modules must be true
788              
789             __END__