File Coverage

lib/File/Data.pm
Criterion Covered Total %
statement 359 433 82.9
branch 122 216 56.4
condition 23 52 44.2
subroutine 39 47 82.9
pod 5 15 33.3
total 548 763 71.8


line stmt bran cond sub pod time code
1             package File::Data;
2              
3 1     1   8944 use strict;
  1         2  
  1         29  
4 1     1   3 use warnings;
  1         1  
  1         25  
5              
6 1     1   3 use Carp;
  1         4  
  1         43  
7 1     1   3 use Data::Dumper;
  1         2  
  1         40  
8 1     1   3 use Fcntl qw(:flock);
  1         1  
  1         123  
9 1     1   397 use FileHandle;
  1         8363  
  1         8  
10             # use Tie::File; # <- todo
11             # use File::stat;
12 1     1   301 use vars qw(@ISA $VERSION $AUTOLOAD);
  1         2  
  1         2348  
13             $VERSION = do { my @r = (q$Revision: 1.18 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
14             $| = 1;
15              
16             =head1 NAME
17              
18             File::Data - interface to file data
19              
20             =head1 DESCRIPTION
21              
22             Wraps all the accessing of a file into a convenient set of calls for
23             reading and writing data, including a simple regex interface.
24              
25             Note that the file needs to exist prior to using this module!
26              
27             See L
28              
29             =head1 SYNOPSIS
30              
31             =over 4
32              
33             use strict;
34              
35             use File::Data;
36              
37             my $o_dat = File::Data->new('./t/example');
38              
39             $o_dat->write("complete file contents\n");
40              
41             $o_dat->prepend("first line\n"); # line 0
42              
43             $o_dat->append("original second (last) line\n");
44              
45             $o_dat->insert(2, "new second line\n"); # inc. zero!
46              
47             $o_dat->replace('line', 'LINE');
48              
49             print $o_dat->READ;
50              
51             Or, perhaps more seriously :-}
52              
53             my $o_sgm = File::Data->new('./sgmlfile');
54              
55             print "new SGML data: ".$o_sgm->REPLACE(
56             '\<\s*((?i)tag)\s*\>\s*((?s).*)\s*\<\s*((?i)\s*\/\s*tag)\s*\>',
57             qq|key="val"|,
58             ) if $o_sgm;
59              
60             See L and L.
61              
62             =back
63              
64             =head1 IMPORTANT
65              
66             lowercase method calls return the object itself, so you can chain calls.
67              
68             my $o_obj = $o_dat->read; # ! <= object !
69              
70             UPPERCASE method calls return the data relevant to the operation.
71              
72             my @data = $o_dat->READ; # ! <= data !
73              
74             While this may occasionally be frustrating, using the B
75             least surprise>, it is at least consistent.
76              
77             See L
78              
79             =head1 EXPLANATION
80              
81             =over 4
82              
83             The idea is to standardise accessing of files for repetitive and straight
84             forward tasks, and remove the repeated and therefore error prone file
85             access I have seen in many sites, where varying, (with equivalently
86             varying success), methods are used to achieve essentially the same result
87             - a simple search and replace and/or a regex match.
88              
89             Approaches to opening and working with files vary so much, where
90             one person may wish to know if a file exists, another wishes to know
91             whether the target is a file, or if it is readable, or writable and so on.
92             Sometimes, in production code even (horror), file's are opened without any
93             checks of whether the open was succesful. Then there's a loop through
94             each line to find the first or many patterns to read and/or replace.
95             With a failure, normally the only message is 'permission denied', is
96             that read or write access, does the file even exist? etc.
97              
98             This module attempts to provide a plain/generic interface to accessing
99             a file's data. This will not suit every situation, but I have included
100             some examples which will hopefully demonstrate that it may be used
101             in situations where people would normally go through varying and
102             inconsistent, (and therefore error-prone), procedures - to get at the
103             same data.
104              
105             Theoretically you can mix and match your read and writes so long as you
106             don't open read-only.
107              
108             my $o_dat = File::Data->new($file);
109              
110             my $i_snrd = $o_dat->append($append)->REPLACE($search, $replace);
111              
112             print $o_dat->READ;
113              
114             One last thing - I'm sure this could be made more efficient, and I'd be
115             receptive to any suggestions to that effect. Note though that the intention
116             has been to create a simple and consistent interface, rather than a complicated
117             one.
118              
119             =back
120              
121             =cut
122              
123             # Methods we like:
124             # ================================================================
125             #
126             my @_METHODS = qw(append insert prepend read replace return search write);
127             my $_METHODS = join('|', @_METHODS);
128              
129             =head1 METHODS
130              
131             =over 4
132              
133             =item new
134              
135             Create a new File::Data object (default read-write).
136              
137             my $o_rw = File::Data->new($filename); # read-write
138              
139             my $o_ro = File::Data->new($filename, 'ro'); # read-only
140              
141             Each file should have it's own discrete object.
142              
143             Note that if you open a file read-only and then attempt to write to it,
144             that will be regarded as an error, even if you change the permissions
145             in the meantime.
146              
147             Further: The file B exist before succesful use of this method
148             is possible. This is B a replacement for modules which create and
149             delete files, this is purely designed as an interface to the B
150             of existing files. A B function is a future possibility.
151              
152             Look in L for a more complete explanation of possible arguments
153             to the B method
154              
155             =cut
156              
157             sub new {
158 556     556 1 15105 my $class = shift;
159 556         561 my $file = shift;
160 556   66     1191 my $perms = shift || $File::Data::PERMISSIONS;
161 556   50     1620 my $h_err = shift || {};
162              
163 556         2243 my $self = bless({
164             '_err' => {},
165             '_var' => {
166             'backup' => 0,
167             'limbo' => '',
168             'state' => 'init',
169             'writable' => 0,
170             },
171             }, $class);
172              
173 556 50       1021 $self->_debug("file($file), perm($perms), h_err($h_err)") if $File::Data::DEBUG;
174 556         843 my $i_ok = $self->_init($file, $perms, $h_err);
175              
176 556 100       1598 return $i_ok == 1 ? $self : undef;
177             }
178              
179             =item read
180              
181             Read all data from file
182              
183             $o_dat = $o_dat->read; # !
184              
185             my @data = $o_dat->READ;
186              
187             =cut
188              
189             sub READ {
190 2     2   2 my $self = shift;
191              
192 2         4 $self->_enter('read');
193 2 50       9 $self->_debug('in: ') if $File::Data::DEBUG;
194              
195 2         6 my @ret = $self->_read;
196              
197 2 50       5 $self->_debug('out: '.Dumper(\@ret)) if $File::Data::DEBUG;
198 2         4 $self->_leave('read');
199              
200 2         5 return @ret;
201             };
202              
203             =item _internal
204              
205             read
206              
207             does this...
208              
209             =cut
210              
211             sub _read { #
212 2     2   1 my $self = shift;
213              
214 2         4 my $FH = $self->_fh;
215 2         7 $FH->seek(0, 0);
216             #
217 2         36 my @ret = <$FH>;
218              
219 2 50       8 return ($File::Data::REFERENCE) ? \@ret : @ret;
220             };
221              
222             =item write
223              
224             Write data to file
225              
226             my $o_dat = $o_dat->WRITE; # !
227              
228             my @written = $o_dat->write;
229              
230             =cut
231              
232             sub WRITE {
233 5     5   10 my $self = shift;
234 5         9 my @args = @_;
235 5         4 my @ret = ();
236              
237 5         10 $self->_enter('write');
238 5 50       8 $self->_debug('in: '.Dumper(\@args)) if $File::Data::DEBUG;
239              
240 5 100       7 if ($self->_writable) {
241 4         6 my $FH = $self->_fh;
242 4         16 $FH->truncate(0);
243 4         161 $FH->seek(0, 0);
244 4         21 @ret = $self->_write(@args);
245             }
246              
247 5 50       10 $self->_debug('out: '.Dumper(\@ret)) if $File::Data::DEBUG;
248 5         7 $self->_leave('write');
249              
250 5 50       15 return ($File::Data::REFERENCE) ? \@ret : @ret;
251             };
252              
253             sub _write { #
254 12     12   16 my $self = shift;
255 12         13 my @ret = ();
256              
257 12         17 my $FH = $self->_fh;
258 12         25 my $pos = $FH->tell;
259 12 50       52 $self->_debug("writing at curpos: $pos") if $File::Data::DEBUG;
260 12         18 foreach (@_) {
261 44 50       89 push(@ret, $_) if print $FH $_;
262 44 50       72 $self->_debug("wrote -->$_<--") if $File::Data::DEBUG;
263             }
264              
265 12 50       47 return ($File::Data::REFERENCE) ? \@ret : @ret;
266             };
267              
268             =item prepend
269              
270             Prepend to file
271              
272             my $o_dat = $o_dat->prepen(\@lines); # !
273              
274             my @prepended = $o_dat->prepend(\@lines);
275              
276             =cut
277              
278             sub PREPEND {
279 3     3 0 15 my $self = shift;
280 3         3 my @ret = ();
281              
282 3         6 $self->_enter('prepend');
283 3 50       6 $self->_debug('in: '.Dumper(@_)) if $File::Data::DEBUG;
284              
285 3 100       4 if ($self->_writable) {
286 2         3 my $FH = $self->_fh;
287 2         7 $FH->seek(0, 0);
288 2         48 my @data = <$FH>;
289 2         7 $FH->truncate(0);
290 2         70 $FH->seek(0, 0);
291 2 50       10 @ret = @_ if $self->_write(@_, @data);
292             }
293              
294 3 50       5 $self->_debug('out: '.Dumper(\@ret)) if $File::Data::DEBUG;
295 3         5 $self->_leave('prepend');
296              
297 3 50       8 return ($File::Data::REFERENCE) ? \@ret : @ret;
298             };
299              
300             =item insert
301              
302             Insert data at line number, starting from '0'
303              
304             my $o_dat = $o_dat->insert($i_lineno, \@lines); # !
305              
306             my @inserted = $o_dat->INSERT($i_lineno, \@lines);
307              
308             =cut
309              
310             sub INSERT {
311 3     3 0 14 my $self = shift;
312 3         3 my $line = shift;
313 3         6 my @ret = ();
314              
315 3         4 $self->_enter('insert');
316 3 50       5 $self->_debug('in: '.Dumper(\@_)) if $File::Data::DEBUG;
317              
318 3 100       9 if ($line !~ /^\d+$/) {
319 1         4 $self->_error("can't go to non-numeric line($line)");
320             } else {
321 2 50       9 if ($self->_writable) {
322 2         5 my $FH = $self->_fh;
323 2         5 $FH->seek(0, 0);
324 2         9 my $i_cnt = -1;
325 2         3 my @pre = ();
326 2         2 my @post = ();
327 2         23 while (<$FH>) {
328 10         6 $i_cnt++; # 0..n
329 10         15 my $pos = $FH->tell;
330 10 100       35 if ($i_cnt < $line) {
    50          
331 4         10 push(@pre, $_);
332             } elsif ($i_cnt >= $line) {
333 6         14 push(@post, $_);
334             }
335             }
336 2         1 $i_cnt++;
337 2 50       4 if (!($i_cnt >= $line)) {
338 0 0       0 my $s = ($i_cnt == 1) ? '' : 's';
339 0         0 $self->_error("couldn't insert($line, ...) while only $i_cnt line$s in file");
340             } else {
341 2         6 $FH->truncate(0);
342 2         97 $FH->seek(0, 0);
343 2 50       12 @ret = @_ if $self->_write(@pre, @_, @post);
344             }
345             }
346             }
347              
348 3 50       6 $self->_debug('out: '.Dumper(\@ret)) if $File::Data::DEBUG;
349 3         6 $self->_leave('insert');
350              
351 3 50       8 return ($File::Data::REFERENCE) ? \@ret : @ret;
352             }
353              
354             =item append
355              
356             Append to file
357              
358             my $o_dat = $o_dat->append(\@lines); # !
359              
360             my @appended = $o_dat->APPEND(\@lines);
361              
362             =cut
363              
364             sub APPEND {
365 3     3 0 15 my $self = shift;
366 3         4 my @ret = ();
367              
368 3         6 $self->_enter('append');
369 3 50       6 $self->_debug('in: '.Dumper(\@_)) if $File::Data::DEBUG;
370              
371 3 100       5 if ($self->_writable) {
372 2         5 my $FH = $self->_fh;
373 2         7 $FH->seek(0, 2);
374 2 50       12 @ret = @_ if $self->_write(@_);
375             }
376              
377 3 50       8 $self->_debug('out: '.Dumper(\@ret)) if $File::Data::DEBUG;
378 3         6 $self->_leave('append');
379              
380 3 50       14 return ($File::Data::REFERENCE) ? \@ret : @ret;
381             };
382              
383             =item search
384              
385             Retrieve data out of a file, simple list of all matches found are returned.
386              
387             Note - you must use capturing parentheses for this to work!
388              
389             my $o_dat = $o_dat->search('/^(.*\@.*)$/'); # !
390              
391             my @addrs = $o_dat->SEARCH('/^(.*\@.*)$/');
392              
393             my @names = $o_dat->SEARCH('/^(?:[^:]:){4}([^:]+):/');
394              
395             =cut
396              
397             sub SEARCH {
398 2     2 0 12 my $self = shift;
399 2         1 my $search = shift;
400 2         4 my @ret = ();
401              
402 2         12 $self->_enter('search');
403 2 50       4 $self->_debug("in: $search") if $File::Data::DEBUG;
404              
405 2 50       7 if ($search !~ /.+/) {
406 0         0 $self->_error("no search($search) given");
407             } else {
408 2         4 my $file = $self->_var('filename');
409 2         4 my $FH = $self->_fh;
410 2         5 $FH->seek(0, 0);
411 2         12 my $i_cnt = 0;
412 2 100       5 if ($File::Data::STRING) { # default
413 1         2 my $orig = $/; $/ = undef; # slurp
  1         3  
414 1         10 my $data = <$FH>; $/ = $orig;
  1         2  
415 1 50       3 $self->_debug("looking at data($data)") if $File::Data::DEBUG;
416 1         21 @ret = ($data =~ /$search/g);
417 1         4 $i_cnt = ($data =~ tr/\n/\n/);
418             } else {
419 1         11 while (<$FH>) {
420 7 50       9 $self->_debug("looking at line($_)") if $File::Data::DEBUG;
421 7         7 my $line = $_;
422 7         39 push(@ret, ($line =~ /$search/));
423 7         18 $i_cnt++;
424             }
425             }
426 2 50       6 if (scalar(@ret) >= 1) {
427 2         11 $self->_debug("search($search) failed(@ret) in file($file) lines($i_cnt)");
428             }
429             }
430              
431 2 50       6 $self->_debug('out: '.Dumper(\@ret)) if $File::Data::DEBUG;
432 2         4 $self->_leave('search');
433              
434 2 50       9 return ($File::Data::REFERENCE) ? \@ret : @ret;
435             }
436              
437             =item replace
438              
439             Replace data in a 'search and replace' manner, returns the final data.
440              
441             my $o_dat = $o_dat->replace($search, $replace); # !
442              
443             my @data = $o_dat->REPLACE($search, $replace);
444              
445             my @data = $o_dat->REPLACE(
446             q|\ q|'my.sales.com'|,
447             );
448              
449             This is B, in that you can do almost anything in the B side,
450             but the B side is a bit more restricted, as we can't effect the
451             replacement modifiers on the fly.
452              
453             If you really need this, perhaps B<(?{})> can help?
454              
455             =cut
456              
457             sub REPLACE {
458 3     3 0 19 my $self = shift;
459 3         7 my %args = @_;
460 3         4 my @ret = ();
461              
462 3         6 $self->_enter('replace');
463 3 50       5 $self->_debug('in: '.Dumper(\%args)) if $File::Data::DEBUG;
464              
465 3 100       6 if ($self->_writable) {
466 2         4 my $file = $self->_var('filename');
467 2         4 my $FH = $self->_fh;
468 2         5 $FH->seek(0, 0);
469 2         34 my $i_cnt = 0;
470             SEARCH:
471 2         5 foreach my $search (keys %args) {
472 2         3 my $replace = $args{$search};
473 2 100       4 if ($File::Data::STRING) { # default
474 1         2 my $orig = $/; $/ = undef; # slurp
  1         3  
475 1         10 my $data = <$FH>; $/ = $orig;
  1         1  
476 1 50       3 $self->_debug("initial ($data)") if $File::Data::DEBUG;
477 1 50       24 if (($i_cnt = ($data =~ s/$search/$replace/g))) {
478 1         2 @ret = $data;
479             } else {
480 0         0 print "unable($i_cnt) to search($search) and replace($replace)\n";
481             }
482             } else {
483 1         9 while (<$FH>) {
484 7 50       9 $self->_debug("initial line($_)") if $File::Data::DEBUG;
485 7         8 my $line = $_;
486 7 100       43 if ($line =~ s/$search/$replace/) {
487 3         3 $i_cnt++;
488             }
489 7         19 push(@ret, $line);
490             }
491             }
492 2 50       5 if (scalar(@ret) >= 1) {
493 2         6 $FH->seek(0, 0);
494 2         11 $FH->truncate(0);
495 2         74 $FH->seek(0, 0);
496 2         10 @ret = $self->_write(@ret);
497             }
498 2 50       7 if (!($i_cnt >= 1)) {
499 0         0 $self->_debug("nonfulfilled search($search) and replace($replace) in file($file)");
500             }
501             }
502             }
503              
504 3 50       8 $self->_debug('out: '.Dumper(\@ret)) if $File::Data::DEBUG;
505 3         9 $self->_leave('replace');
506              
507 3 50       12 return ($File::Data::REFERENCE) ? \@ret : @ret;
508             }
509              
510             =item xreturn
511              
512             Returns the product of the given (or last) B, undef on failure.
513              
514             my $o_dat = $o_dat->prepend($A)->append($b)->return('prepend'); # !
515              
516             my @prepended = $o_dat->prepend($A)->append($b)->RETURN('prepend');
517              
518             my @appended = $o_dat->prepend($A)->append($b)->RETURN; # like read()
519              
520             =cut
521              
522             sub RETURN {
523 5     5 0 6 my $self = shift;
524 5   33     11 my $call = uc(shift) || $self->_var('last');
525              
526 5 50 33     37 if ((defined($self->{'_var'}{$call}) &&
527             ref($self->{'_var'}{$call}) eq 'ARRAY'
528             )) {
529 5         3 return @{$self->_var($call)};
  5         9  
530             } else {
531 0         0 $self->_debug("not returning invalid call($call) ref($self->{'_var'}{$call})");
532 0         0 return undef;
533             }
534             }
535              
536             =item create
537              
538             placeholder - unsupported
539              
540             =cut
541              
542             sub create {
543 0     0 1 0 my $self = shift;
544              
545 0         0 $self->_error("unsupported call: __FILE__(@_)");
546              
547 0         0 return ();
548             }
549              
550             =item delete
551              
552             placeholder - unsupported
553              
554             =cut
555              
556             sub delete {
557 0     0 1 0 my $self = shift;
558              
559 0         0 $self->_error("unsupported call: __FILE__(@_)");
560              
561 0         0 return ();
562             }
563              
564             =item close
565              
566             Close the file
567              
568             my $i_closed = $o_dat->close; # 1|0
569              
570             =cut
571              
572             sub close {
573 0     0 1 0 my $self = shift;
574              
575 0         0 return $self->_close;
576             }
577              
578              
579             =item info
580              
581             placeholder - unsupported
582              
583             =cut
584              
585             # Returns File::stat object for the file.
586              
587             # print 'File size: '.$o_dat->stat->size;
588              
589             sub xFSTAT {
590 0     0 0 0 my $self = shift;
591 0   0     0 my $file = shift || '_';
592              
593             # print "file($file) stat: ".Dumper(stat($file));
594              
595             # return stat($file);
596              
597 0         0 return ();
598             }
599              
600             sub xfstat {
601 0     0 0 0 my $self = shift;
602 0   0     0 my $file = shift || '_';
603              
604             # print "file($file) stat: ".Dumper(stat($file));
605              
606             # stat($file);
607              
608 0         0 return ();
609             }
610              
611             sub dummy {
612 0     0 0 0 my $self = shift;
613 0         0 my %args = @_;
614 0         0 my @ret = ();
615              
616 0         0 $self->_enter('dummy');
617 0 0       0 $self->_debug('in: '.Dumper(\%args)) if $File::Data::DEBUG;
618              
619             # if ($self->_writable) {
620             #
621             # $FH->seek(0, 2);
622             # }
623              
624 0 0       0 $self->_debug('out: '.Dumper(\@ret)) if $File::Data::DEBUG;
625 0         0 $self->_leave('dummy');
626              
627 0 0       0 return ($File::Data::REFERENCE) ? \@ret : @ret;
628             }
629              
630             =back
631              
632             =cut
633              
634             # ================================================================
635              
636             =head1 VARIABLES
637              
638             Various variables may be set affecting the behaviour of the module.
639              
640             =over 4
641              
642             =item $File::Data::DEBUG
643              
644             Set to 0 (default) or 1 for debugging information to be printed on STDOUT.
645              
646             $File::Data::DEBUG = 1;
647              
648             Alternatively set to a regex of any of the prime methods to debug them individually.
649              
650             $File::Data::DEBUG = '(ap|pre)pend';
651              
652             =cut
653              
654             $File::Data::DEBUG ||= $ENV{'File_Data_DEBUG'} || 0;
655             # $File::Data::DEBUG = 1; #
656              
657             =item $File::Data::FATAL
658              
659             Will die if there is any failure in accessing the file, or reading the data.
660              
661             Default = 0 (don't die - just warn);
662              
663             $File::Data::FATAL = 1; # die
664              
665             =cut
666              
667             $File::Data::FATAL ||= $ENV{'File_Data_FATAL'} || 0;
668              
669             =item $File::Data::REFERENCE
670              
671             Will return a reference, not a list, useful with large files.
672              
673             Default is 0, ie; methods normally returns a list. There may be an argument to
674             make returns work with references by default, feedback will decide.
675              
676             $File::Data::REFERENCE = 1;
677              
678             my $a_ref = $o_dat->search('.*');
679              
680             print "The log: \n".@{ $a_ref };
681              
682             =cut
683              
684             $File::Data::REFERENCE ||= $ENV{'File_Data_REFERENCE'} || 0;
685              
686              
687             =item $File::Data::SILENT
688              
689             Set to something other than zero if you don't want error messages ?-\
690              
691             $File::Data::SILENT = 0; # per line
692              
693             =cut
694              
695             $File::Data::SILENT ||= $ENV{'File_Data_SILENT'} || 0;
696              
697              
698             =item $File::Data::STRING
699              
700             Where regex's are used, default behaviour is to treate the entire file as a
701             single scalar string, so that, for example, B<(?ms:...)> matches are effective.
702              
703             Unset if you don't want this behaviour.
704              
705             $File::Data::STRING = 0; # per line
706              
707             =cut
708              
709             $File::Data::STRING ||= $ENV{'File_Data_STRING'} || 1;
710              
711              
712             =item $File::Data::PERMISSIONS
713              
714             File will be opened read-write (B compatible) unless this
715             variable is set explicitly or given via B. In either case,
716             unless it is one of our valid permission B declared below,
717             it will be passed on to B and otherwise not modified.
718             We don't support fancy permission sets, just read or write.
719              
720             Read-only permissions may be explicitly set using one of these B:
721              
722             $File::Data::PERMISSIONS = 'ro'; # or readonly or <
723              
724             Or, equivalently, for read-write (default):
725              
726             $File::Data::PERMISSIONS = 'rw'; # or readwrite or +<
727              
728             Note that it makes no sense to have an 'append only' command (>>),
729             we'd have to disable all of write, search and replace, and insert,
730             etc. in that case - just use the B method only.
731              
732             This is a KISS-compatible module remember?
733              
734             =cut
735              
736             $File::Data::PERMISSIONS ||= $ENV{'File_Data_PERMISSIONS'} || '+<';
737              
738              
739             =back
740              
741             # ================================================================
742              
743             =head1 SPECIAL
744              
745             ...
746              
747             =over 4
748              
749             =item AUTOLOAD
750              
751             Any unrecognised function will be passed to the FileHandle object for final
752             consideration, behaviour is then effectively 'o_dat ISA FileHandle'.
753              
754             $o_dat->truncate;
755              
756             =cut
757              
758             sub AUTOLOAD {
759 6     6   31 my $self = shift;
760 6 50       12 return if $AUTOLOAD =~ /::DESTROY$/o; # protection
761              
762 6         7 my $meth = $AUTOLOAD;
763 6         27 $meth =~ s/.+::([^:]+)$/$1/;
764              
765 6 50       88 if ($meth =~ /^($_METHODS)$/io) { # convenience
766 6         25 $self->_debug("rerouting: $meth(@_)");
767 6         17 return $self->do(uc($meth), @_); # <-
768             # return $self->do(lc($meth), @_);
769             } else { # or fallback
770 0         0 my $FH = $self->_fh;
771 0 0       0 if ($FH->can($meth)) {
772 0         0 return $FH->$meth(@_); # <-
773             } else {
774 0         0 $DB::single=2; #
775 0         0 return $self->_error("no such method($meth)!"); # <-
776             }
777             }
778             }
779              
780             =back
781              
782             =cut
783              
784             # ================================================================
785              
786             =head1 EXAMPLES
787              
788             Typical construction examples:
789              
790             my $o_rw = File::Data->new($filename, 'rw');
791              
792             my $o_ro = File::Data->new($filename, 'ro');
793              
794             =over 4
795              
796             =item complete
797              
798             my $o_dat = File::Data->new('./jabber');
799              
800             $o_dat->write(" Bewxre the Jabberwock my son,\n");
801              
802             $o_dat->prepend("The Jxbberwock by Lewis Cxrroll:\n");
803              
804             $o_dat->append(" the claws thxt snxtch,\n ...\n");
805              
806             $o_dat->insert(2, " the jaws which bite.\n");
807              
808             $o_dat->replace('x', 'a');
809              
810             print $o_dat->SEARCH('The.+\n')->REPLACE("The.+\n", '')->return('search');
811              
812             print $o_dat->READ;
813              
814             =item error
815              
816             Failure is indicated by an error routine being called, this will print
817             out any error to STDERR, unless warnings are declared fatal, in which
818             case we croak. You can register your own error handlers for any method
819             mentioned in the L section of this document, in addition is a
820             special B call for initial file opening and general setting up.
821              
822             Create a read-write object with a callback for all errors:
823              
824             my $o_rw = File::Data->new($filename, 'ro', {
825             'error' => \&myerror,
826             });
827              
828             Create a read-only object with a separate object handler for each error type:
829              
830             my $o_rw = File::Data->new($filename, 'rw', {
831             'error' => $o_generic->error_handler,
832             'insert' => $o_handler->insert_error,
833             'open' => $o_open_handler,
834             'read' => \&carp,
835             'write' => \&write_error,
836             });
837              
838             =item commandline
839              
840             From the command line:
841              
842             Cnew('./test.txt')->write('some stuff')">
843              
844             And (very non-obfuscated)
845              
846             C<
847             perl -MFile::Data -e "@x=sort qw(perl another hacker just);
848             print map {split(\"\n\", ucfirst(\$_).\" \")}\
849             File::Data->new(\"./t/japh\")->\
850             write(shift(@x).\"\n\")-> \
851             append(shift(@x).\"\n\")-> \
852             prepend(shift(@x).\"\n\")-> \
853             insert(2, shift(@x).\"\n\")->\
854             READ;"
855             >
856              
857             If you still have problems, mail me the output of
858            
859             make test TEST_VERBOSE=1
860              
861             =cut
862              
863             # ================================================================
864              
865             # Private methods not expected to be called by anybody, and completely
866             # unsupported. Expected to metamorphose regularly - do B call these - you
867             # have been warned!
868              
869             # Variable get/set method
870             #
871             # my $get = $o_dat->_var($key); # get
872             #
873             # my $set = $o_dat->_var($key, $val); # set
874              
875             # @_METHODS, qw(append insert prepend read replace return search write);
876             my $_VARS = join('|', @_METHODS, qw(
877             backup error errors filename filehandle last limbo permissions state writable
878             ));
879              
880             sub _var {
881 9036     9036   6434 my $self = shift;
882 9036         6473 my $key = shift;
883 9036         6610 my $val = shift;
884 9036         6215 my $ret = '';
885            
886             # if (!(grep(/^_$key$/, keys %{$self{'_var'}}))) {
887 9036 50       18948 if ($key !~ /^($_VARS)$/io) {
888 0         0 $self->_error("No such key($key) val($val)!");
889             } else {
890 9036 100       11307 if (defined($val)) {
891 3411         5465 $self->{'_var'}{$key} = $val;
892             # {"$File::Data::$key"} = $val;
893 3411         6698 $self->_debug("set key($key) => val($val)");
894             }
895 9036         10604 $ret = $self->{'_var'}{$key};
896             }
897              
898 9036         12615 return $ret;
899             }
900              
901             # Print given args on STDOUT
902             #
903             # $o_dat->_debug($msg) if $File::Data::DEBUG;
904              
905             sub _debug {
906 4518     4518   3270 my $self = shift;
907              
908 4518         4219 my $state = $self->{'_var'}{'state'}; # ahem
909 4518         3296 my $debug = $File::Data::DEBUG;
910              
911 4518 50 33     35108 if (($debug =~ /^(\d+)$/o && $1 >= 1) ||
      33        
      33        
912             $debug =~ /^(.+)$/o && $state =~ /$debug/
913             ) {
914 0         0 print ("$state: ", @_, "\n");
915             }
916              
917 4518         4761 return ();
918             }
919              
920             # Return dumped env and object B and B
921             #
922             # print $o_dat->_vars;
923              
924             sub _vars {
925 0     0   0 my $self = shift;
926 0         0 my $h_ret = $self;
927              
928 1     1   7 no strict 'refs';
  1         1  
  1         2131  
929 0         0 foreach my $key (keys %{File::Data::}) {
930 0 0       0 next unless $key =~ /^[A-Z]+$/o;
931 0 0       0 next if $key =~ /^(BEGIN|EXPORT)/o;
932 0         0 my $var = "File::Data::$key";
933 0         0 $$h_ret{'_pck'}{$key} = $$var;
934             }
935              
936 0         0 return Dumper($h_ret);
937             }
938              
939             # Get/set error handling methods/objects
940             #
941             # my $c_sub = $o_dat->_err('insert'); # or default
942              
943             sub _err {
944 21     21   11 my $self = shift;
945 21   33     36 my $state = shift || $self->_var('state');
946              
947 21   33     53 my $err = $self->{'_err'}{$state} || $self->{'_err'}{'default'};
948              
949 21         20 return $err;
950             }
951              
952             # By default prints error to STDERR, will B if B set,
953             # returning (). See L for info on how to pass your own error
954             # handlers in.
955              
956             sub _error {
957 21     21   21 my $self = shift;
958 21         22 my @err = @_;
959 21         21 my @ret = ();
960              
961 21         25 my $state = $self->_var('state');
962 21         35 my $c_ref = $self->_err($state );
963 21         24 my $error = $self->_var('error');
964 21         31 unshift(@err, "$state ERROR: ");
965 21         45 my $ref = $self->_var('errors', join("\n", @err));
966              
967             # $self->_debug($self->_vars) if $File::Data::DEBUG;
968              
969 21 50 33     53 if (ref($c_ref) eq 'CODE') {
    50          
970 0         0 eval { @ret = &$c_ref(@err) };
  0         0  
971 0 0       0 if ($@) {
972 0 0       0 $File::Data::FATAL >= 1
973             ? croak("$0 failed: $c_ref(@err)")
974             : carp("$0 failed: $c_ref(@err)")
975             ;
976             }
977             } elsif (ref($c_ref) && $c_ref->can($state)) {
978 0         0 eval { @ret = $c_ref->$state(@err) };
  0         0  
979 0 0       0 if ($@) {
980 0 0       0 $File::Data::FATAL >= 1
981             ? croak("$0 failed: $c_ref(@err)")
982             : carp("$0 failed: $c_ref(@err)")
983             ;
984             }
985             } else {
986 21 50       30 unless ($File::Data::SILENT) {
987 0 0       0 ($File::Data::FATAL >= 1) ? croak(@err) : carp(@err);
988             }
989             }
990              
991 21         24 return (); #
992             }
993              
994             # my $file = $o_dat->_mapfile($filename);
995              
996             sub _mapfile {
997 556     556   392 my $self = shift;
998 556   100     893 my $file = shift || '';
999              
1000 556         1423 $file =~ s/^\s*//o;
1001 556         1199 $file =~ s/\s*$//o;
1002              
1003 556 100       1126 unless ($file =~ /\w+/o) {
1004 4         4 $file = '';
1005 4         8 $self->_error("inappropriate filename($file)");
1006             } else {
1007 552   50     671 my $xfile = $self->_var('filename') || '';
1008 552 50       996 if ($xfile =~ /.+/o) {
1009 0         0 $file = '';
1010 0         0 $self->_error("can't reuse ".ref($self)." object($xfile) for another file($file)");
1011             }
1012             }
1013              
1014 556         814 return $file;
1015             }
1016              
1017             # Maps given permissions to appropriate form for B
1018             #
1019             # my $perms = $o_dat->_mapperms('+<');
1020              
1021             sub _mapperms {
1022 552     552   419 my $self = shift;
1023 552   50     798 my $args = shift || '';
1024              
1025 552         1141 $args =~ s/^\s*//o;
1026 552         1017 $args =~ s/\s*$//o;
1027              
1028 552         1426 my %map = ( # we only recognise
1029             'ro' => '<',
1030             'readonly' => '<',
1031             'rw' => '+<',
1032             'readwrite' => '+<',
1033             );
1034 552   66     1591 my $ret = $map{$args} || $args;
1035              
1036 552 50       1073 $self->_error("Inappropriate permissions($args) - use this: ".Dumper(\%map))
1037             unless $ret =~ /.+/o;
1038              
1039 552         1080 return $ret;
1040             }
1041              
1042             # Map error handlers, if given
1043             #
1044             # my $h_errs = $o_dat->_maperrs(\%error_handlers);
1045              
1046             sub _mapperrs {
1047 552     552   411 my $self = shift;
1048 552   50     765 my $h_errs = shift || {};
1049            
1050 552 50       871 if (ref($h_errs) ne 'HASH') {
1051 0         0 $self->_error("invalid error_handlers($h_errs)");
1052             } else {
1053 552         400 foreach my $key (%{$h_errs}) {
  552         1407  
1054 0         0 $self->{'_err'}{$key} = $$h_errs{$key};
1055             }
1056             }
1057              
1058 552         803 return $self->{'_err'};
1059             }
1060              
1061             # Mark the entering of a special section, or state
1062             #
1063             # my $entered = $o_dat->enter('search');
1064              
1065             sub _enter {
1066 27     27   22 my $self = shift;
1067 27         23 my $sect = shift;
1068            
1069 27         27 my $last = $self->_var('state');
1070 27 100       48 $self->_var('last' => $last) unless $last eq 'limbo';
1071 27         70 my $next = $self->_var('state' => $sect);
1072              
1073             # $self->_debug("vars") if $File::Data::DEBUG;
1074              
1075 27         27 return $next;
1076             }
1077              
1078             # Mark the leaving of a special section, or state
1079             #
1080             # my $left = $o_dat->_leave('search');
1081              
1082             sub _leave {
1083 583     583   413 my $self = shift;
1084 583         387 my $sect = shift;
1085            
1086 583         656 my $last = $self->_var('state');
1087 583 100       1162 $self->_var('last' => $last) unless $last eq 'limbo';
1088 583         691 my $next = $self->_var('state' => 'limbo');
1089              
1090             # $self->_debug("leaving state($last) => next($next)") if $File::Data::DEBUG;
1091              
1092 583         496 return $last;
1093             }
1094              
1095             # Get and set B. Returns undef otherwise.
1096             #
1097             # my $FH = $o_dat->_fh($FH);
1098              
1099             sub _fh {
1100 1642     1642   1220 my $self = shift;
1101 1642         1251 my $arg = shift;
1102              
1103 1642 100       2624 my $FH = (defined($arg)
1104             ? $self->_var('filehandle', $arg)
1105             : $self->_var('filehandle')
1106             );
1107 1642 50       2264 $self->_error("no filehandle($FH)") unless $FH;
1108              
1109 1642         1559 return $FH;
1110             }
1111              
1112             # ================================================================
1113             # Return values:
1114             #
1115             # 1 = success
1116             #
1117             # 0 = failure
1118              
1119             # Setup object, open a file, with permissions.
1120             #
1121             # my $i_ok = $o_dat->_init( $file, $perm, $h_errs );
1122              
1123             sub _init {
1124 556     556   484 my $self = shift;
1125 556         514 my $file = shift;
1126 556         409 my $perm = shift;
1127 556         407 my $h_err= shift;
1128 556         408 my $i_ok = 0;
1129              
1130             # $self->_enter('init');
1131 556 50       813 $self->_debug("in: file($file), perm($perm), h_err($h_err)") if $File::Data::DEBUG;
1132              
1133 556         720 $file = $self->_mapfile($file );
1134 556 100       1470 $perm = $self->_mapperms($perm ) if $file;
1135 556 100       1132 $h_err = $self->_mapperrs($h_err) if $file; # if $perm
1136              
1137 556 100       775 if ($file) { # unless $h_err
1138 552         841 $i_ok = $self->_check_access($file, $perm);
1139 552 100       838 if ($i_ok == 1) {
1140 551         638 $file = $self->_var('filename', $file);
1141 551         674 $perm = $self->_var('permissions', $perm);
1142 551         822 $i_ok = $self->_open($file, $perm);
1143 551 50 66     1032 $i_ok = $self->_backup() if $i_ok && $self->_var('backup');
1144             }
1145             }
1146             # $self->_error("failed for file($file) and perm($perm)") unless $i_ok == 1;
1147              
1148 556 50       756 $self->_debug("out: $i_ok") if $File::Data::DEBUG;
1149 556         670 $self->_leave('init');
1150              
1151 556         660 return $i_ok;
1152             }
1153              
1154             # Checks the args for existence and appropriate permissions etc.
1155             #
1156             # my $i_isok = $o_dat->_check_access($filename, $permissions);
1157              
1158             sub _check_access {
1159 552     552   430 my $self = shift;
1160 552         396 my $file = shift;
1161 552         491 my $perm = shift;
1162 552         436 my $i_ok = 0;
1163              
1164 552 50 33     2109 if (!($file =~ /.+/o && $perm =~ /.+/o)) {
1165 0         0 $self->_error("no filename($file) or permissions($perm) given!");
1166             } else {
1167 552         5230 stat($file); # just once
1168 552 50       747 if (! -e _) {
1169 0         0 $self->_error("target($file) does not exist!");
1170             } else {
1171 552 100       689 if (! -f _) {
1172 1         4 $self->_error("target($file) is not a file!");
1173             } else {
1174 551 50       841 if (!-r _) {
1175 0         0 $self->_error("file($file) cannot be read by effective uid($>) or gid($))!");
1176             } else {
1177 551 100       865 if ($perm =~ /^<$/o) { # readable
1178 6         7 $i_ok++;
1179             } else {
1180 545 50       2641 if (! -w $file) {
1181 0         0 $self->_error("file($file) cannot be written by effective uid($>) or gid($))!");
1182             } else { # writable
1183 545         793 $self->_var('writable' => 1);
1184 545         502 $i_ok++;
1185             }
1186             }
1187             }
1188             }
1189             }
1190             }
1191              
1192 552         744 return $i_ok;
1193             }
1194              
1195             # Open the file
1196             #
1197             # my $i_ok = $o_dat->_open;
1198              
1199             sub _open {
1200 551     551   439 my $self = shift;
1201 551         612 my $file = $self->_var('filename');
1202 551         687 my $perm = $self->_var('permissions');
1203 551         431 my $i_ok = 0;
1204            
1205 551         594 my $open = "$perm $file";
1206 551         887 $self->_debug("using open($open)");
1207              
1208 551   100     2207 my $FH = FileHandle->new("$perm $file") || '';
1209 551         25799 my @file = ();
1210             # my $FH = tie(@file, 'Tie::File', $file) or '';
1211 551 100       890 if (!$FH) {
1212 13         77 $self->_error("Can't get handle($FH) for file($file) with permissions($perm)! $!");
1213             } else {
1214             # $FH = $self->_fh(\@file);
1215 538         899 $FH = $self->_fh($FH);
1216 538 50       760 if ($FH) {
1217 538         359 $i_ok++;
1218 538         760 $i_ok = $self->_lock(); # if $self->_writable;
1219             }
1220 538         1303 $self->_debug("FH($FH) => i_ok($i_ok)");
1221             }
1222              
1223 551         924 return $i_ok;
1224             };
1225              
1226             # Lock the file
1227             #
1228             # my $i_ok = $o_dat->_lock;
1229              
1230             sub _lock {
1231 538     538   468 my $self = shift;
1232 538         580 my $FH = $self->_fh;
1233 538         411 my $i_ok = 0;
1234              
1235 538 50       671 if ($FH) {
1236 538         688 my $file = $self->_var('filename');
1237 538 100       738 if ($self->_writable) {
1238             # if ($FH->flock(LOCK_EX | LOCK_NB)) {
1239 532 100       2266 if (flock($FH, LOCK_EX | LOCK_NB)) {
1240 530         545 $i_ok++;
1241             } else {
1242 2         8 $self->_error("Can't overlock file($file) handle($FH)!");
1243             }
1244             } else {
1245             # if ($FH->flock(LOCK_SH | LOCK_NB)) {
1246 6 50       33 if (flock($FH, LOCK_SH | LOCK_NB)) {
1247 6         7 $i_ok++;
1248             } else {
1249 0         0 $self->_error("Can't lock shared file($file) handle($FH)!");
1250             }
1251             }
1252             }
1253              
1254 538         705 return $i_ok;
1255             };
1256              
1257             # Unlock the file
1258             #
1259             # my $i_ok = $o_dat->_unlock;
1260              
1261             sub _unlock {
1262 0     0   0 my $self = shift;
1263 0         0 my $FH = $self->_fh;
1264 0         0 my $i_ok = 0;
1265              
1266 0 0       0 if ($FH) {
1267             # if (flock($FH, LOCK_UN)) { apparently there's a race, perl does it better - see close :) }
1268 0         0 $i_ok++;
1269             } else {
1270 0         0 my $file = $self->_var('filename');
1271 0         0 $self->_error("Can't unlock file($file) handle($FH)!");
1272             }
1273              
1274 0         0 return $i_ok;
1275             }
1276              
1277             # Close the filehandle
1278             #
1279             # my $i_ok = $o_dat->_close;
1280              
1281             sub _close {
1282 556     556   442 my $self = shift;
1283 556 100       631 my $FH = $self->_fh if $self->_var('filehandle');
1284 556         468 my $i_ok = 0;
1285              
1286 556 100       706 if ($FH) {
1287             # $FH->untie;
1288 538 50       1006 if ($FH->close) { # perl unlocks it better than we can (race)
1289 538         5540 $i_ok++;
1290             } else {
1291 0         0 $DB::single=2; #
1292 0         0 my $file = $self->_var('filename');
1293 0         0 $self->_error("Can't close file($file) handle($FH)!");
1294             }
1295             }
1296              
1297 556         3077 return $i_ok;
1298             }
1299              
1300             sub _writable {
1301 554     554   425 my $self = shift;
1302              
1303 554         568 my $i_ok = $self->_var('writable');
1304              
1305 554 100       828 if ($i_ok != 1) {
1306 10         12 my $file = $self->_var('filename');
1307 10         13 my $perms = $self->_var('permissions');
1308 10         24 $self->_debug("$file not writable($i_ok) with permissions($perms)");
1309             }
1310              
1311 554         733 return $i_ok;
1312             }
1313              
1314             =item do
1315              
1316             Simple wrapper for method calls, returning the content.
1317              
1318             my @inserted = $o_dat->do('insert', @this);
1319              
1320             my @appended = $o_dat->do('append', @this);
1321              
1322             An addendum to this method, and to make life generally easier, is that
1323             you can also call any of the above methods in uppercase, to call via
1324             B eg;
1325              
1326             my @data = $o_dat->WRITE($this)->APPEND->($that)->read;
1327              
1328             First argument is the method to call, followed by the arguments that
1329             method expects.
1330              
1331             perl -MFile::Data -e "print File::Data->new($file)->INSERT(3,
1332             \"third line\n\")->READ";
1333              
1334             If you want to get at the output of a particular called method see
1335             L
1336              
1337             =cut
1338              
1339             sub DO {
1340 6     6 0 6 my $self = shift;
1341 6         6 my $call = shift;
1342 6         7 my @res = ();
1343              
1344 6         9 $self->_enter('do');
1345 6 50       8 $self->_debug('in: '.Dumper([$call, @_])) if $File::Data::DEBUG;
1346              
1347 6 50       53 if ($call !~ /^($_METHODS)$/io) {
1348 0         0 $self->_error("unsupported method($call)");
1349             } else {
1350 6         9 $call = uc($call);
1351 6         10 $self->_var($call => []);
1352 6         11 my @res = $self->$call(@_);
1353 6 50       14 $self->_var($call => (ref($res[0]) ? $res[0] : \@res));
1354             }
1355              
1356 6 50       10 $self->_debug('out: $self') if $File::Data::DEBUG;
1357 6         8 $self->_leave('do');
1358              
1359 6         6 return @res;
1360             }
1361              
1362             sub do {
1363 6     6 1 5 my $self = shift;
1364              
1365 6         10 $self->DO(@_);
1366              
1367 6         22 return $self;
1368             }
1369              
1370             =back
1371              
1372             =cut
1373              
1374             # ================================================================
1375              
1376             sub DESTROY {
1377 556     556   2449 my $self = shift;
1378 556         733 $self->_close;
1379             }
1380              
1381             =head1 AUTHOR
1382              
1383             "Richard Foley"
1384              
1385             =cut
1386              
1387             1;
1388