File Coverage

blib/lib/File/Spec/BaseParse.pm
Criterion Covered Total %
statement 12 112 10.7
branch 0 28 0.0
condition n/a
subroutine 4 30 13.3
pod 22 22 100.0
total 38 192 19.7


line stmt bran cond sub pod time code
1             #============================== BaseParse.pm =================================
2             # Filename: BaseParse.pm
3             # Description: Object to parse filenames and paths.
4             # Programmed by: Dale Amon
5             # Revised by: $Author: amon $
6             # Date: $Date: 2008-08-28 23:32:45 $
7             # Version: $Revision: 1.3 $
8             # License: LGPL 2.1, Perl Artistic or BDS
9             #
10             #=============================================================================
11 1     1   1408 use strict;
  1         2  
  1         37  
12 1     1   741 use Fault::DebugPrinter;
  1         464  
  1         23  
13 1     1   6 use File::Spec::Unix;
  1         6  
  1         38  
14              
15             package File::Spec::BaseParse;
16 1     1   5 use vars qw{@ISA};
  1         2  
  1         1329  
17             @ISA = qw( File::Spec::Unix );
18              
19             #=============================================================================
20             # Class Methods
21             #=============================================================================
22              
23             sub new {
24 0     0 1   my ($class, $file) = @_;
25 0           my $self = bless {}, $class;
26              
27 0 0         if (defined $file) {$self->splitpath ($file);}
  0            
  0            
28             else {$self->_init;}
29              
30 0           return $self;
31             }
32              
33             #=============================================================================
34             # Object Methods
35             #=============================================================================
36              
37             sub splitpath {
38 0     0 1   my ($self,$file) = (shift, shift);
39 0           $self->_init;
40              
41             # Find the basic system independant file spec parts
42 0           chomp $file;
43 0           $self->{'pathname'} = $file;
44 0           @$self{'volume','rootpath','filename'} =
45             $self->SUPER::splitpath ($file);
46 0           Fault::DebugPrinter->dbg
47             (4, "Beginning parse for File::Spec::BaseParse");
48            
49 0           my @segments = split ('/', $self->{'rootpath'});
50              
51             # If the path only has 1 element, ie the root /, we make the basepath
52             # empty and the directory "/" in hopes this will letother software
53             # work.
54             #
55 0 0         if ($#segments < 0) {
56 0           @$self{'basepath','directory'} = ("", ($self->{'rootpath'}));
57             }
58             else {
59 0           my $directory = pop @segments;
60 0           @$self{'basepath','directory'} =
61             ($self->canonpath (join('/', @segments)), $directory);
62             }
63              
64             # The filename is currently the tail part, so extensions will be
65             # parsed from it.
66             #
67 0           $self->{'tail'} = "filename";
68 0           $self->{'name'} = $self->_parse_extensions_from_tail;
69              
70 0           return (@$self{'volume','basepath','directory',
71 0           'name'},(@{$self->{'extensions'}}));
72             }
73              
74             #=============================================================================
75             # Set parts of pathname
76              
77 0     0 1   sub set_volume {my $s=shift; @$s{'volume', '_dirty'}=(shift,1); return $s;}
  0            
  0            
78 0     0 1   sub set_rootpath {my $s=shift; @$s{'rootpath','_dirty'}=(shift,1); return $s;}
  0            
  0            
79 0     0 1   sub set_filename {my $s=shift; @$s{'filename','_dirty'}=(shift,1); return $s;}
  0            
  0            
80              
81             #-----------------------------------------------------------------------------
82             # Set parts of rootpath.
83              
84 0     0 1   sub set_basepath {my $s=shift; @$s{'basepath','_dirty'}=(shift,1);
  0            
85 0           return $s;}
86 0     0 1   sub set_directory {my $s=shift; @$s{'directory','_dirty'}=(shift,1);
  0            
87 0           return $s;}
88              
89             #-----------------------------------------------------------------------------
90             # Set parts of filename.
91              
92 0     0 1   sub set_name {my $s=shift; @$s{'name', '_dirty'}=(shift,1);
  0            
93 0           return $s;}
94 0     0 1   sub set_extensions {my $s=shift; $s->{'_dirty'}=1; $s->{'extensions'}=[@_];
  0            
  0            
95 0           return $s;}
96              
97             #-----------------------------------------------------------------------------
98              
99             sub reset_filename {
100 0     0 1   my $self = shift;
101 0           my $filename = $self->{'name'};
102 0           foreach (@{$self->{'extensions'}}) {
  0            
103 0 0         $_ || next;
104 0           $filename .= "." . $_;
105             }
106 0 0         return $self->{'filename'} = ($filename) ? $filename : undef;
107             }
108              
109             #-----------------------------------------------------------------------------
110              
111             sub reset_rootpath {
112 0     0 1   my $self = shift;
113 0           my ($rootpath,$del) = ("","");
114 0           foreach (@$self{'basepath','directory'}) {
115 0 0         $_ || next;
116 0           $rootpath .= "$del$_"; $del = "/";
  0            
117             }
118 0 0         return $self->{'rootpath'} = ($rootpath) ? $rootpath : undef;
119             }
120              
121             #-----------------------------------------------------------------------------
122              
123             sub reset_pathname {
124 0     0 1   my $self = shift;
125 0           my ($pathname,$del) = ("","");
126 0           foreach (@$self{'volume','rootpath','filename',}) {
127 0 0         $_ || next;
128 0           $pathname .= "$del$_"; $del = "/";
  0            
129             }
130 0 0         return $self->{'pathname'} =
131             ($pathname) ? $self->canonpath($pathname) : undef;
132             }
133              
134             #-----------------------------------------------------------------------------
135             # Reparse pathname from scratch after reset's.
136              
137 0     0 1   sub reparse {my $s=shift; return $s->splitpath ($s->{'pathname'});}
  0            
138              
139             #=============================================================================
140              
141 0     0 1   sub pathname {return shift->{'pathname'};}
142 0     0 1   sub volume {return shift->{'volume' };}
143 0     0 1   sub rootpath {return shift->{'rootpath'};}
144 0     0 1   sub basepath {return shift->{'basepath'};}
145 0     0 1   sub directory {return shift->{'directory'};}
146 0     0 1   sub filename {return shift->{'filename'};}
147 0     0 1   sub name {return shift->{'name'};}
148 0     0 1   sub extension {return shift->{'extension'};}
149              
150             #-----------------------------------------------------------------------------
151             # Language porting note: this is implicitly function overloading.
152              
153 0           sub extensions {return (wantarray) ?
154 0           @{shift->{'extensions'}} :
155 0 0   0 1   join ".", @{shift->{'extensions'}};}
156              
157             #=============================================================================
158             # INTERNAL: Object Methods
159             #=============================================================================
160              
161             sub _init {
162 0     0     my $self = shift;
163 0           @$self{'pathname','volume','rootpath','basepath','directory',
164             'filename',,'name','extension','extensions',
165             'fmterr','_dirty'} =
166             ( undef,undef,undef,undef,undef,undef,undef,undef,undef,undef,0);
167 0           return $self;
168             }
169              
170             #-----------------------------------------------------------------------------
171              
172 0     0     sub _err {my $self = shift; $self->{'fmterr'} = shift; return $self;}
  0            
  0            
173              
174             #-----------------------------------------------------------------------------
175              
176             sub _parse_extensions_from_tail {
177 0     0     my $self = shift;
178 0           my ($left_lexeme, $tail_lexeme, @extensions);
179 0           @$self{'extension','extensions'} = (undef,[]);
180              
181 0 0         defined $self->{'tail'} || return undef;
182              
183 0           $tail_lexeme = $self->{$self->{'tail'}};
184 0 0         defined $tail_lexeme || return undef;
185              
186 0           ($left_lexeme, @extensions) = split ('\.', $tail_lexeme);
187 0 0         if ($#extensions > -1) {
188 0           $self->{'extension'} = lc ($extensions[$#extensions]);
189 0           $self->{'extensions'} = [@extensions];
190             }
191 0           return $left_lexeme;
192             }
193              
194             #-----------------------------------------------------------------------------
195              
196             sub _append_extensions_to_tail {
197 0     0     my $self = shift;
198 0           my $exts = "." . $self->extensions;
199              
200             # Return the extensions even if there is no tail defined or if it is empty.
201             #
202 0 0         defined $self->{'tail'} || return $exts;
203 0           my $tail_lexeme = $self->{$self->{'tail'}};
204 0 0         defined $tail_lexeme || return $exts;
205            
206 0           return $tail_lexeme . $exts;
207             }
208            
209             #=============================================================================
210             # Pod Documentation
211             #=============================================================================
212             # You may extract and format the documention section with the 'perldoc' cmd.
213              
214             =head1 NAME
215              
216             File::Spec::BaseParse - Parse a basic file name spec in a system independant way.
217              
218             =head1 SYNOPSIS
219              
220             use File::Spec::BaseParse;
221              
222             $obj = File::Spec::BaseParse->new ($pathname);
223             $obj = File::Spec::BaseParse->new;
224              
225             ($volume, $basepath, $directory, $name, @extensions) = $obj->splitpath ($filepath);
226              
227             $pathname = $obj->pathname;
228             $volume = $obj->volume;
229             $rootpath = $obj->rootpath;
230             $basepath = $obj->basepath;
231             $directory = $obj->directory;
232             $filename = $obj->filename;
233             $name = $obj->name;
234             @extensions = $obj->extensions;
235             $extensions = $obj->extensions;
236             $extension = $obj->extension;
237             $obj = $obj->set_volume ($volume);
238             $obj = $obj->set_rootpath ($rootpath);
239             $obj = $obj->set_filename ($filename);
240             $obj = $obj->set_basepath ($basepath);
241             $obj = $obj->set_directory ($directory);
242             $obj = $obj->set_name ($name);
243             $obj = $obj->set_extensions (@extensions);
244             $filename = $obj->reset_filename;
245             $pathname = $obj->reset_pathname;
246             $rootpath = $obj->reset_rootpath;
247             ($volume, $basepath, $directory, $filename) = $obj->reparse;
248              
249             =head1 Inheritance
250              
251             UNIVERSAL
252             File::Spec::Unix
253             File::Spec::BaseParse
254              
255             =head1 Description
256              
257             Split a file pathname into (mostly) system independent parts via the parent
258             class File::Spec::Unix. The resultant rootpath is additionally split into a
259             basepath and directory, and the filename into name and extensions.
260              
261             For example, /my/base/Cards/19901225-XMAS-Title-Subtitle-note.tar.gz would
262             be split up as:
263              
264             volume: undef
265             basepath: /my/base
266             directory: Cards
267             filename: 19901225-XMAS-Title-Subtitle-note.tar.gz
268             name: 19901225-XMAS-Title-Subtitle-note
269             extensions: gz
270             extensions: tar gz
271              
272             At the moment the directory is split in a non-system -independent way.
273              
274             [Not doing much with _dirty flag yet. Set it in all set's, clear it on _init.
275             Doesn't matter on resets because they won't make any changes unless the ivars
276             they used were changed, in which case it was touched already. Could do lazy
277             evaluation if I chose to. Then I could dump all the reset and reparse methods
278             right down through all the child classes.]
279              
280             [Should initialization of unused fields default to undef as it does now, or
281             should it be null strings, ""? The undef's seem to be working well enough,
282             but it is worth reconsidering this point.]
283              
284             =head1 Examples
285              
286             use File::Spec::BaseParse;
287             my $baz = File::Spec::BaseParse->new;
288             my @list = $baz->splitpath
289             ("/my/base/Cards/19901225-XMAS-Title-Subtitle-note.tar.gz");
290              
291             my $foo = File::Spec::BaseParse->new
292             ("/my/base/Cards/19901225-XMAS-Title-Subtitle-note.tar.gz");
293              
294             my $pathname = $foo->pathname;
295             my $volume = $foo->volume;
296             my $rootpath = $foo->rootpath;
297             my $basepath = $foo->basepath;
298             my $directory = $foo->directory;
299             my $filename = $foo->filename;
300              
301             $foo->set_volume ("C:/");
302             $foo->set_rootpath ("/root/Cards/" );
303             $foo->set_filename ("/my/base/Cards/19901225-XMAS-Title-Subtitle-note.ps")
304             my $path = $foo->reset_pathname;
305             my @parts = $foo->reparse;
306              
307             $foo->set_basepath ("/my/base");
308             $foo->set_directory ("Cards");
309             my $rootpath = $foo->reset_rootpath;
310             my $path = $foo->reset_pathname;
311             my @parts = $foo->reparse;
312              
313             $foo->set_name ("SomethingSimpler");
314             $foo->set_extensions ("tar", gz);
315             my $filename = $foo->reset_filename;
316             my $rootpath = $foo->reset_rootpath;
317             my $path = $foo->reset_pathname;
318             my @parts = $foo->reparse;
319              
320             =head1 Class Variables
321              
322             None.
323              
324             =head1 Instance Variables
325              
326             pathname Unmodified version of the pathname string.
327             volume Volume name string as returned by parent class.
328             rootpath Path string as returned by the parent class. We split it into
329             basepath and directory.
330             basepath rootpath string less the rightmost subdirectory. It may be a
331             null string if rootpath is / because the / will be assigned to
332             directory.
333             directory The rightmost subdirectory of rootpath.
334             filename The filename string as returned by the parent class.
335             name The portion of the filename left of the first dot.
336             extensions A list of dot separated elements right of the first dot.
337             extension The rightmost element of the list of extensions.
338             tail The ivar name of the parsed item containing the rightmost
339             portion of the original name.
340              
341             =head1 Class Methods
342              
343             =over 4
344              
345             =item B<$obj = File::Spec::BaseParse-Enew ($pathname)>
346              
347             Create a new object for $pathname. Returns the new object. Might someday
348             return undef on failure... but just now I can't think of anything that is
349             illegal as a Unix filename so it doesn't fail yet.
350              
351             =item B<$obj = File::Spec::BaseParse-Enew>
352              
353             Create a new object with an undef pathname. Use this when the need is for
354             an object to act as a generic filepath name parser / syntax checker.
355              
356             =back 4
357              
358             =head1 Instance Methods
359              
360             =over 4
361              
362             =item B<$basepath = $obj-Ebasepath>
363              
364             Return the base path string.
365              
366             =item B<$directory = $obj-Edirectory>
367              
368             Return the directory string.
369              
370             =item B<$extension = $obj-Eextension>
371              
372             Return the rightmost extension or undef if none.
373              
374             =item B<@extensions = $obj-Eextensions>
375              
376             =item B<$extensions = $obj-Eextensions>
377              
378             Return the extensions as a list in array context ("tar","gz") or as a string
379             in a scalar context ("tar.gz"). undef if there are no extensions.
380              
381             =item B<$filename = $obj-Efilename>
382              
383             Return the filename string.
384              
385             =item B<$name = $obj-Ename>
386              
387             Return the name string, the portion of a filename left of the first dot.
388              
389             =item B<$pathname = $obj-Epathname>
390              
391             Return the original, full path name string.
392              
393             =item B<$filename = $obj-Ereset_filename>
394              
395             Regenerate filename from parts:
396              
397             name + extensions -> filename
398              
399             =item B<$pathname = $obj-Ereset_pathname >
400              
401             Regenerate pathname from parts:
402              
403             volume + rootpath + filename -> pathname
404              
405             =item B<($volume, $basepath, $directory, $filename) = $obj-Ereparse >
406              
407             Reparse the full pathname. Does a splitpath on the current contents of the
408             pathname ivar. Use this method after a a group of set and reset commands to
409             confirm the modified filepath is valid. Returns the same values as
410             splitpath.
411              
412             =item B<$rootpath = $obj-Ereset_rootpath >
413              
414             Regenerate rootpath from parts:
415              
416             basepath + directory -> rootpath
417              
418             =item B<$rootpath = $obj-Erootpath>
419              
420             Return the root path string.
421              
422             =item B<$obj = $obj-Eset_basepath ($basepath)>
423              
424             Unconditionally set the basepath ivar.
425              
426             =item B<$obj = $obj-Eset_directory ($directory)>
427              
428             Unconditionally set the directory ivar.
429              
430             =item B<$obj = $obj-Eset_extensions (@extensions)>
431              
432             Unconditionally set the extensions list of the filename.
433              
434             =item B<$obj = $obj-Eset_filename ($filename)>
435              
436             Unconditionally set the filename ivar.
437              
438             =item B<$obj = $obj-Eset_name ($name)>
439              
440             Unconditionally set the body of the name.
441              
442             =item B<$obj = $obj-Eset_rootpath ($rootpath)>
443              
444             Unconditionally set the rootpath ivar.
445              
446             =item B<$obj = $obj-Eset_volume ($volume)>
447              
448             Unconditionally set the volume ivar.
449              
450             =item B<($volume, $basepath, $directory, $name, @extensions) = $obj-Esplitpath ($filepath)>
451              
452             Returns all the elements of the pathname as a list. Undef or blank $filepaths
453             are allowed and leave the object in the init state. Completely reinitializes
454             the object for the name $filepath. Would return scalar undef on failure if I
455             could think of anything that could fail...
456              
457             =item B<$volume = $obj-Evolume>
458              
459             Return the volume name string.
460              
461             =back 4
462              
463             =head1 Private Class Methods
464              
465             None.
466              
467             =head1 Private Instance Methods
468              
469             =over 4
470              
471             =item B<$obj= $obj-E_init>
472              
473             Internal initializer.
474              
475             This method is for the subclass initializer chaining and should not be used
476             otherwise.
477              
478             =item B<$obj = $obj-E_append_extensions_to_tail>
479              
480             Internal parse helper function. Examines 'tail' ivar to see if it is defined;
481             if it is, the contents are used as the name of a second ivar. That ivar
482             should contain the rightmost portion of the original filename. The extensions
483             are appended to that and returned as the value.
484              
485             It sets all fields to undef or zero as appropriate. This ensures all
486             required fields exist, even if we do not store to them later.
487              
488             This method is for the subclass convenience and should not be used otherwise.
489              
490             Subclasses use this method internally, but it is not intended for use
491             outside of the family as it were.
492              
493             =item B<$obj = $obj-E_err ($msg)>
494              
495             Internal error handling. Doesn't print format error problems at the point of
496             occurence. We also do not want to die or log at that point. So we just save
497             it until we are ready to deal with it.
498              
499             This method is for the subclass convenience and should not be used otherwise.
500              
501             Subclasses use this method internally, but it is not intended for use outside
502             of the family as it were.
503              
504             =item B<$tailext = $obj-E_parse_extensions_from_tail>
505              
506             Break a string up into dot delimited lexemes. The leftmost string is
507             returned as value and assumed to not be an extension; the rest of the string
508             is stored as 'extensions'; the rightmost extension is stored as 'extension'.
509             If there are no extensions, both are undef.
510              
511             'tail' will always be set to the name of the rightmost syntactic entity
512             found thus far. Extensions will thus be parsed off the last, smallest,
513             rightmost entity of which we are aware.
514              
515             Returns the portion of tail string left of first dot or the entire string if
516             no dots. It will be an empty string if dot is the first character.
517              
518             =back 4
519              
520             =head1 KNOWN BUGS
521              
522             See TODO.
523              
524             =head1 SEE ALSO
525              
526             File::Spec::Unix, Fault::DebugPrinter
527              
528             =head1 AUTHOR
529              
530             Dale Amon
531              
532             =cut
533            
534             #=============================================================================
535             # CVS HISTORY
536             #=============================================================================
537             # $Log: BaseParse.pm,v $
538             # Revision 1.3 2008-08-28 23:32:45 amon
539             # perldoc section regularization.
540             #
541             # Revision 1.2 2008-08-16 17:49:06 amon
542             # Update source format, documentation; switch to Fault package
543             #
544             # Revision 1.1.1.1 2004-08-29 16:02:48 amon
545             # File Spec extensions for doc name formats.
546             #
547             # 20040820 Dale Amon
548             # Changed File::Spec:ArchiveBase to File::Spec::BaseParse.
549             # Changed 'category' to the more general 'directory', as
550             # it really is just the leftmost directory; 'basepath' is
551             # the rest of the path before that directory.
552             #
553             # 20040815 Dale Amon
554             # Changed File::Spec:Archivist to File::Spec::ArchiveBase
555             #
556             # 20021208 Dale Amon
557             # Hacked it apart into a Class hierarchy.
558             #
559             # 20021121 Dale Amon
560             # Created FileSpecArchive.
561             #
562             1;