File Coverage

lib/File/Data.pm
Criterion Covered Total %
statement 359 433 82.9
branch 124 218 56.8
condition 23 52 44.2
subroutine 39 47 82.9
pod 5 15 33.3
total 550 765 71.9


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