File Coverage

blib/lib/File/Tail/Multi.pm
Criterion Covered Total %
statement 346 673 51.4
branch 98 314 31.2
condition 20 102 19.6
subroutine 33 49 67.3
pod 8 32 25.0
total 505 1170 43.1


line stmt bran cond sub pod time code
1             package File::Tail::Multi;
2             #
3             # Copyright (c) 2008 Arvind Tripathy. All rights reserved.
4             # This program is free software; you can redistribute it and/or
5             # modify it under the same terms as Perl itself.
6             #
7             # Following are other pachages needed for File::Tail::Multi
8             #
9 2     2   10771 use Carp;
  2         4  
  2         182  
10 2     2   12 use strict;
  2         3  
  2         71  
11 2     2   2027 use File::stat qw(:FIELDS);
  2         23411  
  2         17  
12 2     2   650 use File::Basename;
  2         4  
  2         195  
13 2     2   2411 use FileHandle;
  2         29975  
  2         36  
14 2     2   4861 use DirHandle;
  2         1010  
  2         75  
15 2     2   14 use vars qw( $AUTOLOAD $LastScan $True $False $DEBUG $LastRun_File $DO_MD5_Check $VERSION $GMT $TC $FileAttributeChanged );
  2         4  
  2         2737  
16             #
17             # Define sub
18             #
19             sub new;
20             sub update_attribute;
21             sub getparams;
22             sub GetParams;
23             sub CheckAttributes;
24             sub CreateFileDataStructure;
25             sub CreateListOfFiles;
26             sub OpenFileToTail;
27             sub readline;
28             sub read;
29             sub print;
30             sub printpat;
31             sub printexceptpat;
32             sub printstat;
33             sub getFilePos;
34             sub getFileSize;
35             sub Patterns;
36             sub Prefix;
37             sub RemoveDups;
38             sub CheckIfArrayOrFile;
39             sub ExceptPatterns;
40             sub OpenUpdateFiles;
41             sub UpdateStat;
42             sub Time;
43             sub version;
44             sub debug;
45             sub close_all_files;
46             sub FileState;
47             sub SetFileState;
48             sub PosFileMark;
49             sub printfilestates;
50             sub match_closure;
51             #
52             $True=1;
53             $False=0;
54             $GMT=$False;
55             $TC=$False;
56             $LastScan=time;
57             $DEBUG=$False;
58             $LastRun_File="";
59             $DO_MD5_Check=0;
60             $VERSION=0.1;
61             $FileAttributeChanged=$False;
62             my $pattern_sub;
63             my $exceptpattern_sub;
64             my %Attribute=();
65             my @File_Data_Structure;
66             my @StatArray = qw { dev ino mode nlink uid gid rdev size
67             atime mtime ctime blksize blocks }; # Stat ids
68             #
69             # Code added for MSWin32
70             #
71             my $MSWin32 = ( $^O eq "MSWin32" ) ? $True : $False;
72             #
73             ########################################################################
74             #
75             # Creating a new File::Tail::Multi object
76             #
77             ########################################################################
78             #
79             sub new {
80 1     1 1 24584 my $class=shift;
81 1         39 my(%argvs)=@_;
82 1         17 my $args = getparams(\%argvs);
83 1         6 my $self;
84             my $rds;
85             #
86             # set default vars
87             #
88 1         3 my %Default=();
89 1         3 $Default{'Files'}=$False;
90 1         2 $Default{'MaxAge'}=10;
91 1         3 $Default{'NumLines'}=10;
92 1         2 $Default{'OutputPrefix'}=$False;
93 1         2 $Default{'Pattern'}=$False;
94 1         2 $Default{'ExceptPattern'}=$False;
95 1         2 $Default{'ScanForFiles'}=$False;
96 1         3 $Default{'RemoveDuplicate'}=$False;
97 1         1 $Default{'Function'}=$False;
98 1         5 $Default{'LastRun_File'}="";
99 1         2 $Default{'DO_MD5_CHECK'}=$False;
100 1         2 $Default{'Debug'}=$False;
101             #
102 1         8 foreach my $keys ( keys %$args ) {
103 12 50       26 $args->{$keys}=$Default{$keys} if ( ! exists $args->{$keys} );
104             }
105             #
106 1         6 $DEBUG=$args->{'Debug'};
107 1         2 $LastRun_File=$args->{'LastRun_File'};
108 1         2 $DO_MD5_Check=$args->{'DO_MD5_Check'};
109             #
110 1 50       4 if ( $args->{'Files'} ) {
111 1         7 $rds=CreateFileDataStructure( CreateListOfFiles($args->{'Files'})),
112             }
113             # convert hash array to constant var
114             #
115             %Attribute = (
116 1   50     22 Files => $args->{'Files'},
117             MaxAge => $args->{'MaxAge'},
118             NumLines => $args->{'NumLines'},
119             OutputPrefix => $args->{'OutputPrefix'},
120             Pattern => $args->{'Pattern'},
121             ExceptPattern => $args->{'ExceptPattern'},
122             ScanForFiles => $args->{'ScanForFiles'},
123             RemoveDuplicate => $args->{'RemoveDuplicate'},
124             Function => $args->{'Function'},
125             LastRun_File => $args->{'LastRun_File'},
126             DO_MD5_CHECK => $args->{'DO_MD5_Check'},
127             Debug => $args->{'Debug'} || 0,
128             FileArray => $rds,
129             );
130 1         2 $self=\%Attribute;
131 1         3 bless $self, $class;
132 1         3 CheckAttributes($self);
133             #
134 1         72 return $self;
135              
136             }
137             #
138             ########################################################################
139             #
140             # Update Object Attribute
141             #
142             ########################################################################
143             #
144             sub update_attribute {
145 0     0 1 0 my ($rFD)=shift;
146 0         0 my(%argvs)=@_;
147 0         0 my $args = getparams(\%argvs);
148             #
149 0         0 foreach my $keys ( keys %$args ) {
150 0 0       0 if ( $keys eq "Files" ) {
151 0         0 $FileAttributeChanged=$True;
152             }
153 0 0       0 $rFD->{$keys}=$args->{$keys} if $args->{$keys};
154             }
155             }
156             #
157             ########################################################################
158             #
159             # Front end to Getparams function for File::Tail::Multi
160             #
161             ########################################################################
162             #
163             sub getparams {
164 1     1 0 169 my($rargvs)=@_;
165 1         8 my ($MaxAge,$NumLines, $OutputPrefix,$Pattern,
166             $ExceptPattern,$Debug,$ScanForFiles,$RemoveDuplicate,
167             $Function,$LastRun_File,$DO_MD5_Check,$Files);
168 1         103 my $args =
169             GetParams $rargvs,
170             { MaxAge => \$MaxAge,
171             NumLines => \$NumLines,
172             OutputPrefix => \$OutputPrefix,
173             Pattern => \$Pattern,
174             ExceptPattern => \$ExceptPattern,
175             Debug => \$Debug,
176             ScanForFiles => \$ScanForFiles,
177             RemoveDuplicate => \$RemoveDuplicate,
178             Function => \$Function,
179             LastRun_File => \$LastRun_File,
180             DO_MD5_CHECK => \$DO_MD5_Check,
181             Files => \$Files,
182             },
183             [qw(MaxAge NumLines OutputPrefix Pattern ExceptPattern Debug
184             ScanForFiles RemoveDuplicate Function Files)];
185             #
186             }
187             #
188             ########################################################################
189             #
190             # Check each Attributes for Min and Max Values
191             #
192             # Exit Codes
193             # 1001 - MaxAge is less the zero
194             # 1002 - NumLines is less the zero
195             # 1003 - OutputPrefix must one ( )
196             # 1004 - Pattern must is not a file or ARRAY
197             # 1005 - ExceptPattern is not a file or ARRAY
198             # 1006 - Debug is not 0 or 1
199             # 1007 - ScanForFiles is less the zero
200             # 1008 - RemoveDuplicate is not 0 or 1
201             # 1009 - Function is not ref to fuction
202             # 1010 - File attribute not set
203             # 1011 - LastRun File not found or incorrect path
204             # 1012 - DO_MD5_Check is not 0 or 1
205             #
206             ########################################################################
207             #
208             sub CheckAttributes {
209 1     1 0 2 my($args)=shift;
210 1         1 local($_);
211 1         9 for ( keys %$args ) {
212 13 100       37 /MaxAge/ and do { # /MaxAge/ must be >= zero
213 1 50       5 if ( $args->{MaxAge} < 0 ) {
214 0         0 print STDOUT "ERROR: File::Tail::Multi object MaxAge must be >= zero\n";
215 0         0 exit 1001;
216             }
217 1         2 next;
218             };
219 12 100       25 /NumLines/ and do { # /NumLines/ must be >= zero
220 1 50       5 if ( $args->{NumLines} < 0 ) {
221 0         0 print STDOUT "ERROR: File::Tail::Multi object NumLines must be >= zero\n";
222 0         0 exit 1002;
223             }
224 1         3 next;
225             };
226 11 100       28 /OutputPrefix/ and do { # /OutputPrefix/ must be a file or ARRAY
227 1 50       7 unless ( $args->{OutputPrefix} =~ /^(p|f|t|tg|tc|pt|ptg|ptc|ft|ftg|ftc|tp|tpg|tpc|tf|tfg|tfc)$/){
228 0 0       0 next if ! $args->{OutputPrefix};
229 0         0 print STDOUT "ERROR: File::Tail::Multi object OutputPrefix must ARRAY of file\n";
230 0         0 exit 1003;
231             }
232 1         2 next;
233             };
234 10 100       29 /Pattern/ and do { # /Pattern/ must be a file or ARRAY
235 2 50       6 next if ! $args->{Pattern};
236 2     2   17 no strict 'refs';
  2         4  
  2         886  
237 2 50 33     9 if ( ref($args->{Pattern}) ne "ARRAY" and ! -f $args->{Pattern} ) {
238 0         0 print STDOUT "ERROR: File::Tail::Multi object Pattern must ARRAY or file\n";
239 0         0 exit 1004;
240             }
241 2         8 next;
242             };
243 8 50       15 /ExceptPattern/ and do { # /ExceptPattern/ must be a file or ARRAY
244 0 0       0 next if ! $args->{ExceptPattern};
245 0 0 0     0 if ( ref($args->{ExceptPattern}) ne "ARRAY" and ! -f $args->{ExceptPattern} ) {
246 0         0 print STDOUT "ERROR: File::Tail::Multi object ExceptPattern must ARRAY or file\n";
247 0         0 exit 1005;
248             }
249 0         0 next;
250             };
251 8 100       44 /Debug/ and do { # /Debug/ must be 0 or 1
252 1 50       10 unless ( $args->{Debug} =~ /^[0|1]$/ ) {
253 0         0 print STDOUT "ERROR: File::Tail::Multi object Debug must 0 or 1\n";
254 0         0 exit 1006;
255             }
256 1         2 next;
257             };
258 7 100       27 /ScanForFiles/ and do { # /ScanForFiles/ must be 0 or 1
259 1 50       6 if ( $args->{ScanForFiles} < 0 ) {
260 0         0 print STDOUT "ERROR: File::Tail::Multi object ScanForFiles must be >= zero\n";
261 0         0 exit 1007;
262             }
263 1         2 next;
264             };
265 6 100       11 /RemoveDuplicate/ and do { # /RemoveDuplicate/ must be 0 or 1
266 1 50       10 unless ( $args->{RemoveDuplicate} =~ /^[0|1]$/ ) {
267 0         0 print STDOUT "ERROR: File::Tail::Multi object RemoveDuplicate must 0 or 1\n";
268 0         0 exit 1008;
269             }
270 1         2 next;
271             };
272 5 100       17 /Function/ and do { # /Function/ must be a function
273 1 50       8 if ( ref($args->{Function}) ne "CODE" ) {
274 0 0       0 next if ! $args->{Function};
275 0         0 print STDOUT "ERROR: File::Tail::Multi object Function must be a function\n";
276 0         0 exit 1009;
277             }
278 1         2 next;
279             };
280 4 100       10 /Files/ and do { # All attributes have default except for Files
281 1 50       4 next if ! $args->{Files};
282 1 50       4 unless ( $args->{Files} ) {
283 0         0 print STDOUT "ERROR: File::Tail::Multi object must have attribute Files\n";
284 0         0 exit 1010;
285             }
286 1         35 next;
287             };
288 3 100       8 /LastRun_File/ and do { # /LastRun_File/ must be a function
289 1 50       4 next if ! $args->{'LastRun_File'};
290 2     2   14 no strict 'refs';
  2         4  
  2         1762  
291 0 0       0 if ( ! -f $args->{'LastRun_File'} ) {
292 0         0 print STDOUT "ERROR: File::Tail::Multi object LastRun_File must be a path to a file\n";
293 0         0 exit 1011;
294             }
295 0         0 next;
296             };
297 2 50       5 /DO_MD5_Check/ and do { # /DO_MD5_Check/ must be 0 or 1
298 0 0       0 unless ( $args->{DO_MD5_Check} =~ /^[0|1]$/ ) {
299 0         0 print STDOUT "ERROR: File::Tail::Multi object DO_MD5_Check must 0 or 1\n";
300 0         0 exit 1012;
301             }
302 0         0 next;
303             };
304             }
305             }
306             #
307             ########################################################################
308             #
309             # Get params past to object File::Tail::Multi
310             #
311             ########################################################################
312             #
313             sub GetParams {
314 1 50   1 0 14 my $argvref = shift or croak "Missing required argument.\n";
315 1 50       5 my $params = shift or croak "Missing required parameters hash.\n";
316 1 50       11 my $arglist = shift or croak "Missing required arglist array.\n";
317 1         5 my %args;
318 1         5 my ($param, $var);
319 1 50       14 if (ref($argvref) eq 'HASH') {
320 1         3 my $href = $argvref;
321 1         27 %args = %$href; # initialize result with input hash
322 1         8 foreach $param (keys %$href) { # for each named argument...
323             # Is this a known parameter?
324 6 50       17 if (exists($params->{$param})) {
325 6         9 $var = $params->{$param};
326 6   33     43 while ($var ne '' && ref($var) eq '') { # indirect refs?
327 0         0 $var = $params->{$param = $var};
328             }
329 6 50       16 if ($var ne '') {
330 6         196 $$var = $href->{$param}; # assign the param's variable
331 6         132 $args{$param} = $$var; # make sure canonical param gets defined
332 6         14 next; # go to the next parameter
333             }
334             }
335 0 0       0 if (!exists($params->{$param})) {
336 0         0 croak "Unknown parameter: \"$param\"\n";
337             }
338             }
339             } else { # use args in the order given for variables
340 0         0 my $i;
341 0         0 for ($i = 0; $i <= $#$arglist; $i++) {
342 0         0 $param = $arglist->[$i]; # get the next argument
343 0         0 $var = $params->{$param}; # get it's variable
344 0 0       0 next unless defined($var);
345 0   0     0 while ($var ne '' && ref($var) eq '') {
346 0         0 $var = $params->{$param = $var};
347             }
348 0 0       0 if ($var ne '') {
    0          
349 0 0       0 $$var = $i <= $#$argvref ? $argvref->[$i] : '';
350 0         0 $args{$param} = $$var; # assign to the hash
351             } elsif (!exists($params->{$param})) {
352 0         0 croak "Unknown parameter: \"$param\" for argument $i.\n";
353             }
354             }
355             }
356             # Now, make sure all variables get initialized
357 1         11 foreach $param (keys %$params) {
358 12         16 $var = $params->{$param};
359 12   33     59 while ($var ne '' && ref($var) eq '') {
360 0         0 $var = $params->{$param = $var};
361             }
362 12 100 66     53 if ($var ne '' && !exists($args{$param})) {
363 6         13 $$var = $args{$param} = undef;
364             }
365             }
366 1         5 \%args; # return the HASH ref
367             }
368             #
369             ########################################################################
370             #
371             # Will open all file and create a file data Structure
372             #
373             ########################################################################
374             #
375             sub CreateFileDataStructure {
376 1     1 0 2 my($File_Array)=@_;
377 1         2 my $BFILE;
378             my $fh;
379 0         0 my $Exist;
380 0         0 my $rhash;
381 1         2 my $Pos=0;
382 1         5 my $online=$False;
383 1         3 my %FileHash=();
384             #
385 1 50       4 if ( %Attribute ) {
386 0         0 $online=$True;
387 0         0 foreach my $FH ( @{$Attribute{'FileArray'}} ) {
  0         0  
388 0         0 $FileHash{$FH->{'name'}} = 1;
389             }
390             }
391 1         6 foreach my $FILE ( @$File_Array ) {
392             #
393             # if not run by fuction new check if file is already being monitored
394 1 50       3 if ( %Attribute ) {
395 0 0       0 next if $FileHash{$FILE};
396             }
397             #
398             # stat file
399             #
400 1 50       6 $Exist = ( stat($FILE) ? $True : $False);
401 1 50       242 my $_md5_len = ($st_size < 42 ? $st_size : 42);
402 1         2 my $_md5_chksum = 0;
403             #
404 1         93 $BFILE=basename($FILE);
405             {
406 2     2   22 no strict 'refs';
  2         4  
  2         5812  
  1         2  
407 1         82 %$FILE = (
408             'name' => $FILE,
409             'basename' => $BFILE,
410             'fh' => $fh,
411             'stat' => {
412             'dev' => $st_dev,
413             'ino' => $st_ino,
414             'mode' => $st_mode,
415             'nlink' => $st_nlink,
416             'uid' => $st_uid,
417             'gid' => $st_gid,
418             'rdev' => $st_rdev,
419             'size' => $st_size,
420             'atime' => $st_atime,
421             'mtime' => $st_mtime,
422             'ctime' => $st_ctime,
423             'blksize' => $st_blksize,
424             'blocks' => $st_blocks
425             },
426             'lastrun_stat' => {
427             'ino' => $st_ino,
428             'pos' => $Pos,
429             'md5_len' => $_md5_len,
430             'md5_chksum' => $_md5_chksum
431             },
432             'open' => $False,
433             'exist' => $Exist,
434             'read' => $False,
435             'online' => $online,
436             'pos' => $Pos,
437             'md5_len' => $_md5_len,
438             'md5_chksum' => $_md5_chksum,
439             'FileState' => 0,
440             'LastState' => 0,
441             'LastMtime' => $st_mtime,
442             'OpenTime' => 0,
443             'LineArray' => [],
444             'PatternLineArray' => [],
445             'ExceptPatternLineArray' => []
446             );
447 1         10 $rhash=\%$FILE;
448 1         9 $$rhash{'LastState'} = FileState($rhash);
449 1         2 $$rhash{'FileState'} = $$rhash{'LastState'};
450 1         5 push(@File_Data_Structure,\%$FILE);
451             }
452             }
453 1         4 return \@File_Data_Structure;
454             }
455             #
456             ########################################################################
457             #
458             # Create a array of text file from dirs and filename
459             # Checks files for dups (links) and absolute path
460             #
461             ########################################################################
462             #
463             sub CreateListOfFiles {
464 1     1 0 5 my($rArrayOfFileNames)=@_;
465             #
466             # Check if dir and expand
467             #
468 1         3 my @RegFileArray=();
469 1         3 my @FileArray=();
470 1         8 my @ReturnFileArray=();
471 1         2 my @result=();
472 1         13 my $file;
473             my %path_file;
474             #
475             # Expand all reg file names
476             #
477 1         6 foreach my $FILE ( @$rArrayOfFileNames ) {
478 1         195 @result = glob($FILE);
479 1         5 push(@RegFileArray,@result);
480             }
481             #
482             # check for dir and expand
483             #
484 1         6 foreach my $FILE ( @RegFileArray ) {
485 1 50       23 if ( -d $FILE ) {
486 0 0       0 print STDOUT "Dir $FILE is being expanded\n" if $DEBUG;
487 0         0 my $d = new DirHandle "$FILE";
488 0 0       0 if(defined $d ) {
489 0         0 while(defined($_=$d->read)) {
490 0         0 $file="${FILE}/$_";
491 0 0       0 if ( -T $file ) {
492 0         0 push(@FileArray,$file);
493             }
494             }
495             }
496             }
497             else {
498 1         4 push(@FileArray,$FILE);
499             }
500             }
501             #
502             # Checks files for dups (links) and absolute path
503             #
504 1         2 foreach my $FILE ( @FileArray ) {
505             #
506             # check for absolute path
507             #
508 1 50 33     16 unless ( $FILE =~ m#^/# || $MSWin32 ) {
509 0 0       0 print STDOUT "File $FILE is not absolute path ... will not be used\n" if $DEBUG;
510 0         0 next;
511             }
512 1 50 33     16 unless ( ( $FILE =~ m#^\w:# || $FILE =~ m#^//# ) || ! $MSWin32 ) {
      33        
513 0 0       0 print STDOUT "File $FILE is not NT absolute path ... will not be used\n" if $DEBUG;
514 0         0 next;
515             }
516             #
517             # stat file
518             #
519 1 50       16 next unless stat($FILE);
520             #
521             # Check if any two file names point to the same file
522             # Checking for links (Note: There are not links in NT)
523             #
524 1         510 my $key = "$st_dev $st_ino";
525 1 50 33     5 if ( exists $path_file{$key} && ! $MSWin32 ) {
526 0 0       0 print STDOUT "Warning: $FILE is linked to $path_file{$key}\n" if $DEBUG;
527 0 0       0 print STDOUT "File $path_file{$key} ... will not be used\n" if $DEBUG;
528 0         0 next;
529             }
530 1         8 $path_file{$key}=$FILE;
531             #
532             # Check if text file
533 1 50       104 if ( -T $FILE ) {
534 1         4 push(@ReturnFileArray,$FILE);
535             }
536             else {
537 0 0       0 print STDOUT "$FILE is not a text file , will not be used\n" if $DEBUG;
538             }
539             }
540             #
541 1         7 return \@ReturnFileArray;
542             }
543             #
544             ########################################################################
545             #
546             # Read one date line from file
547             #
548             ########################################################################
549             #
550             sub readline {
551             #
552 0     0 1 0 my ($rFD)=shift;
553 0         0 my @TotalArray=(); # Used with attribute Fuction
554 0         0 my $PresentTime=time;
555             #
556             # Check if file dir should be rescanned
557             # $LastScan is in sec
558             # $rFD->{'ScanForFiles'} is in minutes
559             #
560 0 0 0     0 if (( $rFD->{'ScanForFiles'} and
      0        
561             ($LastScan + ($rFD->{'ScanForFiles'}*60)) < $PresentTime) or
562             $FileAttributeChanged ) {
563 0 0       0 print STDOUT "Scanning for new files\n" if $DEBUG;
564 0         0 $rFD->{'FileArray'} =
565             CreateFileDataStructure( CreateListOfFiles($rFD->{'Files'}));
566             #
567 0         0 $LastScan = $PresentTime;
568 0         0 $FileAttributeChanged=$False;
569             }
570             #
571             # This is for DEBUG
572 0 0       0 if ( $DEBUG ) {
573 0         0 print STDOUT "DEBUG list of file to be checked\n";
574 0         0 foreach my $FH ( @{$rFD->{'FileArray'}} ) {
  0         0  
575 0         0 print $FH->{'name'} . "\n";
576             }
577             }
578             #
579             # Check stat of files
580             #
581 0         0 OpenUpdateFiles($rFD);
582             #
583             #
584 0         0 foreach my $FH ( @{$rFD->{'FileArray'}} ) {
  0         0  
585             # reset array to remove last read data
586 0         0 @{$FH->{'LineArray'}}=();
  0         0  
587             #
588 0 0 0     0 if ( $FH->{'exist'} and $FH->{'open'} ) {
589 0 0       0 if ( defined $FH->{'fh'} ) {
590 0         0 push(@{$FH->{LineArray}}, $FH->{'fh'}->getline);
  0         0  
591 0         0 $FH->{'pos'}=$FH->{'fh'}->getpos;
592 0         0 $FH->{'lastrun_stat'}{'pos'} = $FH->{'fh'}->tell;
593             # Persist the Last Run state
594 0 0       0 _set_lastrun_data( $FH ) if defined $LastRun_File;
595             }
596 0 0       0 if ($FH->{'stat'}{mtime} < ($PresentTime - ($rFD->{'MaxAge'} * 60))) {
597 0         0 $FH->{'fh'}->close;
598 0         0 $FH->{'open'} = $False;
599 0         0 $FH->{'read'} = $False; # required to open the file on every call to readline
600 0         0 $FH->{'online'} = $False; # required to open the file on every call to readline
601             }
602             }
603 0         0 $FH->{'LastState'} = FileState($FH);
604             }
605             #
606             # Run Pattern function if object Pattern attribute was set
607 0 0       0 Patterns($rFD) if $rFD->{'Pattern'};
608             #
609             # Run ExceptPatterns function if object ExceptPatterns attribute was set
610 0 0       0 ExceptPatterns($rFD) if $rFD->{'ExceptPattern'};
611             #
612             # Remove deplicate line from arrays
613 0 0       0 RemoveDups($rFD) if $rFD->{'RemoveDuplicate'};
614             #
615             # create a Prefix Array if object OutputPrefix attribute was set
616 0 0       0 Prefix($rFD,0) if $rFD->{'OutputPrefix'};
617             #
618             # Run custom function Pass complete array to custom user fuction
619 0 0       0 if ( $rFD->{'Function'} ) {
620 0         0 foreach my $FH ( @{$rFD->{'FileArray'}} ) {
  0         0  
621 0         0 push(@TotalArray,@{$FH->{LineArray}});
  0         0  
622             }
623 0         0 &{$rFD->{'Function'}}(\@TotalArray)
  0         0  
624             }
625             #
626 0         0 return($rFD);
627             }
628             #
629             ########################################################################
630             #
631             # Read all new data from file
632             #
633             ########################################################################
634             #
635             sub read {
636             #
637 1     1 1 9 my ($rFD)=shift;
638 1         3 my @TotalArray=(); # Used with attribute Fuction
639 1         2 my $PresentTime=time;
640             #
641             # Check if file dir should be rescanned
642             # $LastScan is in sec
643             # $rFD->{'ScanForFiles'} is in minutes
644             #
645 1 50 33     73 if (( $rFD->{'ScanForFiles'} and
      33        
646             ($LastScan + ($rFD->{'ScanForFiles'}*60)) < $PresentTime) or
647             $FileAttributeChanged ) {
648 0 0       0 print STDOUT "Scanning for new files\n" if $DEBUG;
649 0         0 $rFD->{'FileArray'} =
650             CreateFileDataStructure( CreateListOfFiles($rFD->{'Files'}));
651             #
652 0         0 $LastScan = $PresentTime;
653 0         0 $FileAttributeChanged=$False;
654             }
655             #
656             # This is for DEBUG
657 1 50       4 if ( $DEBUG ) {
658 0         0 print STDOUT "DEBUG list of file to be checked\n";
659 0         0 foreach my $FH ( @{$rFD->{'FileArray'}} ) {
  0         0  
660 0         0 print $FH->{'name'} . "\n";
661             }
662             }
663             #
664             # Check stat of files
665             #
666 1         7 OpenUpdateFiles($rFD);
667             #
668             #
669 1         2 foreach my $FH ( @{$rFD->{'FileArray'}} ) {
  1         3  
670             # reset array to remove last read data
671 1         2 @{$FH->{'LineArray'}}=();
  1         69  
672             #
673 1 50 33     12 if ( $FH->{'exist'} and $FH->{'open'} ) {
674 1 50       4 if ( defined $FH->{'fh'} ) {
675 1         90 @{$FH->{'LineArray'}} = $FH->{'fh'}->getlines;
  1         121  
676 1         7 $FH->{'pos'}=$FH->{'fh'}->getpos;
677 1         9 $FH->{'lastrun_stat'}{'pos'} = $FH->{'fh'}->tell;
678             # Persist the Last Run state
679 1 50       13 _set_lastrun_data( $FH ) if defined $LastRun_File;
680             }
681 1 50       7 if ($FH->{'stat'}{mtime} < ($PresentTime - ($rFD->{'MaxAge'} * 60))) {
682 1         8 $FH->{'fh'}->close;
683 1         15 $FH->{'open'} = $False;
684             }
685             }
686 1         3 $FH->{'LastState'} = FileState($FH);
687             }
688             #
689             # Run Pattern function if object Pattern attribute was set
690 1 50       181 Patterns($rFD) if $rFD->{'Pattern'};
691             #
692             # Run ExceptPatterns function if object ExceptPatterns attribute was set
693 1 50       8 ExceptPatterns($rFD) if $rFD->{'ExceptPattern'};
694             #
695             # Remove deplicate line from arrays
696 1 50       6 RemoveDups($rFD) if $rFD->{'RemoveDuplicate'};
697             #
698             # create a Prefix Array if object OutputPrefix attribute was set
699 1 50       7 Prefix($rFD,0) if $rFD->{'OutputPrefix'};
700             #
701             # Run custom function Pass complete array to custom user fuction
702 1 50       4 if ( $rFD->{'Function'} ) {
703 1         2 foreach my $FH ( @{$rFD->{'FileArray'}} ) {
  1         3  
704 1         2 push(@TotalArray,@{$FH->{LineArray}});
  1         6  
705             }
706 1         2 &{$rFD->{'Function'}}(\@TotalArray)
  1         10  
707             }
708             #
709 1         377 return($rFD);
710             }
711             #
712             ########################################################################
713             #
714             # print out line in from file array (Mostly for help with )
715             #
716             ########################################################################
717             #
718             sub print {
719 1     1 1 6 my($rFD)=shift;
720 1         16 foreach my $FH ( @{$rFD->{FileArray}} ) {
  1         4  
721 1         2 foreach my $LINE ( @{$FH->{LineArray}} ) {
  1         2  
722 10         1236 print $LINE;
723             }
724             }
725             }
726             #
727             ########################################################################
728             #
729             # Gets the last run pos
730             #
731             ########################################################################
732             #
733             sub getFilePos {
734 0     0 0 0 my($rFD)=shift;
735 0         0 my($fileName)=shift;
736 0         0 my @tail_files = @{$rFD->{'Files'}};
  0         0  
737 0         0 my $i = 0;
738 0         0 foreach my $FH ( @{$rFD->{FileArray}} ) {
  0         0  
739 0         0 my $tail_file = $tail_files[$i];
740 0 0       0 if( $tail_file eq $fileName ) {
741 0   0     0 my $last_pos = $FH->{'lastrun_stat'}{'pos'} || 0;
742 0         0 return $last_pos;
743             }
744 0         0 $i++;
745             }
746 0         0 return -1;
747             }
748             #
749             ########################################################################
750             #
751             # Gets the file size
752             #
753             ########################################################################
754             sub getFileSize {
755 0     0 0 0 my($rFD)=shift;
756 0         0 my($fileName)=shift;
757 0         0 my @tail_files = @{$rFD->{'Files'}};
  0         0  
758 0         0 my $i = 0;
759 0         0 foreach my $FH ( @{$rFD->{FileArray}} ) {
  0         0  
760 0         0 my $tail_file = $tail_files[$i];
761 0 0       0 if( $tail_file eq $fileName ) {
762 0   0     0 my $size = int($FH->{'stat'}{size}) || 0;
763 0         0 return $size;
764             }
765 0         0 $i++;
766             }
767 0         0 return -1;
768             }
769             #
770             ########################################################################
771             #
772             # Print out lines from pattern file array (Mostly for help with )
773             #
774             ########################################################################
775             #
776             sub printpat {
777 0     0 0 0 my($rFD)=shift;
778             #print STDOUT Data::Dumper->Dump($rFD) if $DEBUG;
779 0         0 foreach my $FH ( @$rFD{'FileArray'} ) {
780 0         0 foreach my $LINE ( @{$FH->{PatternLineArray}} ) {
  0         0  
781 0         0 print $LINE;
782             }
783             }
784             }
785             #
786             ########################################################################
787             #
788             # print out line from pattern file except array (Mostly for help with )
789             #
790             ########################################################################
791             #
792             sub printexceptpat {
793 0     0 0 0 my($rFD)=shift;
794             #print STDOUT Data::Dumper->Dump($rFD) if $DEBUG;
795 0         0 foreach my $FH ( @$rFD{'FileArray'} ) {
796 0         0 foreach my $LINE ( @{$FH->{ExceptPatternLineArray}} ) {
  0         0  
797 0         0 print $LINE;
798             }
799             }
800             }
801             #
802             ########################################################################
803             #
804             # Print out stat output for each file (Mostly for help with )
805             #
806             ########################################################################
807             #
808             sub printstat {
809 0     0 0 0 my($rFD)=shift;
810 0         0 foreach my $FH ( @$rFD{'FileArray'} ) {
811 0         0 print "Stat ouput for file $FH->{name}\n";
812 0         0 print "------------------------------------------------\n";
813 0         0 foreach my $stat_id ( @StatArray ) {
814 0         0 print "$stat_id = $FH->{'stat'}{$stat_id}\n";
815             }
816             }
817             }
818             #
819             ########################################################################
820             #
821             # Print out All file states
822             # (See note in File::Tail::Multi.pm for function OpenUpdateFiles)
823             #
824             ########################################################################
825             #
826             sub printfilestates {
827 0     0 0 0 my($FH)=@_;
828 0         0 my $vector=pack("b4",0);
829 0         0 my $open; my $read; my $exist;my $online;
  0         0  
  0         0  
830              
831 0 0       0 if ( $FH->{'FileState'} != $FH->{'LastState'} ) {
832 0         0 print STDOUT "The State of file $FH->{name} has changed\n";
833 0         0 print STDOUT "-Old state\n";
834 0         0 vec($vector,0,4)=$FH->{'LastState'};
835 0         0 ($online,$read,$open,$exist) = split(//, unpack("b4", $vector));
836 0         0 print STDOUT "\tExist = $exist Open = $open Read = $read Online = $online \n";
837 0         0 print STDOUT "-New State\n";
838 0         0 vec($vector,0,4)=$FH->{'FileState'};
839 0         0 ($online,$read,$open,$exist) = split(//, unpack("b4", $vector));
840 0         0 print STDOUT "\tExist = $exist Open = $open Read = $read Online = $online \n";
841             }
842             else {
843 0         0 print STDOUT "No Change in state for file $FH->{name}\n";
844             }
845             }
846             ########################################################################
847             #
848             # Check if arg is an arrayof pattern or filename of patterns
849             # Return ref to array of patterns
850             #
851             ########################################################################
852             sub CheckIfArrayOrFile {
853 2     2 0 4 my($r_listofpatterns)=@_;
854             #
855 2         4 my @patterns=();
856 2         3 my $patternfile;
857             my $patfh;
858             #
859             # check if list of pattern is an array of a file
860             #
861 2 50       7 if ( ref($r_listofpatterns) eq "ARRAY" ) {
862 2         6 @patterns=@$r_listofpatterns;
863             }
864             else {
865             #
866             # check if it is a file
867             #
868             {
869 2     2   12 no strict 'refs';
  2         5  
  2         4614  
  0         0  
870 0 0       0 if ( -f $r_listofpatterns ) {
871 0         0 $patternfile=$r_listofpatterns;
872             #
873             # open pattern file
874             #
875 0 0       0 stat($patternfile) || croak "Could not open pattern file\n";
876 0         0 $patfh = new FileHandle "$patternfile", "r";
877 0         0 while(<$patfh>) {
878 0         0 chomp;
879 0 0       0 next if /^#/; # Remove line of comments
880 0 0       0 next if /^\s*$/; # Remove Blank lines
881 0         0 push (@patterns,$_);
882             }
883 0         0 $patfh->close;
884             }
885             else {
886 0         0 croak "Argv for sub Pattern was not an Array or File $patternfile was not found\n";
887             }
888             }
889             }
890             #
891 2         11 return \@patterns;
892             }
893             ########################################################################
894             #
895             # Add prefix define by OutputPrefix option to data array
896             #
897             # List of what is Object Options. Default is False
898             # GMT = Greenwich time ZONE
899             #
900             # p => path name of the input file
901             # f => file name of the input file
902             # t => time in HHMMSS
903             # tg => time in HHMMSS GMT
904             # tc => time in MM/DD/YYYY HH:MM:SS
905             # pt => path and time
906             # ptg => path and time GMT
907             # ptc => path and time complete
908             # ft => file and time
909             # ftg => file and time GMT
910             # ftc => file and time complete
911             # tp => time and path
912             # tpg => time and path GMT
913             # tpc => time complete and path
914             # tf => time and file
915             # tfg => time and file GMT
916             # tfc => time complete and file
917             #
918             ########################################################################
919             sub Prefix {
920 1     1 0 2 my($rFD)=shift;
921 1         2 my($ArrayType)=@_;
922             #
923 1         2 my @TempArray=();
924 1         2 my $InArray="LineArray";
925 1         4 my $OutArray="LineArray";
926 1         2 my $r=$rFD;
927 1         2 my $TmpOutputPrefix;
928             #
929             # Check for GMT
930             #
931 1         2 $TmpOutputPrefix = $rFD->{'OutputPrefix'};
932 1 50       5 if ( $rFD->{'OutputPrefix'} =~ /g/ ) {
933 0         0 $TmpOutputPrefix =~ s/g//;
934 0         0 $GMT=$True;
935             }
936 1 50       5 if ( $rFD->{'OutputPrefix'} =~ /c/ ) {
937 0         0 $TmpOutputPrefix =~ s/c//;
938 0         0 $TC=$True;
939             }
940 1         3 foreach my $FH ( @{$rFD->{'FileArray'}} ) {
  1         5  
941 1         2 foreach my $LINE ( @{$FH->{$InArray}} ) {
  1         3  
942 10 50       20 $TmpOutputPrefix eq "p" &&
943             push(@TempArray,"$FH->{'name'} : $LINE");
944 10 50       17 $TmpOutputPrefix eq "f" &&
945             push(@TempArray,"$FH->{'basename'} : $LINE");
946 10 50       23 $TmpOutputPrefix eq "t" &&
947             push(@TempArray,Time($r) . " : $LINE");
948 10 50       18 $TmpOutputPrefix eq "pt" &&
949             push(@TempArray,"$FH->{'name'} " . Time($r) . " : $LINE");
950 10 50       17 $TmpOutputPrefix eq "tp" &&
951             push(@TempArray,Time($r) . " $FH->{'name'} : $LINE");
952 10 50       35 $TmpOutputPrefix eq "ft" &&
953             push(@TempArray,"$FH->{'basename'} " . Time($r) . " : $LINE");
954 10 50       29 $TmpOutputPrefix eq "tf" &&
955             push(@TempArray,Time($r) . " $FH->{'basename'} : $LINE");
956             }
957 1         70 @{$FH->{$OutArray}} = @TempArray;
  1         8  
958 1         4 @TempArray=();
959             }
960 1         3 return($rFD);
961             }
962             ########################################################################
963             #
964             # open a ( file or Array ) of patterns and check for lines matching
965             # the pattern
966             #
967             # Returns Data Structure with
968             #
969             ########################################################################
970             sub Patterns {
971 1     1 0 5 my($rFD)=shift;
972             #
973 1 50       11 $pattern_sub = match_closure(CheckIfArrayOrFile($rFD->{'Pattern'})) unless defined($pattern_sub);
974 1         3 foreach my $FH ( @{$rFD->{FileArray}} ) {
  1         3  
975 1         2 @{$FH->{PatternLineArray}}=();
  1         3  
976 1         2 foreach my $LINE ( @{$FH->{LineArray}} ) {
  1         2  
977 53 100       2041 push(@{$FH->{PatternLineArray}},$LINE) if ( $pattern_sub->($LINE) );
  11         36  
978             }
979 1         2 @{$FH->{LineArray}}=@{$FH->{PatternLineArray}};
  1         12  
  1         3  
980             }
981 1         2 return($rFD);
982             }
983             #
984             ########################################################################
985             #
986             # open a ( file or Array ) of paterns and return excepticheck them against lines
987             # from file array
988             #
989             ########################################################################
990             #
991             sub ExceptPatterns {
992 1     1 0 3 my($rFD)=shift;
993             #
994 1 50       6 $exceptpattern_sub = match_closure(CheckIfArrayOrFile($rFD->{'ExceptPattern'})) unless defined($exceptpattern_sub);
995 1         3 foreach my $FH ( @{$rFD->{FileArray}} ) {
  1         4  
996 1         2 @{$FH->{ExceptPatternLineArray}}=();
  1         3  
997 1         2 foreach my $LINE ( @{$FH->{LineArray}} ) {
  1         3  
998 11 100       280 push(@{$FH->{ExceptPatternLineArray}},$LINE) unless ( $exceptpattern_sub->($LINE) );
  10         169  
999             }
1000 1         3 @{$FH->{LineArray}}=@{$FH->{ExceptPatternLineArray}};
  1         6  
  1         3  
1001             }
1002             #
1003 1         3 return($rFD);
1004             }
1005             #
1006             ########################################################################
1007             #
1008             # create closures fuction with eval for efficiency in patterm matching
1009             #
1010             ########################################################################
1011             sub match_closure {
1012 2     2 0 3 my($array) = shift;
1013 2         3 my $out;
1014 2         5 $out = join ('|', @$array);
1015 2         236 eval 'sub { $_[0] =~ /($out)/o; }'
1016             }
1017             #
1018             ########################################################################
1019             #
1020             # Remove Duplicate line from file array
1021             #
1022             ########################################################################
1023             #
1024             sub RemoveDups {
1025 1     1 0 2 my($rFD)=shift;
1026 1         2 my %Mark;
1027             #
1028 1         2 foreach my $FH ( @{$rFD->{FileArray}} ) {
  1         7  
1029             #
1030 1         3 undef(%Mark);
1031 1         1 grep($Mark{$_}++, @{$FH->{LineArray}});
  1         13  
1032 1         10 @{$FH->{LineArray}}=(sort keys(%Mark));
  1         12  
1033 1         5 undef(%Mark);
1034             }
1035 1         4 return($rFD);
1036             }
1037             ########################################################################
1038             #
1039             # Open and Close files as needed for tailing
1040             # Should be done before ptail->read
1041             #
1042             # All file are in a combination of four states.
1043             #
1044             # 1. Exist = True (File exist at this time )
1045             # = False ( File does not exist at this time )
1046             #
1047             # 2. Open = True (File is open at this time )
1048             # = False ( File is not open at this time )
1049             #
1050             # 3. Read = True (File has been read since open )
1051             # = False ( File has not been read since open )
1052             #
1053             # 4. online = True (File exist now or existed once during this process)
1054             # = False (File has never existed during this process)
1055             #
1056             # The table below list the states that a file can be in
1057             # and what action should be taken.
1058             #
1059             # BV = Binary Value
1060             # ____________________________________________________________________
1061             # | BV || Exist | Open | Read | OnLine || Action
1062             # |____||_______|______|______|________||______________________________|
1063             # |----||-------|------|------|--------||------------------------------|
1064             # | 0 || F | F | F | F || 1) Skip file
1065             # | || | | | ||
1066             # |----||-------|------|------|--------||------------------------------|
1067             # | 1 || F | F | F | T || 1) Skip file
1068             # | || | | | ||
1069             # |----||-------|------|------|--------||------------------------------|
1070             # | 2 || F | F | T | F || 1) (read = False)
1071             # | || | | | || 2) Skip file
1072             # |----||-------|------|------|--------||------------------------------|
1073             # | 3 || F | F | T | T || 1) (read = False)
1074             # | || | | | || 2) Skip file
1075             # |----||-------|------|------|--------||------------------------------|
1076             # | 4 || F | T | F | F || 1) Close File (open = False)
1077             # | || | | | || 2) Skip file
1078             # |----||-------|------|------|--------||------------------------------|
1079             # | 5 || F | T | F | T || 1) Close File (open = False)
1080             # | || | | | || 2) Skip file
1081             # |----||-------|------|------|--------||------------------------------|
1082             # | 6 || F | T | T | F || 1) Check if file has changed
1083             # | || | | | || 2) if file has not changed for
1084             # | || | | | || MaxAge, close file
1085             # |----||-------|------|------|--------||------------------------------|
1086             # | 7 || F | T | T | T || 1) Close File (open =False)
1087             # | || | | | || 2) Take offline
1088             # | || | | | || (online = False)
1089             # | || | | | || 3) (read = False)
1090             # |----||-------|------|------|--------||------------------------------|
1091             # | 8 || T | F | F | F || 1) Open File (open = True)
1092             # | || | | | || 2) Put online (online = True)
1093             # | || | | | || 3) Start reading from location
1094             # | || | | | || NumLines (read = True)
1095             # |----||-------|------|------|--------||------------------------------|
1096             # | 9 || T | F | F | T || 1) Open File (open = True)
1097             # | || | | | || 2) Start reading from top
1098             # | || | | | || (read = True)
1099             # |----||-------|------|------|--------||------------------------------|
1100             # | 10 || T | F | T | F || 1) Open File (open = True)
1101             # | || | | | || 2) Start reading from last pos
1102             # | || | | | || 3) Put online (online = True)
1103             # |----||-------|------|------|--------||------------------------------|
1104             # | 11 || T | F | T | T || 1) Open File (open = True)
1105             # | || | | | || 2) Start reading from last pos
1106             # |----||-------|------|------|--------||------------------------------|
1107             # | 12 || T | T | F | F || 1) Put online (online = True)
1108             # | || | | | || 2) Start reading from top
1109             # |----||-------|------|------|--------||------------------------------|
1110             # | 13 || T | T | F | T || 1) Start reading from last pos
1111             # | || | | | || (read = True)
1112             # |----||-------|------|------|--------||------------------------------|
1113             # | 14 || T | T | T | F || 1) Put online (online = True)
1114             # | || | | | || 2) Start reading from top
1115             # |----||-------|------|------|--------||------------------------------|
1116             # | 15 || T | T | T | T || 1) Start reading from last pos
1117             # | || | | | ||
1118             # ---------------------------------------------------------------------
1119             #
1120             ########################################################################
1121             sub OpenUpdateFiles {
1122             #
1123 1     1 0 4 my($rFD)=shift;
1124 1         2 my $FS;
1125             #
1126 1         2 foreach my $FH ( @{$rFD->{'FileArray'}} ) {
  1         6  
1127             #
1128             # check if file exist and update stat
1129 1         8 $FS = UpdateStat($FH);
1130             #
1131             SWITCH: {
1132             #
1133 1 50       2 ($FS==0) && do {
  1         3  
1134 0         0 last SWITCH;
1135             };
1136 1 50 33     16 ($FS==2 || $FS==4 || $FS==6 ) && do {
      33        
1137 0         0 SetFileState($FH,0);
1138 0         0 last SWITCH;
1139             };
1140 1 50 33     19 ($FS==1 || $FS==3 || $FS==5 || $FS==7 ) && do {
      33        
      33        
1141 0         0 SetFileState($FH,1);
1142 0         0 last SWITCH;
1143             };
1144 1 50 33     5 ($FS==8 || $FS==14 ) && do {
1145 1         21 $FH->{'fh'} = new FileHandle "$FH->{name}", "r";
1146 1         189 $FH->{'OpenTime'} = time;
1147 1 50       4 if ( defined($FH->{'fh'}) ) {
1148 1         4 SetFileState($FH,15);
1149 1         5 PosFileMark($FH);
1150             }
1151 1         3 last SWITCH;
1152             };
1153 0 0       0 ($FS==9 ) && do {
1154 0         0 $FH->{'fh'} = new FileHandle "$FH->{name}", "r";
1155 0         0 $FH->{'OpenTime'} = time;
1156 0 0       0 if ( defined($FH->{'fh'}) ) {
1157 0         0 SetFileState($FH,15);
1158             }
1159 0         0 last SWITCH;
1160             };
1161 0 0       0 ($FS==12 ) && do {
1162 0         0 OpenFileToTail($rFD,$FH);
1163 0 0       0 if ( $FH->{'open'} ) {
1164 0         0 SetFileState($FH,15);
1165             }
1166 0         0 last SWITCH;
1167             };
1168 0 0 0     0 ($FS==10 || $FS==11 || $FS==13 || $FS==15 ) && do {
      0        
      0        
1169 0         0 OpenFileToTail($rFD,$FH);
1170 0 0       0 if ( $FH->{'open'} ) {
1171 0         0 SetFileState($FH,15);
1172 0         0 $FH->{'fh'}->setpos($FH->{'pos'});
1173             }
1174 0         0 last SWITCH;
1175             };
1176             }
1177             # get current state of file
1178 1         3 $FH->{'FileState'}=FileState($FH);
1179 1 50       5 printfilestates($FH) if $DEBUG;
1180             }
1181             }
1182             #
1183             ########################################################################
1184             #
1185             # Open file if it exist and has been change
1186             #
1187             ########################################################################
1188             #
1189             sub OpenFileToTail {
1190 0     0 0 0 my($rFD)=shift;
1191 0         0 my($FH)=@_;
1192 0         0 my $PresentTime=time;
1193             #
1194             # return if file is already open
1195             #
1196             #
1197             # check if file has been changed in last MaxAge mins
1198 0 0 0     0 if ( ($FH->{'LastMtime'} == $FH->{'stat'}{mtime}) &&
      0        
1199             ($FH->{'stat'}{mtime} < ($PresentTime - ($rFD->{'MaxAge'} * 60))) &&
1200             $FH->{'open'} ) {
1201 0 0       0 $FH->{'fh'}->close if defined($FH->{'fh'});
1202 0         0 $FH->{'OpenTime'} = 0;
1203 0         0 $FH->{'open'}=$False;
1204 0         0 return;
1205             }
1206 0 0       0 if ( $FH->{'LastMtime'} == $FH->{'stat'}{mtime} ) {
1207 0         0 return;
1208             }
1209 0 0 0     0 if ( ! $FH->{'open'} && $FH->{'LastMtime'} < $FH->{'stat'}{mtime} ) {
1210 0         0 $FH->{'fh'} = new FileHandle "$FH->{name}", "r";
1211 0         0 $FH->{'open'}=$True;
1212 0         0 $FH->{'OpenTime'} = $PresentTime;
1213             }
1214             }
1215             #
1216             ########################################################################
1217             #
1218             # Update stat in Data_Structure
1219             #
1220             ########################################################################
1221             #
1222             sub UpdateStat {
1223 1     1 0 8 my($FH)=@_;
1224 1         2 my $tmps;
1225             #
1226             {
1227 2     2   14 no strict 'refs';
  2         3  
  2         197  
  1         2  
1228 1         3 $FH->{'LastMtime'} = $FH->{'stat'}{'mtime'};
1229             }
1230 1 50       5 $FH->{'exist'} = (stat($FH->{'name'}) ? $True : $False);
1231 1 50       126 $FH->{'read'} = $False unless $FH->{exist};
1232             #
1233 1         6 foreach my $stat_id ( @StatArray ) {
1234 13         15 $tmps="st_". $stat_id;
1235             {
1236 2     2   12 no strict 'refs';
  2         4  
  2         3956  
  13         12  
1237 13         209 $FH->{'stat'}{$stat_id} = $$tmps;
1238             }
1239             }
1240             # get current state of file
1241 1         3 $FH->{'LastState'} = $FH->{'FileState'};
1242 1         2 $FH->{'FileState'} = FileState($FH);
1243             #
1244             # this will return the FileState
1245             }
1246             #
1247             ########################################################################
1248             #
1249             # Return the time as a HHMMSS string. $opt_g decides timezone.
1250             #
1251             ########################################################################
1252             #
1253             sub Time {
1254 10     10 0 11 my($rFD)=shift;
1255 10 50       21 return scalar localtime if $TC;
1256 10 50       301 my($sec,$min,$hour)=($GMT ? gmtime : localtime);
1257 10         45 sprintf("%02d%02d%02d", $hour,$min,$sec);
1258             }
1259             #
1260             ########################################################################
1261             #
1262             # Return the version number of the File::Tail::Multi Package
1263             #
1264             ########################################################################
1265             #
1266             sub version {
1267 0     0 1 0 return $VERSION;
1268             }
1269             #
1270             ########################################################################
1271             #
1272             # Turn on output on for File::Tail::Multi package
1273             #
1274             ########################################################################
1275             #
1276             sub debug {
1277 0     0 1 0 my($rFD)=shift;
1278 0 0       0 if ( $rFD->{'Debug'} ) {
1279 0         0 $DEBUG=0;
1280             }
1281             else {
1282 0         0 $DEBUG=1;
1283             }
1284 0         0 $rFD->{'Debug'}=$DEBUG;
1285             }
1286             #
1287             ########################################################################
1288             #
1289             # Close all file that are being tailed
1290             #
1291             ########################################################################
1292             #
1293             sub close_all_files{
1294 0     0 1 0 my ($rFD)=shift;
1295 0         0 foreach my $FH ( @{$rFD->{'FileArray'}} ) {
  0         0  
1296 0 0       0 next if ! defined $FH->{fh};
1297 0 0       0 print "Closing file $FH->{name} ...\n" if $DEBUG;
1298 0         0 $FH->{fh}->close;
1299             }
1300             }
1301             #
1302             ########################################################################
1303             #
1304             # Return the state of a file in Number terms
1305             #
1306             # Exist = 8
1307             # Open = 4
1308             # Read = 2
1309             # Online = 1
1310             #
1311             ########################################################################
1312             #
1313             sub FileState{
1314 4     4 0 7 my($FH)=@_;
1315 4         6 my $vector=pack("b4",0);
1316             #
1317 4 50       195 if( defined($FH->{'online'}) ) {
1318 4         18 vec($vector,0,1)=$FH->{'online'};
1319 4         11 vec($vector,1,1)=$FH->{'read'};
1320 4         9 vec($vector,2,1)=$FH->{'open'};
1321 4         9 vec($vector,3,1)=$FH->{'exist'};
1322 4         20 vec($vector,0,8);
1323             }
1324             }
1325             ########################################################################
1326             #
1327             # Set the new file state in the Data Structure
1328             #
1329             # Exist = 8
1330             # Open = 4
1331             # Read = 2
1332             # Online = 1
1333             #
1334             ########################################################################
1335             #
1336             sub SetFileState{
1337 1     1 0 2 my($FH,$NewState)=@_;
1338 1         2 my $vector=pack("b4",0);
1339             #
1340              
1341 1 50       3 if ( $NewState > 15 ) { $NewState=15; }
  0         0  
1342              
1343 1         2 vec($vector,0,4)=$NewState;
1344 1         21 ($FH->{'online'},$FH->{'read'},$FH->{'open'},$FH->{'exist'}) =
1345             split(//, unpack("b4", $vector));
1346 1         3 $FH->{'LastState'}= $FH->{'FileState'};
1347 1         3 $FH->{'FileState'}=$NewState;
1348             }
1349             #
1350             ########################################################################
1351             #
1352             # Set file seek position to number of line in opt_n
1353             #
1354             ########################################################################
1355             #
1356              
1357             sub PosFileMark {
1358 1     1 0 3 my($FH)=@_;
1359             #
1360 1         1 my $line;
1361             my $pos;
1362 1         2 my @lines=();
1363 1         3 my $fh=$FH->{'fh'};
1364 1         1 my $CharALine=120;
1365 1         2 my $seekbacklines=$Attribute{'NumLines'};
1366 1         8 my $seekback=$CharALine * ($seekbacklines +1);
1367             #
1368 1 50       13 if ( $seekbacklines < 0 ) { # Move to end of file
    50          
1369 0         0 seek($fh,0,2);
1370 0         0 $FH->{'pos'}=$fh->getpos;
1371 0         0 return;
1372             }
1373             elsif ( $seekbacklines > 0 ) {
1374 0         0 seek($fh, -$seekback, 2);
1375             }
1376             else {
1377             # Load Last Run file if program started
1378 1 50       4 if ($FH->{'LastState'}==8 )
1379             {
1380 1 50       4 _get_lastrun_data($FH) if ( defined $LastRun_File );
1381 1 50 33     14 if ( int($FH->{'lastrun_stat'}{'pos'}) <= int($FH->{'stat'}{size}) && $FH->{'lastrun_stat'}{'pos'} ne '') {
1382 1         8 seek($fh, $FH->{'lastrun_stat'}{'pos'}, 0);
1383             }
1384 1         5 $FH->{'pos'}=$fh->getpos;
1385 1         3 return;
1386             } else {
1387             # Just let it be - Read from TOP
1388             }
1389             }
1390             #
1391             # remove line part;
1392             #
1393 0           $line=$fh->getline;
1394             #
1395             # get all line to end of file
1396             #
1397 0           @lines=$fh->getlines;
1398 0 0         splice(@lines,0,$#lines-$seekbacklines + 1) if ( $seekbacklines != 0 );
1399 0           foreach $line ( @lines ) {
1400 0           $pos += length $line;
1401             }
1402             #
1403             # seek to pos in file;
1404             #
1405 0           seek($fh, -$pos, 2);
1406             #
1407 0           $FH->{'pos'}=$fh->getpos;
1408             #
1409             }
1410             #
1411             ########################################################################
1412             #
1413             # Autoload sub-classes
1414             #
1415             ########################################################################
1416             #
1417             sub AUTOLOAD {
1418 0     0     my($rFD)=shift;
1419 0   0       my $type = ref($rFD) || croak "$rFD is not and object";
1420 0           my $attribute = $AUTOLOAD;
1421 0           $attribute =~ s/.*://;
1422 0 0         unless ( exists $rFD->{$attribute} ) {
1423 0           croak "Can't access $attribute field in oject $rFD";
1424             }
1425 0 0         if ( $attribute eq "Files" ) {
1426 0           $FileAttributeChanged=$True;
1427             }
1428 0           CheckAttributes($rFD);
1429 0 0         if (@_) {
1430 0           return $rFD->{$attribute} = shift;
1431             } else {
1432 0           return $rFD->{$attribute};
1433             }
1434             }
1435             #
1436             ########################################################################
1437             #
1438             # Get Last Read data about a FILE into the File Data structure
1439             #
1440             ########################################################################
1441             #
1442             sub _get_lastrun_data {
1443 0     0     my($FH) = @_;
1444              
1445 0           my $previous_position = 0; # start at zero if 1st time ...
1446              
1447 0           my ($logged_name, $last_pos, $logged_ino, $md5_chksum, $md5_len) = ('', 0, 0, $FH->{'md5_chksum'}, $FH->{'md5_len'} );
1448 0 0         if(-e "$LastRun_File") {
1449 0 0         open my $curat_fh, '<', $LastRun_File or croak "Could not read $LastRun_File: $!";
1450 0           my @runlines = <$curat_fh>;
1451 0           close $curat_fh;
1452            
1453 0           foreach ( @runlines ) {
1454 0           chomp;
1455 0           ($logged_name, $last_pos, $logged_ino, $md5_chksum, $md5_len) = split //, $_;
1456 0 0         if ( $logged_name eq $FH->{'name'} ) {
1457 0           last;
1458             } else {
1459 0           ($logged_name, $last_pos, $logged_ino ) = ('', 0, 0);
1460             }
1461             }
1462 0 0         $last_pos = 0 if $logged_ino ne $FH->{'stat'}{ino};
1463 0           $logged_ino = $FH->{'stat'}{ino};
1464              
1465 0           $FH->{'lastrun_stat'}{ino} = int($logged_ino);
1466 0           $FH->{'lastrun_stat'}{'pos'} = int($last_pos);
1467            
1468 0 0         if($DO_MD5_Check) {
1469 0 0 0       $FH->{'lastrun_stat'}{'md5_len'} = int($md5_len) if !defined $md5_len || !$md5_len;
1470 0 0 0       $FH->{'lastrun_stat'}{'md5_chksum'} = int($md5_chksum) if !defined $md5_chksum || !$md5_chksum;
1471             }
1472             }
1473             }
1474             #
1475             ########################################################################
1476             #
1477             # Set Last Read data about a FILE
1478             #
1479             ########################################################################
1480             #
1481             sub _set_lastrun_data {
1482 0     0     my($FH) = shift;
1483              
1484 0           my ($logged_name, $last_pos, $logged_ino, $md5_chksum, $md5_len) = ('', 0, 0, 0, 0);
1485 0           my @runlines;
1486 0           my ($indx, $found) = (0, -1);
1487 0 0         if(-e "$LastRun_File") {
1488 0 0         open my $curat_fh, '<', $LastRun_File or croak "Could not read $LastRun_File: $!";
1489 0           @runlines = <$curat_fh>;
1490 0           close $curat_fh;
1491            
1492 0           foreach ( @runlines ) {
1493 0           chomp;
1494 0           ($logged_name, $last_pos, $logged_ino, $md5_chksum, $md5_len) = split //, $_;
1495 0 0         if ( $logged_name eq $FH->{'name'} ) {
1496 0           $found = $indx;
1497 0           last;
1498             }
1499 0           $indx++;
1500             }
1501             }
1502 0 0         $found = $#runlines + 1 if ( $found == -1 );
1503 0           $runlines[$found] = $FH->{'name'}.''.$FH->{'lastrun_stat'}{'pos'}.''.$FH->{'stat'}{ino}.''.$FH->{'md5_chksum'}.''.$FH->{'md5_len'}."\n";
1504 0           $FH->{'lastrun_stat'}{ino} = $FH->{'stat'}{ino};
1505              
1506 0 0         open my $curpos_fh, '>', $LastRun_File or croak "Could not write $LastRun_File: $!";
1507 0           foreach ( @runlines ) {
1508 0           chomp;
1509 0           print {$curpos_fh} $_."\n";
  0            
1510             }
1511 0           close $curpos_fh;
1512             }
1513             #
1514             ########################################################################
1515             #
1516             # Get MD5 Info about last read state
1517             #
1518             ########################################################################
1519             #
1520             sub _get_md5_info {
1521 0     0     my($FH) = shift;
1522              
1523 0 0         return if !$DO_MD5_Check;
1524 0           require Digest::MD5; # only do the module if needed
1525              
1526 0           my $data_to_md5 = ''; # to avoid uninitialized value warnings
1527 0           my $origpos = $FH->{'pos'};
1528              
1529 0           seek($FH->{'fh'}, 0, 0);
1530 0           sysread $FH->{'fh'}, $data_to_md5, $FH->{'md5_len'};
1531 0           seek($FH->{'fh'}, $origpos, 0);
1532              
1533 0           return Digest::MD5::md5_hex($data_to_md5);
1534             }
1535              
1536             #
1537             #
1538             ########################################################################
1539             # POD
1540             ########################################################################
1541             1;
1542             __END__