File Coverage

blib/lib/Slackware/Slackget/File.pm
Criterion Covered Total %
statement 51 146 34.9
branch 20 84 23.8
condition 13 57 22.8
subroutine 10 25 40.0
pod 21 21 100.0
total 115 333 34.5


line stmt bran cond sub pod time code
1             package Slackware::Slackget::File;
2              
3 5     5   98637 use warnings;
  5         8  
  5         134  
4 5     5   17 use strict;
  5         4  
  5         7297  
5              
6             =head1 NAME
7              
8             Slackware::Slackget::File - A class to manage files.
9              
10             =head1 VERSION
11              
12             Version 1.0.4
13              
14             =cut
15              
16             our $VERSION = '1.0.4';
17              
18             =head1 SYNOPSIS
19              
20             Slackware::Slackget::File is the class which represent a file for slack-get.
21              
22             Access to hard disk are saved by taking a copy of the file in memory, so if you work on big file it may be a bad idea to use this module. Or maybe you have some interest to close the file while you don't work on it.
23              
24             use Slackware::Slackget::File;
25              
26             my $file = Slackware::Slackget::File->new('foo.txt'); # if foo.txt exist the constructor will call the Read() method
27             $file->add("an example\n");
28             $file->Write();
29             $file->Write("bar.txt"); # write foo.txt (plus the addition) into bar.txt
30             $file->Close(); # Free the memory !
31             $file->Read(); # But the Slackware::Slackget::File object is not destroy and you can re-load the file content
32             $file->Read("baz.txt"); # Or changing file (the object will be update with the new file)
33              
34             The main advantage of this module is that you don't work directly on the file but on a copy. So you can make errors, they won't be wrote until you call the Write() method
35              
36             ** ATTENTION ** this module can fail to load file on non-UNIX system because it rely on the "file" and "awk" command line tools. Be sure to use the 'load-raw' => 1 constructor's option on such operating system (most probably the file type will be blank and no problem will happen... but it's still a possibility).
37              
38             =cut
39              
40             sub new
41             {
42 4     4 1 187 my ($class,$file,%args) = @_ ;
43 4         10 my $self={%args};
44             # print "\nActual file-encoding: $self->{'file-encoding'}\nargs : $args{'file-encoding'}\nFile: $file\n";<STDIN>;
45 4         8 bless($self,$class);
46 4 50       23 $self->{'file-encoding'} = 'utf8' unless(defined($self->{'file-encoding'}));
47 4 100 66     81 if(defined($file) && -e $file && !defined($args{'load-raw'}))
      66        
48             {
49 2         7054 $self->{TYPE} = `LC_ALL=C file -b $file | awk '{print \$1}'`;
50 2         15 chomp $self->{TYPE};
51 2 50       25 $self->{TYPE} = 'ASCII' if($self->{TYPE} eq 'empty');
52 2 50 33     391 die "[Slackware::Slackget::File::constructor] unsupported file type \"$self->{TYPE}\" for file $file. Supported file type are gzip, bzip2, ASCII and XML\n" unless($self->{TYPE} eq 'gzip' || $self->{TYPE} eq 'bzip2' || $self->{TYPE} eq 'ASCII' || $self->{TYPE} eq 'XML' || $self->{TYPE} eq 'Quake') ;
      33        
      33        
      33        
53             }
54             # print "using $self->{'file-encoding'} as file-encoding for file $file\n";
55 2         5 $self->{FILENAME} = $file;
56 2 0 0     7 $self->{MODE} = $args{'mode'} if($args{'mode'} && ($args{'mode'} eq 'write' or $args{'mode'} eq 'append' or $args{'mode'} eq 'rewrite'));
      33        
57 2 50 33     10 $self->{MODE} = 'append' if(defined($self->{MODE}) && $self->{MODE} eq 'rewrite');
58 2         5 $self->{BINARY} = 0;
59 2 50       5 $self->{BINARY} = $args{'binary'} if($args{'binary'});
60 2 50       6 $self->{SKIP_WL} = $args{'skip-white-line'} if($args{'skip-white-line'});
61 2 50       5 $self->{SKIP_WL} = $args{'skip-white-lines'} if($args{'skip-white-lines'});
62 2         3 $self->{LOAD_RAW} = undef;
63 2 50       7 $self->{LOAD_RAW} = $args{'load-raw'} if($args{'load-raw'});
64 2 50 33     17 if(defined($file) && -e $file && !defined($self->{'no-auto-load'})){
      33        
65 0         0 $self->Read();
66             }
67             else
68             {
69 2         5 $self->{FILE} = [];
70             }
71 2         7 return $self;
72             }
73              
74             =head1 CONSTRUCTOR
75              
76             =head2 new
77              
78             Take a filename as argument.
79              
80             my $file = Slackware::Slackget::File->new('foo.txt'); # if foo.txt exist the constructor will call the Read() method
81             $file->add("an example\n");
82             $file->Write();
83             $file->Write("bar.txt");
84              
85             This class try to determine the type of the file via the command `file` (so you need `file` in your path). If the type of the file is not in gzip, bzip2, ASCII or XML the constructor die()-ed. You can avoid that, if you need to work with unsupported file, by passing a "load-raw" parameter.
86              
87             Additionnaly you can pass an file encoding (default is utf8). For example as a European I prefer that files are stored and compile in the iso-8859-1 charset so I use the following :
88              
89             my $file = Slackware::Slackget::File->new('foo.txt','file-encoding' => 'iso-8859-1');
90              
91             You can also disabling the auto load of the file by passing a parameter 'no-auto-load' => 1 :
92              
93             my $file = Slackware::Slackget::File->new('foo.txt','file-encoding' => 'iso-8859-1', 'no-auto-load' => 1);
94              
95             You can also pass an argument "mode" which take 'append or 'write' as value :
96              
97             my $file = Slackware::Slackget::File->new('foo.txt','file-encoding' => 'iso-8859-1', 'mode' => 'rewrite');
98              
99             This will decide how to open the file (> or >>). Default is 'write' ('>').
100              
101             Note: for backward compatibility mode => "rewrite" is still accepted as a valid mode. It is an alias for "append"
102              
103             You can also specify if the file must be open as binary or normal text with the "binary" argument. This one is boolean (0 or 1). The default value is 0 :
104              
105             my $file = Slackware::Slackget::File->new('package.tgz','binary' => 1); # In real usage package.tgz will be read UNCOMPRESSED by Read().
106             my $file = Slackware::Slackget::File->new('foo.txt','file-encoding' => 'iso-8859-1', 'mode' => 'rewrite', binary => 0);
107              
108             If you want to load a raw file without uncompressing it you can pass the "load-raw" parameter :
109              
110             my $file = Slackware::Slackget::File->new('package.tgz','binary' => 1, 'load-raw' => 1);
111              
112             =head1 FUNCTIONS
113              
114             =head2 Read
115              
116             Take a filename as argument, and load the file in memory.
117              
118             $file->Read($filename);
119              
120             You can call this method without passing parameters, if you have give a filename to the constructor.
121              
122             $file->Read();
123              
124             This method doesn't return the file, you must call Get_file() to do that.
125              
126             Supported file formats : gzipped, bzipped and ASCII file are natively supported (for compressed formats you need to have gzip and bzip2 installed in your path).
127              
128             If you specify load-raw => 1 to the constructor, read will load in memory a file even if the format is not recognize.
129              
130             =cut
131              
132             sub Read
133             {
134 0     0 1 0 my ($self,$file)=@_;
135 0 0       0 if($file)
136             {
137 0         0 $self->{FILENAME} = $file ;
138             }
139             else
140             {
141 0         0 $file = $self->{FILENAME};
142             }
143 0 0 0     0 unless ( -e $file or -R $file)
144             {
145 0         0 warn "[Slackware::Slackget::File] unable to read $file : $!\n";
146 0         0 return undef ;
147             }
148 0         0 my $tmp;
149 0         0 my @file = ();
150 0 0 0     0 if(defined($self->{TYPE}) && ($self->{TYPE} eq 'ASCII' || $self->{TYPE} eq 'XML' || $self->{TYPE} eq 'Quake') && !defined($self->{LOAD_RAW}))
    0 0        
    0 0        
    0 0        
      0        
      0        
151             {
152             # print "[DEBUG] [Slackware::Slackget::File] loading $file as 'plain text' file.";
153 0 0       0 if(open (F2,"<:encoding($self->{'file-encoding'})",$file))
154             {
155 0 0       0 binmode(F2) if($self->{'BINARY'}) ;
156 0 0       0 if($self->{SKIP_WL})
157             {
158 0         0 print "[Slackware::Slackget::File DEBUG] reading and skipping white lines\n";
159 0         0 while (defined($tmp=<F2>))
160             {
161 0 0       0 next if($tmp=~ /^\s*$/);
162 0         0 push @file,$tmp;
163             }
164             }
165             else
166             {
167 0         0 while (defined($tmp=<F2>))
168             {
169 0         0 push @file,$tmp;
170             }
171             }
172            
173 0         0 close (F2);
174 0         0 $self->{FILE} = \@file ;
175 0         0 return 1;
176             }
177             else
178             {
179 0         0 warn "[Slackware::Slackget::File] cannot open \"$file\" : $!\n";
180 0         0 return undef;
181             }
182             }
183             elsif($self->{TYPE} eq 'bzip2' && !defined($self->{LOAD_RAW}))
184             {
185             # print "[DEBUG] [Slackware::Slackget::File] loading $file as 'bzip2' file.";
186             # my $tmp_file = `bzip2 -dc $file`;
187 0         0 foreach (split(/\n/,`bzip2 -dc $file`))
188             {
189 0         0 push @file, "$_\n";
190             }
191 0         0 $self->{FILE} = \@file ;
192 0         0 return 1;
193             }
194             elsif($self->{TYPE} eq 'gzip' && !defined($self->{LOAD_RAW}))
195             {
196             # print "[DEBUG] [Slackware::Slackget::File] loading $file as 'gzip' file.";
197             # my $tmp_file = `gzip -dc $file`;
198 0         0 foreach (split(/\n/,`gzip -dc $file`))
199             {
200 0         0 push @file, "$_\n";
201             }
202 0         0 $self->{FILE} = \@file ;
203 0         0 return 1;
204             }
205             elsif(defined($self->{LOAD_RAW}) or $self->{TYPE} eq '')
206             {
207             # print "[DEBUG] [Slackware::Slackget::File] loading $file as 'raw' file.";
208 0 0       0 if(open(F2,$file))
209             {
210 0 0       0 binmode(F2) if($self->{'BINARY'}) ;
211 0         0 while (defined($tmp=<F2>))
212             {
213 0         0 push @file,$tmp;
214             }
215 0         0 close (F2);
216 0         0 $self->{FILE} = \@file ;
217 0         0 return 1;
218             }
219             else
220             {
221 0         0 warn "[Slackware::Slackget::File] cannot (raw) open \"$file\" : $!\n";
222 0         0 return undef;
223             }
224             }
225             else
226             {
227 0         0 die "[Slackware::Slackget::File] Read() method cannot load file \"$file\" in memory : \"$self->{TYPE}\" is an unsupported format.\n";
228             }
229              
230             }
231              
232             =head2 Lock_file (deprecated)
233              
234             Same as lock_file, provided for backward compatibility.
235              
236             =cut
237              
238             sub Lock_file {
239 0     0 1 0 return lock_file(@_);
240             }
241              
242             =head2 lock_file
243              
244             This method lock the file for slack-get application (not really for others...) by creating a file with the name of the current open file plus a ".lock". This is not a protection but an information system for slack-getd sub process. This method return undef if the lock can't be made.
245              
246             my $file = new Slackware::Slackget::File ('test.txt');
247             $file->lock_file ; # create a file test.txt.lock
248              
249             ATTENTION: You can only lock the current file of the object. With the previous example you can't do :
250              
251             $file->Lock_file('toto.txt') ;
252              
253             ATTENTION 2 : Don't forget to unlock your locked file :)
254              
255             =cut
256              
257             sub lock_file
258             {
259 0     0 1 0 my $self = shift;
260 0 0       0 return undef if $self->is_locked ;
261             # print "\t[DEBUG] ( Slackware::Slackget::File in Lock_file() ) locking file $self->{FILENAME} for $self\n";
262 0 0       0 Write({'file-encoding'=>$self->{'file-encoding'}},"$self->{FILENAME}.lock",$self) or return undef;
263 0         0 return 1;
264             }
265              
266             =head2 Unlock_file (deprecated)
267              
268             Same as unlock_file(), provided for backward compatibility.
269              
270             =cut
271              
272             sub Unlock_file {
273 0     0 1 0 return unlock_file(@_);
274             }
275              
276             =head2 unlock_file
277              
278             Unlock a locked file. Only the locker object can unlock a file ! Return 1 if all goes well, else return undef. Return 2 if the file was not locked. Return 0 (false in scalar context) if the file was locked but by another Slackware::Slackget::File object.
279              
280             my $status = $file->unlock_file ;
281              
282             Returned value are :
283              
284             0 : error -> the file was locked by another instance of this class
285            
286             1 : ok lock removed
287            
288             2 : the file was not locked
289            
290             undef : unable to remove the lock.
291              
292             =cut
293              
294             sub unlock_file
295             {
296 0     0 1 0 my $self = shift;
297 0 0       0 if($self->is_locked)
298             {
299 0 0       0 if($self->_verify_lock_maker)
300             {
301 0 0       0 unlink "$self->{FILENAME}.lock" or return undef ;
302             }
303             else
304             {
305 0         0 return 0;
306             }
307             }
308             else
309             {
310             # print "\t[DEBUG] ( Slackware::Slackget::File in Unlock_file() ) $self->{FILENAME} is not lock\n";
311 0         0 return 2;
312             }
313 0         0 return 1;
314             }
315              
316             sub _verify_lock_maker
317             {
318 0     0   0 my $self = shift;
319 0         0 my $file = new Slackware::Slackget::File ("$self->{FILENAME}.lock");
320 0         0 my $locker = $file->get_line(0) ;
321             # print "\t[DEBUG] ( Slackware::Slackget::File in _verify_lock_maker() ) locker of file \"$self->{FILENAME}\" is $locker and current object is $self\n";
322 0         0 $file->Close ;
323 0         0 undef($file);
324 0         0 my $object = ''.$self;
325             # print "[debug file] compare object=$object and locker=$locker\n";
326 0 0       0 if($locker eq $object)
327             {
328             # print "\t[DEBUG] ( Slackware::Slackget::File in _verify_lock_maker() ) locker access granted for file \"$self->{FILENAME}\"\n";
329 0         0 return 1;
330             }
331             else
332             {
333             # print "\t[DEBUG] ( Slackware::Slackget::File in _verify_lock_maker() ) locker access ungranted for file \"$self->{FILENAME}\"\n";
334 0         0 return undef;
335             }
336             }
337              
338             =head2 is_locked
339              
340             Return 1 if the file is locked by a Slackware::Slackget::File object, else return undef.
341              
342             print "File is locked\n" if($file->is_locked);
343              
344             =cut
345              
346             sub is_locked
347             {
348 0     0 1 0 my $self = shift;
349 0 0       0 return 1 if(-e $self->{FILENAME}.".lock");
350 0         0 return undef;
351             }
352              
353             =head2 Write
354              
355             Take a filename to write data and raw data
356              
357             $file->Write($filename,@data);
358              
359             You can call this method with just a filename (in this case the file currently loaded will be wrote in the file you specify)
360              
361             $file->Write($another_filename) ; # Write the currently loaded file into $another_filename
362              
363             You also can call this method without any parameter :
364              
365             $file->Write ;
366              
367             In this case, the Write() method will wrote data in memory into the last opened file (with Read() or new()).
368              
369             The default encoding of this method is utf-8, pass an extra argument : file-encoding to the constructor to change that.
370              
371             =cut
372              
373             sub Write
374             {
375 2     2 1 6 my ($self,$name,@data)=@_;
376 2 50       7 $name=$self->{FILENAME} unless($name);
377 2 50       7 @data = @{$self->{FILE}} unless(@data);
  2         5  
378             # if(open (FILE, ">$name"))
379             # print "using $self->{'file-encoding'} as file-encoding for writing\n";
380 2         3 my $mode = '>';
381 2 50 33     10 if(defined($self->{MODE}) && $self->{MODE} eq 'append')
382             {
383 0         0 $mode = '>>';
384             }
385 2 50   2   54 if(open (FILE, "$mode:encoding($self->{'file-encoding'})",$name))
  2         13  
  2         2  
  2         11  
386             {
387 2 50       16477 binmode(FILE) if($self->{'BINARY'}) ;
388             # NOTE: In the case you need to clear the white line of your file, their will be a if() test no each array slot
389             # This is really time consumming, so id you don't need this feature we just test once for all and gain a lot in performance.
390 2 50       7 if($self->{SKIP_WL})
391             {
392 0         0 print "[Slackware::Slackget::File DEBUG] mode 'skip-white-line' activate\n";
393 0         0 foreach (@data)
394             {
395 0         0 foreach my $tmp (split(/\n/,$_))
396             {
397 0 0       0 next if($tmp =~ /^\s*$/) ;
398 0         0 print FILE "$tmp\n" ;
399             }
400             }
401             }
402             else
403             {
404 2         7 foreach (@data)
405             {
406 2         14 print FILE $_;
407             }
408             }
409 2 50       109 close (FILE) or return(undef);
410             }
411             else
412             {
413 0         0 warn "[ Slackware::Slackget::File ] unable to write '$name' : $!\n";
414 0         0 return undef;
415             }
416 2         8 return 1;
417             }
418              
419             =head2 Add (deprecated)
420              
421             Same as add(), provided for backward compatibility.
422              
423             =cut
424              
425             sub Add {
426 1     1 1 603 return add(@_);
427             }
428              
429             =head2 add
430              
431             Take a table of lines and add them to the end of file image (in memory). You need to commit your change by calling the Write() method !
432              
433             $file->add(@data);
434             or
435             $file->add($data);
436             or
437             $file->add("this is some data\n");
438              
439             =cut
440              
441             sub add {
442 2     2 1 670 my ($self,@data) = @_;
443 2         4 $self->{FILE} = [@{$self->{FILE}},@data];
  2         8  
444             }
445              
446             =head2 Get_file (deprecated)
447              
448             Same as get_file(), provided for backward compatibility.
449              
450             =cut
451              
452             sub Get_file {
453 0     0 1 0 return get_file(@_);
454             }
455              
456             =head2 get_file
457              
458             Return the current file in memory as an array.
459              
460             @file = $file->get_file();
461              
462             =cut
463              
464             sub get_file{
465 0     0 1 0 my $self = shift;
466 0         0 return @{$self->{FILE}};
  0         0  
467             }
468              
469              
470             =head2 Get_line (deprecated)
471              
472             Same as get_line(), provided for backward compatibility.
473              
474             =cut
475              
476             sub Get_line {
477 1     1 1 5 return get_line(@_);
478             }
479              
480              
481             =head2 get_line
482              
483             Return the $index line of the file (the index start at 0).
484              
485             @file = $file->get_line($index);
486              
487             =cut
488              
489             sub get_line {
490 2     2 1 6 my ($self,$index) = @_;
491 2         8 return $self->{FILE}->[$index];
492             }
493              
494             =head2 Get_selection (deprecated)
495              
496             Same as get_selection(), provided for backward compatibility.
497              
498             =cut
499              
500             sub Get_selection {
501 0     0 1 0 return get_selection(@_);
502             }
503              
504             =head2 get_selection
505              
506             Same as get file but return only lines between $start and $stop.
507              
508             my @array = $file->get_selection($start,$stop);
509              
510             You can ommit the $stop parameter (in this case Get_line() return the lines from $start to the end of file)
511              
512             =cut
513              
514             sub get_selection {
515 0     0 1 0 my ($self,$start,$stop) = @_ ;
516 0 0       0 $start = 0 unless($start);
517 0 0       0 $stop = $#{$self->{FILE}} unless($stop);
  0         0  
518 0         0 return @{$self->{FILE}}[$start..$stop];
  0         0  
519             }
520              
521              
522             =head2 Close
523              
524             Free the memory. This method close the current file memory image. If you don't call the Write() method before closing, the changes you have made on the file are lost !
525              
526             $file->Close();
527              
528             =cut
529              
530             sub Close {
531 0     0 1 0 my $self = shift;
532 0         0 $self->{FILE} = [];
533 0         0 return 1;
534             }
535              
536              
537             =head2 Write_and_close (deprecated)
538              
539             Same as write_and_close(), provided for backward compatibility.
540              
541             =cut
542              
543             sub Write_and_close {
544 0     0 1 0 return write_and_close(@_);
545             }
546              
547             =head2 write_and_close
548              
549             An alias which call Write() and then Close();
550              
551             $file->write_and_close();
552             or
553             $file->write_and_close("foo.txt");
554              
555             =cut
556              
557             sub write_and_close{
558 0     0 1 0 my ($self,$file) = @_;
559 0         0 $self->Write($file);
560 0         0 $self->Close();
561             }
562              
563             =head2 encoding
564              
565             Without parameter return the current file encoding, with a parameter set the encoding for the current file.
566              
567             print "The current file encoding is ",$file->encoding,"\n"; # return the current encoding
568             $file->encoding('utf8'); # set the current file encoding to utf8
569              
570             =cut
571              
572             sub encoding
573             {
574 0 0   0 1 0 return $_[1] ? $_[0]->{'file-encoding'}=$_[1] : $_[0]->{'file-encoding'};
575             }
576              
577             =head2 filename
578              
579             Return the filename of the file which is currently process by the Slackware::Slackget::File instance.
580              
581             print $file->filename
582              
583             You can also set the filename :
584              
585             $file->filename('foo.txt');
586              
587             =cut
588              
589             sub filename
590             {
591 4 100   4 1 16 return $_[1] ? $_[0]->{FILENAME}=$_[1] : $_[0]->{FILENAME};
592             }
593              
594             =head1 AUTHOR
595              
596             DUPUIS Arnaud, C<< <a.dupuis@infinityperl.org> >>
597              
598             =head1 BUGS
599              
600             Please report any bugs or feature requests to
601             C<bug-Slackware-Slackget@rt.cpan.org>, or through the web interface at
602             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Slackware-Slackget>.
603             I will be notified, and then you'll automatically be notified of progress on
604             your bug as I make changes.
605              
606             =head1 SUPPORT
607              
608             You can find documentation for this module with the perldoc command.
609              
610             perldoc Slackware::Slackget::File
611              
612              
613             You can also look for information at:
614              
615             =over 4
616              
617             =item * Infinity Perl website
618              
619             L<http://www.infinityperl.org/category/slack-get>
620              
621             =item * slack-get specific website
622              
623             L<http://slackget.infinityperl.org>
624              
625             =item * RT: CPAN's request tracker
626              
627             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Slackware-Slackget>
628              
629             =item * AnnoCPAN: Annotated CPAN documentation
630              
631             L<http://annocpan.org/dist/Slackware-Slackget>
632              
633             =item * CPAN Ratings
634              
635             L<http://cpanratings.perl.org/d/Slackware-Slackget>
636              
637             =item * Search CPAN
638              
639             L<http://search.cpan.org/dist/Slackware-Slackget>
640              
641             =back
642              
643              
644             =head1 ACKNOWLEDGEMENTS
645              
646             Thanks to Bertrand Dupuis (yes my brother) for his contribution to the documentation.
647              
648             =head1 COPYRIGHT & LICENSE
649              
650             Copyright 2005 DUPUIS Arnaud, All Rights Reserved.
651              
652             This program is free software; you can redistribute it and/or modify it
653             under the same terms as Perl itself.
654              
655             =cut
656              
657             1; # End of Slackware::Slackget::File