File Coverage

blib/lib/Getopt/ApacheCommonsCLI.pm
Criterion Covered Total %
statement 83 94 88.3
branch 27 44 61.3
condition 3 6 50.0
subroutine 11 12 91.6
pod 0 2 0.0
total 124 158 78.4


line stmt bran cond sub pod time code
1             package Getopt::ApacheCommonsCLI;
2              
3 1     1   32573 use 5.008008;
  1         3  
  1         30  
4 1     1   3 use strict;
  1         1  
  1         26  
5 1     1   4 use warnings;
  1         7  
  1         86  
6              
7             require Exporter;
8              
9             our @ISA = qw(Exporter);
10              
11             our %EXPORT_TAGS = ();
12              
13 1     1   8 use constant OPT_PREC_UNIQUE => 1;
  1         1  
  1         68  
14 1     1   4 use constant OPT_PREC_LEFT_TO_RIGHT => 0;
  1         1  
  1         38  
15 1     1   4 use constant OPT_PREC_RIGHT_TO_LEFT => 2;
  1         1  
  1         75  
16              
17             our @EXPORT_OK = qw(
18             GetOptionsApacheCommonsCLI
19             OPT_PREC_UNIQUE
20             OPT_PREC_LEFT_TO_RIGHT
21             OPT_PREC_RIGHT_TO_LEFT
22             );
23              
24             our @EXPORT = qw(
25             );
26              
27             our $VERSION = '0.01';
28              
29             # Preloaded methods go here.
30              
31 1     1   670 use Getopt::Long 2.35;
  1         9279  
  1         22  
32              
33             sub GetOptionsApacheCommonsCLI {
34 13     13 0 11511 my ($rspec, $ropts, $roptions, $rerrsub) = @_;
35              
36             # process user-supplied options
37 13 50       35 my $DEBUG = defined $roptions->{'DEBUG'} ? $roptions->{'DEBUG'} : 0;
38 13 50       23 my $JAVA_DOPTS = defined $roptions->{'JAVA_DOPTS'} ? $roptions->{'JAVA_DOPTS'} : 0;
39 13 50       23 my $OPT_PREC = defined $roptions->{'OPT_PRECEDENCE'} ? $roptions->{'OPT_PRECEDENCE'} : OPT_PREC_RIGHT_TO_LEFT;
40 13 50       18 my $BUNDLING = defined $roptions->{'BUNDLING'} ? $roptions->{'BUNDLING'} : 1;
41 13 50       25 my $rambigs = defined $roptions->{'AMBIGUITIES'} ? $roptions->{'AMBIGUITIES'} : undef; # reserved for future use
42              
43 13         17 $ropts->{__argv__} = '';
44 13         20 $ropts->{__errors__} = [];
45 13         39 $ropts->{__argv_original__} = join(' ', @ARGV);
46              
47 13         26 my @GO_config = qw(pass_through no_auto_abbrev no_ignore_case prefix_pattern=--|-); # passed to Getopt::Long for behavior of Apache Common CLI Java library
48              
49 13 100       21 if ($BUNDLING) {
50 7         9 push @GO_config, 'bundling_override';
51             }
52              
53 13         10 my @GO_options;
54              
55             # setup a validation handler for missing argument
56 13 50       28 if (ref($rerrsub) ne 'CODE') {
57 0     0   0 $rerrsub = sub { my ($option, $value, $rhash) = @_; print "error: missing value for option: $option\n"; die "!FINISH"; };
  0         0  
  0         0  
  0         0  
58             }
59              
60 13         12 my %longs;
61              
62             # read user input specification and process for Getopt::Long
63 13         12 for my $s (@{$rspec}) {
  13         22  
64 260         742 my ($long, $short, $type) = $s =~ /([a-zA-Z0-9_-]+)\|([a-zA-Z0-9]*)[=:]?([fios]*)/;
65 260 50       379 next if $long eq '';
66              
67 260 50       299 if ($short eq '') {
68 0         0 $short = $long;
69             }
70              
71 260 50       348 if (length($short) > length($long)) {
72 0         0 ($short, $long) = ($long, $short);
73             }
74              
75 260         404 $longs{$long} = $short;
76              
77             # use either the first or second anonymous subroutine as a reference (we are not calling them ... GO will call them)
78             push @GO_options, ($s, $type ne '' ?
79             sub {
80 26     26   9716 my ($option, $value, $rhash) = @_;
81              
82 26 50 33     111 if (not defined $value or $value eq "") {
83 0         0 push @{$ropts->{__errors__}}, "no value for option $option";
  0         0  
84 0         0 &$rerrsub($option, $value, 0);
85 0         0 return 0;
86             }
87              
88 26 100       77 if (exists $ropts->{$option}) {
89 1 50       6 if ($OPT_PREC == OPT_PREC_UNIQUE) {
    0          
    0          
90 1         2 push @{$ropts->{__errors__}}, "duplicate option $option with $value";
  1         3  
91 1         12 &$rerrsub($option, $value, 1);
92 1         36 return 0;
93             }
94             elsif ($OPT_PREC == OPT_PREC_RIGHT_TO_LEFT) {
95 0         0 $ropts->{$option} = $value;
96             }
97             elsif ($OPT_PREC == OPT_PREC_LEFT_TO_RIGHT) {
98             ; # ignore
99             }
100             }
101             else {
102 25         123 $ropts->{$option} = $value;
103             }
104             } :
105             sub {
106 9     9   2231 my ($option, $value, $rhash) = @_;
107              
108 9         19 $ropts->{$option} = 1; # boolean option
109             }
110 260 100       783 );
111             }
112              
113             # # bundling_override handles this fairly well ...
114             #
115             # # args pre-processing - to reduce parsing ambiguities, replace some of the short options with long options before calling Getopt::Long
116             #
117             # if (scalar(@ARGV) > 0) {
118             # for (my $n=0; $n < scalar(@ARGV); $n++) {
119             # last if $ARGV[$n] eq '--';
120             # $ARGV[$n] =~ s/^-([\w]+)$/exists $longs{$1} ? "--$1" : "-$1"/e; # double-dash long args which only start with a single-dash
121             # $ARGV[$n] =~ s/^(--?)([\w]{2,3})$/exists $rambigs->{$2} ? "--$rambigs->{$2}" : "$1$2"/e; # convert short options to long options because of bundling ambiguity
122             # }
123             # }
124              
125 13         42 Getopt::Long::Configure(@GO_config);
126 13         880 my $result = GetOptions(@GO_options);
127              
128             # args post-processing
129 13 100       2360 if (scalar(@ARGV)) {
130 11 100       21 if ($JAVA_DOPTS) {
131 2         6 for (my $n=0; $n < scalar(@ARGV); $n++) {
132 3 100       6 if ($ARGV[$n] eq '--') {
133 1         1 last;
134             }
135 2         4 $ARGV[$n] =~ s/^ +//;
136 2         2 $ARGV[$n] =~ s/ +$//;
137 2         11 $ARGV[$n] =~ s/^--?D(\w+)=['"]?([\w.]+)['"]?$/$ropts->{$1} = $2; '';/e; # process -Dabc=z.y.z, overwrite existing values in the special case of -D (behavior is like OPT_PREC_RIGHT_TO_LEFT)
  2         4  
  2         7  
138             }
139             }
140             }
141              
142 13         25 my $cmd = join(' ', @ARGV);
143 13         45 $cmd =~ s/ +/ /g; # is there a case where we care about embedded spaces in remaining ARGV?
144 13         16 $cmd =~ s/^ +//g;
145 13         19 $cmd =~ s/ +$//g;
146              
147 13 50       21 if ($DEBUG) {
148 13 50       28 debug_print($ropts) if $DEBUG;
149 13         59 print "cmd=$cmd\n";
150             }
151              
152             # stash remaining ARGV in the output hash
153 13         17 $ropts->{'__argv__'} = $cmd;
154              
155 13 100 66     31 if ($result == 0 or @{$ropts->{'__errors__'}}) {
  13         40  
156 1         37 return 0; # failure (according to Getopt::Long protocol)
157             }
158             else {
159 12         437 return 1; # success (according to Getopt::Long protocol)
160             }
161             }
162              
163             # sub value_not_required {
164             # # option arg not expected, but we still want to set it to 1
165             # my ($option, $value, $rhash) = @_;
166             #
167             # if ($option ne "") {
168             # $ropts->{$option} = 1;
169             # }
170             # }
171             #
172             # sub value_required {
173             # # option arg expected, do error handling if missing, including a custom error message
174             # my ($option, $value, $rhash) = @_;
175             #
176             # if ($option ne "") {
177             # if (not defined $value or $value eq "") {
178             # print "Missing argument for option:$option\n";
179             # $n_errs++;
180             # die "!FINISH";
181             # }
182             # else {
183             # if (exists $ropts->{$option} and $OPT_PREC == OPT_PREC_UNIQUE) {
184             # print "Unrecognized command: $value\n";
185             # $n_errs++;
186             # die "!FINISH";
187             # }
188             # elsif (exists $ropts->{$option} and $OPT_PREC == OPT_PREC_LEFT_TO_RIGHT) {
189             # ;
190             # }
191             # else {
192             # $ropts->{$option} = $value;
193             # }
194             # }
195             # }
196             # }
197              
198             sub debug_print {
199 13     13 0 14 my ($ropts) = @_;
200              
201 13         12 for my $o (sort keys %{$ropts}) {
  13         58  
202 75         483 print "$o=$ropts->{$o}\n";
203             }
204             }
205             1;
206             __END__