1package File::Spec::OS2; 2 3use strict; 4use vars qw(@ISA $VERSION); 5require File::Spec::Unix; 6 7$VERSION = '1.2'; 8 9@ISA = qw(File::Spec::Unix); 10 11sub devnull { 12 return "/dev/nul"; 13} 14 15sub case_tolerant { 16 return 1; 17} 18 19sub file_name_is_absolute { 20 my ($self,$file) = @_; 21 return scalar($file =~ m{^([a-z]:)?[\\/]}is); 22} 23 24sub path { 25 my $path = $ENV{PATH}; 26 $path =~ s:\\:/:g; 27 my @path = split(';',$path); 28 foreach (@path) { $_ = '.' if $_ eq '' } 29 return @path; 30} 31 32sub _cwd { 33 # In OS/2 the "require Cwd" is unnecessary bloat. 34 return Cwd::sys_cwd(); 35} 36 37my $tmpdir; 38sub tmpdir { 39 return $tmpdir if defined $tmpdir; 40 my $self = shift; 41 $tmpdir = $self->_tmpdir( @ENV{qw(TMPDIR TEMP TMP)}, 42 '/tmp', 43 '/' ); 44} 45 46sub catdir { 47 my $self = shift; 48 my @args = @_; 49 foreach (@args) { 50 tr[\\][/]; 51 # append a backslash to each argument unless it has one there 52 $_ .= "/" unless m{/$}; 53 } 54 return $self->canonpath(join('', @args)); 55} 56 57sub canonpath { 58 my ($self,$path) = @_; 59 $path =~ s/^([a-z]:)/\l$1/s; 60 $path =~ s|\\|/|g; 61 $path =~ s|([^/])/+|$1/|g; # xx////xx -> xx/xx 62 $path =~ s|(/\.)+/|/|g; # xx/././xx -> xx/xx 63 $path =~ s|^(\./)+(?=[^/])||s; # ./xx -> xx 64 $path =~ s|/\Z(?!\n)|| 65 unless $path =~ m#^([a-z]:)?/\Z(?!\n)#si;# xx/ -> xx 66 $path =~ s{^/\.\.$}{/}; # /.. -> / 67 1 while $path =~ s{^/\.\.}{}; # /../xx -> /xx 68 return $path; 69} 70 71 72sub splitpath { 73 my ($self,$path, $nofile) = @_; 74 my ($volume,$directory,$file) = ('','',''); 75 if ( $nofile ) { 76 $path =~ 77 m{^( (?:[a-zA-Z]:|(?:\\\\|//)[^\\/]+[\\/][^\\/]+)? ) 78 (.*) 79 }xs; 80 $volume = $1; 81 $directory = $2; 82 } 83 else { 84 $path =~ 85 m{^ ( (?: [a-zA-Z]: | 86 (?:\\\\|//)[^\\/]+[\\/][^\\/]+ 87 )? 88 ) 89 ( (?:.*[\\\\/](?:\.\.?\Z(?!\n))?)? ) 90 (.*) 91 }xs; 92 $volume = $1; 93 $directory = $2; 94 $file = $3; 95 } 96 97 return ($volume,$directory,$file); 98} 99 100 101sub splitdir { 102 my ($self,$directories) = @_ ; 103 split m|[\\/]|, $directories, -1; 104} 105 106 107sub catpath { 108 my ($self,$volume,$directory,$file) = @_; 109 110 # If it's UNC, make sure the glue separator is there, reusing 111 # whatever separator is first in the $volume 112 $volume .= $1 113 if ( $volume =~ m@^([\\/])[\\/][^\\/]+[\\/][^\\/]+\Z(?!\n)@s && 114 $directory =~ m@^[^\\/]@s 115 ) ; 116 117 $volume .= $directory ; 118 119 # If the volume is not just A:, make sure the glue separator is 120 # there, reusing whatever separator is first in the $volume if possible. 121 if ( $volume !~ m@^[a-zA-Z]:\Z(?!\n)@s && 122 $volume =~ m@[^\\/]\Z(?!\n)@ && 123 $file =~ m@[^\\/]@ 124 ) { 125 $volume =~ m@([\\/])@ ; 126 my $sep = $1 ? $1 : '/' ; 127 $volume .= $sep ; 128 } 129 130 $volume .= $file ; 131 132 return $volume ; 133} 134 135 136sub abs2rel { 137 my($self,$path,$base) = @_; 138 139 # Clean up $path 140 if ( ! $self->file_name_is_absolute( $path ) ) { 141 $path = $self->rel2abs( $path ) ; 142 } else { 143 $path = $self->canonpath( $path ) ; 144 } 145 146 # Figure out the effective $base and clean it up. 147 if ( !defined( $base ) || $base eq '' ) { 148 $base = $self->_cwd(); 149 } elsif ( ! $self->file_name_is_absolute( $base ) ) { 150 $base = $self->rel2abs( $base ) ; 151 } else { 152 $base = $self->canonpath( $base ) ; 153 } 154 155 # Split up paths 156 my ( $path_volume, $path_directories, $path_file ) = $self->splitpath( $path, 1 ) ; 157 my ( $base_volume, $base_directories ) = $self->splitpath( $base, 1 ) ; 158 return $path unless $path_volume eq $base_volume; 159 160 # Now, remove all leading components that are the same 161 my @pathchunks = $self->splitdir( $path_directories ); 162 my @basechunks = $self->splitdir( $base_directories ); 163 164 while ( @pathchunks && 165 @basechunks && 166 lc( $pathchunks[0] ) eq lc( $basechunks[0] ) 167 ) { 168 shift @pathchunks ; 169 shift @basechunks ; 170 } 171 172 # No need to catdir, we know these are well formed. 173 $path_directories = CORE::join( '/', @pathchunks ); 174 $base_directories = CORE::join( '/', @basechunks ); 175 176 # $base_directories now contains the directories the resulting relative 177 # path must ascend out of before it can descend to $path_directory. So, 178 # replace all names with $parentDir 179 180 #FA Need to replace between backslashes... 181 $base_directories =~ s|[^\\/]+|..|g ; 182 183 # Glue the two together, using a separator if necessary, and preventing an 184 # empty result. 185 186 #FA Must check that new directories are not empty. 187 if ( $path_directories ne '' && $base_directories ne '' ) { 188 $path_directories = "$base_directories/$path_directories" ; 189 } else { 190 $path_directories = "$base_directories$path_directories" ; 191 } 192 193 return $self->canonpath( 194 $self->catpath( "", $path_directories, $path_file ) 195 ) ; 196} 197 198 199sub rel2abs { 200 my ($self,$path,$base ) = @_; 201 202 if ( ! $self->file_name_is_absolute( $path ) ) { 203 204 if ( !defined( $base ) || $base eq '' ) { 205 $base = $self->_cwd(); 206 } 207 elsif ( ! $self->file_name_is_absolute( $base ) ) { 208 $base = $self->rel2abs( $base ) ; 209 } 210 else { 211 $base = $self->canonpath( $base ) ; 212 } 213 214 my ( $path_directories, $path_file ) = 215 ($self->splitpath( $path, 1 ))[1,2] ; 216 217 my ( $base_volume, $base_directories ) = 218 $self->splitpath( $base, 1 ) ; 219 220 $path = $self->catpath( 221 $base_volume, 222 $self->catdir( $base_directories, $path_directories ), 223 $path_file 224 ) ; 225 } 226 227 return $self->canonpath( $path ) ; 228} 229 2301; 231__END__ 232 233=head1 NAME 234 235File::Spec::OS2 - methods for OS/2 file specs 236 237=head1 SYNOPSIS 238 239 require File::Spec::OS2; # Done internally by File::Spec if needed 240 241=head1 DESCRIPTION 242 243See L<File::Spec> and L<File::Spec::Unix>. This package overrides the 244implementation of these methods, not the semantics. 245 246Amongst the changes made for OS/2 are... 247 248=over 4 249 250=item tmpdir 251 252Modifies the list of places temp directory information is looked for. 253 254 $ENV{TMPDIR} 255 $ENV{TEMP} 256 $ENV{TMP} 257 /tmp 258 / 259 260=item splitpath 261 262Volumes can be drive letters or UNC sharenames (\\server\share). 263 264=back 265 266=cut 267