File Coverage

blib/lib/Script/Toolbox/Util.pm
Criterion Covered Total %
statement 264 667 39.5
branch 76 292 26.0
condition 12 40 30.0
subroutine 41 98 41.8
pod 0 23 0.0
total 393 1120 35.0


line stmt bran cond sub pod time code
1             package Script::Toolbox::Util;
2              
3 10     10   125 use 5.006;
  10         27  
4 10     10   42 use strict;
  10         15  
  10         181  
5 10     10   3576 use Script::Toolbox::Util::Opt;
  10         20  
  10         372  
6 10     10   3975 use Script::Toolbox::Util::Formatter;
  10         20  
  10         385  
7 10     10   3659 use Script::Toolbox::Util::Menues;
  10         20  
  10         372  
8 10     10   3866 use Script::Toolbox::Util::Menus;
  10         20  
  10         380  
9 10     10   3285 use Script::Toolbox::TableO;
  10         22  
  10         356  
10 10     10   53 use IO::File;
  10         13  
  10         948  
11 10     10   3649 use IO::Dir;
  10         79314  
  10         396  
12 10     10   5013 use Data::Dumper;
  10         53411  
  10         496  
13 10     10   4859 use Fatal qw(open close);
  10         99095  
  10         35  
14 10     10   13236 use POSIX qw(strftime);
  10         38238  
  10         49  
15 10     10   15586 use Time::ParseDate;
  10         86214  
  10         11231  
16              
17             require Exporter;
18              
19             our @ISA = qw(Exporter);
20              
21             # Items to export into callers namespace by default. Note: do not export
22             # names by default without a very good reason. Use EXPORT_OK instead.
23             # Do not simply export all your public functions/methods/constants.
24              
25             # This allows declaration use Script::Toolbox::Util ':all';
26             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
27             # will save memory.
28             our %EXPORT_TAGS = ( 'all' => [ qw(Open Log Exit Table Usage Dir
29             File FileC System Now Menu KeyMap
30             Stat TmpFile DataMenu Menue DataMenue
31             CheckBox RadioButton
32             )] );
33              
34             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
35              
36             our @EXPORT = qw(
37            
38             );
39              
40             #our $VERSION = '0.50';
41              
42             # Preloaded methods go here.
43             sub _getKV(@);
44             sub _getCSV(@);
45              
46             #------------------------------------------------------------------------------
47             #------------------------------------------------------------------------------
48             sub new
49             {
50 8     8 0 1594 my $classname = shift;
51 8         15 my $optDef = shift; # options definition
52 8         14 my $self = {};
53 8         15 bless( $self, $classname );
54              
55 8         30 @Script::Toolbox::Util::caller = caller();
56 8         43 $self->_init( $optDef, \@Script::Toolbox::Util::caller, @_ );
57              
58 8         680 return $self;
59             }
60              
61             #------------------------------------------------------------------------------
62             #------------------------------------------------------------------------------
63             sub _init($)
64             {
65 8     8   20 my ($self,$ops,$caller,$args) = @_;
66              
67 8         18 my $log = $caller->[1];
68 8         40 $log =~ s|^.*/||;
69 8         32 $log =~ s/[.].*$//;
70 8         19 $Script::Toolbox::Util{'_logFH'} = undef; # use default STDERR
71              
72             # Install signal handler
73 8         46 $self->_installSigHandlers();
74              
75             # install options
76 8         47 $self->_installOps( $ops );
77             Exit( 1, "Invalid option definition, 'opsDef' => {} invalid." )
78 8 50 66     54 if( defined $ops && !defined $self->{'ops'});
79              
80             # init log file
81 8         38 my $logdir = $self->GetOpt('logdir');
82 8 100       24 if( defined $logdir )
83             {
84 3         9320 system( "mkdir -p $logdir" );
85 3         159 $Script::Toolbox::Util{'_logFH'} = Open( ">> $logdir/$log.log" );
86 3         84 $Script::Toolbox::Util{'_logFH'}->autoflush();
87             }
88             }
89              
90             #------------------------------------------------------------------------------
91             #------------------------------------------------------------------------------
92             sub _installOps($)
93             {
94 8     8   20 my ($self, $opsDef) = @_;
95              
96 8         47 $self->{'ops'} = Script::Toolbox::Util::Opt->new( $opsDef, \@Script::Toolbox::Util::caller );
97 8 50       24 return if( !defined $self->{'ops'} );
98              
99 8         13 foreach my $key ( keys %{$self->{'ops'}} )
  8         26  
100             {
101 28 50       50 if( defined $self->{$key} )
102             {
103 0         0 print STDERR "Script::Toolbox internal error. ";
104 0         0 print STDERR "Can't use command line option $key (internal used)\n";
105 0         0 next;
106             }
107 28         66 $self->{$key} = $self->{'ops'}->get($key);
108             }
109 8         13 return;
110             }
111              
112             #------------------------------------------------------------------------------
113             # Signal handler.
114             #------------------------------------------------------------------------------
115             sub _sigExit($)
116             {
117 0     0   0 my ($sig) = @_;
118 0         0 Exit( 1, "program aborted by signal SIG$sig." );
119             }
120              
121             #------------------------------------------------------------------------------
122             #------------------------------------------------------------------------------
123             sub _installSigHandlers()
124             {
125 8     8   17 my ($self) = @_;
126 8         168 $SIG{'INT'} = \&_sigExit;
127 8         94 $SIG{'HUP'} = \&_sigExit;
128 8         85 $SIG{'QUIT'}= \&_sigExit;
129 8         96 $SIG{'TERM'}= \&_sigExit;
130             }
131              
132              
133             #------------------------------------------------------------------------------
134             # Log a message and exit the programm with the given error code.
135             #------------------------------------------------------------------------------
136             sub Exit(@)
137             {
138 0     0 0 0 my ($exitCode, $message, $file, $line) = _getParam(@_);
139              
140 0 0 0     0 $message .= sprintf "\n\tSource:%s:%s", $file, $line if( defined $line &&
141             defined $file );
142 0         0 Log( $message );
143 0         0 exit $exitCode;
144             }
145              
146             #------------------------------------------------------------------------------
147             # Write 'die' messages via Log().
148             #------------------------------------------------------------------------------
149             sub _dieHook
150             {
151 0 0   0   0 die @_ if $^S;
152              
153 0         0 my @y = split /\n/, $_[0];
154 0         0 map { Log( $_ ); } @y;
  0         0  
155             };
156             $main::SIG{'__DIE__'} = \&_dieHook; # install die hook
157              
158              
159             #------------------------------------------------------------------------------
160             # Write a log message with time stamp to a channel.
161             # $severity, $logtag only required for syslog.
162             #------------------------------------------------------------------------------
163             sub Log(@)
164             {
165 0     0 0 0 my ($message, $canal, $severity, $logtag) = _getParam(@_);
166              
167 0         0 my $prog = $0;
168 0         0 $prog =~ s|^.*/||;
169 0         0 my $msg = sprintf "%s: %s: %s\n", $prog, scalar localtime(), $message;
170              
171 0         0 my $fh;
172 0         0 my $can = *STDERR;
173            
174 0 0       0 if ( !defined $canal )
175             {
176 0 0       0 if( defined $Script::Toolbox::Util{'_logFH'}) { $can = $Script::Toolbox::Util{'_logFH'}; }
  0         0  
177             }else{
178             # canel is defined here
179 0 0       0 if ( ref($canal) eq 'IO::File' ){ $can = $canal; }
  0 0       0  
    0          
    0          
180 0         0 elsif ( $canal eq 'STDERR') { $can = *STDERR; }
181 0         0 elsif ( $canal eq 'STDOUT') { $can = *STDOUT; }
182 0         0 elsif ( $canal eq 'syslog') { $can = new IO::File "| logger -p '$severity' -t '$logtag'"; }
183 0         0 else { $can = _openFromString($canal); }
184             }
185 0         0 print $can $msg;
186 0         0 return $msg;
187             }
188              
189             #------------------------------------------------------------------------------
190             # We got a string like "/tmp/x", ">> /tmp/x" or "| someProgram".
191             # Try to open it as a log canal. If it fails open STDERR instead.
192             #------------------------------------------------------------------------------
193             sub _openFromString($)
194             {
195 0     0   0 my ($canal) = @_;
196              
197 0 0 0     0 if( $canal !~ /^\s*>/ && $canal !~ /^\s*[|]/ ) { $canal = '>>' . $canal; }
  0         0  
198              
199 0         0 my $can;
200 0         0 my $fh = new IO::File "$canal";
201 0 0       0 if( !defined $fh )
202             {
203 0         0 $can = *STDERR;
204 0         0 printf $can "%s: %s: %s %s\n",
205             $0, scalar localtime(), "WARNING: can't write to", $canal;
206             }else{
207 0         0 $can = $fh;
208             }
209 0         0 return $can;
210             }
211              
212             #------------------------------------------------------------------------------
213             # Open a file via IO::File with Fatal handling
214             #------------------------------------------------------------------------------
215             sub Open(@)
216             {
217 19     19 0 1435 my ($file, $iolayer) = _getParam(@_);
218 19         244 my $fh = new IO::File;
219 19         1270 $fh->open( "$file" );
220             #$fh->open( "$file" ) || return undef;
221 19         4248 return $fh;
222             }
223 10     10   70 use Fatal qw(IO::File::open);
  10         17  
  10         47  
224              
225              
226             #------------------------------------------------------------------------------
227             # Format $param as table and return a reference to the result array.
228             #------------------------------------------------------------------------------
229             sub Table(@)
230             {
231 10     10 0 9832 my ($self, $param, $separator) = @_;
232 10 50       38 if( ref $self ne 'Script::Toolbox') { # only for compatibility
233 0         0 $separator = $param; # to versions befor 0.50
234 0         0 $param = $self;
235 0         0 $self = undef;
236             }
237              
238 10         57 my $t = Script::Toolbox::TableO->new($param, $separator);
239 10         36 my @T = $t->asArray();
240 10         53 return \@T;
241             }
242              
243             #------------------------------------------------------------------------------
244             # $param must be a hash reference. This Hash must have a key "data".
245             # This key may point to:
246             # arrayref
247             # hashref
248             #------------------------------------------------------------------------------
249             sub _noData($)
250             {
251 0     0   0 my ($param) = @_;
252              
253 0 0       0 return 0 if( ref $param ne 'HASH' );
254 0 0       0 return 0 if( ref $param->{'data'} eq 'HASH' );
255 0 0       0 return 0 if( ref $param->{'data'} eq 'ARRAY');
256              
257 0 0       0 if( !defined $param->{'data'}[0] )
258             {
259 0         0 Log( "WARNING: no input data for Table()." );
260 0         0 return 1;
261             }
262 0         0 return 0;
263             }
264              
265             #------------------------------------------------------------------------------
266             # Valid Calls:
267             # [ "csvString", "csvString",...], undef
268             # [ "csvString", "csvString",...], separatorString
269             # [ "TitelString", [headArray], [dataArray],...], undef
270             # [ [dataArray],...], undef
271             # {title=>"", head=>[], data=>[[],[],...] }, undef
272             # {title=>"", head=>[], data=>[{},{},...] }, undef
273             # {title=>"", head=>[], data=>{r1=>{c1=>,c2=>,},r2=>{c1=>,c2=>,},}, undef
274             #------------------------------------------------------------------------------
275             sub _normParam($$)
276             {
277 0     0   0 my ($param, $separator) = @_;
278              
279 0 0       0 if( ref $param eq 'HASH' )
280             {
281             # keine Ahnung wozu: return _sepHash($param, $separator) if( _isCSV($param->{'data'}) );
282 0         0 return $param;
283             }
284 0 0       0 return _sepTitleHead($param) if( _isTitleHead($param) );
285 0 0       0 return _sepCSV($param, $separator) if( _isCSV($param, $separator) );
286 0         0 return { 'data' => $param };
287             }
288              
289             #------------------------------------------------------------------------------
290             #------------------------------------------------------------------------------
291             sub _sepHash($$)
292             {
293 0     0   0 my ($param,$separator) = @_;
294              
295 0         0 my $d = _sepCSV($param->{'data'}, $separator);
296 0         0 $param->{'data'} = $d->{'data'};
297 0         0 return $param;
298             }
299              
300             # ------------------------------------------------------------------------------
301             # Check if we found the special data array format.
302             # ["TitleString", [headString,headString,...],[data,...],...]
303             #------------------------------------------------------------------------------
304             sub _isTitleHead($)
305             {
306 0     0   0 my ($param) = @_;
307              
308 0 0 0     0 return 1 if( ref \$param->[0] eq 'SCALAR' && ref $param->[1] eq 'ARRAY' );
309 0         0 return 0;
310             }
311              
312             #------------------------------------------------------------------------------
313             # Transform the special data array
314             # ["TitleString", [headString,headString,...],[data,...],...]
315             # into hash format.
316             #------------------------------------------------------------------------------
317             sub _sepTitleHead($)
318             {
319 0     0   0 my ($param) = @_;
320              
321 0         0 my $title= splice @{$param}, 0,1;
  0         0  
322 0         0 my $head = splice @{$param}, 0,1;
  0         0  
323              
324             return {
325 0         0 'title' => $title,
326             'head' => $head,
327             'data' => $param
328             };
329             }
330              
331              
332             #------------------------------------------------------------------------------
333             # [[],[],...]
334             # [{},{},...]
335             # {r1=>{c1=>,c2=>,},r2=>{c1=>,c2=>,},}
336             #------------------------------------------------------------------------------
337             sub _isCSV($$)
338             {
339 0     0   0 my ($param, $separator) = @_;
340              
341 0 0       0 return 0 if( ref $param ne 'ARRAY' );
342              
343 0 0       0 $separator = ';' unless defined $separator; #FIXME default sep
344 0 0       0 return 1 if( $param->[0] =~ /$separator/ ); #assume it's a CSV record
345 0         0 return 0;
346             }
347              
348             #------------------------------------------------------------------------------
349             # Convert an array of CSV strings into an array of arrays.
350             #
351             # [ "a;b","c,d"] becomes
352             # [[a,b], [c,d]]
353             #------------------------------------------------------------------------------
354             sub _sepCSV($$)
355             {
356 0     0   0 my ($param, $separator) = @_;
357              
358 0 0       0 $separator = ';' if( !defined $separator);
359 0         0 my @R;
360 0         0 foreach my $l ( @{$param} )
  0         0  
361             {
362 0         0 my @r = split /$separator/, $l;
363 0         0 push @R, \@r;
364             }
365              
366 0         0 return { 'data' => \@R };
367             }
368              
369             #------------------------------------------------------------------------------
370             #------------------------------------------------------------------------------
371             sub SetOpsDef($)
372             {
373 1     1 0 769 my ($self,$opsDef) = @_;
374 1         3 my $old = $self->{'ops'};
375 1         3 $self->{'ops'} = Script::Toolbox::Util::Opt->new( $opsDef );
376 1         4 return ($self->{'ops'}, $old);
377             }
378              
379             #------------------------------------------------------------------------------
380             #------------------------------------------------------------------------------
381             sub GetOpt($)
382             {
383 10     10 0 466 my ($self,$opt) = @_;
384 10 50       43 return undef if( ! defined $self->{'ops'} );
385 10         38 return $self->{'ops'}->get($opt);
386             }
387              
388             #------------------------------------------------------------------------------
389             # Read the entire file into an array.
390             # File can be a file name or an IO::File handle.
391             # Chomp all array elements.
392             #------------------------------------------------------------------------------
393             sub FileC(@)
394             {
395 0     0 0 0 my ($filename,$newContent) = _getParam(@_);
396              
397 0         0 my $f = undef;
398 0 0       0 if( !defined $newContent) { $f = _ReadFile($filename); }
  0 0       0  
399 0         0 elsif( ref $newContent eq 'CODE'){ $f = _ReadFile($filename,$newContent);}
400 0         0 else { return undef; }
401              
402 0 0       0 chomp @{$f} if( defined $f );
  0         0  
403 0         0 return $f;
404             }
405              
406             #------------------------------------------------------------------------------
407             # Read the entire file into an array or write the new content to the file.
408             # File can be a file name or an IO::File handle.
409             # Newcontent may be a SCALAR value, an ARRAY reference, a HASH reference or
410             # a reference to a callback function.
411             #------------------------------------------------------------------------------
412             sub File(@)
413             {
414 14     14 0 60686 my ($filename,$newContent,$recSep,$fieldSep) = _getParam(@_);
415              
416 14 100       68 if( !defined $newContent) { return _ReadFile($filename); }
  4 100       25  
417 1         2 elsif( ref $newContent eq 'CODE'){ return _ReadFile($filename,$newContent);}
418 9         66 else { _WriteFile($filename,$newContent,$recSep,$fieldSep); }
419             }
420              
421             #------------------------------------------------------------------------------
422             # Read the entire file into a hash or write the new content to the file.
423             # File can be a file name or an IO::File handle.
424             # Newcontent may be a reference to a keyMap HASH or a reference to a callback
425             # function.
426             # The Hash looks like:
427             # keyA1 => keyB1 ... =>keyN1 => value1
428             # keyA2 => keyB2 ... =>keyN2 => value2
429             #------------------------------------------------------------------------------
430             sub KeyMap(@)
431             {
432 2     2 0 480 my ($filename,$fieldSep,$newContent) = _getParam(@_);
433              
434 2 50       5 if( !defined $newContent)
    0          
435 2         5 { return _ReadKeyMap($filename, $fieldSep); }
436             elsif( ref $newContent eq 'CODE' )
437 0         0 { return _ReadKeyMap($filename, $fieldSep, $newContent); }
438 0         0 else { _WriteKeyMap($filename,$fieldSep,$newContent); }
439             }
440              
441             #------------------------------------------------------------------------------
442             # The Hash looks like:
443             # keyA1 => keyB1 ... =>keyN1 => value1
444             # keyA2 => keyB2 ... =>keyN2 => value2
445             #------------------------------------------------------------------------------
446             sub _WriteKeyMap($$$)
447             {
448 0     0   0 my ($filename,$fieldSep,$newContent) = @_;
449              
450 0 0       0 $fieldSep = ',' if( !defined $fieldSep );
451              
452 0         0 my $TXT = '';
453 0         0 _getCSV( \$TXT, '', $newContent, $fieldSep );
454              
455 0         0 File( "> $filename", $TXT );
456             }
457              
458             #------------------------------------------------------------------------------
459             # Write a KeyMap (HASH) to a file.
460             #
461             # The Hash looks like:
462             # keyA1 => keyB1 ... =>keyN1 => value1
463             # keyA2 => keyB2 ... =>keyN2 => value2
464             #------------------------------------------------------------------------------
465             sub _getCSV(@)
466             {
467 0     0   0 my ($txt, $prev, $newContent,$fieldSep) = @_;
468              
469 0         0 my $prefix = '';
470 0         0 foreach my $k ( sort keys %{$newContent} )
  0         0  
471             {
472 0         0 $$txt .= $prefix .$k . $fieldSep;
473 0 0       0 if( ref $newContent->{$k} ne 'HASH' )
474             {
475 0         0 $$txt .= $newContent->{$k} . "\n";
476 0         0 $prefix = $prev;
477 0         0 next;
478             }
479 0         0 _getCSV($txt, "$prev$k$fieldSep", $newContent->{$k}, $fieldSep);
480             }
481             }
482              
483             #------------------------------------------------------------------------------
484             #
485             #------------------------------------------------------------------------------
486             sub _checkParam($$)
487             {
488 2     2   4 my ( $fieldSep, $callBack) = @_;
489              
490 2         3 my $def=',';
491 2 100       4 $$fieldSep = $def if( !defined $$fieldSep );
492              
493 2         3 my ( $fs, $cb ) = ( $$fieldSep, $$callBack);
494 2         3 my $rfs = ref $fs; my $rcb = ref $cb;
  2         3  
495            
496 2   66     6 my $scalar_code = ref $fs eq '' && ref $cb eq 'CODE';
497 2   66     5 my $scalar_undef= ref $fs eq '' && !defined $cb;
498 2   66     8 my $code_scalar = ref $fs eq 'CODE' && ref $cb eq '' && defined $cb;
499 2   66     5 my $code_undef = ref $fs eq 'CODE' && !defined $cb;
500              
501 2 50       7 if ( $scalar_code ){return;}
  0 100       0  
    50          
    50          
502 1         3 elsif( $scalar_undef){return;}
503 0         0 elsif( $code_scalar ){$$fieldSep = $cb; $$callBack = $fs;}
  0         0  
504 1         2 elsif( $code_undef ){$$fieldSep = $def;$$callBack = $fs;}
  1         2  
505 0         0 else { $$fieldSep = $def; $$callBack = undef;}
  0         0  
506             }
507              
508             #------------------------------------------------------------------------------
509             # Read a CSV file into a hash. The lines of the CSV files are "\n" separated.
510             # Default field separator is ",".
511             # The Hash looks like:
512             # keyA1 => keyB1 ... =>keyN1 => value1
513             # keyA2 => keyB2 ... =>keyN2 => value2
514             #------------------------------------------------------------------------------
515             sub _ReadKeyMap($$$)
516             {
517 2     2   2 my ($file, $fieldSep, $callBack) = @_;
518            
519 2         5 _checkParam(\$fieldSep, \$callBack);
520              
521 2         2 my $f;
522 2 100       3 if( defined $callBack ) { $f = File( $file,$callBack, $fieldSep ); }
  1         3  
523 1         2 else { $f = File( $file ); }
524 2         4 chomp( @{$f} );
  2         5  
525              
526 2         2 my %P;
527 2         3 foreach my $line ( @{$f} )
  2         3  
528             {
529 4         20 my @L = split /$fieldSep/, $line;
530 4         7 _getKV( \%P, @L );
531             }
532 2         6 return \%P;
533             }
534              
535             #------------------------------------------------------------------------------
536             # Add one line (from @_ array) to the hash. Hash looks like:
537             # key1 => key2 ... =>keyN => value1
538             # key1 => key2 ... =>keyX => value2
539             #------------------------------------------------------------------------------
540             sub _getKV(@)
541             {
542 8     8   15 my ($P, $k, @v) = @_;
543            
544 8 50       14 return if( ! defined $k );
545 8 100       14 if( ref $P->{$k} eq 'HASH' ){
546 2         4 _getKV( $P->{$k}, @v );
547 2         4 return;
548             }
549 6 100       10 if( @v == 1 ){
550 4         6 $P->{$k} = $v[0];
551 4         8 return;
552             }else{
553 2         3 my $x = {};
554 2         4 $P->{$k} = $x;
555 2         4 _getKV( $x, @v );
556             }
557             }
558              
559              
560             #------------------------------------------------------------------------------
561             # Open the file in required write mode (default append mode) and write the new
562             # content to the file.
563             # Newcontent can be any kind of data structure.
564             #------------------------------------------------------------------------------
565             sub _WriteFile($$)
566             {
567 9     9   26 my($file,$newContent,$recSep,$fieldSep) =@_;
568              
569 9         11 my $fh;
570 9 50       19 if( ref $file eq 'IO::File' )
571             {
572 0         0 $fh = $file;
573             }else{
574 9         85 $file =~ s/^\s+//;
575 9         27 $file =~ s/^<+//; # write mode only
576 9 100       37 $file = '>>' . $file if( $file !~ /^[|>]/ );
577 9   50     48 $fh = Open( $file ) || return undef;
578             }
579 9 100       51 if( ref $newContent eq '' ) {print $fh $newContent;}
  6 100       1584  
    100          
580             elsif( _simpleArray( $newContent))
581 1         5 { _printSimpleArray($newContent, $fh, $recSep)}
582             elsif( _simpleHash( $newContent ))
583 1         6 { _printSimpleHash($newContent, $fh, $recSep,$fieldSep)}
584 1         24 else { print $fh Dumper $newContent; }
585             }
586              
587             #------------------------------------------------------------------------------
588             #------------------------------------------------------------------------------
589             sub _printSimpleArray($$$)
590             {
591 1     1   3 my ($content,$fh,$recSep) = @_;
592              
593             map
594             {
595 3 50       9 my $rs = defined $recSep ? $recSep : '';
596 3         44 print $fh "$_$rs";
597 1         1 } @{$content};
  1         5  
598             }
599              
600             #------------------------------------------------------------------------------
601             #------------------------------------------------------------------------------
602             sub _printSimpleHash($$$$)
603             {
604 1     1   6 my ($content,$fh,$recSep,$fieldSep) = @_;
605 1         2 foreach my $key (sort keys %{$content})
  1         10  
606             {
607 3 50       12 my $rs = defined $recSep ? $recSep : '';
608 3 50       5 my $fs = defined $fieldSep ? $fieldSep : ':';
609 3         23 printf $fh "%s%s%s%s", $key, $fs, $content->{$key},$rs;
610             }
611 1         29 return;
612             }
613              
614             #------------------------------------------------------------------------------
615             #------------------------------------------------------------------------------
616             sub _simpleHash($)
617             {
618 2     2   9 my ($content) = @_;
619              
620 2 50       16 return 0 if( ref $content ne 'HASH');
621 2         4 foreach my $key ( keys %{$content} )
  2         12  
622             {
623 6 100       25 return 0 if( ref $content->{$key} ne '' ); # scalar estimated
624             }
625 1         7 return 1;
626             }
627              
628             #------------------------------------------------------------------------------
629             #------------------------------------------------------------------------------
630             sub _simpleArray($)
631             {
632 3     3   7 my ($content) = @_;
633              
634 3 100       24 return 0 if( ref $content ne 'ARRAY');
635 1         7 foreach my $line ( @{$content} )
  1         8  
636             {
637 3 50       6 return 0 if( ref $line ne '' ); # scalar estimated
638             }
639 1         3 return 1;
640             }
641              
642             #------------------------------------------------------------------------------
643             # Read the file content into an array and return a referenz to this array.
644             # Return undef if the file isn't readable.
645             # File can be a file name or an IO::File handle.
646             #------------------------------------------------------------------------------
647             sub _ReadFile($$)
648             {
649 5     5   12 my($file,$callBack) =@_;
650              
651 5         14 my ($fh,@F);
652 5 50       17 if( ref $file eq 'IO::File' )
653             {
654 0         0 $fh = $file;
655             }else{
656 5         34 $file =~ s/^\s*>+\s*//; # read only mode
657 5   50     30 $fh= Open( $file ) || return undef;
658             }
659 5         21853 @F = <$fh>; my $rf = \@F;
  5         25  
660 5 100       26 $rf = &{$callBack}( \@F ) if( defined $callBack );
  1         4  
661 5 50       21 $rf = \@F if(!defined $rf );
662 5         141 return $rf;
663             }
664              
665             #------------------------------------------------------------------------------
666             # Without an input argument TmpFile() returns an file handle to an new
667             # temporary file.
668             # Otherwise read the tempfile into an array and return a reference to it.
669             #------------------------------------------------------------------------------
670             sub TmpFile(@)
671             {
672 2     2 0 39268 my ($file) = _getParam(@_);
673              
674 2         9 my ($f,@F);
675 2 100       13 if( ref $file eq 'IO::File' ) { $file->seek(0,0); @F = <$file>; $f=\@F; }
  1         33  
  1         44  
  1         7  
676 1         156 else { $f = IO::File::new_tmpfile; }
677 2         17 return $f;
678             }
679              
680              
681             #------------------------------------------------------------------------------
682             # Return the filenames of a directory as array reference.
683             # Skip '.','..' and all filenames not matching search pattern if a search
684             # pattern is defined.
685             #------------------------------------------------------------------------------
686             sub Dir(@)
687             {
688 7     7 0 8562 my ($dirPath,$searchPattern) = _getParam(@_);
689              
690 7         53 my $d = IO::Dir->new($dirPath);
691 7 50       510 return undef if( !defined $d );
692            
693 7         11 my @D;
694 7         26 while( defined($_ = $d->read))
695             {
696 67 100       527 next if( _toSkip( $_, $searchPattern ));
697 37         83 push @D, $_;
698             }
699 7         100 @D = sort @D;
700 7         38 return \@D;
701             }
702              
703             #------------------------------------------------------------------------------
704             #------------------------------------------------------------------------------
705             sub _toSkip($$)
706             {
707 67     67   94 my ($line,$pattern) = @_;
708              
709 67 100       159 return 1 if( $line =~ /^[.]{1,2}$/ );
710 53 100       94 return 0 if( !defined $pattern );
711              
712 22 100       57 if( $pattern =~ /^\s*!/ )
713             {
714 4         8 $pattern = substr($pattern, 1 );
715 4 100       31 return 1 if( $line =~ /$pattern/ );
716             }else{
717 18 100       99 return 1 if( $line !~ /$pattern/ );
718             }
719             }
720              
721             #------------------------------------------------------------------------------
722             #------------------------------------------------------------------------------
723             sub Usage($$)
724             {
725 0     0 0 0 my ($self, $add) = @_;
726              
727 0         0 return $self->{'ops'}->usage($add);
728             }
729              
730             #------------------------------------------------------------------------------
731             #------------------------------------------------------------------------------
732             sub SetOpt($$$)
733             {
734 1     1 0 3 my ($self,$opt,$value) = @_;
735 1 50       4 return undef unless defined $self->{'ops'};
736 1 50       3 return undef unless ref($self->{'ops'}) eq 'Script::Toolbox::Util::Opt';
737 1         3 my $old = $self->{'ops'}->set($opt,$value);
738 1         2 $self->{$opt} = $value;
739 1         4 return $old;
740             }
741              
742             #------------------------------------------------------------------------------
743             #------------------------------------------------------------------------------
744             sub _getParam(@)
745             {
746             #if( isa( $_[0], "Script::Toolbox::Util" ))
747 54     54   173 my $x = ref $_[0];
748 54 100       254 if( $x =~ /Script::Toolbox/ )
749             {
750 26 50       221 shift @_ if( $_[0]->isa("Script::Toolbox::Util" ));
751             }
752 54         213 return @_;
753             }
754              
755             #------------------------------------------------------------------------------
756             # Start a shell command with logging.
757             # Return 0 if shell command failed otherwise 1.
758             #------------------------------------------------------------------------------
759             sub System($)
760             {
761 0     0 0 0 my( $cmd ) = _getParam(@_);
762              
763 0         0 my $fh = new IO::File;
764 0         0 my $pid = $fh->open("$cmd ". '2>&1; echo __RC__$? |' );
765              
766 0         0 my $rc;
767 0         0 while( <$fh> )
768             {
769 0         0 chomp;
770 0 0       0 $rc = $_, next if( /^__RC__/ );
771 0 0       0 next if( /^\s*$/ );
772 0         0 Log( " $_" );
773             }
774              
775 0         0 $rc =~ s/__RC__//;
776              
777 0 0       0 return 1 if( $rc == 0 );
778 0         0 return 0;
779             }
780              
781             #------------------------------------------------------------------------------
782             # Compute the difference between NOW[+offset] and the time value given as
783             # second parameter. Return a hash reference holding the difference in seconds,
784             # minutes, hours and days. Every value as a floating point number.
785             #
786             # The referenz time (rtime) may be an epoch value or any string parsable by
787             # Time::ParseDate.
788             #------------------------------------------------------------------------------
789             sub _nowDiff($$)
790             {
791 3     3   6 my ($now,$rtime) = @_;
792              
793 3 100       16 $rtime = parsedate( $rtime ) if( $rtime !~ /^[0-9]+$/ );
794              
795 3         863 my $secDiff= $now - $rtime;
796 3         8 my $D = int $secDiff / 86400; my $x = $secDiff % 86400;
  3         5  
797 3         4 my $H = int $x / 3600; $x = $x % 3600;
  3         4  
798 3         4 my $M = int $x / 60; $x = $x % 60;
  3         5  
799 3         4 my $S = $x;
800              
801 3         4 my %R;
802 3         6 $R{seconds}= $secDiff;
803 3         6 $R{minutes}= $R{seconds} / 60.0;
804 3         4 $R{hours} = $R{seconds} / 3600.0;
805 3         5 $R{days} = $R{seconds} / 86400.0;
806 3         20 $R{DHMS} = sprintf "%dd %.2d:%.2d:%.2d", $D,$H,$M,$S;
807 3         14 return \%R;
808             }
809              
810              
811             #------------------------------------------------------------------------------
812             # Return the actual date and time. If $format is undef the result is a hash
813             # ref with keys sec,min,hour,mday,mon,year,wday,yday,isdst,epoch.
814             # Mon and year are corrected. Epoch is the time in seconds since 1.1.1970.
815             # If $format is not undef it must be a strftime() format string. The result
816             # of Now() is then the strftime() formated string.
817             # $opt may be {format=><'strftime-format'>, offset=><+-seconds>, diff=>
818             #------------------------------------------------------------------------------
819             sub Now(@)
820             {
821 7     7 0 6006 my( $opt ) = _getParam(@_);
822              
823 7 100       15 my $offset = defined $opt->{offset} ? $opt->{offset}+0 : 0;
824 7         11 my $epoch = time+$offset;
825              
826 7 100       17 return _nowDiff( $epoch, $opt->{diff} ) if( $opt->{diff} );
827              
828 4         90 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($epoch);
829              
830             return strftime $opt->{format},
831             $sec,$min,$hour,$mday,$mon, $year,$wday,$yday,$isdst
832 4 100       66 if( defined $opt->{format} );
833              
834 2         3 $mon++;
835 2         3 $year+=1900;
836 2         16 return {sec=>$sec,min=>$min,hour=>$hour, mday=>$mday,mon=>$mon,year=>$year,
837             wday=>$wday,yday=>$yday,isdst=>$isdst,epoch=>$epoch};
838             }
839              
840             #------------------------------------------------------------------------------
841             #------------------------------------------------------------------------------
842             sub _printMenuHeader($) {
843 0     0   0 my ($head) = @_;
844 0 0       0 return if( ! defined $head );
845 0         0 printf "%s", $head;
846             }
847              
848             #------------------------------------------------------------------------------
849             #------------------------------------------------------------------------------
850             sub _printMenuFooter($) {
851 0     0   0 my ($foot) = @_;
852 0 0       0 return if( ! defined $foot );
853 0         0 printf "%s", $foot;
854             }
855              
856             #------------------------------------------------------------------------------
857             # Compute real index if we have NON-Lable lines in the opts-array.
858             #------------------------------------------------------------------------------
859             sub _getRealIndex($$) {
860 0     0   0 my ($o, $opts) = @_;
861              
862 0         0 my $real = 0;
863 0         0 my $curr = 0;
864 0         0 foreach my $op ( @{$opts} )
  0         0  
865             {
866 0 0       0 $real++ if( ! $op->{'label'} );
867 0 0       0 return $real if( $o == $curr );
868 0         0 $real++;
869 0         0 $curr++;
870             }
871 0         0 return $real;
872             }
873              
874             #------------------------------------------------------------------------------
875             # Concatenate several header/footer options into header/footer strings.
876             # Return original options array-ref and header/footer strings.
877             #------------------------------------------------------------------------------
878             sub _normMenuOpts($){
879 0     0   0 my ($opts) = @_;
880              
881 0         0 my $head = '';
882 0         0 my $foot = '';
883 0         0 my @OPTS;
884 0         0 foreach my $l ( @{$opts} ){
  0         0  
885 0 0       0 $head .= sprintf "%s\n", $l->{'header'} if( defined $l->{'header'} );
886 0 0       0 $foot .= sprintf "%s\n", $l->{'footer'} if( defined $l->{'footer'} );
887 0 0       0 push @OPTS, $l if( defined $l->{'label'} );
888             }
889 0 0       0 $head = $head eq '' ? undef : $head;
890 0 0       0 $foot = $foot eq '' ? undef : $foot;
891              
892 0         0 return \@OPTS, $head, $foot;
893             }
894              
895             #------------------------------------------------------------------------------
896             #------------------------------------------------------------------------------
897             sub _printOption($$$) {
898 0     0   0 my ($op,$form1,$i) = @_;
899              
900 0         0 my ($def,$form)=_getDefForm($form1,$op);
901 0 0       0 if( defined $def ) {
902 0         0 printf $form, $i,$op->{'label'},$def;
903 0         0 return;
904             }
905 0         0 printf $form, $i,$op->{'label'};
906             }
907             #------------------------------------------------------------------------------
908             # Display a menu, return the selected index number and the menu data structure.
909             # If a VALUE or DEFAULT key of a menu option points to a value this value can
910             # be changed.
911             # If a jump target is defined, the corresponding function will be called with
912             # argv=> as arguments.
913             # Data structure: [{label=>,value=>,jump=>,argv=>},...]
914             # - label=> must be defined all other keys are optinal
915             # - jump=> must point to a subroutine if set
916             # - argv=> arguments for the subroutine jump points to
917             # - header=> an optional head line
918             # - footer=> an optional footer line
919             #------------------------------------------------------------------------------
920             sub Menu($) {
921 0     0 0 0 my ($OPTS) = @_;
922              
923 0         0 my ($opts,$head,$foot) = _normMenuOpts($OPTS);
924 0         0 my ($i,$o) = (0,0);
925 0         0 my $maxLen = _maxLabelLength($opts);
926 0         0 my $form1 = "%3d %-${maxLen}s ";
927 0         0 system("clear");
928 0         0 _printMenuHeader($head);
929 0         0 ($i,$o) = (0,0);
930 0         0 foreach my $op ( @{$opts} )
  0         0  
931             {
932 0 0       0 next if( ! $op->{'label'} );
933 0         0 _printOption($op,$form1,$i++);
934             }
935 0         0 _printMenuFooter($foot);
936 0         0 printf "\nSelect: ";
937 0         0 $o = _getNumber( $i-1);
938 0         0 _setValue($o, $opts);
939 0         0 _jump($o, $opts); # jump to callback if defined
940 0         0 return $o,$opts;
941             }
942             #------------------------------------------------------------------------------
943             # Deprecated Menu function. Only for compatibility with legacy software.
944             #------------------------------------------------------------------------------
945             sub Menue($){
946 0     0 0 0 return Menu($_[0]);
947             }
948              
949             #------------------------------------------------------------------------------
950             # Prepare input for Data Menu. Allowed input formats:
951             # INPUT2: [{'label'=>'Max','value'=>'foo'},{'label'=>'Tim','value'=>99}]
952             #------------------------------------------------------------------------------
953             sub _arrayHandler($$) {
954 0     0   0 my ($dataMenu,$opts) = @_;
955              
956 0         0 foreach my $line ( @{$opts} ) {
  0         0  
957 0 0       0 next if( ref $line ne 'HASH' );
958 0 0       0 next if( ! defined $line-{'label'} );
959 0 0       0 next if( ! defined $line-{'value'} );
960 0         0 push @{$dataMenu}, $line,
  0         0  
961             }
962 0         0 return 'ARRAY';
963             }
964              
965             #------------------------------------------------------------------------------
966             # Prepare input for Data Menu. Allowed input formats:
967             # INPUT3: {}[, {'header'=>'','footer'=>''}]
968             #------------------------------------------------------------------------------
969             sub _hashHandler($$$) {
970 0     0   0 my ($dataMenu,$opts,$frame) = @_;
971              
972 0         0 foreach my $l ( sort keys %{$opts} ) {
  0         0  
973 0 0       0 next if( ref $opts->{$l} ne '' );
974 0         0 my $line = {'label' => $l, 'value' => $opts->{$l}};
975 0         0 push @{$dataMenu}, $line,
  0         0  
976             }
977 0 0       0 return 'HASH' if( ! defined $frame );
978              
979 0 0       0 push @{$dataMenu}, {'header' => $frame->{'header'}} if( defined $frame->{'header'});
  0         0  
980 0 0       0 push @{$dataMenu}, {'footer' => $frame->{'footer'}} if( defined $frame->{'footer'});
  0         0  
981 0         0 return 'HASH';
982             }
983              
984              
985             #------------------------------------------------------------------------------
986             # Prepare input for Data Menu. Allowed input formats:
987             # INPUT1: 'value1 value2 ..'[, {'header'=>'','footer'=>''}]
988             #------------------------------------------------------------------------------
989             sub _scalarHandler($$$) {
990 0     0   0 my ($dataMenu,$opts,$frame) = @_;
991              
992 0         0 my $i=1;
993 0         0 foreach my $l ( split /\s+/, $opts ) {
994 0         0 my $line = {'label' => 'V'.$i++, 'value' => $l};
995 0         0 push @{$dataMenu}, $line,
  0         0  
996             }
997 0 0       0 return 'SCALAR' if( ! defined $frame );
998 0 0       0 push @{$dataMenu}, {'header' => $frame->{'header'}} if( defined $frame->{'header'});
  0         0  
999 0 0       0 push @{$dataMenu}, {'footer' => $frame->{'footer'}} if( defined $frame->{'footer'});
  0         0  
1000 0         0 return 'SCALAR';
1001             }
1002              
1003             #------------------------------------------------------------------------------
1004             # Prepare input for Data Menu. Allowed input formats:
1005             # INPUT1: 'value1 value2 ..'[, {'header'=>'','footer'=>''}]
1006             # INPUT2: [{'label'=>'Max','value'=>'foo'},{'label'=>'Tim','value'=>99}]
1007             # INPUT3: {}[, {'header'=>'','footer'=>''}]
1008             #------------------------------------------------------------------------------
1009             sub _addData($$$)
1010             {
1011 0     0   0 my ($dataMenu,$opts,$frame) = @_;
1012              
1013 0 0       0 return _arrayHandler ($dataMenu,$opts) if(ref $opts eq 'ARRAY');
1014 0 0       0 return _hashHandler ($dataMenu,$opts,$frame) if(ref $opts eq 'HASH' );
1015 0 0       0 return _scalarHandler($dataMenu,$opts,$frame) if(ref $opts eq '' );
1016             }
1017              
1018             #------------------------------------------------------------------------------
1019             # Remove {label=>"EXIT"} line.
1020             #------------------------------------------------------------------------------
1021             sub _returnArray($) {
1022 0     0   0 my ($dataMenu) = @_;
1023 0         0 splice @{$dataMenu},0,1;
  0         0  
1024 0         0 return $dataMenu;
1025             }
1026              
1027             #------------------------------------------------------------------------------
1028             # Remove {label=>"EXIT"} line. Return values as white space separated string.
1029             #------------------------------------------------------------------------------
1030             sub _returnScalar($) {
1031 0     0   0 my ($dataMenu) = @_;
1032              
1033 0         0 splice @{$dataMenu},0,1;
  0         0  
1034 0         0 my $data='';
1035 0 0       0 map { $data .= $_->{'value'} .' ' if( defined $_->{'value'})} @{$dataMenu};
  0         0  
  0         0  
1036 0         0 chop $data;
1037 0         0 return $data;
1038             }
1039              
1040             #------------------------------------------------------------------------------
1041             # Remove {label=>"EXIT"}, header and footer lines.
1042             # Return values as white hash ref with 'label' as key and 'value' as value.
1043             #------------------------------------------------------------------------------
1044             sub _returnHash($) {
1045 0     0   0 my ($dataMenu) = @_;
1046              
1047 0         0 splice @{$dataMenu},0,1;
  0         0  
1048 0         0 my %data;
1049 0 0 0     0 map { if( defined $_->{'value'} && defined $_->{'label'} ){
1050 0         0 my $k = $_->{'label'};
1051 0         0 my $v = $_->{'value'};
1052 0         0 $data{$k} = $v;
1053             }
1054 0         0 } @{$dataMenu};
  0         0  
1055 0         0 return \%data;
1056             }
1057              
1058             #------------------------------------------------------------------------------
1059             # Use Menu to edit small data sets. Two input formats allowed.
1060             # INPUT1: 'value1 value2 ..'
1061             # INPUT2: [{'label'=>'Max','value'=>'foo'},{'label'=>'Tim','value'=>99}]
1062             #------------------------------------------------------------------------------
1063             sub DataMenu(@) {
1064 0     0 0 0 my ($opts,$head) = @_;
1065              
1066 0         0 my $dataMenu = [{label=>"RETURN"}];
1067 0         0 my $format = _addData($dataMenu, $opts,$head);
1068              
1069 0         0 while( 1 ) {
1070 0         0 my ($o,$dataMenu) = Menu($dataMenu);
1071 0 0       0 last if( $o == 0 );
1072             }
1073 0 0       0 return _returnArray ( $dataMenu) if( $format eq 'ARRAY' );
1074 0 0       0 return _returnScalar($dataMenu) if( $format eq 'SCALAR');
1075 0 0       0 return _returnHash ($dataMenu) if( $format eq 'HASH' );
1076             }
1077             #------------------------------------------------------------------------------
1078             # Deprecated Menu function. Only for compatibility with legacy software.
1079             #------------------------------------------------------------------------------
1080             sub DataMenue(@){
1081 0     0 0 0 return DataMenu(@_);
1082             }
1083              
1084             #------------------------------------------------------------------------------
1085             # Read a directory and return a hash with filenames stat() structure infos
1086             # for every file. An optional pattern (regexp) may be used for selecting files.
1087             #------------------------------------------------------------------------------
1088             sub Stat($@)
1089             {
1090 3     3 0 3166 my ($path,$patt) = _getParam( @_ );
1091              
1092 3         6 my $dir = Dir($path,$patt);
1093              
1094 3         77 my $stat;
1095 3         4 foreach my $f ( @{$dir} )
  3         6  
1096             {
1097 28         284 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
1098             $atime,$mtime,$ctime,$blksize,$blocks) = CORE::stat("$path/$f");
1099              
1100 28         86 $stat->{$f}{'dev'} = $dev;
1101 28         34 $stat->{$f}{'ino'} = $ino;
1102 28         36 $stat->{$f}{'mode'} = $mode;
1103 28         41 $stat->{$f}{'nlink'}= $nlink;
1104 28         341 $stat->{$f}{'uid'} = $uid;
1105 28         38 $stat->{$f}{'gid'} = $gid;
1106 28         30 $stat->{$f}{'rdev'} = $rdev;
1107 28         50 $stat->{$f}{'size'} = $size;
1108 28         33 $stat->{$f}{'atime'}= $atime;
1109 28         39 $stat->{$f}{'mtime'}= $mtime;
1110 28         30 $stat->{$f}{'ctime'}= $ctime;
1111 28         48 $stat->{$f}{'blksize'} = $blksize;
1112 28         46 $stat->{$f}{'blocks'} = $blocks;
1113             }
1114 3         24 return $stat;
1115             }
1116              
1117             #------------------------------------------------------------------------------
1118             # Jump to a callback function of a menu option.
1119             #------------------------------------------------------------------------------
1120             sub _jump($$)
1121             {
1122 0     0     my ($o,$menu) = @_;
1123              
1124 0 0         return if( !defined $menu->[$o]->{'jump'} ); #option has no callback
1125              
1126 0           my $call = $menu->[$o]->{'jump'};
1127 0 0         my $args = defined $menu->[$o]->{'argv'} ? $menu->[$o]->{'argv'} : undef;
1128              
1129 0 0         if( ref $call eq 'CODE' ) { $call->($args); return; }
  0            
  0            
1130 0           Log("\nERROR: Can't call function $call(). It's not a code reference.");
1131 0           sleep 5;
1132             }
1133              
1134             #------------------------------------------------------------------------------
1135             # Compute the maximum length of all labels found in the menu array @{$opts}.
1136             #------------------------------------------------------------------------------
1137             sub _maxLabelLength($)
1138             {
1139 0     0     my ($opts) = @_;
1140 0           my $len=0;
1141 0           foreach my $op ( @{$opts} )
  0            
1142             {
1143 0 0         next if( ! defined $op->{'label'});
1144 0           my $l = length($op->{'label'});
1145 0 0         $len = $len < $l ? $l : $len;
1146             }
1147 0           return $len;
1148             }
1149              
1150             #------------------------------------------------------------------------------
1151             # Compute the default value and the format string.
1152             #------------------------------------------------------------------------------
1153             sub _getDefForm($$)
1154             {
1155 0     0     my ($form1,$op) = @_;
1156              
1157 0           my $def;
1158 0 0         $def = $op->{'value'} if( defined $op->{'value'} );
1159 0           my $form;
1160 0 0         if( $op->{'readOnly'} ) { $form = defined $def ? "$form1 <%s>" : $form1 }
  0 0          
1161 0 0         else { $form = defined $def ? "$form1 [%s]" : $form1 }
1162              
1163 0           return $def,"$form\n";
1164             }
1165              
1166             #------------------------------------------------------------------------------
1167             # Remove last entered character from input. Move cursor one position backward.
1168             #------------------------------------------------------------------------------
1169             sub _delLastChar(){
1170 0     0     printf "%c %c", 0x08, 0x08; # backspace,blank,backspace
1171             }
1172              
1173             #------------------------------------------------------------------------------
1174             # Print one character to STDOUT without buffering.
1175             #------------------------------------------------------------------------------
1176             sub _flushChar($){
1177 0     0     my ($char) = @_;
1178 0           my $old = $|;
1179 0           $| = 1;
1180 0           printf "%s", $char;
1181 0           $| = $old;
1182 0           return;
1183             }
1184              
1185             #------------------------------------------------------------------------------
1186             # Check if first digit exeeds the range if used on ten-spot position. In this
1187             # case we got a valid number in the range of 0-9.
1188             #------------------------------------------------------------------------------
1189             sub _unique($$){
1190 0     0     my ($o,$maxNum) = @_;
1191 0 0         return 1 if( $$o =~ s/^\n$/0/ ); # set default option 0 if we got ENTER
1192 0 0         return 1 if( $$o*10 > $maxNum);
1193             }
1194              
1195             #------------------------------------------------------------------------------
1196             # Check if first digit is in range. \n is valid for special handling later.
1197             #------------------------------------------------------------------------------
1198             sub _invalid($$){
1199 0     0     my ($o,$maxNum) = @_;
1200 0 0         return 0 if( $o =~ /^\n$/ );
1201 0 0         return 0 if( $o <= $maxNum);
1202 0           return 1;
1203             }
1204              
1205             #------------------------------------------------------------------------------
1206             # Read the next number from STDIN. Return 0 if given character is not a digit.
1207             # Read two characters if max option number is greater than 9.
1208             # Valid option numbers are 0...99.
1209             #------------------------------------------------------------------------------
1210             sub _getNumber($) {
1211 0     0     my ($maxNum) = @_;
1212              
1213 0           my $o;
1214 0           while( 1 ) {
1215 0           $o=_getChar('^\d+$');
1216 0 0         next if( _invalid($o,$maxNum) );
1217 0 0         last if( _unique(\$o,$maxNum) );
1218              
1219 0 0         if( $maxNum > 9 ) {
1220 0           _flushChar($o);
1221 0           my $oo=_getChar('^\d+$');
1222 0 0         last if( $oo eq "\n" );
1223 0           $o=10*$o+$oo;
1224             }
1225 0 0 0       last if( $o <= $maxNum && $o > -1 );
1226 0           _delLastChar();
1227             }
1228 0           _delLastChar();
1229 0           _flushChar($o);
1230 0           return $o;
1231             }
1232             #------------------------------------------------------------------------------
1233             # Read one character from STDIN. FIXME: stty method is not portable
1234             #------------------------------------------------------------------------------
1235             sub _getChar($)
1236             {
1237 0     0     my ($patt) = @_;
1238              
1239 0           while( 1 ) {
1240 0           system "stty", '-echo', '-icanon', 'eol', "\001";
1241 0           my $key = getc(STDIN);
1242 0           system "stty", 'echo', 'icanon', 'eol', '^@'; # ASCII null
1243 0 0         return $key if( $key =~ /$patt/ );
1244 0 0         return $key if( $key =~ /\n/ ); # use default
1245             }
1246             }
1247              
1248             #------------------------------------------------------------------------------
1249             # Read a line from STDIN and assign it to the "value" key of an menu option.
1250             # Data structure: [{label=>,value=>,readOnly=>,jump=>,argv=>},...]
1251             # After this function value=> has one of the following values:
1252             # - the read line if not empty
1253             # - the old value if read an empty line and an old value exists
1254             # - the value is not changeable if the key readOnly is defined
1255             #------------------------------------------------------------------------------
1256             sub _setValue($)
1257             {
1258 0     0     my ($o,$opts) = @_;
1259              
1260 0 0         return undef if( !defined $opts->[$o]{'value'} );
1261              
1262 0           my $op = $opts->[$o];
1263 0 0         my $def = defined $op->{'value'} ? $op->{'value'} : '';
1264              
1265 0 0         return $def if( $op->{'readOnly'} );
1266 0           printf "\n%s [%s]:", $op->{'label'}, $def;
1267              
1268 0           my $resp = ;
1269 0           chomp $resp;
1270              
1271 0 0         $resp = $def if( $resp eq '' );
1272 0 0 0       $resp = $op->{'default'} if( defined $op->{'default'} && $resp eq ' ' );
1273 0           $op->{'value'} = $resp;
1274 0           return $resp;
1275             }
1276              
1277             #------------------------------------------------------------------------------
1278             # Return the options as array reference.
1279             # If $opt is not already an array reference, it mut be a string like:
1280             # '...'
1281             #------------------------------------------------------------------------------
1282             sub _checkOptions($){
1283 0     0     my ($opt) = @_;
1284              
1285 0 0         return $opt if( ref $opt eq 'ARRAY');
1286 0           my ($sep,$opstr) = $opt =~ m/(.)(.*)/;
1287 0           my @Ops = split /[$sep]/, $opstr;
1288 0           return \@Ops;
1289             }
1290              
1291             #------------------------------------------------------------------------------
1292             # Add given $options as read only menu options to the menu with name $menuName
1293             # in menu-container $m.
1294             # $options: an array reference
1295             # or a CSV like string (first character is the separator).
1296             # Such as: ',opt1,opt2,opt3'
1297             # $defaults: a regular expression. If it matches against an option, this
1298             # option gets into selected state.
1299             #------------------------------------------------------------------------------
1300             sub _ops2CheckBox($$$$){
1301 0     0     my ($m,$menuName,$options,$defaults) = @_;
1302              
1303 0           $options = _checkOptions($options);
1304 0           foreach my $x (@{$options}) {
  0            
1305 0           my $v = '';
1306 0 0         $v = $x =~ /$defaults/ ? 'x' : '' if(defined $defaults);
    0          
1307 0           $m->addOption($menuName =>{'label'=>$x, 'value'=>$v, 'readOnly'=>1});
1308             }
1309 0           return;
1310             }
1311              
1312             #------------------------------------------------------------------------------
1313             # Add the check box menu to the menus container $m.
1314             #------------------------------------------------------------------------------
1315             sub _ckeckBox2Container($$$){
1316 0     0     my ($m,$menuName,$header) = @_;
1317              
1318 0           $m->addMenu( {$menuName =>[{'header'=>$header}]});
1319 0           return;
1320             }
1321              
1322             #------------------------------------------------------------------------------
1323             # Return the menuname if defined or a random number between 0 and 10000.
1324             #------------------------------------------------------------------------------
1325             sub _checkMenuName($){
1326 0     0     my ($name) = @_;
1327              
1328 0 0         return $name if( defined $name );
1329 0           return sprintf "TMP_%d", int(rand(10000));
1330             }
1331              
1332             #------------------------------------------------------------------------------
1333             # Is $m pointing to a menus container? If yes, do nothig otherwise return
1334             # an empty menus container.
1335             #------------------------------------------------------------------------------
1336             sub _checkMenuContainer($){
1337 0     0     my ($m) = @_;
1338 0 0         return $m if( ref $m eq 'Script::Toolbox::Util::Menus' );
1339 0           return Script::Toolbox::Util::Menus->new();
1340             }
1341              
1342             #------------------------------------------------------------------------------
1343             # Return the labels of all selected and unselected options.
1344             #------------------------------------------------------------------------------
1345             sub _getOptStates($$){
1346 0     0     my ($m,$name) = @_;
1347              
1348 0           my $on = $m->getMatching($name,'.+','value','label');
1349 0           my $off= $m->getMatching($name,'^$','value','label');
1350 0           return {'on'=>$on,'off'=>$off};
1351             }
1352              
1353             #------------------------------------------------------------------------------
1354             # Clear all option values of menu $menuName in container $m.
1355             # Set the option with $label in selected state.
1356             #------------------------------------------------------------------------------
1357             sub _setRadioButton($$$){
1358 0     0     my ($m,$menuName,$label) = @_;
1359              
1360 0           my $lv = $m->getLabelValueHash($menuName);
1361 0           map { $lv->{$_} = '' } keys %{$lv};
  0            
  0            
1362 0           $lv->{$label} = 'x';
1363 0           $m->setValues($menuName,$lv);
1364 0           return;
1365             }
1366              
1367             #------------------------------------------------------------------------------
1368             #------------------------------------------------------------------------------
1369             sub _isOneSelected($$){
1370 0     0     my ($m,$name) = @_;
1371              
1372 0           my $l = $m->getMatching($name,'^[^\s]+$','value','number');
1373              
1374 0 0         return 1 if( @{$l} > 0);
  0            
1375 0           return 0;
1376             }
1377              
1378             #------------------------------------------------------------------------------
1379             # Set all option value to of-state except for the selected.
1380             #------------------------------------------------------------------------------
1381             sub _radioButton($$){
1382 0     0     my ($m,$name) = @_;
1383              
1384 0           while(1) {
1385 0           my $op = $m->run($name,1);
1386 0           my $cl = $m->currLabel($name);
1387 0 0 0       last if( $cl eq 'RETURN' && _isOneSelected($m,$name));
1388 0           _setRadioButton($m,$name,$cl);
1389             }
1390 0           return;
1391             }
1392             #------------------------------------------------------------------------------
1393             #------------------------------------------------------------------------------
1394             sub _cleanUpTmps($){
1395 0     0     my ($m) = @_;
1396              
1397 0           my $def = $m->{'def'};
1398 0           foreach my $x (sort keys %{$def}) {
  0            
1399 0 0         next if( $x !~ /^TMP_[0-9]+/ );
1400 0           delete $m->{'def'}{$x};
1401             }
1402 0           return;
1403             }
1404              
1405             #------------------------------------------------------------------------------
1406             # If no radio button checkbox accept all default patterns.
1407             # If it's a radio button checkbox accept only the first pattern (asume
1408             # we have patterns like 'a|b|c'). Otherwise clear all defaults.
1409             #------------------------------------------------------------------------------
1410             sub _checkDefaults($$$){
1411 0     0     my ($m,$menuName,$defaults) = @_;
1412              
1413 0           my @x = split /[|]/, $defaults;
1414 0           _setRadioButton($m,$menuName,$x[0]);
1415             }
1416              
1417             #------------------------------------------------------------------------------
1418             # Display some options via menu.
1419             # defaults (like me|dk|ek) are preselected
1420             # Return a hash with two keys 'on' and 'off'. These keys points to arrays
1421             # with option labels. 'on' means the value of this option is not an empty
1422             # string.
1423             # Radio flag switches to radio button mode.
1424             #------------------------------------------------------------------------------
1425             sub _CheckBox($$@){
1426 0     0     my ($header,$options,$defaults,$radio,$m,$menuName) = @_;
1427              
1428 0           $menuName = _checkMenuName($menuName);
1429 0           $m = _checkMenuContainer($m);
1430 0 0         if( ! defined $m->{'def'}{$menuName} ) {
1431 0           _ckeckBox2Container($m,$menuName,$header);
1432 0           _ops2CheckBox($m,$menuName,$options,$defaults);
1433             }
1434 0 0         _checkDefaults($m, $menuName, $defaults) if($radio);
1435 0           $m->setHeader($menuName,$header);
1436 0 0         if($radio) { _radioButton($m,$menuName) }
  0            
1437 0           else { $m->run($menuName,0) }
1438            
1439 0           my $ret = _getOptStates($m,$menuName); #{selected,unselected}
1440 0           _cleanUpTmps($m);
1441 0           return $ret;
1442             }
1443             #------------------------------------------------------------------------------
1444             # Multiple selections possible.
1445             #------------------------------------------------------------------------------
1446             sub CheckBox($$@){
1447 0     0 0   my ($header,$options,$defaults,$m,$menuName) = @_;
1448              
1449 0           return( _CheckBox($header,$options,$defaults,0,$m,$menuName));
1450             }
1451              
1452             #------------------------------------------------------------------------------
1453             # Only one option may and must be selected.
1454             #------------------------------------------------------------------------------
1455             sub RadioButton($$@){
1456 0     0 0   my ($header,$options,$defaults,$m,$menuName) = @_;
1457              
1458 0           return( _CheckBox($header,$options,$defaults,1,$m,$menuName));
1459             }
1460              
1461             1;
1462             __END__