File Coverage

blib/lib/Getopt/Long/EvenLess.pm
Criterion Covered Total %
statement 106 114 92.9
branch 51 64 79.6
condition 6 6 100.0
subroutine 5 6 83.3
pod 3 3 100.0
total 171 193 88.6


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