File Coverage

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


line stmt bran cond sub pod time code
1             package Script::Toolbox::Util::Opt;
2              
3 10     10   66 use strict;
  10         21  
  10         384  
4 10     10   52 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  10         72  
  10         867  
5 10     10   7865 use Getopt::Long;
  10         129805  
  10         58  
6 10     10   6467 use IO::File;
  10         87627  
  10         23592  
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              
17              
18             # Preloaded methods go here.
19              
20             # Autoload methods go after =cut, and are processed by the autosplit program.
21              
22             #------------------------------------------------------------------------------
23             # Create a new Opt object.
24             #------------------------------------------------------------------------------
25             sub new
26             {
27 9     9 0 22 my $classname = shift;
28 9         17 my $optDef = shift; # options definition
29 9         17 my $caller = shift; # may be omited
30              
31 9 100       59 $optDef = {} if( !defined $optDef );
32 9 50       68 $optDef = {} if( ref $optDef ne 'HASH' );
33 9 100       18 $optDef = {} if( scalar keys %{$optDef} == 0 );
  9         44  
34 9         43 _addDefaultOptions( \$optDef );
35 9 50       37 return undef if( _invalidOptDef( $optDef ));
36 9         22 my $self = {};
37 9         24 bless( $self, $classname );
38 9         32 $self->_instCaller($caller);
39              
40 9         44 my $rc = $self->_init( $optDef, @_ );
41 9 50       40 exit $rc if( $rc != 0 );
42              
43 9         98 return $self;
44             }
45              
46             #------------------------------------------------------------------------------
47             #------------------------------------------------------------------------------
48             sub _invalidOptDef($)
49             {
50 9     9   24 my ($optDef) = @_;
51              
52 9         20 foreach my $val ( values %{$optDef} )
  9         32  
53             {
54 15 50       49 return 1 if( ref $val ne 'HASH' );
55 15 50       24 return 1 if( scalar keys %{$val} == 0 );
  15         195  
56 15         27 foreach my $key ( keys %{$val} )
  15         127  
57             {
58 29 50 100     194 return 1 if( $key ne 'mod' && $key ne 'desc'&&
      100        
      66        
59             $key ne 'mand' && $key ne 'default' );
60             }
61             }
62 9         31 return 0;
63             }
64              
65             #------------------------------------------------------------------------------
66             #------------------------------------------------------------------------------
67             sub _instCaller(@)
68             {
69 9     9   25 my ($self, $call) = @_;
70              
71 9         52 my @caller = caller();
72 9 100       84 $self->{'caller'} = defined $call ? $call : \@caller;
73             }
74              
75             #------------------------------------------------------------------------------
76             # Get the options definition as input. Read the options from command line.
77             # Write usage message to STDERR if missing mandatory options.
78             #------------------------------------------------------------------------------
79             sub _init()
80             {
81 9     9   25 my ( $self, $ops, $addUsage ) = @_;
82              
83 9         32 $self->{'opsDef'} = _normalize($ops);
84 9 50       42 $self->{'addUsage'} = defined $addUsage ? $addUsage :''; # additional usage text
85 9         30 return $self->_processCmdLine();
86             }
87              
88             #------------------------------------------------------------------------------
89             # Used for compatibility with old $ops format (array).
90             #------------------------------------------------------------------------------
91             sub _normalize($)
92             {
93 9     9   20 my ($ops) = @_;
94              
95 9 50       44 return $ops if( ref $ops eq 'HASH' );
96              
97 0         0 my %o;
98 0         0 foreach my $old ( @{$ops} )
  0         0  
99             {
100 0         0 my $op = $old->{'op'};
101 0         0 my $mod= $op;
102 0         0 $mod=~ s/^[^:=]+//;
103 0         0 $op =~ s/[:=].*$//;
104 0         0 my %oo;
105 0 0       0 $oo{'mod'} = $mod if( $mod ne '' );
106 0 0       0 $oo{'desc'} = $old->{'desc'} if( defined $old->{'desc'});
107 0 0       0 $oo{'mand'} = $old->{'mand'} if( defined $old->{'mand'});
108 0 0       0 $oo{'default'} = $old->{'default'} if( defined $old->{'default'});
109              
110 0         0 $o{$op} = \%oo;
111             }
112 0         0 return \%o;
113             }
114              
115             #------------------------------------------------------------------------------
116             #------------------------------------------------------------------------------
117             sub _addDefaultOptions
118             {
119 9     9   26 my ($optDef) = @_;
120              
121 9 50       40 if( ! defined $$optDef->{'help'} )
122             {
123 9         48 $$optDef->{help} = {desc=>'Print online docu.'};
124             }
125             }
126              
127             #------------------------------------------------------------------------------
128             # Return the value of an option.
129             #------------------------------------------------------------------------------
130             sub get($)
131             {
132 40     40 0 1223 my ( $self, $key ) = @_;
133              
134 40         140 return $self->{$key};
135             }
136              
137             #------------------------------------------------------------------------------
138             # Set the value of an option.
139             #------------------------------------------------------------------------------
140             sub set($)
141             {
142 1     1 0 3 my ( $self, $key, $val ) = @_;
143              
144 1         2 my $old = $self->{$key};
145              
146 1         3 $self->{$key} = $val;
147              
148 1         3 return $old;
149             }
150              
151             #------------------------------------------------------------------------------
152             # Read options from command line and start checking of the options.
153             # Exit with 1 if GetOptions found an internal error.
154             #------------------------------------------------------------------------------
155             sub _processCmdLine($)
156             {
157 9     9   32 my ( $self ) = @_;
158 9         33 my @opt = _mkOps( $self->{'opsDef'} );
159              
160 9         67 my $rc = GetOptions( $self, (@opt) );
161 9 50       2078 $self->usage(), exit 1 if( ! $rc );
162              
163 9         63 $rc = $self->_checkOps();
164 9         28 return $rc;
165             }
166              
167             #------------------------------------------------------------------------------
168             # Print usage message if missing any mandatory option and exit.
169             # Call perldoc of main programm if option -help is found.
170             # Exit with 2 if a mandatory option is missing.
171             #------------------------------------------------------------------------------
172             sub _checkOps($)
173             {
174 9     9   37 my ( $self ) = @_;
175              
176 9         19 my $rc=0;
177 9         16 my $errMsg;
178              
179 9 50       34 if( defined $self->{'help'} )
180             {
181 0         0 my $hasPerldoc = system("type perldoc >/dev/null 2>&1") / 256;
182 0         0 my $hasNroff = system("type nroff >/dev/null 2>&1") / 256;
183 0 0 0     0 if( $hasPerldoc == 0 && $hasNroff == 0 )
184             {
185             #my $fh = new IO::File "perldoc $0 |";
186             #while( <$fh> ) { print STDERR $_; }
187 0         0 system("perldoc $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         20 foreach ( keys %{$self->{'opsDef'}} )
  9         53  
195             {
196 15 50       46 $errMsg .= "Missing mandatory option '$_'.\n"
197             if( ! $self->setDefaults( $_ ));
198             }
199              
200 9 50       32 if( defined $errMsg )
201             {
202 0         0 print STDERR $errMsg;
203 0         0 $self->usage();
204 0         0 $rc = 2;
205             }
206 9         21 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 37 my ($self, $opt) = @_;
217              
218             # We are happy, found the option on command line
219 15 50       51 return 1 if( defined $self->{$opt} );
220              
221             # Nothing to do, option not found on command line but not mandatory
222 15 100       66 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     18 $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         12 $self->{$opt} = $self->{'opsDef'}{$opt}{'default'};
232 5         16 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   21 my ( $ops ) = @_;
383 9         20 my @OPS;
384              
385             my $mod;
386 9         16 foreach my $opt ( keys %{$ops} )
  9         28  
387             {
388 15 100       52 $mod = defined $ops->{$opt}{'mod'} ? $ops->{$opt}{'mod'} : '';
389 15         44 push @OPS, $opt . $mod;
390             }
391 9         33 return @OPS;
392             }
393             ##############################################################################
394             1;
395             __END__