File Coverage

blib/lib/Getopt/Long/EvenLess.pm
Criterion Covered Total %
statement 105 112 93.7
branch 47 58 81.0
condition 3 3 100.0
subroutine 5 6 83.3
pod 3 3 100.0
total 163 182 89.5


line stmt bran cond sub pod time code
1             package Getopt::Long::EvenLess;
2              
3             our $DATE = '2017-08-09'; # DATE
4             our $VERSION = '0.110'; # VERSION
5              
6             # IFUNBUILT
7             # # use strict 'subs', 'vars';
8             # # use warnings;
9             # END IFUNBUILT
10              
11             our @EXPORT = qw(GetOptions);
12             our @EXPORT_OK = qw(GetOptionsFromArray);
13              
14             my $config = {
15             pass_through => 0,
16             };
17              
18             sub Configure {
19 10     10 1 19208 my $old_config = { %$config };
20              
21 10 100       30 if (ref($_[0]) eq 'HASH') {
22 5         9 for (keys %{$_[0]}) {
  5         17  
23 5         11 $config->{$_} = $_[0]{$_};
24             }
25             } else {
26 5         15 for (@_) {
27 5 50       10 if ($_ eq 'pass_through') {
    0          
28 5         12 $config->{pass_through} = 1;
29             } elsif ($_ eq 'no_pass_through') {
30 0         0 $config->{pass_through} = 0;
31             } else {
32 0         0 die "Unknown configuration '$_'";
33             }
34             }
35             }
36 10         20 $old_config;
37             }
38              
39             sub import {
40 1     1   7 my $pkg = shift;
41 1         2 my $caller = caller;
42 1 50       4 my @imp = @_ ? @_ : @EXPORT;
43 1         2 for my $imp (@imp) {
44 2 50       4 if (grep {$_ eq $imp} (@EXPORT, @EXPORT_OK)) {
  4         10  
45 2         2 *{"$caller\::$imp"} = \&{$imp};
  2         31  
  2         6  
46             } else {
47 0         0 die "$imp is not exported by ".__PACKAGE__;
48             }
49             }
50             }
51              
52             sub GetOptionsFromArray {
53 23     23 1 68654 my ($argv, %spec) = @_;
54              
55 23         41 my $success = 1;
56              
57 23         32 my %spec_by_opt_name;
58 23         51 for (keys %spec) {
59 35         48 my $orig = $_;
60 35         133 s/=[fios][@%]?\z//;
61 35         72 s/\|.+//;
62 35         85 $spec_by_opt_name{$_} = $orig;
63             }
64              
65             my $code_find_opt = sub {
66 34     34   56 my ($wanted, $short_mode) = @_;
67 34         49 my @candidates;
68             OPT_SPEC:
69 34         69 for my $spec (keys %spec) {
70 51         145 $spec =~ s/=[fios][@%]?\z//;
71 51         122 my @opts = split /\|/, $spec;
72 51         84 for my $o (@opts) {
73 64 100 100     173 next if $short_mode && length($o) > 1;
74 52 100       182 if ($o eq $wanted) {
    100          
75             # perfect match, we immediately go with this one
76 22         44 @candidates = ($opts[0]);
77 22         49 last OPT_SPEC;
78             } elsif (index($o, $wanted) == 0) {
79             # prefix match, collect candidates first
80 7         14 push @candidates, $opts[0];
81 7         15 next OPT_SPEC;
82             }
83             }
84             }
85 34 100       84 if (!@candidates) {
    100          
86 8 100       24 unless ($config->{pass_through}) {
87 4         88 warn "Unknown option: $wanted\n";
88 4         11 $success = 0;
89             }
90 8         20 return undef; # means unknown
91             } elsif (@candidates > 1) {
92 3 100       8 unless ($config->{pass_through}) {
93 1         22 warn "Option $wanted is ambiguous (" .
94             join(", ", @candidates) . ")\n";
95 1         3 $success = 0;
96             }
97 3         8 return ''; # means ambiguous
98             }
99 23         49 return $candidates[0];
100 23         111 };
101              
102             my $code_set_val = sub {
103 21     21   46 my $name = shift;
104              
105 21         41 my $spec_key = $spec_by_opt_name{$name};
106 21         33 my $handler = $spec{$spec_key};
107              
108 21 100       79 $handler->({name=>$name}, @_ ? $_[0] : 1);
109 23         65 };
110              
111 23         30 my $i = -1;
112 23         31 my @remaining;
113             ELEM:
114 23         57 while (++$i < @$argv) {
115 38 100       459 if ($argv->[$i] eq '--') {
    100          
    100          
116              
117 2         6 push @remaining, @{$argv}[$i+1 .. @$argv-1];
  2         5  
118 2         33 last ELEM;
119              
120             } elsif ($argv->[$i] =~ /\A--(.+?)(?:=(.*))?\z/) {
121              
122 26         97 my ($used_name, $val_in_opt) = ($1, $2);
123 26         49 my $opt = $code_find_opt->($used_name);
124 26 100       108 if (!defined($opt)) {
    100          
125             # unknown option
126 7         16 push @remaining, $argv->[$i];
127 7         20 next ELEM;
128             } elsif (!length($opt)) {
129 3         6 push @remaining, $argv->[$i];
130 3         9 next ELEM; # ambiguous
131             }
132              
133 16         29 my $spec = $spec_by_opt_name{$opt};
134             # check whether option requires an argument
135 16 50       57 if ($spec =~ /=[fios][@%]?\z/) {
136 16 100       33 if (defined $val_in_opt) {
137             # argument is taken after =
138 3         20 $code_set_val->($opt, $val_in_opt);
139             } else {
140 13 100       31 if ($i+1 >= @$argv) {
141             # we are the last element
142 1         21 warn "Option $used_name requires an argument\n";
143 1         4 $success = 0;
144 1         3 last ELEM;
145             }
146 12         17 $i++;
147 12         21 $code_set_val->($opt, $argv->[$i]);
148             }
149             } else {
150 0         0 $code_set_val->($opt);
151             }
152              
153             } elsif ($argv->[$i] =~ /\A-(.*)/) {
154              
155 5         11 my $str = $1;
156 5         6 my $remaining_pushed;
157             SHORT_OPT:
158 5         22 while ($str =~ s/(.)//) {
159 8         32 my $used_name = $1;
160 8         11 my $short_opt = $1;
161 8         28 my $opt = $code_find_opt->($short_opt, 'short');
162 8 100       22 if (!defined $opt) {
    50          
163             # unknown short option
164 1 50       5 push @remaining, "-" unless $remaining_pushed++;
165 1         3 $remaining[-1] .= $short_opt;
166 1         4 next SHORT_OPT;
167             } elsif (!length $opt) {
168             # ambiguous short option
169 0 0       0 push @remaining, "-" unless $remaining_pushed++;
170 0         0 $remaining[-1] .= $short_opt;
171             }
172              
173 7         13 my $spec = $spec_by_opt_name{$opt};
174             # check whether option requires an argument
175 7 100       21 if ($spec =~ /=[fios][@%]?\z/) {
176 4 100       11 if (length $str) {
177             # argument is taken from $str
178 2         7 $code_set_val->($opt, $str);
179 2         10 next ELEM;
180             } else {
181 2 100       7 if ($i+1 >= @$argv) {
182             # we are the last element
183 1 50       4 unless ($config->{pass_through}) {
184 1         24 warn "Option $used_name requires an argument\n";
185 1         3 $success = 0;
186             }
187 1         3 last ELEM;
188             }
189             # take the next element as argument
190 1         3 $i++;
191 1         3 $code_set_val->($opt, $argv->[$i]);
192             }
193             } else {
194 3         7 $code_set_val->($opt);
195             }
196             }
197              
198             } else { # argument
199              
200 5         11 push @remaining, $argv->[$i];
201 5         11 next;
202              
203             }
204             }
205              
206             RETURN:
207 23         94 splice @$argv, 0, ~~@$argv, @remaining; # replace with remaining elements
208 23         242 return $success;
209             }
210              
211             sub GetOptions {
212 0     0 1   GetOptionsFromArray(\@ARGV, @_);
213             }
214              
215             1;
216             # ABSTRACT: Like Getopt::Long::Less, but with even less features
217              
218             __END__