File Coverage

blib/lib/Script/Toolbox/Util/Opt.pm
Criterion Covered Total %
statement 83 170 48.8
branch 24 76 31.5
condition 9 15 60.0
subroutine 16 23 69.5
pod 0 5 0.0
total 132 289 45.6


line stmt bran cond sub pod time code
1             package Script::Toolbox::Util::Opt;
2              
3 10     10   49 use strict;
  10         15  
  10         267  
4 10     10   37 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  10         16  
  10         648  
5 10     10   5806 use Getopt::Long;
  10         101221  
  10         66  
6 10     10   4763 use IO::File;
  10         67795  
  10         18623  
7              
8             require Exporter;
9              
10             @ISA = qw(Exporter );
11             # Items to export into callers namespace by default. Note: do not export
12             # names by default without a very good reason. Use EXPORT_OK instead.
13             # Do not simply export all your public functions/methods/constants.
14             @EXPORT = qw(
15             );
16             #$VERSION = '';
17              
18              
19             # Preloaded methods go here.
20              
21             # Autoload methods go after =cut, and are processed by the autosplit program.
22              
23             #------------------------------------------------------------------------------
24             # Create a new Opt object.
25             #------------------------------------------------------------------------------
26             sub new
27             {
28 9     9 0 18 my $classname = shift;
29 9         17 my $optDef = shift; # options definition
30 9         13 my $caller = shift; # may be omited
31              
32 9 100       27 $optDef = {} if( !defined $optDef );
33 9 50       33 $optDef = {} if( ref $optDef ne 'HASH' );
34 9 100       12 $optDef = {} if( scalar keys %{$optDef} == 0 );
  9         32  
35 9         33 _addDefaultOptions( \$optDef );
36 9 50       23 return undef if( _invalidOptDef( $optDef ));
37 9         15 my $self = {};
38 9         15 bless( $self, $classname );
39 9         26 $self->_instCaller($caller);
40              
41 9         73 my $rc = $self->_init( $optDef, @_ );
42 9 50       30 exit $rc if( $rc != 0 );
43              
44 9         55 return $self;
45             }
46              
47             #------------------------------------------------------------------------------
48             #------------------------------------------------------------------------------
49             sub _invalidOptDef($)
50             {
51 9     9   18 my ($optDef) = @_;
52              
53 9         13 foreach my $val ( values %{$optDef} )
  9         25  
54             {
55 15 50       38 return 1 if( ref $val ne 'HASH' );
56 15 50       18 return 1 if( scalar keys %{$val} == 0 );
  15         135  
57 15         22 foreach my $key ( keys %{$val} )
  15         94  
58             {
59 29 50 100     151 return 1 if( $key ne 'mod' && $key ne 'desc'&&
      100        
      66        
60             $key ne 'mand' && $key ne 'default' );
61             }
62             }
63 9         25 return 0;
64             }
65              
66             #------------------------------------------------------------------------------
67             #------------------------------------------------------------------------------
68             sub _instCaller(@)
69             {
70 9     9   20 my ($self, $call) = @_;
71              
72 9         40 my @caller = caller();
73 9 100       62 $self->{'caller'} = defined $call ? $call : \@caller;
74             }
75              
76             #------------------------------------------------------------------------------
77             # Get the options definition as input. Read the options from command line.
78             # Write usage message to STDERR if missing mandatory options.
79             #------------------------------------------------------------------------------
80             sub _init()
81             {
82 9     9   21 my ( $self, $ops, $addUsage ) = @_;
83              
84 9         22 $self->{'opsDef'} = _normalize($ops);
85 9 50       30 $self->{'addUsage'} = defined $addUsage ? $addUsage :''; # additional usage text
86 9         22 return $self->_processCmdLine();
87             }
88              
89             #------------------------------------------------------------------------------
90             # Used for compatibility with old $ops format (array).
91             #------------------------------------------------------------------------------
92             sub _normalize($)
93             {
94 9     9   16 my ($ops) = @_;
95              
96 9 50       52 return $ops if( ref $ops eq 'HASH' );
97              
98 0         0 my %o;
99 0         0 foreach my $old ( @{$ops} )
  0         0  
100             {
101 0         0 my $op = $old->{'op'};
102 0         0 my $mod= $op;
103 0         0 $mod=~ s/^[^:=]+//;
104 0         0 $op =~ s/[:=].*$//;
105 0         0 my %oo;
106 0 0       0 $oo{'mod'} = $mod if( $mod ne '' );
107 0 0       0 $oo{'desc'} = $old->{'desc'} if( defined $old->{'desc'});
108 0 0       0 $oo{'mand'} = $old->{'mand'} if( defined $old->{'mand'});
109 0 0       0 $oo{'default'} = $old->{'default'} if( defined $old->{'default'});
110              
111 0         0 $o{$op} = \%oo;
112             }
113 0         0 return \%o;
114             }
115              
116             #------------------------------------------------------------------------------
117             #------------------------------------------------------------------------------
118             sub _addDefaultOptions
119             {
120 9     9   16 my ($optDef) = @_;
121              
122 9 50       30 if( ! defined $$optDef->{'help'} )
123             {
124 9         29 $$optDef->{help} = {desc=>'Print online docu.'};
125             }
126             }
127              
128             #------------------------------------------------------------------------------
129             # Return the value of an option.
130             #------------------------------------------------------------------------------
131             sub get($)
132             {
133 40     40 0 1099 my ( $self, $key ) = @_;
134              
135 40         97 return $self->{$key};
136             }
137              
138             #------------------------------------------------------------------------------
139             # Set the value of an option.
140             #------------------------------------------------------------------------------
141             sub set($)
142             {
143 1     1 0 2 my ( $self, $key, $val ) = @_;
144              
145 1         2 my $old = $self->{$key};
146              
147 1         2 $self->{$key} = $val;
148              
149 1         2 return $old;
150             }
151              
152             #------------------------------------------------------------------------------
153             # Read options from command line and start checking of the options.
154             # Exit with 1 if GetOptions found an internal error.
155             #------------------------------------------------------------------------------
156             sub _processCmdLine($)
157             {
158 9     9   16 my ( $self ) = @_;
159 9         24 my @opt = _mkOps( $self->{'opsDef'} );
160              
161 9         80 my $rc = GetOptions( $self, (@opt) );
162 9 50       1573 $self->usage(), exit 1 if( ! $rc );
163              
164 9         23 $rc = $self->_checkOps();
165 9         23 return $rc;
166             }
167              
168             #------------------------------------------------------------------------------
169             # Print usage message if missing any mandatory option and exit.
170             # Call perldoc of main programm if option -help is found.
171             # Exit with 2 if a mandatory option is missing.
172             #------------------------------------------------------------------------------
173             sub _checkOps($)
174             {
175 9     9   19 my ( $self ) = @_;
176              
177 9         12 my $rc=0;
178 9         20 my $errMsg;
179              
180 9 50       25 if( defined $self->{'help'} )
181             {
182 0         0 my $hasPerldoc = system("type perldoc >/dev/null 2>&1") / 256;
183 0         0 my $hasNroff = system("type nroff >/dev/null 2>&1") / 256;
184 0 0 0     0 if( $hasPerldoc == 0 && $hasNroff == 0 )
185             {
186 0         0 my $fh = new IO::File "perldoc $0 |";
187 0         0 while( <$fh> ) { print STDERR $_; }
  0         0  
188 0         0 $rc = 1;
189             }else{
190 0         0 $errMsg .= "Can't display online manual. Missing nroff and/or perldoc.\n";
191             }
192             }
193              
194 9         15 foreach ( keys %{$self->{'opsDef'}} )
  9         26  
195             {
196 15 50       40 $errMsg .= "Missing mandatory option '$_'.\n"
197             if( ! $self->setDefaults( $_ ));
198             }
199              
200 9 50       33 if( defined $errMsg )
201             {
202 0         0 print STDERR $errMsg;
203 0         0 $self->usage();
204 0         0 $rc = 2;
205             }
206 9         17 return $rc;
207             }
208              
209             #------------------------------------------------------------------------------
210             # Set option to default value if option is missing and default value defined.
211             # Return 0 if option is mandatory, not set on comand line and no default value
212             # available. Otherwise return 1;
213             #------------------------------------------------------------------------------
214             sub setDefaults($$)
215             {
216 15     15 0 38 my ($self, $opt) = @_;
217              
218             # We are happy, found the option on command line
219 15 50       47 return 1 if( defined $self->{$opt} );
220              
221             # Nothing to do, option not found on command line but not mandatory
222 15 100       62 return 1 if(! $self->{'opsDef'}{$opt}{'mand'} );
223              
224             # WOW!! We found an error!
225             # Option not on comand line, mandatory and no default defined
226             return 0 if(!defined $self->{'opsDef'}{$opt}{'default'} &&
227 5 0 33     14 $self->{'opsDef'}{$opt}{'mand'} );
228              
229             # Option not on comand line, mandatory and default defined
230             # -> so we can use the default value
231 5         10 $self->{$opt} = $self->{'opsDef'}{$opt}{'default'};
232 5         14 return 1;
233              
234              
235             }
236              
237             #------------------------------------------------------------------------------
238             # Print an usage message to STDERR.
239             #------------------------------------------------------------------------------
240             sub usage($$)
241             {
242 0     0 0 0 my ( $self, $addMsg ) = @_;
243              
244 0         0 my $call = $self->{'caller'}->[1];
245 0         0 $call =~ s/^.*\///;
246              
247 0         0 my $cols = _getCols(); my $col2 = $cols/2-6;
  0         0  
248             printf STDERR "\nUsage: %s %s\n%s %s %s\n",
249             $call,
250 0         0 $self->{'addUsage'},
251             '-' x $col2, 'Options', '-' x $col2;
252              
253 0         0 my ($form, $max) = _calcForm( $self->{'opsDef'} );
254              
255 0         0 foreach my $key ( sort keys %{$self->{'opsDef'}} )
  0         0  
256             {
257 0         0 my $val = $self->{'opsDef'}{$key};
258 0         0 printf STDERR "$form\n", _getOpDesc( $val, $max, $cols );
259             }
260 0 0       0 printf STDERR "%s\n%s\n", '-' x ($cols-3),
261             defined $addMsg ? "$addMsg\n" : "\n";
262             }
263              
264             #-----------------------------------------------------------------------------
265             # Compute the number of columns of then contoling terminal.
266             #-----------------------------------------------------------------------------
267             sub _getCols
268             {
269 0     0   0 my $line = `stty -a 2>/dev/null`;
270 0 0       0 return 80 if( !defined $line );
271 0         0 $line =~ /(.*columns[^0-9]+)([0-9]+)/;
272 0 0       0 return defined $2 ? $2 : 80;
273             }
274              
275             #------------------------------------------------------------------------------
276             # Calculate format template for usage message.
277             #------------------------------------------------------------------------------
278             sub _calcForm($)
279             {
280 0     0   0 my ( $ops ) = @_;
281              
282 0         0 _prepUsage( $ops );
283 0         0 my ($form, $max) = ( '', 0 );
284 0         0 foreach my $op ( values %{$ops} )
  0         0  
285             {
286 0         0 my $ln = length $op->{'usage'};
287 0 0       0 my $ad = _optionaly($op) ? 2 : 0;
288              
289 0 0       0 $max = $ln+$ad > $max ? $ln+$ad : $max;
290             }
291              
292 0         0 $form = "%${max}s - %s";
293 0         0 return ($form, $max+3);
294             }
295              
296             #------------------------------------------------------------------------------
297             # Prepare usage message using [] for optional options and <> for input values.
298             #------------------------------------------------------------------------------
299             sub _prepUsage($)
300             {
301 0     0   0 my ( $ops ) = @_;
302              
303 0         0 foreach my $op ( keys %{$ops} )
  0         0  
304             {
305 0         0 my $o = $ops->{$op}{'mod'};
306 0 0       0 $o = '' if( !defined $o );
307 0         0 $o =~ s/=s.*/ /;
308 0         0 $o =~ s/:s.*/ []/;
309 0         0 $o =~ s/=i.*/ /;
310 0         0 $o =~ s/:i.*/ []/;
311 0         0 $o =~ s/=f.*/ /;
312 0         0 $o =~ s/:f.*/ []/;
313              
314 0         0 $ops->{$op}{'usage'} = "-$op$o";
315             }
316             }
317             #------------------------------------------------------------------------------
318             # Build the description of an option.
319             #------------------------------------------------------------------------------
320             sub _getOpDesc($)
321             {
322 0     0   0 my ( $op, $max, $cols ) = @_;
323              
324 0         0 my $rc;
325 0 0       0 if( _optionaly($op) )
326             {
327 0         0 $rc = '[' . $op->{'usage'} . ']';
328             }else{
329 0         0 $rc = $op->{'usage'};
330             }
331 0         0 my $desc = _insertNL( $op, $max, $cols );
332 0         0 return ( $rc, $desc );
333             }
334             #-----------------------------------------------------------------------------
335             # Return false if the option is madatory and has no default value.
336             # Return true otherwise.
337             #-----------------------------------------------------------------------------
338             sub _optionaly($)
339             {
340 0     0   0 my ($op) = @_;
341              
342 0 0       0 if( defined $op->{'mand'})
343             {
344 0 0       0 return 1 if( ! $op->{'mand'} );
345 0 0       0 return 0 if( ! defined $op->{'default'} );
346             }
347 0         0 return 1
348             }
349              
350              
351             #-----------------------------------------------------------------------------
352             # Fold line into two lines if line length exceeds number of columns .
353             #-----------------------------------------------------------------------------
354             sub _insertNL
355             {
356 0     0   0 my ($op, $max, $cols) = @_;
357              
358 0         0 my $l='';
359 0         0 my $line='';
360 0 0       0 $op->{'desc'} = '--no description--' if( !defined $op->{'desc'} );
361              
362 0         0 foreach my $x ( split /\s+/, $op->{'desc'} )
363             {
364 0 0       0 if( length($l) + length($x) + $max >= $cols )
365             {
366 0         0 $line .= sprintf "%s\n%s", $l, ' ' x $max;
367 0         0 $l = '';
368             }
369 0         0 $l .= "$x ";
370             }
371 0         0 $line .= $l;
372             $line .= sprintf "\n%s(default=%s)", ' ' x $max,$op->{'default'}
373 0 0       0 if( defined $op->{'default'} );
374 0         0 return $line;
375             }
376              
377             #------------------------------------------------------------------------------
378             # Prepare the option hash for Getopt::Long::GetOptions().
379             #------------------------------------------------------------------------------
380             sub _mkOps()
381             {
382 9     9   15 my ( $ops ) = @_;
383 9         15 my @OPS;
384              
385             my $mod;
386 9         20 foreach my $opt ( keys %{$ops} )
  9         23  
387             {
388 15 100       65 $mod = defined $ops->{$opt}{'mod'} ? $ops->{$opt}{'mod'} : '';
389 15         62 push @OPS, $opt . $mod;
390             }
391 9         29 return @OPS;
392             }
393             ##############################################################################
394             1;
395             __END__