Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

505 lines
14 KiB

  1. package File::Spec::VMS;
  2. use strict;
  3. use vars qw(@ISA $VERSION);
  4. require File::Spec::Unix;
  5. $VERSION = '1.1';
  6. @ISA = qw(File::Spec::Unix);
  7. use Cwd;
  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. =over
  19. =item eliminate_macros
  20. Expands MM[KS]/Make macros in a text string, using the contents of
  21. identically named elements of C<%$self>, and returns the result
  22. as a file specification in Unix syntax.
  23. =cut
  24. sub eliminate_macros {
  25. my($self,$path) = @_;
  26. return '' unless $path;
  27. $self = {} unless ref $self;
  28. if ($path =~ /\s/) {
  29. return join ' ', map { $self->eliminate_macros($_) } split /\s+/, $path;
  30. }
  31. my($npath) = unixify($path);
  32. my($complex) = 0;
  33. my($head,$macro,$tail);
  34. # perform m##g in scalar context so it acts as an iterator
  35. while ($npath =~ m#(.*?)\$\((\S+?)\)(.*)#gs) {
  36. if ($self->{$2}) {
  37. ($head,$macro,$tail) = ($1,$2,$3);
  38. if (ref $self->{$macro}) {
  39. if (ref $self->{$macro} eq 'ARRAY') {
  40. $macro = join ' ', @{$self->{$macro}};
  41. }
  42. else {
  43. print "Note: can't expand macro \$($macro) containing ",ref($self->{$macro}),
  44. "\n\t(using MMK-specific deferred substitutuon; MMS will break)\n";
  45. $macro = "\cB$macro\cB";
  46. $complex = 1;
  47. }
  48. }
  49. else { ($macro = unixify($self->{$macro})) =~ s#/\Z(?!\n)##; }
  50. $npath = "$head$macro$tail";
  51. }
  52. }
  53. if ($complex) { $npath =~ s#\cB(.*?)\cB#\${$1}#gs; }
  54. $npath;
  55. }
  56. =item fixpath
  57. Catchall routine to clean up problem MM[SK]/Make macros. Expands macros
  58. in any directory specification, in order to avoid juxtaposing two
  59. VMS-syntax directories when MM[SK] is run. Also expands expressions which
  60. are all macro, so that we can tell how long the expansion is, and avoid
  61. overrunning DCL's command buffer when MM[KS] is running.
  62. If optional second argument has a TRUE value, then the return string is
  63. a VMS-syntax directory specification, if it is FALSE, the return string
  64. is a VMS-syntax file specification, and if it is not specified, fixpath()
  65. checks to see whether it matches the name of a directory in the current
  66. default directory, and returns a directory or file specification accordingly.
  67. =cut
  68. sub fixpath {
  69. my($self,$path,$force_path) = @_;
  70. return '' unless $path;
  71. $self = bless {} unless ref $self;
  72. my($fixedpath,$prefix,$name);
  73. if ($path =~ /\s/) {
  74. return join ' ',
  75. map { $self->fixpath($_,$force_path) }
  76. split /\s+/, $path;
  77. }
  78. if ($path =~ m#^\$\([^\)]+\)\Z(?!\n)#s || $path =~ m#[/:>\]]#) {
  79. if ($force_path or $path =~ /(?:DIR\)|\])\Z(?!\n)/) {
  80. $fixedpath = vmspath($self->eliminate_macros($path));
  81. }
  82. else {
  83. $fixedpath = vmsify($self->eliminate_macros($path));
  84. }
  85. }
  86. elsif ((($prefix,$name) = ($path =~ m#^\$\(([^\)]+)\)(.+)#s)) && $self->{$prefix}) {
  87. my($vmspre) = $self->eliminate_macros("\$($prefix)");
  88. # is it a dir or just a name?
  89. $vmspre = ($vmspre =~ m|/| or $prefix =~ /DIR\Z(?!\n)/) ? vmspath($vmspre) : '';
  90. $fixedpath = ($vmspre ? $vmspre : $self->{$prefix}) . $name;
  91. $fixedpath = vmspath($fixedpath) if $force_path;
  92. }
  93. else {
  94. $fixedpath = $path;
  95. $fixedpath = vmspath($fixedpath) if $force_path;
  96. }
  97. # No hints, so we try to guess
  98. if (!defined($force_path) and $fixedpath !~ /[:>(.\]]/) {
  99. $fixedpath = vmspath($fixedpath) if -d $fixedpath;
  100. }
  101. # Trim off root dirname if it's had other dirs inserted in front of it.
  102. $fixedpath =~ s/\.000000([\]>])/$1/;
  103. # Special case for VMS absolute directory specs: these will have had device
  104. # prepended during trip through Unix syntax in eliminate_macros(), since
  105. # Unix syntax has no way to express "absolute from the top of this device's
  106. # directory tree".
  107. if ($path =~ /^[\[>][^.\-]/) { $fixedpath =~ s/^[^\[<]+//; }
  108. $fixedpath;
  109. }
  110. =back
  111. =head2 Methods always loaded
  112. =over
  113. =item canonpath (override)
  114. Removes redundant portions of file specifications according to VMS syntax.
  115. =cut
  116. sub canonpath {
  117. my($self,$path) = @_;
  118. if ($path =~ m|/|) { # Fake Unix
  119. my $pathify = $path =~ m|/\Z(?!\n)|;
  120. $path = $self->SUPER::canonpath($path);
  121. if ($pathify) { return vmspath($path); }
  122. else { return vmsify($path); }
  123. }
  124. else {
  125. $path =~ s-\]\[--g; $path =~ s/><//g; # foo.][bar ==> foo.bar
  126. $path =~ s/([\[<])000000\./$1/; # [000000.foo ==> foo
  127. 1 while $path =~ s{([\[<-])\.-}{$1-}; # [.-.- ==> [--
  128. $path =~ s/\.[^\[<\.]+\.-([\]\>])/$1/; # bar.foo.-] ==> bar]
  129. $path =~ s/([\[<])(-+)/$1 . "\cx" x length($2)/e; # encode leading '-'s
  130. $path =~ s/([\[<\.])([^\[<\.\cx]+)\.-\.?/$1/g; # bar.-.foo ==> foo
  131. $path =~ s/([\[<])(\cx+)/$1 . '-' x length($2)/e; # then decode
  132. return $path;
  133. }
  134. }
  135. =item catdir
  136. Concatenates a list of file specifications, and returns the result as a
  137. VMS-syntax directory specification. No check is made for "impossible"
  138. cases (e.g. elements other than the first being absolute filespecs).
  139. =cut
  140. sub catdir {
  141. my ($self,@dirs) = @_;
  142. my $dir = pop @dirs;
  143. @dirs = grep($_,@dirs);
  144. my $rslt;
  145. if (@dirs) {
  146. my $path = (@dirs == 1 ? $dirs[0] : $self->catdir(@dirs));
  147. my ($spath,$sdir) = ($path,$dir);
  148. $spath =~ s/\.dir\Z(?!\n)//; $sdir =~ s/\.dir\Z(?!\n)//;
  149. $sdir = $self->eliminate_macros($sdir) unless $sdir =~ /^[\w\-]+\Z(?!\n)/s;
  150. $rslt = $self->fixpath($self->eliminate_macros($spath)."/$sdir",1);
  151. # Special case for VMS absolute directory specs: these will have had device
  152. # prepended during trip through Unix syntax in eliminate_macros(), since
  153. # Unix syntax has no way to express "absolute from the top of this device's
  154. # directory tree".
  155. if ($spath =~ /^[\[<][^.\-]/s) { $rslt =~ s/^[^\[<]+//s; }
  156. }
  157. else {
  158. if (not defined $dir or not length $dir) { $rslt = ''; }
  159. elsif ($dir =~ /^\$\([^\)]+\)\Z(?!\n)/s) { $rslt = $dir; }
  160. else { $rslt = vmspath($dir); }
  161. }
  162. return $self->canonpath($rslt);
  163. }
  164. =item catfile
  165. Concatenates a list of file specifications, and returns the result as a
  166. VMS-syntax file specification.
  167. =cut
  168. sub catfile {
  169. my ($self,@files) = @_;
  170. my $file = pop @files;
  171. @files = grep($_,@files);
  172. my $rslt;
  173. if (@files) {
  174. my $path = (@files == 1 ? $files[0] : $self->catdir(@files));
  175. my $spath = $path;
  176. $spath =~ s/\.dir\Z(?!\n)//;
  177. if ($spath =~ /^[^\)\]\/:>]+\)\Z(?!\n)/s && basename($file) eq $file) {
  178. $rslt = "$spath$file";
  179. }
  180. else {
  181. $rslt = $self->eliminate_macros($spath);
  182. $rslt = vmsify($rslt.($rslt ? '/' : '').unixify($file));
  183. }
  184. }
  185. else { $rslt = (defined($file) && length($file)) ? vmsify($file) : ''; }
  186. return $self->canonpath($rslt);
  187. }
  188. =item curdir (override)
  189. Returns a string representation of the current directory: '[]'
  190. =cut
  191. sub curdir {
  192. return '[]';
  193. }
  194. =item devnull (override)
  195. Returns a string representation of the null device: '_NLA0:'
  196. =cut
  197. sub devnull {
  198. return "_NLA0:";
  199. }
  200. =item rootdir (override)
  201. Returns a string representation of the root directory: 'SYS$DISK:[000000]'
  202. =cut
  203. sub rootdir {
  204. return 'SYS$DISK:[000000]';
  205. }
  206. =item tmpdir (override)
  207. Returns a string representation of the first writable directory
  208. from the following list or '' if none are writable:
  209. sys$scratch:
  210. $ENV{TMPDIR}
  211. =cut
  212. my $tmpdir;
  213. sub tmpdir {
  214. return $tmpdir if defined $tmpdir;
  215. foreach ('sys$scratch:', $ENV{TMPDIR}) {
  216. next unless defined && -d && -w _;
  217. $tmpdir = $_;
  218. last;
  219. }
  220. $tmpdir = '' unless defined $tmpdir;
  221. return $tmpdir;
  222. }
  223. =item updir (override)
  224. Returns a string representation of the parent directory: '[-]'
  225. =cut
  226. sub updir {
  227. return '[-]';
  228. }
  229. =item case_tolerant (override)
  230. VMS file specification syntax is case-tolerant.
  231. =cut
  232. sub case_tolerant {
  233. return 1;
  234. }
  235. =item path (override)
  236. Translate logical name DCL$PATH as a searchlist, rather than trying
  237. to C<split> string value of C<$ENV{'PATH'}>.
  238. =cut
  239. sub path {
  240. my (@dirs,$dir,$i);
  241. while ($dir = $ENV{'DCL$PATH;' . $i++}) { push(@dirs,$dir); }
  242. return @dirs;
  243. }
  244. =item file_name_is_absolute (override)
  245. Checks for VMS directory spec as well as Unix separators.
  246. =cut
  247. sub file_name_is_absolute {
  248. my ($self,$file) = @_;
  249. # If it's a logical name, expand it.
  250. $file = $ENV{$file} while $file =~ /^[\w\$\-]+\Z(?!\n)/s && $ENV{$file};
  251. return scalar($file =~ m!^/!s ||
  252. $file =~ m![<\[][^.\-\]>]! ||
  253. $file =~ /:[^<\[]/);
  254. }
  255. =item splitpath (override)
  256. Splits using VMS syntax.
  257. =cut
  258. sub splitpath {
  259. my($self,$path) = @_;
  260. my($dev,$dir,$file) = ('','','');
  261. vmsify($path) =~ /(.+:)?([\[<].*[\]>])?(.*)/s;
  262. return ($1 || '',$2 || '',$3);
  263. }
  264. =item splitdir (override)
  265. Split dirspec using VMS syntax.
  266. =cut
  267. sub splitdir {
  268. my($self,$dirspec) = @_;
  269. $dirspec =~ s/\]\[//g; $dirspec =~ s/\-\-/-.-/g;
  270. $dirspec = "[$dirspec]" unless $dirspec =~ /[\[<]/; # make legal
  271. my(@dirs) = split('\.', vmspath($dirspec));
  272. $dirs[0] =~ s/^[\[<]//s; $dirs[-1] =~ s/[\]>]\Z(?!\n)//s;
  273. @dirs;
  274. }
  275. =item catpath (override)
  276. Construct a complete filespec using VMS syntax
  277. =cut
  278. sub catpath {
  279. my($self,$dev,$dir,$file) = @_;
  280. if ($dev =~ m|^/+([^/]+)|) { $dev = "$1:"; }
  281. else { $dev .= ':' unless $dev eq '' or $dev =~ /:\Z(?!\n)/; }
  282. if (length($dev) or length($dir)) {
  283. $dir = "[$dir]" unless $dir =~ /[\[<\/]/;
  284. $dir = vmspath($dir);
  285. }
  286. "$dev$dir$file";
  287. }
  288. =item abs2rel (override)
  289. Use VMS syntax when converting filespecs.
  290. =cut
  291. sub abs2rel {
  292. my $self = shift;
  293. return vmspath(File::Spec::Unix::abs2rel( $self, @_ ))
  294. if ( join( '', @_ ) =~ m{/} ) ;
  295. my($path,$base) = @_;
  296. # Note: we use '/' to glue things together here, then let canonpath()
  297. # clean them up at the end.
  298. # Clean up $path
  299. if ( ! $self->file_name_is_absolute( $path ) ) {
  300. $path = $self->rel2abs( $path ) ;
  301. }
  302. else {
  303. $path = $self->canonpath( $path ) ;
  304. }
  305. # Figure out the effective $base and clean it up.
  306. if ( !defined( $base ) || $base eq '' ) {
  307. $base = cwd() ;
  308. }
  309. elsif ( ! $self->file_name_is_absolute( $base ) ) {
  310. $base = $self->rel2abs( $base ) ;
  311. }
  312. else {
  313. $base = $self->canonpath( $base ) ;
  314. }
  315. # Split up paths
  316. my ( $path_directories, $path_file ) =
  317. ($self->splitpath( $path, 1 ))[1,2] ;
  318. $path_directories = $1
  319. if $path_directories =~ /^\[(.*)\]\Z(?!\n)/s ;
  320. my $base_directories = ($self->splitpath( $base, 1 ))[1] ;
  321. $base_directories = $1
  322. if $base_directories =~ /^\[(.*)\]\Z(?!\n)/s ;
  323. # Now, remove all leading components that are the same
  324. my @pathchunks = $self->splitdir( $path_directories );
  325. my @basechunks = $self->splitdir( $base_directories );
  326. while ( @pathchunks &&
  327. @basechunks &&
  328. lc( $pathchunks[0] ) eq lc( $basechunks[0] )
  329. ) {
  330. shift @pathchunks ;
  331. shift @basechunks ;
  332. }
  333. # @basechunks now contains the directories to climb out of,
  334. # @pathchunks now has the directories to descend in to.
  335. $path_directories = '-.' x @basechunks . join( '.', @pathchunks ) ;
  336. $path_directories =~ s{\.\Z(?!\n)}{} ;
  337. return $self->canonpath( $self->catpath( '', $path_directories, $path_file ) ) ;
  338. }
  339. =item rel2abs (override)
  340. Use VMS syntax when converting filespecs.
  341. =cut
  342. sub rel2abs {
  343. my $self = shift ;
  344. return vmspath(File::Spec::Unix::rel2abs( $self, @_ ))
  345. if ( join( '', @_ ) =~ m{/} ) ;
  346. my ($path,$base ) = @_;
  347. # Clean up and split up $path
  348. if ( ! $self->file_name_is_absolute( $path ) ) {
  349. # Figure out the effective $base and clean it up.
  350. if ( !defined( $base ) || $base eq '' ) {
  351. $base = cwd() ;
  352. }
  353. elsif ( ! $self->file_name_is_absolute( $base ) ) {
  354. $base = $self->rel2abs( $base ) ;
  355. }
  356. else {
  357. $base = $self->canonpath( $base ) ;
  358. }
  359. # Split up paths
  360. my ( $path_directories, $path_file ) =
  361. ($self->splitpath( $path ))[1,2] ;
  362. my ( $base_volume, $base_directories ) =
  363. $self->splitpath( $base ) ;
  364. $path_directories = '' if $path_directories eq '[]' ||
  365. $path_directories eq '<>';
  366. my $sep = '' ;
  367. $sep = '.'
  368. if ( $base_directories =~ m{[^.\]>]\Z(?!\n)} &&
  369. $path_directories =~ m{^[^.\[<]}s
  370. ) ;
  371. $base_directories = "$base_directories$sep$path_directories";
  372. $base_directories =~ s{\.?[\]>][\[<]\.?}{.};
  373. $path = $self->catpath( $base_volume, $base_directories, $path_file );
  374. }
  375. return $self->canonpath( $path ) ;
  376. }
  377. =back
  378. =head1 SEE ALSO
  379. L<File::Spec>
  380. =cut
  381. 1;