Once again syncing after too long an absence
[p5sagit/p5-mst-13.2.git] / lib / File / Spec / Epoc.pm
CommitLineData
0e06870b 1package File::Spec::Epoc;
2
3use strict;
4use Cwd;
5use vars qw(@ISA);
6require File::Spec::Unix;
7@ISA = qw(File::Spec::Unix);
8
9=head1 NAME
10
11File::Spec::Epoc - methods for Epoc file specs
12
13=head1 SYNOPSIS
14
15 require File::Spec::Epoc; # Done internally by File::Spec if needed
16
17=head1 DESCRIPTION
18
19See File::Spec::Unix for a documentation of the methods provided
20there. This package overrides the implementation of these methods, not
21the semantics.
22
23This package is still work in progress ;-)
24o.flebbe@gmx.de
25
26
27=over
28
29=item devnull
30
31Returns a string representation of the null device.
32
33=cut
34
35sub devnull {
36 return "nul:";
37}
38
39=item tmpdir
40
41Returns a string representation of a temporay directory:
42
43=cut
44
45my $tmpdir;
46sub tmpdir {
47 return "C:/System/temp";
48}
49
50sub case_tolerant {
51 return 1;
52}
53
54sub file_name_is_absolute {
55 my ($self,$file) = @_;
56 return scalar($file =~ m{^([a-z?]:)?[\\/]}is);
57}
58
59=item path
60
61Takes no argument, returns the environment variable PATH as an array. Since
62there is no search path supported, it returns undef, sorry.
63
64=cut
65sub path {
66 return undef;
67}
68
69=item canonpath
70
71No physical check on the filesystem, but a logical cleanup of a
72path. On UNIX eliminated successive slashes and successive "/.".
73
74=cut
75
76sub canonpath {
77 my ($self,$path) = @_;
78 $path =~ s/^([a-z]:)/\u$1/s;
79
80 $path =~ s|/+|/|g unless($^O eq 'cygwin'); # xx////xx -> xx/xx
81 $path =~ s|(/\.)+/|/|g; # xx/././xx -> xx/xx
82 $path =~ s|^(\./)+||s unless $path eq "./"; # ./xx -> xx
83 $path =~ s|^/(\.\./)+|/|s; # /../../xx -> xx
84 $path =~ s|/\z|| unless $path eq "/"; # xx/ -> xx
85 return $path;
86}
87
88=item splitpath
89
90 ($volume,$directories,$file) = File::Spec->splitpath( $path );
91 ($volume,$directories,$file) = File::Spec->splitpath( $path, $no_file );
92
93Splits a path in to volume, directory, and filename portions. Assumes that
94the last file is a path unless the path ends in '\\', '\\.', '\\..'
95or $no_file is true. On Win32 this means that $no_file true makes this return
96( $volume, $path, undef ).
97
98Separators accepted are \ and /.
99
100The results can be passed to L</catpath> to get back a path equivalent to
101(usually identical to) the original path.
102
103=cut
104
105sub splitpath {
106 my ($self,$path, $nofile) = @_;
107 my ($volume,$directory,$file) = ('','','');
108 if ( $nofile ) {
109 $path =~
110 m{^( (?:[a-zA-Z?]:|(?:\\\\|//)[^\\/]+[\\/][^\\/]+)? )
111 (.*)
112 }xs;
113 $volume = $1;
114 $directory = $2;
115 }
116 else {
117 $path =~
118 m{^ ( (?: [a-zA-Z?]: |
119 (?:\\\\|//)[^\\/]+[\\/][^\\/]+
120 )?
121 )
122 ( (?:.*[\\\\/](?:\.\.?\z)?)? )
123 (.*)
124 }xs;
125 $volume = $1;
126 $directory = $2;
127 $file = $3;
128 }
129
130 return ($volume,$directory,$file);
131}
132
133
134=item splitdir
135
136The opposite of L</catdir()>.
137
138 @dirs = File::Spec->splitdir( $directories );
139
140$directories must be only the directory portion of the path on systems
141that have the concept of a volume or that have path syntax that differentiates
142files from directories.
143
144Unlike just splitting the directories on the separator, leading empty and
145trailing directory entries can be returned, because these are significant
146on some OSs. So,
147
148 File::Spec->splitdir( "/a/b/c" );
149
150Yields:
151
152 ( '', 'a', 'b', '', 'c', '' )
153
154=cut
155
156sub splitdir {
157 my ($self,$directories) = @_ ;
158 #
159 # split() likes to forget about trailing null fields, so here we
160 # check to be sure that there will not be any before handling the
161 # simple case.
162 #
163 if ( $directories !~ m|[\\/]\z| ) {
164 return split( m|[\\/]|, $directories );
165 }
166 else {
167 #
168 # since there was a trailing separator, add a file name to the end,
169 # then do the split, then replace it with ''.
170 #
171 my( @directories )= split( m|[\\/]|, "${directories}dummy" ) ;
172 $directories[ $#directories ]= '' ;
173 return @directories ;
174 }
175}
176
177
178=item catpath
179
180Takes volume, directory and file portions and returns an entire path. Under
181Unix, $volume is ignored, and this is just like catfile(). On other OSs,
182the $volume become significant.
183
184=cut
185
186sub catpath {
187 my ($self,$volume,$directory,$file) = @_;
188
189 # If it's UNC, make sure the glue separator is there, reusing
190 # whatever separator is first in the $volume
191 $volume .= $1
192 if ( $volume =~ m@^([\\/])[\\/][^\\/]+[\\/][^\\/]+\z@s &&
193 $directory =~ m@^[^\\/]@s
194 ) ;
195
196 $volume .= $directory ;
197
198 # If the volume is not just A:, make sure the glue separator is
199 # there, reusing whatever separator is first in the $volume if possible.
200 if ( $volume !~ m@^[a-zA-Z]:\z@s &&
201 $volume =~ m@[^\\/]\z@ &&
202 $file =~ m@[^\\/]@
203 ) {
204 $volume =~ m@([\\/])@ ;
205 my $sep = $1 ? $1 : '\\' ;
206 $volume .= $sep ;
207 }
208
209 $volume .= $file ;
210
211 return $volume ;
212}
213
214
215=item abs2rel
216
217Takes a destination path and an optional base path returns a relative path
218from the base path to the destination path:
219
220 $rel_path = File::Spec->abs2rel( $destination ) ;
221 $rel_path = File::Spec->abs2rel( $destination, $base ) ;
222
223If $base is not present or '', then L</cwd()> is used. If $base is relative,
224then it is converted to absolute form using L</rel2abs()>. This means that it
225is taken to be relative to L<cwd()>.
226
227On systems with the concept of a volume, this assumes that both paths
228are on the $destination volume, and ignores the $base volume.
229
230On systems that have a grammar that indicates filenames, this ignores the
231$base filename as well. Otherwise all path components are assumed to be
232directories.
233
234If $path is relative, it is converted to absolute form using L</rel2abs()>.
235This means that it is taken to be relative to L</cwd()>.
236
237Based on code written by Shigio Yamaguchi.
238
239No checks against the filesystem are made.
240
241=cut
242
243sub abs2rel {
244 my($self,$path,$base) = @_;
245
246 # Clean up $path
247 if ( ! $self->file_name_is_absolute( $path ) ) {
248 $path = $self->rel2abs( $path ) ;
249 }
250 else {
251 $path = $self->canonpath( $path ) ;
252 }
253
254 # Figure out the effective $base and clean it up.
255 if ( ! $self->file_name_is_absolute( $base ) ) {
256 $base = $self->rel2abs( $base ) ;
257 }
258 elsif ( !defined( $base ) || $base eq '' ) {
259 $base = cwd() ;
260 }
261 else {
262 $base = $self->canonpath( $base ) ;
263 }
264
265 # Split up paths
266 my ( $path_volume, $path_directories, $path_file ) =
267 $self->splitpath( $path, 1 ) ;
268
269 my ( undef, $base_directories, undef ) =
270 $self->splitpath( $base, 1 ) ;
271
272 # Now, remove all leading components that are the same
273 my @pathchunks = $self->splitdir( $path_directories );
274 my @basechunks = $self->splitdir( $base_directories );
275
276 while ( @pathchunks &&
277 @basechunks &&
278 lc( $pathchunks[0] ) eq lc( $basechunks[0] )
279 ) {
280 shift @pathchunks ;
281 shift @basechunks ;
282 }
283
284 # No need to catdir, we know these are well formed.
285 $path_directories = CORE::join( '\\', @pathchunks );
286 $base_directories = CORE::join( '\\', @basechunks );
287
288 # $base_directories now contains the directories the resulting relative
289 # path must ascend out of before it can descend to $path_directory. So,
290 # replace all names with $parentDir
291
292 #FA Need to replace between backslashes...
293 $base_directories =~ s|[^\\]+|..|g ;
294
295 # Glue the two together, using a separator if necessary, and preventing an
296 # empty result.
297
298 #FA Must check that new directories are not empty.
299 if ( $path_directories ne '' && $base_directories ne '' ) {
300 $path_directories = "$base_directories\\$path_directories" ;
301 } else {
302 $path_directories = "$base_directories$path_directories" ;
303 }
304
305 # It makes no sense to add a relative path to a UNC volume
306 $path_volume = '' unless $path_volume =~ m{^[A-Z]:}is ;
307
308 return $self->canonpath(
309 $self->catpath($path_volume, $path_directories, $path_file )
310 ) ;
311}
312
313=item rel2abs
314
315Converts a relative path to an absolute path.
316
317 $abs_path = File::Spec->rel2abs( $destination ) ;
318 $abs_path = File::Spec->rel2abs( $destination, $base ) ;
319
320If $base is not present or '', then L<cwd()> is used. If $base is relative,
321then it is converted to absolute form using L</rel2abs()>. This means that it
322is taken to be relative to L</cwd()>.
323
324Assumes that both paths are on the $base volume, and ignores the
325$destination volume.
326
327On systems that have a grammar that indicates filenames, this ignores the
328$base filename as well. Otherwise all path components are assumed to be
329directories.
330
331If $path is absolute, it is cleaned up and returned using L</canonpath()>.
332
333Based on code written by Shigio Yamaguchi.
334
335No checks against the filesystem are made.
336
337=cut
338
339sub rel2abs($;$;) {
340 my ($self,$path,$base ) = @_;
341
342 if ( ! $self->file_name_is_absolute( $path ) ) {
343
344 if ( !defined( $base ) || $base eq '' ) {
345 $base = cwd() ;
346 }
347 elsif ( ! $self->file_name_is_absolute( $base ) ) {
348 $base = $self->rel2abs( $base ) ;
349 }
350 else {
351 $base = $self->canonpath( $base ) ;
352 }
353
354 my ( undef, $path_directories, $path_file ) =
355 $self->splitpath( $path, 1 ) ;
356
357 my ( $base_volume, $base_directories, undef ) =
358 $self->splitpath( $base, 1 ) ;
359
360 $path = $self->catpath(
361 $base_volume,
362 $self->catdir( $base_directories, $path_directories ),
363 $path_file
364 ) ;
365 }
366
367 return $self->canonpath( $path ) ;
368}
369
370=back
371
372=head1 SEE ALSO
373
374L<File::Spec>
375
376=cut
377
3781;