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

/build_tools/ActivePerl-5.10.1.1007/perl/lib/File/Spec/VMS.pm

https://bitbucket.org/the____tiger/windows_3rdparty
Perl | 1165 lines | 779 code | 220 blank | 166 comment | 280 complexity | df7e5871b435a6c8e4661733dfb621e8 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.0, MPL-2.0-no-copyleft-exception, BSD-2-Clause, LGPL-2.1, LGPL-3.0
  1. package File::Spec::VMS;
  2. use strict;
  3. use vars qw(@ISA $VERSION);
  4. require File::Spec::Unix;
  5. $VERSION = '3.30';
  6. $VERSION = eval $VERSION;
  7. @ISA = qw(File::Spec::Unix);
  8. use File::Basename;
  9. use VMS::Filespec;
  10. =head1 NAME
  11. File::Spec::VMS - methods for VMS file specs
  12. =head1 SYNOPSIS
  13. require File::Spec::VMS; # Done internally by File::Spec if needed
  14. =head1 DESCRIPTION
  15. See File::Spec::Unix for a documentation of the methods provided
  16. there. This package overrides the implementation of these methods, not
  17. the semantics.
  18. The mode of operation of these routines depend on the VMS features that
  19. are controlled by the DECC features C<DECC$FILENAME_REPORT_UNIX> and
  20. C<DECC$EFS_CHARSET>.
  21. Perl needs to be at least at 5.10 for these feature settings to work.
  22. Use of them on older perl versions on VMS will result in unpredictable
  23. operations.
  24. The default and traditional mode of these routines have been to expect VMS
  25. syntax on input and to return VMS syntax on output, even when Unix syntax was
  26. given on input.
  27. The default and traditional mode is also incompatible with the VMS
  28. C<EFS>, Extended File system character set, and with running Perl scripts
  29. under <GNV>, Gnu is not VMS, an optional Unix like runtime environment on VMS.
  30. If the C<DECC$EFS_CHARSET> feature is enabled, These routines will now accept
  31. either VMS or UNIX syntax. If the input parameters are clearly VMS syntax,
  32. the return value will be in VMS syntax. If the input parameters are clearly
  33. in Unix syntax, the output will be in Unix syntax.
  34. This corresponds to the way that the VMS C library routines have always
  35. handled filenames, and what a programmer who has not specifically read this
  36. pod before would also expect.
  37. If the C<DECC$FILENAME_REPORT_UNIX> feature is enabled, then if the output
  38. syntax can not be determined from the input syntax, the output syntax will be
  39. UNIX. If the feature is not enabled, VMS output will be the default.
  40. =over 4
  41. =cut
  42. # Need to look up the feature settings. The preferred way is to use the
  43. # VMS::Feature module, but that may not be available to dual life modules.
  44. my $use_feature;
  45. BEGIN {
  46. if (eval { local $SIG{__DIE__}; require VMS::Feature; }) {
  47. $use_feature = 1;
  48. }
  49. }
  50. # Need to look up the UNIX report mode. This may become a dynamic mode
  51. # in the future.
  52. sub _unix_rpt {
  53. my $unix_rpt;
  54. if ($use_feature) {
  55. $unix_rpt = VMS::Feature::current("filename_unix_report");
  56. } else {
  57. my $env_unix_rpt = $ENV{'DECC$FILENAME_UNIX_REPORT'} || '';
  58. $unix_rpt = $env_unix_rpt =~ /^[ET1]/i;
  59. }
  60. return $unix_rpt;
  61. }
  62. # Need to look up the EFS character set mode. This may become a dynamic
  63. # mode in the future.
  64. sub _efs {
  65. my $efs;
  66. if ($use_feature) {
  67. $efs = VMS::Feature::current("efs_charset");
  68. } else {
  69. my $env_efs = $ENV{'DECC$EFS_CHARSET'} || '';
  70. $efs = $env_efs =~ /^[ET1]/i;
  71. }
  72. return $efs;
  73. }
  74. =item canonpath (override)
  75. Removes redundant portions of file specifications according to the syntax
  76. detected.
  77. =cut
  78. sub canonpath {
  79. my($self,$path) = @_;
  80. return undef unless defined $path;
  81. my $efs = $self->_efs;
  82. if ($path =~ m|/|) { # Fake Unix
  83. my $pathify = $path =~ m|/\Z(?!\n)|;
  84. $path = $self->SUPER::canonpath($path);
  85. # Do not convert to VMS when EFS character sets are in use
  86. return $path if $efs;
  87. if ($pathify) { return vmspath($path); }
  88. else { return vmsify($path); }
  89. }
  90. else {
  91. #FIXME - efs parsing has different rules. Characters in a VMS filespec
  92. # are only delimiters if not preceded by '^';
  93. $path =~ tr/<>/[]/; # < and > ==> [ and ]
  94. $path =~ s/\]\[\./\.\]\[/g; # ][. ==> .][
  95. $path =~ s/\[000000\.\]\[/\[/g; # [000000.][ ==> [
  96. $path =~ s/\[000000\./\[/g; # [000000. ==> [
  97. $path =~ s/\.\]\[000000\]/\]/g; # .][000000] ==> ]
  98. $path =~ s/\.\]\[/\./g; # foo.][bar ==> foo.bar
  99. 1 while ($path =~ s/([\[\.])(-+)\.(-+)([\.\]])/$1$2$3$4/);
  100. # That loop does the following
  101. # with any amount of dashes:
  102. # .-.-. ==> .--.
  103. # [-.-. ==> [--.
  104. # .-.-] ==> .--]
  105. # [-.-] ==> [--]
  106. 1 while ($path =~ s/([\[\.])[^\]\.]+\.-(-+)([\]\.])/$1$2$3/);
  107. # That loop does the following
  108. # with any amount (minimum 2)
  109. # of dashes:
  110. # .foo.--. ==> .-.
  111. # .foo.--] ==> .-]
  112. # [foo.--. ==> [-.
  113. # [foo.--] ==> [-]
  114. #
  115. # And then, the remaining cases
  116. $path =~ s/\[\.-/[-/; # [.- ==> [-
  117. $path =~ s/\.[^\]\.]+\.-\./\./g; # .foo.-. ==> .
  118. $path =~ s/\[[^\]\.]+\.-\./\[/g; # [foo.-. ==> [
  119. $path =~ s/\.[^\]\.]+\.-\]/\]/g; # .foo.-] ==> ]
  120. $path =~ s/\[[^\]\.]+\.-\]/\[000000\]/g;# [foo.-] ==> [000000]
  121. $path =~ s/\[\]// unless $path eq '[]'; # [] ==>
  122. return $path;
  123. }
  124. }
  125. =item catdir (override)
  126. Concatenates a list of file specifications, and returns the result as a
  127. directory specification. No check is made for "impossible"
  128. cases (e.g. elements other than the first being absolute filespecs).
  129. =cut
  130. sub catdir {
  131. my $self = shift;
  132. my $dir = pop;
  133. my $efs = $self->_efs;
  134. my $unix_rpt = $self->_unix_rpt;
  135. my @dirs = grep {defined() && length()} @_;
  136. if ($efs) {
  137. # Legacy mode removes blank entries.
  138. # But that breaks existing generic perl code that
  139. # uses a blank path at the beginning of the array
  140. # to indicate an absolute path.
  141. # So put it back if found.
  142. if (@_) {
  143. if ($_[0] eq '') {
  144. unshift @dirs, '';
  145. }
  146. }
  147. }
  148. my $rslt;
  149. if (@dirs) {
  150. my $path = (@dirs == 1 ? $dirs[0] : $self->catdir(@dirs));
  151. my ($spath,$sdir) = ($path,$dir);
  152. if ($efs) {
  153. # Extended character set in use, go into DWIM mode.
  154. # Now we need to identify what the directory is in
  155. # of the specification in order to merge them.
  156. my $path_unix = 0;
  157. $path_unix = 1 if ($path =~ m#/#);
  158. $path_unix = 1 if ($path =~ /^\.\.?$/);
  159. my $path_vms = 0;
  160. $path_vms = 1 if ($path =~ m#[\[<\]]#);
  161. $path_vms = 1 if ($path =~ /^--?$/);
  162. my $dir_unix = 0;
  163. $dir_unix = 1 if ($dir =~ m#/#);
  164. $dir_unix = 1 if ($dir =~ /^\.\.?$/);
  165. my $dir_vms = 0;
  166. $dir_vms = 1 if ($dir =~ m#[\[<\]]#);
  167. $dir_vms = 1 if ($dir =~ /^--?$/);
  168. my $unix_mode = 0;
  169. if (($path_unix != $dir_unix) && ($path_vms != $dir_vms)) {
  170. # Ambiguous, so if in $unix_rpt mode then assume UNIX.
  171. $unix_mode = 1 if $unix_rpt;
  172. } else {
  173. $unix_mode = 1 if (!$path_vms && !$dir_vms && $unix_rpt);
  174. $unix_mode = 1 if ($path_unix || $dir_unix);
  175. }
  176. if ($unix_mode) {
  177. # Fix up mixed syntax imput as good as possible - GIGO
  178. $path = unixify($path) if $path_vms;
  179. $dir = unixify($dir) if $dir_vms;
  180. $rslt = $path;
  181. # Append a path delimiter
  182. $rslt .= '/' unless ($rslt =~ m#/$#);
  183. $rslt .= $dir;
  184. return $self->SUPER::canonpath($rslt);
  185. } else {
  186. #with <> posible instead of [.
  187. # Normalize the brackets
  188. # Fixme - need to not switch when preceded by ^.
  189. $path =~ s/</\[/g;
  190. $path =~ s/>/\]/g;
  191. $dir =~ s/</\[/g;
  192. $dir =~ s/>/\]/g;
  193. # Fix up mixed syntax imput as good as possible - GIGO
  194. $path = vmsify($path) if $path_unix;
  195. $dir = vmsify($dir) if $dir_unix;
  196. #Possible path values: foo: [.foo] [foo] foo, and $(foo)
  197. #or starting with '-', or foo.dir
  198. #If path is foo, it needs to be converted to [.foo]
  199. # Fix up a bare path name.
  200. unless ($path_vms) {
  201. $path =~ s/\.dir\Z(?!\n)//i;
  202. if (($path ne '') && ($path !~ /^-/)) {
  203. # Non blank and not prefixed with '-', add a dot
  204. $path = '[.' . $path;
  205. } else {
  206. # Just start a directory.
  207. $path = '[' . $path;
  208. }
  209. } else {
  210. $path =~ s/\]$//;
  211. }
  212. #Possible dir values: [.dir] dir and $(foo)
  213. # No punctuation may have a trailing .dir
  214. unless ($dir_vms) {
  215. $dir =~ s/\.dir\Z(?!\n)//i;
  216. } else {
  217. #strip off the brackets
  218. $dir =~ s/^\[//;
  219. $dir =~ s/\]$//;
  220. }
  221. #strip off the leading dot if present.
  222. $dir =~ s/^\.//;
  223. # Now put the specifications together.
  224. if ($dir ne '') {
  225. # Add a separator unless this is an absolute path
  226. $path .= '.' if ($path ne '[');
  227. $rslt = $path . $dir . ']';
  228. } else {
  229. $rslt = $path . ']';
  230. }
  231. }
  232. } else {
  233. # Traditional ODS-2 mode.
  234. $spath =~ s/\.dir\Z(?!\n)//i; $sdir =~ s/\.dir\Z(?!\n)//i;
  235. $sdir = $self->eliminate_macros($sdir)
  236. unless $sdir =~ /^[\w\-]+\Z(?!\n)/s;
  237. $rslt = $self->fixpath($self->eliminate_macros($spath)."/$sdir",1);
  238. # Special case for VMS absolute directory specs: these will have
  239. # had device prepended during trip through Unix syntax in
  240. # eliminate_macros(), since Unix syntax has no way to express
  241. # "absolute from the top of this device's directory tree".
  242. if ($spath =~ /^[\[<][^.\-]/s) { $rslt =~ s/^[^\[<]+//s; }
  243. }
  244. } else {
  245. # Single directory, just make sure it is in directory format
  246. # Return an empty string on null input, and pass through macros.
  247. if (not defined $dir or not length $dir) { $rslt = ''; }
  248. elsif ($dir =~ /^\$\([^\)]+\)\Z(?!\n)/s) {
  249. $rslt = $dir;
  250. } else {
  251. my $unix_mode = 0;
  252. if ($efs) {
  253. my $dir_unix = 0;
  254. $dir_unix = 1 if ($dir =~ m#/#);
  255. $dir_unix = 1 if ($dir =~ /^\.\.?$/);
  256. my $dir_vms = 0;
  257. $dir_vms = 1 if ($dir =~ m#[\[<\]]#);
  258. $dir_vms = 1 if ($dir =~ /^--?$/);
  259. if ($dir_vms == $dir_unix) {
  260. # Ambiguous, so if in $unix_rpt mode then assume UNIX.
  261. $unix_mode = 1 if $unix_rpt;
  262. } else {
  263. $unix_mode = 1 if $dir_unix;
  264. }
  265. }
  266. if ($unix_mode) {
  267. return $dir;
  268. } else {
  269. # For VMS, force it to be in directory format
  270. $rslt = vmspath($dir);
  271. }
  272. }
  273. }
  274. return $self->canonpath($rslt);
  275. }
  276. =item catfile (override)
  277. Concatenates a list of directory specifications with a filename specification
  278. to build a path.
  279. =cut
  280. sub catfile {
  281. my $self = shift;
  282. my $tfile = pop();
  283. my $file = $self->canonpath($tfile);
  284. my @files = grep {defined() && length()} @_;
  285. my $efs = $self->_efs;
  286. my $unix_rpt = $self->_unix_rpt;
  287. # Assume VMS mode
  288. my $unix_mode = 0;
  289. my $file_unix = 0;
  290. my $file_vms = 0;
  291. if ($efs) {
  292. # Now we need to identify format the file is in
  293. # of the specification in order to merge them.
  294. $file_unix = 1 if ($tfile =~ m#/#);
  295. $file_unix = 1 if ($tfile =~ /^\.\.?$/);
  296. $file_vms = 1 if ($tfile =~ m#[\[<\]]#);
  297. $file_vms = 1 if ($tfile =~ /^--?$/);
  298. # We may know for sure what the format is.
  299. if (($file_unix != $file_vms)) {
  300. $unix_mode = 1 if ($file_unix && $unix_rpt);
  301. }
  302. }
  303. my $rslt;
  304. if (@files) {
  305. # concatenate the directories.
  306. my $path;
  307. if (@files == 1) {
  308. $path = $files[0];
  309. } else {
  310. if ($file_vms) {
  311. # We need to make sure this is in VMS mode to avoid doing
  312. # both a vmsify and unixfy on the same path, as that may
  313. # lose significant data.
  314. my $i = @files - 1;
  315. my $tdir = $files[$i];
  316. my $tdir_vms = 0;
  317. my $tdir_unix = 0;
  318. $tdir_vms = 1 if ($tdir =~ m#[\[<\]]#);
  319. $tdir_unix = 1 if ($tdir =~ m#/#);
  320. $tdir_unix = 1 if ($tdir =~ /^\.\.?$/);
  321. if (!$tdir_vms) {
  322. if ($tdir_unix) {
  323. $tdir = vmspath($tdir);
  324. } else {
  325. $tdir =~ s/\.dir\Z(?!\n)//i;
  326. $tdir = '[.' . $tdir . ']';
  327. }
  328. $files[$i] = $tdir;
  329. }
  330. }
  331. $path = $self->catdir(@files);
  332. }
  333. my $spath = $path;
  334. # Some thing building a VMS path in pieces may try to pass a
  335. # directory name in filename format, so normalize it.
  336. $spath =~ s/\.dir\Z(?!\n)//i;
  337. # if the spath ends with a directory delimiter and the file is bare,
  338. # then just concat them.
  339. # FIX-ME: In VMS format "[]<>:" are not delimiters if preceded by '^'
  340. # Quite a bit of Perl does not know that yet.
  341. if ($spath =~ /^[^\)\]\/:>]+\)\Z(?!\n)/s && basename($file) eq $file) {
  342. $rslt = "$spath$file";
  343. } else {
  344. if ($efs) {
  345. # Now we need to identify what the directory is in
  346. # of the specification in order to merge them.
  347. my $spath_unix = 0;
  348. $spath_unix = 1 if ($spath =~ m#/#);
  349. $spath_unix = 1 if ($spath =~ /^\.\.?$/);
  350. my $spath_vms = 0;
  351. $spath_vms = 1 if ($spath =~ m#[\[<\]]#);
  352. $spath_vms = 1 if ($spath =~ /^--?$/);
  353. # Assume VMS mode
  354. if (($spath_unix == $spath_vms) &&
  355. ($file_unix == $file_vms)) {
  356. # Ambigous, so if in $unix_rpt mode then assume UNIX.
  357. $unix_mode = 1 if $unix_rpt;
  358. } else {
  359. $unix_mode = 1
  360. if (($spath_unix || $file_unix) && $unix_rpt);
  361. }
  362. if (!$unix_mode) {
  363. if ($spath_vms) {
  364. $spath = '[' . $spath . ']' if $spath =~ /^-/;
  365. $rslt = vmspath($spath);
  366. } else {
  367. $rslt = '[.' . $spath . ']';
  368. }
  369. $file = vmsify($file) if ($file_unix);
  370. } else {
  371. $spath = unixify($spath) if ($spath_vms);
  372. $rslt = $spath;
  373. $file = unixify($file) if ($file_vms);
  374. # Unix merge may need a directory delimitor.
  375. # A null path indicates root on Unix.
  376. $rslt .= '/' unless ($rslt =~ m#/$#);
  377. }
  378. $rslt .= $file;
  379. $rslt =~ s/\]\[//;
  380. } else {
  381. # Traditional VMS Perl mode expects that this is done.
  382. # Note for future maintainers:
  383. # This is left here for compatibility with perl scripts
  384. # that have come to expect this behavior, even though
  385. # usually the Perl scripts ported to VMS have to be
  386. # patched because of it changing Unix syntax file
  387. # to VMS format.
  388. $rslt = $self->eliminate_macros($spath);
  389. $rslt = vmsify($rslt.((defined $rslt) &&
  390. ($rslt ne '') ? '/' : '').unixify($file));
  391. }
  392. }
  393. }
  394. else {
  395. # Only passed a single file?
  396. my $xfile = $file;
  397. # Traditional VMS perl expects this conversion.
  398. $xfile = vmsify($file) unless ($efs);
  399. $rslt = (defined($file) && length($file)) ? $xfile : '';
  400. }
  401. return $self->canonpath($rslt) unless $unix_rpt;
  402. # In Unix report mode, do not strip off redundent path information.
  403. return $rslt;
  404. }
  405. =item curdir (override)
  406. Returns a string representation of the current directory: '[]' or '.'
  407. =cut
  408. sub curdir {
  409. my $self = shift @_;
  410. return '.' if ($self->_unix_rpt);
  411. return '[]';
  412. }
  413. =item devnull (override)
  414. Returns a string representation of the null device: '_NLA0:' or '/dev/null'
  415. =cut
  416. sub devnull {
  417. my $self = shift @_;
  418. return '/dev/null' if ($self->_unix_rpt);
  419. return "_NLA0:";
  420. }
  421. =item rootdir (override)
  422. Returns a string representation of the root directory: 'SYS$DISK:[000000]'
  423. or '/'
  424. =cut
  425. sub rootdir {
  426. my $self = shift @_;
  427. if ($self->_unix_rpt) {
  428. # Root may exist, try it first.
  429. my $try = '/';
  430. my ($dev1, $ino1) = stat('/');
  431. my ($dev2, $ino2) = stat('.');
  432. # Perl falls back to '.' if it can not determine '/'
  433. if (($dev1 != $dev2) || ($ino1 != $ino2)) {
  434. return $try;
  435. }
  436. # Fall back to UNIX format sys$disk.
  437. return '/sys$disk/';
  438. }
  439. return 'SYS$DISK:[000000]';
  440. }
  441. =item tmpdir (override)
  442. Returns a string representation of the first writable directory
  443. from the following list or '' if none are writable:
  444. /tmp if C<DECC$FILENAME_REPORT_UNIX> is enabled.
  445. sys$scratch:
  446. $ENV{TMPDIR}
  447. Since perl 5.8.0, if running under taint mode, and if $ENV{TMPDIR}
  448. is tainted, it is not used.
  449. =cut
  450. my $tmpdir;
  451. sub tmpdir {
  452. my $self = shift @_;
  453. return $tmpdir if defined $tmpdir;
  454. if ($self->_unix_rpt) {
  455. $tmpdir = $self->_tmpdir('/tmp', '/sys$scratch', $ENV{TMPDIR});
  456. return $tmpdir;
  457. }
  458. $tmpdir = $self->_tmpdir( 'sys$scratch:', $ENV{TMPDIR} );
  459. }
  460. =item updir (override)
  461. Returns a string representation of the parent directory: '[-]' or '..'
  462. =cut
  463. sub updir {
  464. my $self = shift @_;
  465. return '..' if ($self->_unix_rpt);
  466. return '[-]';
  467. }
  468. =item case_tolerant (override)
  469. VMS file specification syntax is case-tolerant.
  470. =cut
  471. sub case_tolerant {
  472. return 1;
  473. }
  474. =item path (override)
  475. Translate logical name DCL$PATH as a searchlist, rather than trying
  476. to C<split> string value of C<$ENV{'PATH'}>.
  477. =cut
  478. sub path {
  479. my (@dirs,$dir,$i);
  480. while ($dir = $ENV{'DCL$PATH;' . $i++}) { push(@dirs,$dir); }
  481. return @dirs;
  482. }
  483. =item file_name_is_absolute (override)
  484. Checks for VMS directory spec as well as Unix separators.
  485. =cut
  486. sub file_name_is_absolute {
  487. my ($self,$file) = @_;
  488. # If it's a logical name, expand it.
  489. $file = $ENV{$file} while $file =~ /^[\w\$\-]+\Z(?!\n)/s && $ENV{$file};
  490. return scalar($file =~ m!^/!s ||
  491. $file =~ m![<\[][^.\-\]>]! ||
  492. $file =~ /:[^<\[]/);
  493. }
  494. =item splitpath (override)
  495. ($volume,$directories,$file) = File::Spec->splitpath( $path );
  496. ($volume,$directories,$file) = File::Spec->splitpath( $path, $no_file );
  497. Passing a true value for C<$no_file> indicates that the path being
  498. split only contains directory components, even on systems where you
  499. can usually (when not supporting a foreign syntax) tell the difference
  500. between directories and files at a glance.
  501. =cut
  502. sub splitpath {
  503. my($self,$path, $nofile) = @_;
  504. my($dev,$dir,$file) = ('','','');
  505. my $efs = $self->_efs;
  506. my $vmsify_path = vmsify($path);
  507. if ($efs) {
  508. my $path_vms = 0;
  509. $path_vms = 1 if ($path =~ m#[\[<\]]#);
  510. $path_vms = 1 if ($path =~ /^--?$/);
  511. if (!$path_vms) {
  512. return $self->SUPER::splitpath($path, $nofile);
  513. }
  514. $vmsify_path = $path;
  515. }
  516. if ( $nofile ) {
  517. #vmsify('d1/d2/d3') returns '[.d1.d2]d3'
  518. #vmsify('/d1/d2/d3') returns 'd1:[d2]d3'
  519. if( $vmsify_path =~ /(.*)\](.+)/ ){
  520. $vmsify_path = $1.'.'.$2.']';
  521. }
  522. $vmsify_path =~ /(.+:)?(.*)/s;
  523. $dir = defined $2 ? $2 : ''; # dir can be '0'
  524. return ($1 || '',$dir,$file);
  525. }
  526. else {
  527. $vmsify_path =~ /(.+:)?([\[<].*[\]>])?(.*)/s;
  528. return ($1 || '',$2 || '',$3);
  529. }
  530. }
  531. =item splitdir (override)
  532. Split a directory specification into the components.
  533. =cut
  534. sub splitdir {
  535. my($self,$dirspec) = @_;
  536. my @dirs = ();
  537. return @dirs if ( (!defined $dirspec) || ('' eq $dirspec) );
  538. my $efs = $self->_efs;
  539. my $dir_unix = 0;
  540. $dir_unix = 1 if ($dirspec =~ m#/#);
  541. $dir_unix = 1 if ($dirspec =~ /^\.\.?$/);
  542. # Unix filespecs in EFS mode handled by Unix routines.
  543. if ($efs && $dir_unix) {
  544. return $self->SUPER::splitdir($dirspec);
  545. }
  546. # FIX ME, only split for VMS delimiters not prefixed with '^'.
  547. $dirspec =~ tr/<>/[]/; # < and > ==> [ and ]
  548. $dirspec =~ s/\]\[\./\.\]\[/g; # ][. ==> .][
  549. $dirspec =~ s/\[000000\.\]\[/\[/g; # [000000.][ ==> [
  550. $dirspec =~ s/\[000000\./\[/g; # [000000. ==> [
  551. $dirspec =~ s/\.\]\[000000\]/\]/g; # .][000000] ==> ]
  552. $dirspec =~ s/\.\]\[/\./g; # foo.][bar ==> foo.bar
  553. while ($dirspec =~ s/(^|[\[\<\.])\-(\-+)($|[\]\>\.])/$1-.$2$3/g) {}
  554. # That loop does the following
  555. # with any amount of dashes:
  556. # .--. ==> .-.-.
  557. # [--. ==> [-.-.
  558. # .--] ==> .-.-]
  559. # [--] ==> [-.-]
  560. $dirspec = "[$dirspec]" unless $dirspec =~ /[\[<]/; # make legal
  561. $dirspec =~ s/^(\[|<)\./$1/;
  562. @dirs = split /(?<!\^)\./, vmspath($dirspec);
  563. $dirs[0] =~ s/^[\[<]//s; $dirs[-1] =~ s/[\]>]\Z(?!\n)//s;
  564. @dirs;
  565. }
  566. =item catpath (override)
  567. Construct a complete filespec.
  568. =cut
  569. sub catpath {
  570. my($self,$dev,$dir,$file) = @_;
  571. my $efs = $self->_efs;
  572. my $unix_rpt = $self->_unix_rpt;
  573. my $unix_mode = 0;
  574. my $dir_unix = 0;
  575. $dir_unix = 1 if ($dir =~ m#/#);
  576. $dir_unix = 1 if ($dir =~ /^\.\.?$/);
  577. my $dir_vms = 0;
  578. $dir_vms = 1 if ($dir =~ m#[\[<\]]#);
  579. $dir_vms = 1 if ($dir =~ /^--?$/);
  580. if ($efs && (length($dev) == 0)) {
  581. if ($dir_unix == $dir_vms) {
  582. $unix_mode = $unix_rpt;
  583. } else {
  584. $unix_mode = $dir_unix;
  585. }
  586. }
  587. # We look for a volume in $dev, then in $dir, but not both
  588. # but only if using VMS syntax.
  589. if (!$unix_mode) {
  590. $dir = vmspath($dir) if $dir_unix;
  591. my ($dir_volume, $dir_dir, $dir_file) = $self->splitpath($dir);
  592. $dev = $dir_volume unless length $dev;
  593. $dir = length $dir_file ? $self->catfile($dir_dir, $dir_file) :
  594. $dir_dir;
  595. }
  596. if ($dev =~ m|^/+([^/]+)|) { $dev = "$1:"; }
  597. else { $dev .= ':' unless $dev eq '' or $dev =~ /:\Z(?!\n)/; }
  598. if (length($dev) or length($dir)) {
  599. if ($efs) {
  600. if ($unix_mode) {
  601. $dir .= '/' unless ($dir =~ m#/$#);
  602. } else {
  603. $dir = vmspath($dir) if (($dir =~ m#/#) || ($dir =~ /^\.\.?$/));
  604. $dir = "[$dir]" unless $dir =~ /^[\[<]/;
  605. }
  606. } else {
  607. $dir = "[$dir]" unless $dir =~ /[\[<\/]/;
  608. $dir = vmspath($dir);
  609. }
  610. }
  611. "$dev$dir$file";
  612. }
  613. =item abs2rel (override)
  614. Attempt to convert a file specification to a relative specification.
  615. On a system with volumes, like VMS, this may not be possible.
  616. =cut
  617. sub abs2rel {
  618. my $self = shift;
  619. my($path,$base) = @_;
  620. my $efs = $self->_efs;
  621. my $unix_rpt = $self->_unix_rpt;
  622. if (!$efs) {
  623. return vmspath(File::Spec::Unix::abs2rel( $self, @_ ))
  624. if grep m{/}, @_;
  625. }
  626. # We need to identify what the directory is in
  627. # of the specification in order to process them
  628. my $path_unix = 0;
  629. $path_unix = 1 if ($path =~ m#/#);
  630. $path_unix = 1 if ($path =~ /^\.\.?$/);
  631. my $path_vms = 0;
  632. $path_vms = 1 if ($path =~ m#[\[<\]]#);
  633. $path_vms = 1 if ($path =~ /^--?$/);
  634. my $unix_mode = 0;
  635. if ($path_vms == $path_unix) {
  636. $unix_mode = $unix_rpt;
  637. } else {
  638. $unix_mode = $path_unix;
  639. }
  640. my $base_unix = 0;
  641. my $base_vms = 0;
  642. if (defined $base) {
  643. $base_unix = 1 if ($base =~ m#/#);
  644. $base_unix = 1 if ($base =~ /^\.\.?$/);
  645. $base_vms = 1 if ($base =~ m#[\[<\]]#);
  646. $base_vms = 1 if ($base =~ /^--?$/);
  647. if ($path_vms == $path_unix) {
  648. if ($base_vms == $base_unix) {
  649. $unix_mode = $unix_rpt;
  650. } else {
  651. $unix_mode = $base_unix;
  652. }
  653. } else {
  654. $unix_mode = 0 if $base_vms;
  655. }
  656. }
  657. if ($efs) {
  658. if ($unix_mode) {
  659. # We are UNIX mode.
  660. $base = unixpath($base) if $base_vms;
  661. $base = unixify($path) if $path_vms;
  662. # Here VMS is different, and in order to do this right
  663. # we have to take the realpath for both the path and the base
  664. # so that we can remove the common components.
  665. if ($path =~ m#^/#) {
  666. if (defined $base) {
  667. # For the shorterm, if the starting directories are
  668. # common, remove them.
  669. my $bq = qq($base);
  670. $bq =~ s/\$/\\\$/;
  671. $path =~ s/^$bq//i;
  672. }
  673. return $path;
  674. }
  675. return File::Spec::Unix::abs2rel( $self, $path, $base );
  676. } else {
  677. $base = vmspath($base) if $base_unix;
  678. $path = vmsify($path) if $path_unix;
  679. }
  680. }
  681. unless (defined $base and length $base) {
  682. $base = $self->_cwd();
  683. if ($efs) {
  684. $base_unix = 1 if ($base =~ m#/#);
  685. $base_unix = 1 if ($base =~ /^\.\.?$/);
  686. $base = vmspath($base) if $base_unix;
  687. }
  688. }
  689. for ($path, $base) { $_ = $self->canonpath($_) }
  690. # Are we even starting $path on the same (node::)device as $base? Note that
  691. # logical paths or nodename differences may be on the "same device"
  692. # but the comparison that ignores device differences so as to concatenate
  693. # [---] up directory specs is not even a good idea in cases where there is
  694. # a logical path difference between $path and $base nodename and/or device.
  695. # Hence we fall back to returning the absolute $path spec
  696. # if there is a case blind device (or node) difference of any sort
  697. # and we do not even try to call $parse() or consult %ENV for $trnlnm()
  698. # (this module needs to run on non VMS platforms after all).
  699. my ($path_volume, $path_directories, $path_file) = $self->splitpath($path);
  700. my ($base_volume, $base_directories, $base_file) = $self->splitpath($base);
  701. return $path unless lc($path_volume) eq lc($base_volume);
  702. for ($path, $base) { $_ = $self->rel2abs($_) }
  703. # Now, remove all leading components that are the same
  704. my @pathchunks = $self->splitdir( $path_directories );
  705. my $pathchunks = @pathchunks;
  706. unshift(@pathchunks,'000000') unless $pathchunks[0] eq '000000';
  707. my @basechunks = $self->splitdir( $base_directories );
  708. my $basechunks = @basechunks;
  709. unshift(@basechunks,'000000') unless $basechunks[0] eq '000000';
  710. while ( @pathchunks &&
  711. @basechunks &&
  712. lc( $pathchunks[0] ) eq lc( $basechunks[0] )
  713. ) {
  714. shift @pathchunks ;
  715. shift @basechunks ;
  716. }
  717. # @basechunks now contains the directories to climb out of,
  718. # @pathchunks now has the directories to descend in to.
  719. if ((@basechunks > 0) || ($basechunks != $pathchunks)) {
  720. $path_directories = join '.', ('-' x @basechunks, @pathchunks) ;
  721. }
  722. else {
  723. $path_directories = join '.', @pathchunks;
  724. }
  725. $path_directories = '['.$path_directories.']';
  726. return $self->canonpath( $self->catpath( '', $path_directories, $path_file ) ) ;
  727. }
  728. =item rel2abs (override)
  729. Return an absolute file specification from a relative one.
  730. =cut
  731. sub rel2abs {
  732. my $self = shift ;
  733. my ($path,$base ) = @_;
  734. return undef unless defined $path;
  735. my $efs = $self->_efs;
  736. my $unix_rpt = $self->_unix_rpt;
  737. # We need to identify what the directory is in
  738. # of the specification in order to process them
  739. my $path_unix = 0;
  740. $path_unix = 1 if ($path =~ m#/#);
  741. $path_unix = 1 if ($path =~ /^\.\.?$/);
  742. my $path_vms = 0;
  743. $path_vms = 1 if ($path =~ m#[\[<\]]#);
  744. $path_vms = 1 if ($path =~ /^--?$/);
  745. my $unix_mode = 0;
  746. if ($path_vms == $path_unix) {
  747. $unix_mode = $unix_rpt;
  748. } else {
  749. $unix_mode = $path_unix;
  750. }
  751. my $base_unix = 0;
  752. my $base_vms = 0;
  753. if (defined $base) {
  754. $base_unix = 1 if ($base =~ m#/#);
  755. $base_unix = 1 if ($base =~ /^\.\.?$/);
  756. $base_vms = 1 if ($base =~ m#[\[<\]]#);
  757. $base_vms = 1 if ($base =~ /^--?$/);
  758. # If we could not determine the path mode, see if we can find out
  759. # from the base.
  760. if ($path_vms == $path_unix) {
  761. if ($base_vms != $base_unix) {
  762. $unix_mode = $base_unix;
  763. }
  764. }
  765. }
  766. if (!$efs) {
  767. # Legacy behavior, convert to VMS syntax.
  768. $unix_mode = 0;
  769. if (defined $base) {
  770. $base = vmspath($base) if $base =~ m/\//;
  771. }
  772. if ($path =~ m/\//) {
  773. $path = ( -d $path || $path =~ m/\/\z/ # educated guessing about
  774. ? vmspath($path) # whether it's a directory
  775. : vmsify($path) );
  776. }
  777. }
  778. # Clean up and split up $path
  779. if ( ! $self->file_name_is_absolute( $path ) ) {
  780. # Figure out the effective $base and clean it up.
  781. if ( !defined( $base ) || $base eq '' ) {
  782. $base = $self->_cwd;
  783. }
  784. elsif ( ! $self->file_name_is_absolute( $base ) ) {
  785. $base = $self->rel2abs( $base ) ;
  786. }
  787. else {
  788. $base = $self->canonpath( $base ) ;
  789. }
  790. if ($efs) {
  791. # base may have changed, so need to look up format again.
  792. if ($unix_mode) {
  793. $base_vms = 1 if ($base =~ m#[\[<\]]#);
  794. $base_vms = 1 if ($base =~ /^--?$/);
  795. $base = unixpath($base) if $base_vms;
  796. $base .= '/' unless ($base =~ m#/$#);
  797. } else {
  798. $base_unix = 1 if ($base =~ m#/#);
  799. $base_unix = 1 if ($base =~ /^\.\.?$/);
  800. $base = vmspath($base) if $base_unix;
  801. }
  802. }
  803. # Split up paths
  804. my ( $path_directories, $path_file ) =
  805. ($self->splitpath( $path ))[1,2] ;
  806. my ( $base_volume, $base_directories ) =
  807. $self->splitpath( $base ) ;
  808. $path_directories = '' if $path_directories eq '[]' ||
  809. $path_directories eq '<>';
  810. my $sep = '' ;
  811. if ($efs) {
  812. # Merge the paths assuming that the base is absolute.
  813. $base_directories = $self->catdir('',
  814. $base_directories,
  815. $path_directories);
  816. } else {
  817. # Legacy behavior assumes VMS only paths
  818. $sep = '.'
  819. if ( $base_directories =~ m{[^.\]>]\Z(?!\n)} &&
  820. $path_directories =~ m{^[^.\[<]}s
  821. ) ;
  822. $base_directories = "$base_directories$sep$path_directories";
  823. $base_directories =~ s{\.?[\]>][\[<]\.?}{.};
  824. }
  825. $path_file = '' if ($path_file eq '.') && $unix_mode;
  826. $path = $self->catpath( $base_volume, $base_directories, $path_file );
  827. }
  828. return $self->canonpath( $path ) ;
  829. }
  830. # eliminate_macros() and fixpath() are MakeMaker-specific methods
  831. # which are used inside catfile() and catdir(). MakeMaker has its own
  832. # copies as of 6.06_03 which are the canonical ones. We leave these
  833. # here, in peace, so that File::Spec continues to work with MakeMakers
  834. # prior to 6.06_03.
  835. #
  836. # Please consider these two methods deprecated. Do not patch them,
  837. # patch the ones in ExtUtils::MM_VMS instead.
  838. #
  839. # Update: MakeMaker 6.48 is still using these routines on VMS.
  840. # so they need to be kept up to date with ExtUtils::MM_VMS.
  841. #
  842. # The traditional VMS mode using ODS-2 disks depends on these routines
  843. # being here. These routines should not be called in when the
  844. # C<DECC$EFS_CHARSET> or C<DECC$FILENAME_REPORT_UNIX> modes are enabled.
  845. sub eliminate_macros {
  846. my($self,$path) = @_;
  847. return '' unless (defined $path) && ($path ne '');
  848. $self = {} unless ref $self;
  849. if ($path =~ /\s/) {
  850. return join ' ', map { $self->eliminate_macros($_) } split /\s+/, $path;
  851. }
  852. my $npath = unixify($path);
  853. # sometimes unixify will return a string with an off-by-one trailing null
  854. $npath =~ s{\0$}{};
  855. my($complex) = 0;
  856. my($head,$macro,$tail);
  857. # perform m##g in scalar context so it acts as an iterator
  858. while ($npath =~ m#(.*?)\$\((\S+?)\)(.*)#gs) {
  859. if (defined $self->{$2}) {
  860. ($head,$macro,$tail) = ($1,$2,$3);
  861. if (ref $self->{$macro}) {
  862. if (ref $self->{$macro} eq 'ARRAY') {
  863. $macro = join ' ', @{$self->{$macro}};
  864. }
  865. else {
  866. print "Note: can't expand macro \$($macro) containing ",ref($self->{$macro}),
  867. "\n\t(using MMK-specific deferred substitutuon; MMS will break)\n";
  868. $macro = "\cB$macro\cB";
  869. $complex = 1;
  870. }
  871. }
  872. else { ($macro = unixify($self->{$macro})) =~ s#/\Z(?!\n)##; }
  873. $npath = "$head$macro$tail";
  874. }
  875. }
  876. if ($complex) { $npath =~ s#\cB(.*?)\cB#\${$1}#gs; }
  877. $npath;
  878. }
  879. # Deprecated. See the note above for eliminate_macros().
  880. # Catchall routine to clean up problem MM[SK]/Make macros. Expands macros
  881. # in any directory specification, in order to avoid juxtaposing two
  882. # VMS-syntax directories when MM[SK] is run. Also expands expressions which
  883. # are all macro, so that we can tell how long the expansion is, and avoid
  884. # overrunning DCL's command buffer when MM[KS] is running.
  885. # fixpath() checks to see whether the result matches the name of a
  886. # directory in the current default directory and returns a directory or
  887. # file specification accordingly. C<$is_dir> can be set to true to
  888. # force fixpath() to consider the path to be a directory or false to force
  889. # it to be a file.
  890. sub fixpath {
  891. my($self,$path,$force_path) = @_;
  892. return '' unless $path;
  893. $self = bless {}, $self unless ref $self;
  894. my($fixedpath,$prefix,$name);
  895. if ($path =~ /\s/) {
  896. return join ' ',
  897. map { $self->fixpath($_,$force_path) }
  898. split /\s+/, $path;
  899. }
  900. if ($path =~ m#^\$\([^\)]+\)\Z(?!\n)#s || $path =~ m#[/:>\]]#) {
  901. if ($force_path or $path =~ /(?:DIR\)|\])\Z(?!\n)/) {
  902. $fixedpath = vmspath($self->eliminate_macros($path));
  903. }
  904. else {
  905. $fixedpath = vmsify($self->eliminate_macros($path));
  906. }
  907. }
  908. elsif ((($prefix,$name) = ($path =~ m#^\$\(([^\)]+)\)(.+)#s)) && $self->{$prefix}) {
  909. my($vmspre) = $self->eliminate_macros("\$($prefix)");
  910. # is it a dir or just a name?
  911. $vmspre = ($vmspre =~ m|/| or $prefix =~ /DIR\Z(?!\n)/) ? vmspath($vmspre) : '';
  912. $fixedpath = ($vmspre ? $vmspre : $self->{$prefix}) . $name;
  913. $fixedpath = vmspath($fixedpath) if $force_path;
  914. }
  915. else {
  916. $fixedpath = $path;
  917. $fixedpath = vmspath($fixedpath) if $force_path;
  918. }
  919. # No hints, so we try to guess
  920. if (!defined($force_path) and $fixedpath !~ /[:>(.\]]/) {
  921. $fixedpath = vmspath($fixedpath) if -d $fixedpath;
  922. }
  923. # Trim off root dirname if it's had other dirs inserted in front of it.
  924. $fixedpath =~ s/\.000000([\]>])/$1/;
  925. # Special case for VMS absolute directory specs: these will have had device
  926. # prepended during trip through Unix syntax in eliminate_macros(), since
  927. # Unix syntax has no way to express "absolute from the top of this device's
  928. # directory tree".
  929. if ($path =~ /^[\[>][^.\-]/) { $fixedpath =~ s/^[^\[<]+//; }
  930. $fixedpath;
  931. }
  932. =back
  933. =head1 COPYRIGHT
  934. Copyright (c) 2004 by the Perl 5 Porters. All rights reserved.
  935. This program is free software; you can redistribute it and/or modify
  936. it under the same terms as Perl itself.
  937. =head1 SEE ALSO
  938. See L<File::Spec> and L<File::Spec::Unix>. This package overrides the
  939. implementation of these methods, not the semantics.
  940. An explanation of VMS file specs can be found at
  941. L<"http://h71000.www7.hp.com/doc/731FINAL/4506/4506pro_014.html#apps_locating_naming_files">.
  942. =cut
  943. 1;