File Coverage

blib/lib/Slackware/Slackget/File.pm
Criterion Covered Total %
statement 107 157 68.1
branch 49 92 53.2
condition 25 60 41.6
subroutine 18 26 69.2
pod 22 22 100.0
total 221 357 61.9


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