/*
* Copyright 1995, 1996 Perforce Software. All rights reserved.
*
* This file is part of Perforce - the FAST SCM System.
*/
/*
* PathVMS - pathname manipuation for VMS.
*
* What a VMS pathname looks like:
*
* device:[dir.dir.dir]file.suffix
*
* Suffix note:
*
* VMS has a penchange for providing a (wrong) file suffix when none
* is present. To deal with this, the routines which produce local
* file names (SetCanon() and SetLocal()) append a . if
* none is present, and GetCanon() strip it if it is the
* last character in the file name. This makes it not possible to
* refer to a file that just ends in . in the depot, though.
*
* Methods defined:
*
* PathSys::SetCanon() - combine (local) root and canonical path
*
* ROOT PATH RESULT
* -----------------------------------------------
* x:[d1.d2] + f x:[d1.d2]f
* x:[d1.d2] + d3/f x:[d1.d2.d3]f
* x:[000000] + f x:[000000]f
* x:[000000] + d3/f x:[d3]f
*
* PathSys::SetLocal() - combine (local) root and local path
*
* CWD PATH RESULT
* -----------------------------------------------
* x:[d1.d2] + f x:[d1.d2]f
* x:[d1.d2] + [.d3]f x:[d1.d2.d3]f
* x:[d1.d2] + [d3]f x:[d3]f
* x:[d1.d2] + x2:[d3]f x2:[d3]f
* x:[000000] + f x:[000000]f
* x:[000000] + [d3]f x:[d3]f
* x:[000000] + [.d3]f x:[d3]f
*
* x:[d1.d2] + [-]f x:[d1]f
* x:[d1.d2] + [--]f x:[000000]f
* x:[d1.d2] + [---]f x:[000000]f
* x:[d1.d2] + [-.d3]f x:[d1.d3]f
*
* logical: + f logical:f
*
* PathSys::GetCanon() - strip root and return rest as canon
*
* ROOT PATH RESULT
* -----------------------------------------------
* x:[d1.d2] - x:[d1.d2]f f
* x:[d1] - x:[d1.d2]f d2/f
* x:[000000] - x:[000000]f f
* x: - x:[d1]f d1/f
*
* PathSys::ToParent() - strip last element of path
*
* PATH PARENT
* -----------------------------------------------
* x:[d1.d2]f x:[d1.d2]
* x:[d1.d2] x:[d1]
* x:[d1] x:[000000]
* x:[000000] x:[000000]
*
* Private methods:
*
* PathVMS::GetPointers() - set up pointers to the []'s in the path
* PathVMS::ToRoot() - reset the path to be the root dir on the device
* PathVMS::AddDirectory() - add a directory component onto end of path
*
*/
# include <stdhdrs.h>
# include <ctype.h>
# include <error.h>
# include <strbuf.h>
# include <pathsys.h>
# include <pathvms.h>
/*
* PathVMS::GetPointers() - set up pointers to the []'s in the path
*/
void
PathVMS::GetPointers()
{
char *l, *r;
if( ( l = strchr( Text(), '[' ) ) && ( r = strchr( l, ']' ) ) )
{
lbrace = l - Text();
rbrace = r - Text();
atroot = r - l == 7 && !strncmp( "[000000]", l, 8 );
}
else
{
// no braces mean it's "logical:" -- we're at the root
lbrace = rbrace = -1;
atroot = 1;
}
}
int
PathVMS::IsUnderRoot( const StrPtr &root )
{
return 0;
}
/*
* PathVMS::ToRoot() - reset the path to be the root dir on the device
*/
void
PathVMS::ToRoot()
{
// If there are no braces, we can't go to the e
// root ('cause we're already there)
if( lbrace >= 0 )
{
SetLength( lbrace );
Append( "[000000]" );
rbrace = Length() - 1;
}
atroot = 1;
}
/*
* PathVMS::AddDirectory() - add a directory component onto end of path
*/
void
PathVMS::AddDirectory( const char *dir, int len )
{
// logical + dir = logical:[dir]
// dev:[000000] + dir = dev:[dir]
// dev:[dir0] + dir = dev:[dir0.dir]
if( lbrace < 0 )
{
lbrace = Length();
Append( "[" );
atroot = 0;
}
else if( atroot )
{
SetLength( lbrace + 1 );
atroot = 0;
}
else
{
SetLength( rbrace );
Append( "." );
}
// Append directory
Append( dir, len );
// Set new rbrace
rbrace = Length();
// Append closing brace
Append( "]" );
}
/*
* PathSys::ToParent() - strip last element of path
*/
int
PathVMS::ToParent( StrBuf *file )
{
GetPointers();
if( file )
file->Set( Text() + rbrace + 1 );
return ToParentHavePointers();
}
int
PathVMS::ToParentHavePointers()
{
// Actually, lbrace<0 implies atroot (i.e. implies logical:)
// But we like to be safe.
if( lbrace < 0 || atroot )
return 0;
// Truncate filename if there is one.
if( Length() > rbrace + 1 )
{
SetLength( rbrace + 1 );
// geeze, shouldn't SetLength do this?
Terminate();
return 1;
}
// Look for previous directory name
while( --rbrace > lbrace )
if( Text()[rbrace] == '.' )
break;
// If it's there, back over it.
// Else, set to root.
if( rbrace > lbrace )
{
SetLength( rbrace );
Append( "]" );
}
else
{
ToRoot();
}
return 1;
}
/*
* PathSys::SetCanon() - combine local root and canonical path
*/
void
PathVMS::SetCanon( const StrPtr &root, const StrPtr &canon )
{
// Step 1: use root as basis, but find the pieces.
if( &root != this )
Set( root );
GetPointers();
// Step 2: Add each directory component
const char *c;
const char *cn = canon.Text();
for( ; c = strchr( cn, '/' ); cn = c + 1 )
AddDirectory( cn, c - cn );
// Step 3: add file component
Append( cn );
// Step 4: append . if none in file name
if( !strchr( cn, '.' ) )
Append( ".", 1 );
}
/*
* PathSys::SetLocal() - combine (local) root and local path
*/
void
PathVMS::SetLocal( const StrPtr &root, const StrPtr &localptr )
{
// Step 1: if local has a ":" in it, just use local wholesale.
if( strchr( localptr.Text(), ':' ) )
{
Set( localptr );
return;
}
// Step 2: use root as basis, but find the pieces.
if( this != &root )
Set( root );
GetPointers();
// Step 3: handle directory stuff "[xxxx]"
const char *local = localptr.Text();
if( local[0] == '[' )
{
// If it isn't [-xxx] or [.xxx], we start at the root
++local;
if( *local != '-' && *local != '.' )
ToRoot();
// Handler [-whatever]
for( ; *local == '-'; ++local )
ToParentHavePointers();
// We've already accounted for a leading .
if( *local == '.' )
++local;
// Now tack on directories, dot by dot, until we hit ]
const char *rbrack = strchr( local, ']' );
const char *d;
// Directories before .
while( ( d = strchr( local, '.' ) ) && d < rbrack )
{
AddDirectory( local, d - local );
local = d + 1;
}
// Directory before ]
if( local < rbrack )
AddDirectory( local, rbrack - local );
if( rbrack )
local = rbrack + 1;
}
// Step 4: add file
Append( local );
// Step 5: append . if none in file name
if( !strchr( local, '.' ) )
Append( ".", 1 );
}
/*
* PathSys::GetCanon() - strip root and return rest as canon
*/
int
PathVMS::GetCanon( const StrPtr &root, StrBuf &target )
{
const char *r = root.Text();
const char *s = Text();
const char *t;
// Step 1: See how far strings match.
while( *s && tolower( *s ) == tolower( *r ) )
++s, ++r;
// Root (r) must match entirely or to its ].
// If matched to its ], then remaining path (s) must be .xxx]
if( *r != ']' ? *r : *s++ != '.' )
return 0;
// Root (r) may not have had a [] component, which leaves
// remaining path (s) beginning with a [. Strip it.
if( !*r && *s == '[' )
++s;
// Step 2: Tack on directory components, separated by /'s
if( *s )
target.Append( "/" );
// 2a: directories before .'s
const char *rbrack = strchr( s, ']' );
while( ( t = strchr( s, '.' ) ) && t < rbrack )
{
target.Append( s, t - s );
target.Append( "/" );
s = t + 1;
}
// 2b: directory before ]
if( s < rbrack )
{
target.Append( s, rbrack - s );
target.Append( "/" );
}
if( rbrack )
s = rbrack + 1;
// Step 3: Add file, stripping a trailing .
if( !( t = strchr( s, '.' ) ) || t[1] )
t = s + strlen( s );
target.Append( s, t - s );
return 1;
}