File Coverage

blib/lib/FileHash/Entry.pm
Criterion Covered Total %
statement 24 146 16.4
branch 0 74 0.0
condition 0 6 0.0
subroutine 8 39 20.5
pod 30 30 100.0
total 62 295 21.0


line stmt bran cond sub pod time code
1             #================================ Entry.pm ===================================
2             # Filename: Entry.pm
3             # Description: Container for data about a file.
4             # Original Author: Dale M. Amon
5             # Revised by: $Author: amon $
6             # Date: $Date: 2008-08-28 23:35:28 $
7             # Version: $Revision: 1.8 $
8             # License: LGPL 2.1, Perl Artistic or BSD
9             #
10             #=============================================================================
11 1     1   6 use strict;
  1         3  
  1         109  
12 1     1   7 use File::Spec;
  1         2  
  1         21  
13 1     1   6 use Digest::MD5;
  1         1  
  1         49  
14 1     1   7 use Fault::Logger;
  1         2  
  1         19  
15 1     1   805 use Fault::Notepad;
  1         648  
  1         21  
16 1     1   540 use FileHash::FormatString;
  1         3  
  1         34  
17 1     1   8712 use Data::Dumper;
  1         15651  
  1         112  
18              
19             package FileHash::Entry;
20 1     1   10 use vars qw{@ISA};
  1         2  
  1         2630  
21             @ISA = qw( UNIVERSAL );
22              
23             # Update this number if the format of the entry dump is changed.
24             #
25             my $FileHashEntryVersion = "0.05";
26              
27             #=============================================================================
28             # CLASS METHODS
29             #=============================================================================
30              
31             sub alloc ($$) {
32 0     0 1   my ($class) = @_;
33 0           my $self = bless {}, $class;
34 0           @$self{'device','directory','file',
35             'md5sum',
36             'deviceNumber','inode','mode','hardlinks',
37             'uid','uidName','gid','gidName',
38             'deviceSpecialId','sizeBytes',
39             'atime','mtime','ctime',
40             'blocksizePreference','blocksAllocated','notepad'} = undef;
41 0           return $self;
42             }
43              
44 0     0 1   sub dumpversion ($) {$FileHashEntryVersion;}
45              
46             #=============================================================================
47             # INSTANCE METHODS
48             #=============================================================================
49              
50             sub init ($$) {
51 0     0 1   my ($self,$path) = @_;
52              
53 0 0         if ($::DEBUG) {Fault::Logger->arg_check_noref ($path,"path")
  0 0          
54             or return undef;}
55              
56 0           my ($dev,$dir,$file) = File::Spec->splitpath ($path);
57              
58 0           @$self{'device','directory','file','notepad'} =
59             ($dev,$dir,$file,Fault::Notepad->new);
60              
61 0           return $self;
62             }
63              
64             #-----------------------------------------------------------------------------
65             # Arg check responsibility is passed on to the init method.
66              
67             sub initFromStat ($$) {
68 0     0 1   my $self = shift;
69 0           $self->init(@_);
70 0           my $filename = $self->path;
71              
72 0           my (@stat);
73 0 0         if (@stat = lstat($filename)) {
74 0           @$self{'deviceNumber','inode','mode','hardlinks',
75             'uid','gid','deviceSpecialId','sizeBytes',
76             'atime','mtime','ctime',
77             'blocksizePreference','blocksAllocated'} = (@stat);
78              
79             # md5sum can only handle plain files, use 0 for md5sum otherwise.
80 0           $self->{'md5sum'} = 0;
81 0 0         if (-f $filename) {
82 0 0         if (open(FILE, $filename)) {
83 0           binmode FILE;
84 0           $self->{'md5sum'} = Digest::MD5->new->addfile(*FILE)->hexdigest;
85 0           close(FILE);
86             }
87             }
88             else {
89 0           $self->{'notepad'}->add ("Can not open '$filename' for hashing: $!\n");
90             }
91             }
92             else {
93 0           $self->{'notepad'}->add ("Can not lstat '$filename': $!\n");
94             }
95 0           return $self;
96             }
97              
98             #-----------------------------------------------------------------------------
99              
100             sub initFromLine ($$$) {
101 0     0 1   my ($self,$format,$line) = @_;
102 0           $self->{'notepad'} = Fault::Notepad->new;
103              
104 0 0         if ($::DEBUG) {
105 0 0         Fault::Logger->arg_check_isa ($format,"FileHash::FormatString","format")
106             or return undef;
107 0 0         Fault::Logger->arg_check_noref ($line,"line")
108             or return undef;
109             }
110              
111 0           chomp $line;
112 0           my @string = $self->_lexical_parse ($line,$format->fields);
113 0           my $vals = $format->parse (@string);
114              
115             # This way we can skip lines that do not match our format. It's a silly
116             # cheat for working with files with headers and we'll need something
117             # better.
118 0 0         defined $vals or return undef;
119              
120 0           @$self{'device','directory','file',
121             'md5sum',
122             'deviceNumber','inode','mode','hardlinks',
123             'uid','uidName','gid','gidName',
124             'deviceSpecialId','sizeBytes',
125             'atime','mtime','ctime',
126             'blocksizePreference','blocksAllocated'} =
127             (@$vals{'device','directory','file',
128             'md5sum',
129             'deviceNumber','inode','mode','hardlinks',
130             'uid','uidName','gid','gidName',
131             'deviceSpecialId','sizeBytes',
132             'atime','mtime','ctime',
133             'blocksizePreference','blocksAllocated'}
134             );
135              
136 0           $self->{'notepad'}->merge ($vals->{'notepad'});
137 0           return $self;
138             }
139              
140             #-----------------------------------------------------------------------------
141             # NOTE: eval could fail if we opened the wrong file. Need to check it.
142              
143             sub initFromDump ($\*) {
144 0     0 1   my ($self,$fh) = @_;
145 0           $self->{'notepad'} = Fault::Notepad->new;
146              
147 0 0         if ($::DEBUG) {
148 0 0         Fault::Logger->arg_check_isa ($fh,"IO:Handle","filehandle")
149             or return undef;
150             }
151              
152             # Lines are of the form:
153             # $entry = bless( {'mode' => 33188, ... }, 'FileHash::Entry' );
154             #
155 0           my $entry;
156 0           my $line = readline $fh;
157 0 0         defined $line or return undef;
158              
159 0 0         if (! eval $line) {
160 0           Fault::Logger->log ("Eval on dump file line failed: $@");
161 0           return undef;
162             }
163              
164             # Initiatlize self from the input data
165 0           @$self{keys %$entry} = (values %$entry);
166 0           return $self;
167             }
168              
169             #-----------------------------------------------------------------------------
170              
171             sub _lexical_parse ($$$) {
172 0     0     my ($class,$string,$fields) = @_;
173 0           my ($quote, @pass2);
174              
175 0           my @pass1 = split /((?
176              
177 0           foreach my $i (@pass1) {
178 0           my $needed = $fields - ($#pass2+1);
179              
180             # The rest of the line goes in the last lexeme.
181 0 0         if ($needed == 0) {$pass2[$#pass2] .= $i;}
  0            
182             else {
183 0 0         if (!defined $quote) {
184              
185             # Start a quoted section
186 0 0 0       if (length $i == 1 and ($i eq '"' or $i eq "'")) {
  0   0        
187 0           $quote = $i;
188 0           push @pass2, ("");
189             }
190              
191             # Split nonquoted sections.
192             else {push @pass2, (split " ", $i, $needed);}
193             }
194              
195             # Append everything inside a quoted section.
196             else {
197 0 0         if ($quote eq $i) {$quote = undef;}
  0            
  0            
198             else {$pass2[$#pass2] .= $i; }
199             }
200             }
201             }
202 0           return @pass2;
203             }
204              
205             #=============================================================================
206              
207 0     0 1   sub print ($) {my $s = shift; $s->fprint (*STDOUT); $s;}
  0            
  0            
208 0     0 1   sub dump ($\*) {my $s = shift; $s->fprint (@_); $s;}
  0            
  0            
209              
210             #-----------------------------------------------------------------------------
211              
212             sub sprint ($) {
213 0     0 1   my $self = shift;
214 0           my $dd = Data::Dumper->new ([$self], ["self"]);
215 0           $dd->Indent(0);
216 0           return $dd->dump;
217             }
218              
219             #-----------------------------------------------------------------------------
220              
221             sub fprint ($\*) {
222 0     0 1   my ($self,$fh) = @_;
223              
224             # NOTE: NEED A fault_check_isglob method
225              
226 0           my $dd = Data::Dumper->new ([$self], ["entry"]);
227 0           $dd->Indent(0);
228              
229 0           my $ok = printf $fh "%s\n",$dd->Dump;
230 0 0         $ok or Fault::Logger->log_once ("Failed to print to dumpfile: $!");
231              
232 0           return $self;
233             }
234              
235             #=============================================================================
236              
237             sub path ($) {
238 0     0 1   my $s = shift;
239 0           return File::Spec->catpath (@$s{'device','directory','file'});
240             }
241              
242             #-----------------------------------------------------------------------------
243             # Return values for printing or hashing: "" or 0 on undef for hashing.
244             #-----------------------------------------------------------------------------
245              
246 0     0 1   sub device ($) {my $s=shift;
247 0 0         (defined $s->{'device'})
248             ? $s->{'device'} : "";}
249              
250 0     0 1   sub directory ($) {my $s=shift;
251 0 0         (defined $s->{'directory'})
252             ? $s->{'device'} : "";}
253              
254 0     0 1   sub file ($) {my $s=shift;
255 0 0         (defined $s->{'file'})
256             ? $s->{'file'} : "";}
257              
258 0     0 1   sub md5sum ($) {my $s=shift;
259 0 0         (defined $s->{'md5sum'})
260             ? $s->{'md5sum'} : 0;}
261              
262 0     0 1   sub sizeBytes ($) {my $s=shift;
263 0 0         (defined $s->{'sizeBytes'})
264             ? $s->{'sizeBytes'} : 0;}
265              
266 0     0 1   sub atime ($) {my $s=shift;
267 0 0         (defined $s->{'atime'})
268             ? $s->{'atime'} : 0;}
269              
270 0     0 1   sub mtime ($) {my $s=shift;
271 0 0         (defined $s->{'mtime'})
272             ? $s->{'mtime'} : 0;}
273              
274 0     0 1   sub ctime ($) {my $s=shift;
275 0 0         (defined $s->{'ctime'})
276             ? $s->{'ctime'} : 0;}
277              
278             #-----------------------------------------------------------------------------
279             # Values for printing, return "undef" string if undef.
280             #-----------------------------------------------------------------------------
281 0     0 1   sub deviceNumber ($) {my $s=shift;
282 0 0         (defined $s->{'deviceNumber'})
283             ? $s->{'deviceNumber'} : "undef";}
284              
285 0     0 1   sub inode ($) {my $s=shift;
286 0 0         (defined $s->{'inode'})
287             ? $s->{'inode'} : "undef";}
288              
289 0     0 1   sub mode ($) {my $s=shift;
290 0 0         (defined $s->{'mode'})
291             ? $s->{'mode'} : "undef";}
292              
293 0     0 1   sub hardlinks ($) {my $s=shift;
294 0 0         (defined $s->{'hardlinks'})
295             ? $s->{'hardlinks'} : "undef";}
296              
297 0     0 1   sub uid ($) {my $s=shift;
298 0 0         (defined $s->{'uid'})
299             ? $s->{'uid'} : "undef";}
300              
301 0     0 1   sub uidName ($) {my $s=shift;
302 0 0         (defined $s->{'uidName'})
303             ? $s->{'uidName'} : "undef";}
304              
305 0     0 1   sub gid ($) {my $s=shift;
306 0 0         (defined $s->{'gid'})
307             ? $s->{'gid'} : "undef";}
308              
309 0     0 1   sub gidName ($) {my $s=shift;
310 0 0         (defined $s->{'gidName'})
311             ? $s->{'gidName'} : "undef";}
312              
313 0     0 1   sub deviceSpecialId ($) {my $s=shift;
314 0 0         (defined $s->{'deviceSpecialId'})
315             ? $s->{'deviceSpecialId'} : "undef";}
316              
317 0     0 1   sub blocksizePreference ($) {my $s=shift;
318 0 0         (defined $s->{'blocksizePreference'})
319             ? $s->{'blocksizePreference'} : "undef";}
320              
321 0     0 1   sub blocksAllocated ($) {my $s=shift;
322 0 0         (defined $s->{'blocksAllocated'})
323             ? $s->{'blocksAllocated'} : "undef";}
324            
325             #=============================================================================
326             # POD DOCUMENTATION
327             #=============================================================================
328             # You may extract and format the documention section with the 'perldoc' cmd.
329              
330             =head1 NAME
331              
332             FileHash::Entry - Container for data about a file.
333              
334             =head1 SYNOPSIS
335              
336             use FileHash::Entry;
337             $obj = FileHash::Entry->alloc;
338             $ver = FileHash::Entry->dumpversion;
339              
340             $obj = $obj->init ($path);
341             $obj = $obj->initFromStat ($path);
342             $obj = $obj->initFromLine ($format,$line);
343             $obj = $obj->initFromDump ($fh);
344              
345              
346             $str = $obj->sprint;
347             $obj = $obj->print;
348             $obj = $obj->fprint($fh);
349             $obj = $obj->dump ($fh);
350              
351             $path = $obj->path;
352              
353             $atime = $obj->atime;
354             $blocksizePreference = $obj->blocksizePreference;
355             $blocksAllocated = $obj->blocksAllocated;
356             $ctime = $obj->ctime;
357             $device = $obj->device;
358             $deviceNumber = $obj->deviceNumber;
359             $deviceSpecialId = $obj->deviceSpecialId;
360             $directory = $obj->directory;
361             $file = $obj->file;
362             $gid = $obj->gid;
363             $gidName = $obj->gidName;
364             $hardlinks = $obj->hardlinks;
365             $inode = $obj->inode;
366             $md5sum = $obj->md5sum;
367             $mode = $obj->mode;
368             $mtime = $obj->mtime;
369             $sizeBytes = $obj->sizeBytes;
370             $uid = $obj->uid;
371             $uidName = $obj->uidName;
372              
373             =head1 Inheritance
374             UNIVERSAL
375              
376             =head1 Description
377              
378             This is an internal class used by FileHashes.
379              
380             Entry objects are containers for information about files collected
381             from various sources.
382              
383             =head1 Examples
384              
385             use FileHash::Entry;
386              
387             # Create an entry by collecting metadata about a live file.
388             my $a = FileHash::Entry->alloc;
389             $a->initFromStat ("/root/myfile");
390              
391             # Create another Entry by parsing a line of data.
392             my $f = FileHash::FormatString->alloc;
393             $f->init ("path md5sum sizeBytes");
394             my $b = FileHash::Entry->alloc;
395             $b->initFromLine ($f, "/root/myfile 0bdebef6bc59cabe489442ef9ddecf5f 10050");
396              
397             # Dump the object data to a file.
398             open $fh, ">mydump";
399             $b->dump ($fh);
400             close $fh;
401              
402             # Reload the dumped object data.
403             my $c = FileHash::Entry->alloc;
404             open $fh, "
405             $c->initFromDump ($fh);
406             close $fh;
407              
408             # print data on the console.
409             $c->print;
410            
411             =head1 Class Variables
412              
413             None.
414              
415             =head1 Instance Variables
416              
417             In most cases an item will be undef if it is not available via the
418             source of information used to create the FileHash::Entry.
419              
420             device File device portion of file path, non Unix systems.
421             directory File directory portion of file path.
422             file File name portion of file path.
423             deviceNumber Device number.
424             sizeBytes Size of file in bytes.
425             uid User id number.
426             uidName User name in ascii.
427             gid Group id number.
428             gidName Group name in ascii.
429             mode File access mode integer.
430             atime Access time in nonleap seconds since 19700101 UTC.
431             mtime Modify time in nonleap seconds since 19700101 UTC.
432             ctime Create time in nonleap seconds since 19700101 UTC.
433             inode File Inode number.
434             hardlinks Number of hard links to file.
435             deviceSpecialId Device special id, integer.
436             blocksizePreference Preferred block size in bytes.
437             blocksAllocated Number of blocks allocated to the file.
438             md5sum md5sum of file content.
439             notepad A Notepad object to record unusual events.
440              
441             =head1 Class Methods
442              
443             =over 4
444              
445             =item B<$obj = FileHash::Entry-Ealloc>
446              
447             Allocate an empty FileHash Entry object.
448              
449             =item B<$ver = FileHash::Entry-Edumpversion>
450              
451             Return the FileHash Entry dump format version id.
452              
453             =back 4
454              
455             =head1 Instance Methods for printing
456              
457             =over 4
458              
459             =item B<$atime = $obj-Eatime>
460              
461             Return the file atime or 0 if not known.
462              
463             =item B<$blocksizePreference = $obj-EblocksizePreference>
464              
465             Return the file blocksizePreference or "undef" if not known.
466              
467             =item B<$blocksAllocated = $obj-EblocksAllocated>
468              
469             Return the file blocksAllocated or "undef" if not known.
470              
471             =item B<$ctime = $obj-Ectime>
472              
473             Return the file ctime or 0 if not known.
474              
475             =item B<$device = $obj-Edevice>
476              
477             Return the file device or "" if not known.
478              
479             =item B<$deviceNumber = $obj-EdeviceNumber>
480              
481             Return the file deviceNumber or "undef" if not known.
482              
483             =item B<$deviceSpecialId = $obj-EdeviceSpecialId>
484              
485             Return the file deviceSpecialId or "undef" if not known.
486              
487             =item B<$directory = $obj-Edirectory>
488              
489             Return the file directory or "" if not known.
490              
491             =item B<$file = $obj-Efile>
492              
493             Return the file name or "" if not known.
494              
495             =item B<$gid = $obj-Egid>
496              
497             Return the file gid or "undef" if not known.
498              
499             =item B<$gidName = $obj-EgidName>
500              
501             return the file gidName or "undef" if not known.
502              
503             =item B<$hardlinks = $obj-Ehardlinks>
504              
505             Return the file hardlinks or "undef" if not known.
506              
507             =item B<$inode = $obj-Einode>
508              
509             Return the file inodes or "undef" if not known.
510              
511             =item B<$md5sum = $obj-Emd5sum>
512              
513             Return the file md5sum or "undef" if not known.
514              
515             =item B<$mode = $obj-Emode>
516              
517             Return the file mode or "undef" if not known.
518              
519             =item B<$mtime = $obj-Emtime>
520              
521             Return the file mtime or 0 if not known.
522              
523             =item B<$sizeBytes = $obj-EsizeBytes>
524              
525             Return the file size in bytes or 0 if not known.
526              
527             =item B<$uid = $obj-Euid>
528              
529             Return the file uid or "undef" if not known.
530              
531             =item B<$uidName = $obj-EuidName>
532              
533             Return the file uidName or "undef" if not known.
534              
535             =back 4
536              
537             =head1 Instance Methods
538              
539             =over 4
540              
541             =item B<$obj = $obj-Edump($fh)>
542              
543             Dump contents of the file data entry to one line in the specified file
544             defined by the opened file handle $fh.
545              
546             Synonym for fprint.
547              
548             =item B<$obj = $obj-Efprint($fh)>
549              
550             Dump contents of the file data entry to one line in the specified file
551             defined by the opened file handle $fh.
552              
553             Synonym for dump.
554              
555             =item B<$obj = $obj-Einit ($path)>
556              
557             Initialize a FileHash Entry object to contain the path name.
558              
559             =item B<$obj = $obj-EinitFromDump ($fh)>
560              
561             Replace the alloc'd object with one recreated from a line of dump
562             file data.
563              
564             =item B<$obj = $obj-EinitFromLine ($format,$line)>
565              
566             Create a FileHash Entry from the information parsed out of a line
567             of text. A format object defines what information is contained in
568             that line.
569              
570             =item B<$obj = $obj-EinitFromStat ($path)>
571              
572             Initialized an object with metadata collected via a 'stat' and 'md5sum'
573             applied to the file at $path.
574              
575             =item B<$obj = $obj-Epath>
576              
577             Return the full path name.
578              
579             =item B<$obj = $obj-Eprint>
580              
581             Dump contents of the file data entry as one line on stdout.
582              
583             =item B<$str = $obj-Esprint>
584              
585             Dump contents of the file data entry as a string. The string is
586             not terminated by a newline.
587              
588             =back 4
589              
590             =head1 Private Class Method
591              
592             None.
593              
594             =head1 Private Instance Methods
595              
596             =item B<@lexemes = $obj-E_lexical_parse ($line,$fields)>
597              
598             This is the point at which the field data is split it needs to handle a
599             mix of blank delimited fields and quoted fields. If want to parse
600             lines of code here, you'll just have to write your own subclass and
601             override this method.
602              
603             =head1 Errors and Warnings
604              
605             Lots.
606              
607             =head1 KNOWN BUGS
608              
609             See TODO.
610              
611             =head1 SEE ALSO
612              
613             File::Spec, Digest::MD5, Fault::Notepad, Fault::Logger,
614             FileHash::FormatString, Data::Dumper.
615              
616             =head1 AUTHOR
617              
618             Dale Amon
619              
620             =cut
621            
622             #=============================================================================
623             # CVS HISTORY
624             #=============================================================================
625             # $Log: Entry.pm,v $
626             # Revision 1.8 2008-08-28 23:35:28 amon
627             # perldoc section regularization.
628             #
629             # Revision 1.7 2008-08-09 12:56:42 amon
630             # Added parens to fix math error.
631             #
632             # Revision 1.6 2008-07-27 15:16:17 amon
633             # Wrote lexical parse for Entry; error checking on eval and other minor issues.
634             #
635             # Revision 1.5 2008-07-25 14:30:42 amon
636             # Documentation improvements and corrections.
637             #
638             # Revision 1.4 2008-07-24 20:19:43 amon
639             # Just in case I missed anything.
640             #
641             # Revision 1.3 2008-07-24 13:35:26 amon
642             # switch to NeXT style alloc/init format for FileHash and Entry classes.
643             #
644             # Revision 1.2 2008-07-23 21:12:24 amon
645             # Moved notes out of file headers; a few doc updates; added assertion checks;
646             # minor bug fixes.
647             #
648             # 20080625 Dale Amon
649             # Created.
650             1;