File Coverage

blib/lib/Script/Toolbox/Util.pm
Criterion Covered Total %
statement 275 580 47.4
branch 79 262 30.1
condition 12 31 38.7
subroutine 42 80 52.5
pod 0 19 0.0
total 408 972 41.9


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