File Coverage

blib/lib/Getargs/Long.pm
Criterion Covered Total %
statement 197 217 90.7
branch 117 152 76.9
condition 36 47 76.6
subroutine 18 20 90.0
pod 4 4 100.0
total 372 440 84.5


line stmt bran cond sub pod time code
1             # -*- Mode: perl -*-
2              
3 5     5   140334 use strict;
  5         44  
  5         149  
4 5     5   140 use 5.005;
  5         27  
5              
6             package Getargs::Long;
7              
8 5     5   89 use vars qw($VERSION @ISA @EXPORT);
  5         15  
  5         682  
9             $VERSION = sprintf "%d.%02d%02d", q/1.10.11/ =~ /(\d+)/g;
10              
11             BEGIN
12             {
13 5 50   5   156 die "This module is known to exercise a bug in 5.6.0. Please upgrade your perl.\n"
14             if $] eq '5.006';
15             }
16              
17 5     5   2372 use Log::Agent;
  5         39661  
  5         460  
18 5     5   2789 use Data::Dumper;
  5         29721  
  5         327  
19              
20             require Exporter;
21 5     5   32 use vars qw(@EXPORT);
  5         10  
  5         10425  
22             @ISA = qw(Exporter);
23             @EXPORT = qw(getargs cgetargs xgetargs cxgetargs);
24              
25             #
26             # %ignore
27             #
28             # Cache whether argument names are to be handled case-insensitively or not,
29             # on a package basis. Default is case-sensitive processing.
30             #
31             my %ignore = ();
32              
33             #
34             # ->import
35             #
36             # Trap Exporter's one to handle 'ignorecase' here (or lack thereof).
37             # Then use ->export_to_level() to tell Exporter to continue the export
38             # as if its import method had been called directly via inheritance.
39             #
40             sub import {
41 6     6   50 my $module = shift;
42 6         15 my @syms = grep($_ ne 'ignorecase', @_);
43 6         13 my $callpkg = caller;
44              
45 6 100       18 if (@syms == @_) { # There was no "ignorecase" seen
46 3         5 delete $ignore{$callpkg};
47 3         44 logdbg 'info', "will process arguments case-sensitively in $callpkg";
48             } else {
49 3         7 $ignore{$callpkg} = 1;
50 3         36 logdbg 'info', "will process arguments case-insensitively in $callpkg";
51             }
52              
53 6         6320 Getargs::Long->export_to_level(1, $module, @syms);
54             }
55              
56             #
57             # %subcache
58             #
59             # Cache validation routine, indexed by "package::routine".
60             #
61             my %subcache = ();
62              
63             #
64             # getargs
65             #
66             # Parse arguments for subroutine, and validate them if typechecking requested.
67             # Optional arguments with no default return undef. Mandatory arguments cannot
68             # be undefined.
69             #
70 9     9 1 14171 sub getargs (\@@) { _getargs(scalar(caller), 0, "", @_) } ## no critic (ProhibitSubroutinePrototypes)
71              
72             #
73             # cgetargs
74             #
75             # Same as getargs, but cache data for next call.
76             #
77             # When called from within an eval, caching is not possible, so this routine
78             # must not be called.
79             #
80             sub cgetargs (\@@) { ## no critic (ProhibitSubroutinePrototypes)
81 0     0 1 0 my $sub = (caller(1))[3]; # Anomaly in caller(), will also get pkg name
82 0 0       0 logcroak "can't call cgetargs from within an eval"
83             if $sub =~ /^\(eval/;
84 0         0 _getargs(scalar(caller), 0, $sub, @_)
85             }
86              
87             #
88             # xgetargs
89             #
90             # Like getargs(), but with extended specifications allowing to specify
91             # defaults for non-mandatory arguments.
92             #
93 8     8 1 23567 sub xgetargs (\@@) { _getargs(scalar(caller), 1, "", @_) } ## no critic (ProhibitSubroutinePrototypes)
94              
95             #
96             # cxgetargs
97             #
98             # Like cgetargs(), but with extended specifications allowing to specify
99             # defaults for non-mandatory arguments. Be careful: those defaults are
100             # deep-cloned and "frozen", so to speak.
101             #
102             # When called from within an eval, caching is not possible, so this routine
103             # must not be called.
104             #
105             sub cxgetargs (\@@) { ## no critic (ProhibitSubroutinePrototypes)
106 8     8 1 17904 my $sub = (caller(1))[3]; # Anomaly in caller(), will also get pkg name
107 8 50       73 logcroak "can't call cxgetargs from within an eval"
108             if $sub =~ /^\(eval/;
109 8         28 _getargs(scalar(caller), 1, $sub, @_)
110             }
111              
112             #
113             # _getargs
114             #
115             # Factorized work for *getargs() routines
116             #
117             # Our signature is:
118             #
119             # _getargs(
120             # # arguments added by our wrappers
121             # $callpkg, $extended, $subname,
122             # # argument list to parse
123             # \@x,
124             # # optional switches
125             # {
126             # -strict => 1, # unknown switches are fatal
127             # -inplace => 1, # edit \@x inplace: remove parsed args
128             # -ignorecase => 1, # override package's global
129             # -extra => 0, # suppress return of extra arguments
130             # },
131             # # argument definition list
132             #
133             # );
134             #
135             # With:
136             # $callpkg Calling package
137             # $extended Are they using x*getargs()?
138             # $subname Cache key, if we use it
139             #
140             # Returns the list of values in the same order given in the definition list
141             # (the part), followed by the extra arguments we did not recognize,
142             # with leading '-' removal and transformation to lowercase if ignorecase is on.
143             #
144             sub _getargs {
145 25     25   89 my ($callpkg, $extended, $subname, $args) = splice(@_, 0, 4);
146              
147 25 50       107 logconfess "first argument must be a reference to the argument list"
148             unless ref $args eq 'ARRAY';
149              
150             #
151             # Check cache if told to do so.
152             #
153              
154 25 100       66 if ($subname ne '') {
155 8         16 my $sref = $subcache{$subname};
156 8 100       20 if (defined $sref) {
157 6         158 logdbg 'info', "calling cached subroutine $sref";
158 6         571 return &$sref($args);
159             } else {
160 2         49 logdbg 'info', "no cached subroutine yet for $subname";
161             }
162             }
163              
164             #
165             # Nothing in cache, or cache was disabled.
166             #
167              
168 19 100       228 my $case_insensitive = $ignore{$callpkg} ? 1 : 0;
169 19         545 logdbg 'info', "case_insensitive=$case_insensitive for package $callpkg";
170              
171             #
172             # If next argument is a HASH, then it's a set of extra switches that
173             # may alter our behaviour. Parse them manually.
174             #
175             # Following are the defaults:
176             #
177              
178 19         1652 my $strict = 1; # Fatal error on unknown switches
179 19         32 my $inplace = 0; # No inplace editing of arguments
180 19         30 my $extra; # Don't return extra args by default
181              
182 19 100       66 if (ref $_[0] eq 'HASH') {
183 8         17 my $swref = shift;
184              
185 8         42 my %set = (
186             -strict => \$strict,
187             -ignorecase => \$case_insensitive,
188             -inplace => \$inplace,
189             -extra => \$extra,
190             );
191              
192 8         41 while (my ($sw, $val) = each %$swref) {
193 19         46 my $vset = $set{lc($sw)};
194 19 50       44 logcroak "unknown switch $sw" unless ref $vset;
195 19         63 $$vset = $val;
196             }
197              
198             #
199             # If they did not set -extra, compute suitable default: false
200             # when -strict, true otherwise.
201             #
202              
203 8 50       44 $extra = $strict ? 0 : 1 unless defined $extra;
    100          
204              
205             #
206             # If strict, we ignore true settings for -inplace and -extra
207             #
208              
209 8 50       31 if ($strict) {
210 0 0       0 if ($inplace) {
211 0         0 logcarp "ignoring -inplace when -strict";
212 0         0 $inplace = 0;
213             }
214 0 0       0 if ($extra) {
215 0         0 logcarp "ignoring -extra when -strict";
216 0         0 $extra = 0;
217             }
218             }
219             }
220              
221             #
222             # If we have one argument, it may be '[list]' or 'x'.
223             # In extended mode, we must have an even amount of arguments.
224             #
225              
226 19         30 my @specs; # User specification list
227 19         30 my $all_optional = 0; # True if all arguments are optional
228              
229 19 100 66     73 if (@_ == 1 && ref $_[0]) {
230 3 50       9 logcroak "must use an array reference for optional args"
231             unless ref $_[0] eq 'ARRAY';
232 3         5 @specs = @{$_[0]};
  3         9  
233 3         6 $all_optional = 1;
234             } else {
235 16         47 @specs = @_;
236 16 50 66     76 logcroak "must supply an even amount of arguments in extend mode"
237             if $extended && (@specs % 2);
238             }
239              
240             #
241             # Parse our argument list and compile it into @args
242             #
243              
244 19         37 my %seen;
245             my @args; # List of [name, type, is_optional, default]
246              
247 19 100       70 for (my $i = 0, my $step = $extended ? 2 : 1; $i < @specs; $i += $step) {
248 80         136 my $arg = $specs[$i];
249 80         108 my ($name, $type, $optional, $dflt);
250 80 100       123 if ($extended) {
251 50         65 $name = $arg;
252 50         82 my $spec = $specs[$i+1];
253 50 100       90 if (ref $spec) {
254             # Given as an array ref -> optional, with possible default
255 35 50       70 logcroak "specs for optional '$name' are $spec, expected ARRAY"
256             unless ref $spec eq 'ARRAY';
257 35         66 ($type, $dflt) = @$spec;
258 35         58 $optional = 1;
259             } else {
260             # simple scalar is type, argument is mandatory
261 15         30 $type = $spec;
262 15         22 $optional = 0;
263             }
264             } else {
265             # Can be either "name" or "name=Type"
266 30         88 ($name, $type) = $arg =~ /^-?(\w+)=(\S+)/;
267 30 100       55 $name = $arg unless defined $name;
268 30         32 $optional = $all_optional;
269             }
270              
271 80 100       140 $name = lc($name) if $case_insensitive;
272 80         150 $name =~ s/^-//;
273              
274 80 50       151 logcroak "argument name cannot be empty" if $name eq '';
275 80 50       123 logcroak "argument name must be scalar, not $name" if ref $name;
276 80 50       192 logcroak "duplicate argument definition for '$name'" if $seen{$name}++;
277              
278 80 100       363 push(@args, [
    100          
279             $name,
280             defined($type) ? $type : undef,
281             $optional,
282             defined($dflt) ? $dflt : undef
283             ]);
284             }
285              
286             #
287             # If caching, generate the subroutine that will perform the checks.
288             #
289             # We use logxcroak to report errors to the caller of the caller
290             # of *getargs, i.e. the caller of the routine for which we're checking
291             # the arguments.
292             #
293              
294 19 100       44 if ($subname ne '') {
295 2 50       5 my $lc = $case_insensitive ? 'lc' : '';
296 2         7 my $sub = &_q(<<'EOS');
297             :sub {
298             : my $aref_orig = shift;
299             : my @result;
300             : my $cur;
301             : my $isthere;
302             : my $ctype;
303             : local $Getargs::Long::dflt;
304             : my $i = 0;
305             EOS
306 2         8 $sub .= &_q(<
307             : logxcroak 3, "expected an even number of arguments" if \@\$aref_orig % 2;
308             :
309             : my \%args = map {
310             : (\$i++ % 2) ? \$_ : $lc(/^-/ ? substr(\$_, 1) : \$_) } \@\$aref_orig;
311             EOS
312              
313             # Sanity check: no argument can be given twice
314 2         6 $sub .= &_q(<
315             : _spot_dups(\$aref_orig, $case_insensitive, 3)
316             : if 2 * scalar(keys \%args) != \@\$aref_orig;
317             :
318             EOS
319             # Work on a copy if extra and no inplace
320 2 50 33     8 if ($extra && !$inplace) {
321 0         0 $sub .= &_q(<<'EOS');
322             : my $aref = [@$aref_orig];
323             EOS
324             } else {
325 2         4 $sub .= &_q(<<'EOS');
326             : my $aref = $aref_orig;
327             EOS
328             }
329              
330             # Index arguments if inplace editing or extra
331 2 100 66     16 if ($inplace || $extra) {
332 1         3 $sub .= &_q(<<'EOS');
333             :
334             : my $idx;
335             : my %idx;
336             : for (my $j = 0; $j < @$aref; $j += 2) {
337             : my $key = $aref->[$j];
338             : $key =~ s/^-//;
339             EOS
340 1 50       5 $sub .= &_q(<<'EOS') if $case_insensitive;
341             : $key = lc($key);
342             EOS
343 1         3 $sub .= &_q(<<'EOS');
344             : $idx{$key} = $j;
345             : }
346             :
347             EOS
348             }
349              
350 2         5 foreach my $arg (@args) {
351 10         21 my ($name, $type, $optional, $dflt) = @$arg;
352 10         15 my $has_default = defined $dflt;
353 10         23 local $^W = 0; # Shut up Test::Harness
354 10         37 $sub .= &_q(<
355             : # Argument [name=$name, type=$type, optional=$optional, dflt=$dflt]
356             : \$cur = undef;
357             : \$isthere = 0;
358             : if (exists \$args{$name}) {
359             : \$isthere = 1;
360             : my \$val = delete \$args{$name};
361             : \$cur = \\\$val;
362             EOS
363 10 100 66     32 $sub .= &_q(<
364             : # Splice argument out
365             : \$idx = \$idx{$name};
366             EOS
367 10 100 66     26 $sub .= &_q(<<'EOS') if $inplace || $extra;
368             : splice(@$aref, $idx, 2);
369             : while (my ($k, $v) = each %idx) {
370             : $idx{$k} -= 2 if $v > $idx;
371             : }
372             EOS
373 10         13 $sub .= &_q(<<'EOS');
374             : }
375             EOS
376 10 100       17 if ($optional) {
377 7 100       14 if ($has_default) {
378 4         7 $sub .= &_q(<
379             : else {
380             : eval {
381             : package Getargs::Long::_;
382             : no strict;
383             : \$Getargs::Long::dflt =
384             EOS
385 4         40 my $obj = Data::Dumper->new([$dflt], []);
386 4         132 $obj->Purity(1);
387 4         90 $sub .= $obj->Dumpxs;
388 4         9 $sub .= &_q(<<'EOS');
389             : };
390             : $cur = \$Getargs::Long::dflt;
391             : }
392             EOS
393             }
394             } else {
395 3         9 $sub .= &_q(<
396             : logxcroak 3, "mandatory argument '$name' missing" unless \$isthere;
397             EOS
398             }
399 10 100       20 if ($type ne '') {
400 7 100       12 if ($optional) {
401 5         11 $sub .= &_q(<
402             : logxcroak 3, "argument '$name' cannot be undef"
403             : if \$isthere && !defined \$\$cur;
404             EOS
405             } else {
406 2         5 $sub .= &_q(<
407             : logxcroak 3, "argument '$name' cannot be undef" unless defined \$\$cur;
408             EOS
409             }
410 7 100       18 my $opt_is_there = $optional ? "\$isthere &&" : "";
411 7 100       18 if ($type =~ /^[isn]$/) { # Make sure it's a scalar
412             # XXX Check that i is integer, s string and n natural
413 3         11 $sub .= &_q(<
414             : logxcroak 3,
415             : "argument '$name' must be scalar (type '$type') but is \$\$cur"
416             : if $opt_is_there ref \$\$cur;
417             EOS
418             } else {
419 4         15 $sub .= &_q(<
420             : \$ctype = \$isthere ? ref \$\$cur : undef;
421             : logxcroak 3, "argument '$name' must be of type $type but is \$ctype"
422             : if $opt_is_there (UNIVERSAL::isa(\$\$cur, 'UNIVERSAL') ?
423             : !\$\$cur->isa('$type') :
424             : \$ctype ne '$type');
425             EOS
426             }
427             }
428 10         16 $sub .= &_q(<<'EOS');
429             : push(@result, defined($cur) ? $$cur : undef);
430             :
431             EOS
432             }
433              
434             # If we're strict, we must report unprocessed switches
435 2 100       6 $sub .= &_q(<<'EOS') if $strict;
436             :
437             : _spot_unknown(\%args, 3) if scalar keys %args;
438             :
439             EOS
440              
441             # Add extra unprocessed switches to the result list
442 2 50       5 $sub .= &_q(<<'EOS') if $extra;
443             : push(@result, @$aref);
444             EOS
445 2         5 $sub .= &_q(<<'EOS');
446             : return @result;
447             :}
448             EOS
449 2         45 logdbg 'debug', "anonymous subroutine: $sub";
450 2     1   734 my $code = eval $sub; ## no critic (ProhibitStringyEval)
  1     1   9  
  1     1   4  
  1     1   240  
  1         6  
  1         2  
  1         226  
  1         6  
  1         2  
  1         281  
  1         7  
  1         3  
  1         323  
451 2 50       9 if (chop($@)) {
452 0         0 logerr "can't create subroutine for checking args of $subname: $@";
453 0         0 logwarn "ignoring caching directive for $subname";
454             } else {
455 2         7 $subcache{$subname} = $code;
456 2         50 logdbg 'info', "calling newly built subroutine $code";
457 2         170 return &$code($args);
458             }
459             }
460              
461             #
462             # No caching made, perform validation by interpreting the structure
463             #
464             # There is some unfortunate code duplication between the following checks
465             # and the above routine-construction logic. Some place are identical,
466             # but the main argument processing loop is noticeably different, even
467             # though the same logic is used.
468             #
469              
470 17         364 logdbg 'info', "interpreting structure to validate arguments";
471              
472 17         1279 my @result;
473             my $cur;
474 17         0 my $ctype;
475              
476 17         26 my $i = 0;
477 17         21 my %args;
478              
479 17 100 66     57 $args = [@$args] if $extra && !$inplace; # Work on a copy
480              
481 17 50       57 logxcroak 2, "expected an even number of arguments" if @$args % 2;
482              
483 17 100       39 if ($case_insensitive) {
484 10 100       24 %args = map { ($i++ % 2) ? $_ : lc(/^-/ ? substr($_, 1) : $_) } @$args;
  66 100       233  
485             } else {
486 7 50       16 %args = map { ($i++ % 2) ? $_ : (/^-/ ? substr($_, 1) : $_) } @$args;
  52 100       139  
487             }
488              
489             # Sanity check: no argument can be given twice
490 17 50       68 _spot_dups($args, $case_insensitive, 2)
491             if 2 * scalar(keys %args) != @$args;
492              
493             # Index arguments if inplace editing or extra
494 17         24 my %idx;
495 17 100 100     73 if ($inplace || $extra) {
496 7         35 for (my $j = 0; $j < @$args; $j += 2) {
497 19         32 my $key = $args->[$j];
498 19         53 $key =~ s/^-//;
499 19 100       50 $key = lc($key) if $case_insensitive;
500 19         55 $idx{$key} = $j;
501             }
502             }
503              
504             # Process each argument
505 17         28 foreach my $arg (@args) {
506 68         133 my ($name, $type, $optional, $dflt) = @$arg;
507 68         78 my $cur;
508 68         87 my $isthere = 0;
509 68 100       121 if (exists $args{$name}) {
    100          
510 50         60 $isthere = 1;
511 50         72 my $val = delete $args{$name};
512 50         71 $cur = \$val;
513              
514             # Splice argument out if requested
515 50 100 100     131 if ($inplace || $extra) {
516 17         31 my $idx = $idx{$name};
517 17         31 splice(@$args, $idx, 2);
518 17         55 while (my ($k, $v) = each %idx) {
519 53 100       191 $idx{$k} -= 2 if $v > $idx;
520             }
521             }
522             } elsif ($optional) {
523 15 100       32 $cur = \$dflt if defined $dflt;
524             } else {
525 3         55 logxcroak 2, "mandatory argument '$name' missing";
526             }
527              
528 65 100       137 push(@result, defined($cur) ? $$cur : undef);
529 65 100 66     205 next if !defined $type || $type eq '';
530              
531 42 100       69 if ($optional) {
532 29 50 66     95 logxcroak 2, "argument '$name' cannot be undef"
533             if $isthere && !defined $$cur;
534             } else {
535 13 100       63 logxcroak 2,
536             "argument '$name' cannot be undef" unless defined $$cur;
537             }
538              
539             # XXX Check that i is integer, s string and n natural
540 41 100       114 if ($type =~ /^[isn]$/) { # Make sure it's a scalar
541 16 50 100     88 logxcroak 2,
      66        
542             "argument '$name' must be scalar (type '$type') but is $$cur"
543             if (!$optional || $isthere) && ref $$cur;
544             } else {
545 25 100       54 my $ctype = $isthere ? ref $$cur : undef;
546 25 100 100     183 logxcroak 2, "argument '$name' must be of type $type but is $ctype"
    100 100        
547             if (!$optional || $isthere) &&
548             (UNIVERSAL::isa($$cur, 'UNIVERSAL') ?
549             !$$cur->isa($type) :
550             $ctype ne $type);
551             }
552             }
553              
554             # If we're strict, we must report unprocessed switches
555 12 100 100     58 _spot_unknown(\%args, 2) if $strict && scalar keys %args;
556              
557             # Add extra unprocessed switches to the result list
558 8 100       31 push(@result, @$args) if $extra;
559              
560 8         65 return @result;
561             }
562              
563             #
564             # _spot_dups
565             #
566             # Given a list of arguments in $aref, where we know there are duplicate "keys",
567             # identify them and croak by listing the culprits.
568             #
569             sub _spot_dups {
570 0     0   0 my ($aref, $ignorecase, $level) = @_;
571 0         0 my %seen;
572             my @duplicates;
573 0         0 for (my $i = 0; $i < @$aref; $i += 2) {
574 0 0       0 my $key = $ignorecase ? lc($aref->[$i]) : $aref->[$i];
575 0         0 $key =~ s/^-//;
576 0 0       0 push(@duplicates, "-$key") if $seen{$key}++;
577             }
578 0 0       0 logconfess "bug in Getargs::Long -- should have found duplicates"
579             unless @duplicates;
580 0         0 logxcroak ++$level,
581             "multiple switches given for: " . join(", ", @duplicates);
582             }
583              
584             #
585             # _spot_unknown
586             #
587             # Report keys held in supplied hashref as unknown switches.
588             #
589             sub _spot_unknown {
590 5     5   11 my ($href, $level) = @_;
591 5         16 my @unprocessed = map { "-$_" } keys %$href;
  5         19  
592 5 50       16 my $es = @unprocessed == 1 ? '' : 'es';
593 5         94 logxcroak ++$level, "unknown switch$es: " . join(", ", @unprocessed);
594             }
595              
596             sub _q {
597 79     79   103 local $_ = shift;
598 79         276 s/^://gm;
599 79         190 return $_;
600             }
601              
602             1;
603              
604             __END__