PageRenderTime 64ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/dist/Cwd/lib/File/Spec/Unix.pm

https://github.com/dougwilson/perl
Perl | 522 lines | 436 code | 70 blank | 16 comment | 25 complexity | 483d4d093b9b0c87b96ef3efa21bf9e5 MD5 | raw file
  1. package File::Spec::Unix;
  2. use strict;
  3. use vars qw($VERSION);
  4. $VERSION = '3.34';
  5. $VERSION = eval $VERSION;
  6. =head1 NAME
  7. File::Spec::Unix - File::Spec for Unix, base for other File::Spec modules
  8. =head1 SYNOPSIS
  9. require File::Spec::Unix; # Done automatically by File::Spec
  10. =head1 DESCRIPTION
  11. Methods for manipulating file specifications. Other File::Spec
  12. modules, such as File::Spec::Mac, inherit from File::Spec::Unix and
  13. override specific methods.
  14. =head1 METHODS
  15. =over 2
  16. =item canonpath()
  17. No physical check on the filesystem, but a logical cleanup of a
  18. path. On UNIX eliminates successive slashes and successive "/.".
  19. $cpath = File::Spec->canonpath( $path ) ;
  20. Note that this does *not* collapse F<x/../y> sections into F<y>. This
  21. is by design. If F</foo> on your system is a symlink to F</bar/baz>,
  22. then F</foo/../quux> is actually F</bar/quux>, not F</quux> as a naive
  23. F<../>-removal would give you. If you want to do this kind of
  24. processing, you probably want C<Cwd>'s C<realpath()> function to
  25. actually traverse the filesystem cleaning up paths like this.
  26. =cut
  27. sub canonpath {
  28. my ($self,$path) = @_;
  29. return unless defined $path;
  30. # Handle POSIX-style node names beginning with double slash (qnx, nto)
  31. # (POSIX says: "a pathname that begins with two successive slashes
  32. # may be interpreted in an implementation-defined manner, although
  33. # more than two leading slashes shall be treated as a single slash.")
  34. my $node = '';
  35. my $double_slashes_special = $^O eq 'qnx' || $^O eq 'nto';
  36. if ( $double_slashes_special
  37. && ( $path =~ s{^(//[^/]+)/?\z}{}s || $path =~ s{^(//[^/]+)/}{/}s ) ) {
  38. $node = $1;
  39. }
  40. # This used to be
  41. # $path =~ s|/+|/|g unless ($^O eq 'cygwin');
  42. # but that made tests 29, 30, 35, 46, and 213 (as of #13272) to fail
  43. # (Mainly because trailing "" directories didn't get stripped).
  44. # Why would cygwin avoid collapsing multiple slashes into one? --jhi
  45. $path =~ s|/{2,}|/|g; # xx////xx -> xx/xx
  46. $path =~ s{(?:/\.)+(?:/|\z)}{/}g; # xx/././xx -> xx/xx
  47. $path =~ s|^(?:\./)+||s unless $path eq "./"; # ./xx -> xx
  48. $path =~ s|^/(?:\.\./)+|/|; # /../../xx -> xx
  49. $path =~ s|^/\.\.$|/|; # /.. -> /
  50. $path =~ s|/\z|| unless $path eq "/"; # xx/ -> xx
  51. return "$node$path";
  52. }
  53. =item catdir()
  54. Concatenate two or more directory names to form a complete path ending
  55. with a directory. But remove the trailing slash from the resulting
  56. string, because it doesn't look good, isn't necessary and confuses
  57. OS2. Of course, if this is the root directory, don't cut off the
  58. trailing slash :-)
  59. =cut
  60. sub catdir {
  61. my $self = shift;
  62. $self->canonpath(join('/', @_, '')); # '' because need a trailing '/'
  63. }
  64. =item catfile
  65. Concatenate one or more directory names and a filename to form a
  66. complete path ending with a filename
  67. =cut
  68. sub catfile {
  69. my $self = shift;
  70. my $file = $self->canonpath(pop @_);
  71. return $file unless @_;
  72. my $dir = $self->catdir(@_);
  73. $dir .= "/" unless substr($dir,-1) eq "/";
  74. return $dir.$file;
  75. }
  76. =item curdir
  77. Returns a string representation of the current directory. "." on UNIX.
  78. =cut
  79. sub curdir { '.' }
  80. =item devnull
  81. Returns a string representation of the null device. "/dev/null" on UNIX.
  82. =cut
  83. sub devnull { '/dev/null' }
  84. =item rootdir
  85. Returns a string representation of the root directory. "/" on UNIX.
  86. =cut
  87. sub rootdir { '/' }
  88. =item tmpdir
  89. Returns a string representation of the first writable directory from
  90. the following list or the current directory if none from the list are
  91. writable:
  92. $ENV{TMPDIR}
  93. /tmp
  94. Since perl 5.8.0, if running under taint mode, and if $ENV{TMPDIR}
  95. is tainted, it is not used.
  96. =cut
  97. my $tmpdir;
  98. sub _tmpdir {
  99. return $tmpdir if defined $tmpdir;
  100. my $self = shift;
  101. my @dirlist = @_;
  102. {
  103. no strict 'refs';
  104. if (${"\cTAINT"}) { # Check for taint mode on perl >= 5.8.0
  105. require Scalar::Util;
  106. @dirlist = grep { ! Scalar::Util::tainted($_) } @dirlist;
  107. }
  108. }
  109. foreach (@dirlist) {
  110. next unless defined && -d && -w _;
  111. $tmpdir = $_;
  112. last;
  113. }
  114. $tmpdir = $self->curdir unless defined $tmpdir;
  115. $tmpdir = defined $tmpdir && $self->canonpath($tmpdir);
  116. return $tmpdir;
  117. }
  118. sub tmpdir {
  119. return $tmpdir if defined $tmpdir;
  120. $tmpdir = $_[0]->_tmpdir( $ENV{TMPDIR}, "/tmp" );
  121. }
  122. =item updir
  123. Returns a string representation of the parent directory. ".." on UNIX.
  124. =cut
  125. sub updir { '..' }
  126. =item no_upwards
  127. Given a list of file names, strip out those that refer to a parent
  128. directory. (Does not strip symlinks, only '.', '..', and equivalents.)
  129. =cut
  130. sub no_upwards {
  131. my $self = shift;
  132. return grep(!/^\.{1,2}\z/s, @_);
  133. }
  134. =item case_tolerant
  135. Returns a true or false value indicating, respectively, that alphabetic
  136. is not or is significant when comparing file specifications.
  137. =cut
  138. sub case_tolerant { 0 }
  139. =item file_name_is_absolute
  140. Takes as argument a path and returns true if it is an absolute path.
  141. This does not consult the local filesystem on Unix, Win32, OS/2 or Mac
  142. OS (Classic). It does consult the working environment for VMS (see
  143. L<File::Spec::VMS/file_name_is_absolute>).
  144. =cut
  145. sub file_name_is_absolute {
  146. my ($self,$file) = @_;
  147. return scalar($file =~ m:^/:s);
  148. }
  149. =item path
  150. Takes no argument, returns the environment variable PATH as an array.
  151. =cut
  152. sub path {
  153. return () unless exists $ENV{PATH};
  154. my @path = split(':', $ENV{PATH});
  155. foreach (@path) { $_ = '.' if $_ eq '' }
  156. return @path;
  157. }
  158. =item join
  159. join is the same as catfile.
  160. =cut
  161. sub join {
  162. my $self = shift;
  163. return $self->catfile(@_);
  164. }
  165. =item splitpath
  166. ($volume,$directories,$file) = File::Spec->splitpath( $path );
  167. ($volume,$directories,$file) = File::Spec->splitpath( $path,
  168. $no_file );
  169. Splits a path into volume, directory, and filename portions. On systems
  170. with no concept of volume, returns '' for volume.
  171. For systems with no syntax differentiating filenames from directories,
  172. assumes that the last file is a path unless $no_file is true or a
  173. trailing separator or /. or /.. is present. On Unix this means that $no_file
  174. true makes this return ( '', $path, '' ).
  175. The directory portion may or may not be returned with a trailing '/'.
  176. The results can be passed to L</catpath()> to get back a path equivalent to
  177. (usually identical to) the original path.
  178. =cut
  179. sub splitpath {
  180. my ($self,$path, $nofile) = @_;
  181. my ($volume,$directory,$file) = ('','','');
  182. if ( $nofile ) {
  183. $directory = $path;
  184. }
  185. else {
  186. $path =~ m|^ ( (?: .* / (?: \.\.?\z )? )? ) ([^/]*) |xs;
  187. $directory = $1;
  188. $file = $2;
  189. }
  190. return ($volume,$directory,$file);
  191. }
  192. =item splitdir
  193. The opposite of L</catdir()>.
  194. @dirs = File::Spec->splitdir( $directories );
  195. $directories must be only the directory portion of the path on systems
  196. that have the concept of a volume or that have path syntax that differentiates
  197. files from directories.
  198. Unlike just splitting the directories on the separator, empty
  199. directory names (C<''>) can be returned, because these are significant
  200. on some OSs.
  201. On Unix,
  202. File::Spec->splitdir( "/a/b//c/" );
  203. Yields:
  204. ( '', 'a', 'b', '', 'c', '' )
  205. =cut
  206. sub splitdir {
  207. return split m|/|, $_[1], -1; # Preserve trailing fields
  208. }
  209. =item catpath()
  210. Takes volume, directory and file portions and returns an entire path. Under
  211. Unix, $volume is ignored, and directory and file are concatenated. A '/' is
  212. inserted if needed (though if the directory portion doesn't start with
  213. '/' it is not added). On other OSs, $volume is significant.
  214. =cut
  215. sub catpath {
  216. my ($self,$volume,$directory,$file) = @_;
  217. if ( $directory ne '' &&
  218. $file ne '' &&
  219. substr( $directory, -1 ) ne '/' &&
  220. substr( $file, 0, 1 ) ne '/'
  221. ) {
  222. $directory .= "/$file" ;
  223. }
  224. else {
  225. $directory .= $file ;
  226. }
  227. return $directory ;
  228. }
  229. =item abs2rel
  230. Takes a destination path and an optional base path returns a relative path
  231. from the base path to the destination path:
  232. $rel_path = File::Spec->abs2rel( $path ) ;
  233. $rel_path = File::Spec->abs2rel( $path, $base ) ;
  234. If $base is not present or '', then L<cwd()|Cwd> is used. If $base is
  235. relative, then it is converted to absolute form using
  236. L</rel2abs()>. This means that it is taken to be relative to
  237. L<cwd()|Cwd>.
  238. On systems that have a grammar that indicates filenames, this ignores the
  239. $base filename. Otherwise all path components are assumed to be
  240. directories.
  241. If $path is relative, it is converted to absolute form using L</rel2abs()>.
  242. This means that it is taken to be relative to L<cwd()|Cwd>.
  243. No checks against the filesystem are made. On VMS, there is
  244. interaction with the working environment, as logicals and
  245. macros are expanded.
  246. Based on code written by Shigio Yamaguchi.
  247. =cut
  248. sub abs2rel {
  249. my($self,$path,$base) = @_;
  250. $base = $self->_cwd() unless defined $base and length $base;
  251. ($path, $base) = map $self->canonpath($_), $path, $base;
  252. if (grep $self->file_name_is_absolute($_), $path, $base) {
  253. ($path, $base) = map $self->rel2abs($_), $path, $base;
  254. }
  255. else {
  256. # save a couple of cwd()s if both paths are relative
  257. ($path, $base) = map $self->catdir('/', $_), $path, $base;
  258. }
  259. my ($path_volume) = $self->splitpath($path, 1);
  260. my ($base_volume) = $self->splitpath($base, 1);
  261. # Can't relativize across volumes
  262. return $path unless $path_volume eq $base_volume;
  263. my $path_directories = ($self->splitpath($path, 1))[1];
  264. my $base_directories = ($self->splitpath($base, 1))[1];
  265. # For UNC paths, the user might give a volume like //foo/bar that
  266. # strictly speaking has no directory portion. Treat it as if it
  267. # had the root directory for that volume.
  268. if (!length($base_directories) and $self->file_name_is_absolute($base)) {
  269. $base_directories = $self->rootdir;
  270. }
  271. # Now, remove all leading components that are the same
  272. my @pathchunks = $self->splitdir( $path_directories );
  273. my @basechunks = $self->splitdir( $base_directories );
  274. if ($base_directories eq $self->rootdir) {
  275. shift @pathchunks;
  276. return $self->canonpath( $self->catpath('', $self->catdir( @pathchunks ), '') );
  277. }
  278. while (@pathchunks && @basechunks && $self->_same($pathchunks[0], $basechunks[0])) {
  279. shift @pathchunks ;
  280. shift @basechunks ;
  281. }
  282. return $self->curdir unless @pathchunks || @basechunks;
  283. # $base now contains the directories the resulting relative path
  284. # must ascend out of before it can descend to $path_directory.
  285. my $result_dirs = $self->catdir( ($self->updir) x @basechunks, @pathchunks );
  286. return $self->canonpath( $self->catpath('', $result_dirs, '') );
  287. }
  288. sub _same {
  289. $_[1] eq $_[2];
  290. }
  291. =item rel2abs()
  292. Converts a relative path to an absolute path.
  293. $abs_path = File::Spec->rel2abs( $path ) ;
  294. $abs_path = File::Spec->rel2abs( $path, $base ) ;
  295. If $base is not present or '', then L<cwd()|Cwd> is used. If $base is
  296. relative, then it is converted to absolute form using
  297. L</rel2abs()>. This means that it is taken to be relative to
  298. L<cwd()|Cwd>.
  299. On systems that have a grammar that indicates filenames, this ignores
  300. the $base filename. Otherwise all path components are assumed to be
  301. directories.
  302. If $path is absolute, it is cleaned up and returned using L</canonpath()>.
  303. No checks against the filesystem are made. On VMS, there is
  304. interaction with the working environment, as logicals and
  305. macros are expanded.
  306. Based on code written by Shigio Yamaguchi.
  307. =cut
  308. sub rel2abs {
  309. my ($self,$path,$base ) = @_;
  310. # Clean up $path
  311. if ( ! $self->file_name_is_absolute( $path ) ) {
  312. # Figure out the effective $base and clean it up.
  313. if ( !defined( $base ) || $base eq '' ) {
  314. $base = $self->_cwd();
  315. }
  316. elsif ( ! $self->file_name_is_absolute( $base ) ) {
  317. $base = $self->rel2abs( $base ) ;
  318. }
  319. else {
  320. $base = $self->canonpath( $base ) ;
  321. }
  322. # Glom them together
  323. $path = $self->catdir( $base, $path ) ;
  324. }
  325. return $self->canonpath( $path ) ;
  326. }
  327. =back
  328. =head1 COPYRIGHT
  329. Copyright (c) 2004 by the Perl 5 Porters. All rights reserved.
  330. This program is free software; you can redistribute it and/or modify
  331. it under the same terms as Perl itself.
  332. =head1 SEE ALSO
  333. L<File::Spec>
  334. =cut
  335. # Internal routine to File::Spec, no point in making this public since
  336. # it is the standard Cwd interface. Most of the platform-specific
  337. # File::Spec subclasses use this.
  338. sub _cwd {
  339. require Cwd;
  340. Cwd::getcwd();
  341. }
  342. # Internal method to reduce xx\..\yy -> yy
  343. sub _collapse {
  344. my($fs, $path) = @_;
  345. my $updir = $fs->updir;
  346. my $curdir = $fs->curdir;
  347. my($vol, $dirs, $file) = $fs->splitpath($path);
  348. my @dirs = $fs->splitdir($dirs);
  349. pop @dirs if @dirs && $dirs[-1] eq '';
  350. my @collapsed;
  351. foreach my $dir (@dirs) {
  352. if( $dir eq $updir and # if we have an updir
  353. @collapsed and # and something to collapse
  354. length $collapsed[-1] and # and its not the rootdir
  355. $collapsed[-1] ne $updir and # nor another updir
  356. $collapsed[-1] ne $curdir # nor the curdir
  357. )
  358. { # then
  359. pop @collapsed; # collapse
  360. }
  361. else { # else
  362. push @collapsed, $dir; # just hang onto it
  363. }
  364. }
  365. return $fs->catpath($vol,
  366. $fs->catdir(@collapsed),
  367. $file
  368. );
  369. }
  370. 1;