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   25338 use 5.008008;
  1         3  
  1         31  
4 1     1   4 use strict;
  1         1  
  1         22  
5 1     1   4 use warnings;
  1         4  
  1         59  
6              
7             require Exporter;
8              
9             our @ISA = qw(Exporter);
10              
11             our %EXPORT_TAGS = ();
12              
13 1     1   5 use constant OPT_PREC_UNIQUE => 1;
  1         1  
  1         53  
14 1     1   4 use constant OPT_PREC_LEFT_TO_RIGHT => 0;
  1         1  
  1         35  
15 1     1   4 use constant OPT_PREC_RIGHT_TO_LEFT => 2;
  1         1  
  1         76  
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.02';
28              
29             # Preloaded methods go here.
30              
31 1     1   726 use Getopt::Long 2.35;
  1         9167  
  1         20  
32              
33             sub GetOptionsApacheCommonsCLI {
34 13     13 0 20192 my ($rspec, $ropts, $roptions, $rerrsub) = @_;
35              
36             # process user-supplied options
37 13 50       38 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       22 my $OPT_PREC = defined $roptions->{'OPT_PRECEDENCE'} ? $roptions->{'OPT_PRECEDENCE'} : OPT_PREC_RIGHT_TO_LEFT;
40 13 50       21 my $BUNDLING = defined $roptions->{'BUNDLING'} ? $roptions->{'BUNDLING'} : 1;
41 13 50       27 my $rambigs = defined $roptions->{'AMBIGUITIES'} ? $roptions->{'AMBIGUITIES'} : undef; # reserved for future use
42              
43 13         20 $ropts->{__argv__} = '';
44 13         21 $ropts->{__errors__} = [];
45 13         38 $ropts->{__argv_original__} = join(' ', @ARGV);
46              
47 13         25 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       24 if ($BUNDLING) {
50 7         10 push @GO_config, 'bundling_override';
51             }
52              
53 13         11 my @GO_options;
54              
55             # setup a validation handler for missing argument
56 13 50       30 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         9 for my $s (@{$rspec}) {
  13         22  
64 260         838 my ($long, $short, $type) = $s =~ /([a-zA-Z0-9_-]+)\|([a-zA-Z0-9]*)[=:]?([fios]*)/;
65 260 50       404 next if $long eq '';
66              
67 260 50       320 if ($short eq '') {
68 0         0 $short = $long;
69             }
70              
71 260 50       376 if (length($short) > length($long)) {
72 0         0 ($short, $long) = ($long, $short);
73             }
74              
75 260         318 $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   10107 my ($option, $value, $rhash) = @_;
81              
82 26 50 33     112 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       83 if (exists $ropts->{$option}) {
89 1 50       6 if ($OPT_PREC == OPT_PREC_UNIQUE) {
    0          
    0          
90 1         1 push @{$ropts->{__errors__}}, "duplicate option $option with $value";
  1         3  
91 1         13 &$rerrsub($option, $value, 1);
92 1         118 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         125 $ropts->{$option} = $value;
103             }
104             } :
105             sub {
106 9     9   2489 my ($option, $value, $rhash) = @_;
107              
108 9         23 $ropts->{$option} = 1; # boolean option
109             }
110 260 100       852 );
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         41 Getopt::Long::Configure(@GO_config);
126 13         909 my $result = GetOptions(@GO_options);
127              
128             # args post-processing
129 13 100       2509 if (scalar(@ARGV)) {
130 11 100       18 if ($JAVA_DOPTS) {
131 2         7 for (my $n=0; $n < scalar(@ARGV); $n++) {
132 3 100       7 if ($ARGV[$n] eq '--') {
133 1         1 last;
134             }
135 2         6 $ARGV[$n] =~ s/^ +//;
136 2         3 $ARGV[$n] =~ s/ +$//;
137 2         16 $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         8  
  2         11  
138             }
139             }
140             }
141              
142 13         30 my $cmd = join(' ', @ARGV);
143 13         54 $cmd =~ s/ +/ /g; # is there a case where we care about embedded spaces in remaining ARGV?
144 13         20 $cmd =~ s/^ +//g;
145 13         23 $cmd =~ s/ +$//g;
146              
147 13 50       23 if ($DEBUG) {
148 13 50       29 debug_print($ropts) if $DEBUG;
149 13         1365 print "cmd=$cmd\n";
150             }
151              
152             # stash remaining ARGV in the output hash
153 13         38 $ropts->{'__argv__'} = $cmd;
154              
155 13 100 66     41 if ($result == 0 or @{$ropts->{'__errors__'}}) {
  13         46  
156 1         54 return 0; # failure (according to Getopt::Long protocol)
157             }
158             else {
159 12         484 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 17 my ($ropts) = @_;
200              
201 13         11 for my $o (sort keys %{$ropts}) {
  13         60  
202 75         7696 print "$o=$ropts->{$o}\n";
203             }
204             }
205             1;
206             __END__