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.
 
 
 
 
 
 

1001 lines
24 KiB

/*++
Copyright (c) 1989-92 Microsoft Corporation
Module Name:
pathtype.c
Abstract:
The NetpPathType routine attempts to determine the type of a path
Net path type routines:
NetpwPathType
(TokenAdvance)
(DeviceTokenToDeviceType)
(SysnameTokenToSysnameType)
(TypeParseMain)
(TypeParseLeadSlashName)
(TypeParseUNCName)
(TypeParseOneLeadSlashName)
(TypeParseNoLeadSlashName)
(TypeParseRelPath)
(TypeParseDeviceName)
(TypeParseMailslotPath)
(TypeParseAbsPath)
(TypeParseUNCPath)
(TypeParseSysPath)
(TypeParseDevPath)
(TypeParseOptSlash)
(TypeParseOptColon)
(TypeParseOptRelPath)
Author:
Danny Glasser (dannygl) 16 June 1989
Revision History:
01-Dec-1992 JohnRo
RAID 4371: probable device canon bug.
Use NetpKdPrint instead of NT-specific version.
--*/
#include "nticanon.h"
#define PATHLEN1_1 128 // Lanman 1.0 path len ((ie OS2 1.1/FAT))
//
// PARSER_PARMS - A structure containing the parameters passed to each
// of the parser functions. We put all of these parameters in a
// structure so that they're not all separate function parameters.
// This saves stack space and makes it easier to change the parameter
// format, at the expense of re-entrancy.
//
typedef struct
{
LPDWORD PathType; /* Pointer to path type */
LPTSTR Token; /* Pointer to the front of the current token */
LPTSTR TokenEnd; /* Pointer to the end of the current token */
DWORD TokenType; /* Type of the current token */
DWORD Flags; /* Flags to determine function operation */
} PARSER_PARMS;
typedef PARSER_PARMS* PPARSER_PARMS;
//
// Values for <Flags> in PARSER_PARMS
//
#define PPF_MATCH_OPTIONAL 0x00000001L
#define PPF_8_DOT_3 0x00000002L
#define PPF_RESERVED (~(PPF_MATCH_OPTIONAL | PPF_8_DOT_3))
//
// data
//
DWORD cbMaxPathLen = MAX_PATH-1;
DWORD cbMaxPathCompLen = MAX_PATH; // ??
//
// local prototypes
//
STATIC DWORD TokenAdvance(PARSER_PARMS far *parms);
STATIC DWORD DeviceTokenToDeviceType(ULONG TokenType);
STATIC DWORD SysnameTokenToSysnameType(ULONG TokenType);
STATIC DWORD TypeParseMain (PPARSER_PARMS parms);
STATIC DWORD TypeParseLeadSlashName (PPARSER_PARMS parms);
STATIC DWORD TypeParseUNCName (PPARSER_PARMS parms);
STATIC DWORD TypeParseOneLeadSlashName (PPARSER_PARMS parms);
STATIC DWORD TypeParseNoLeadSlashName (PPARSER_PARMS parms);
STATIC DWORD TypeParseRelPath (PPARSER_PARMS parms);
STATIC DWORD TypeParseDeviceName (PPARSER_PARMS parms);
STATIC DWORD TypeParseMailslotPath (PPARSER_PARMS parms);
STATIC DWORD TypeParseAbsPath (PPARSER_PARMS parms);
STATIC DWORD TypeParseUNCPath (PPARSER_PARMS parms);
STATIC DWORD TypeParseSysPath (PPARSER_PARMS parms);
STATIC DWORD TypeParseDevPath (PPARSER_PARMS parms);
STATIC DWORD TypeParseOptSlash (PPARSER_PARMS parms);
STATIC DWORD TypeParseOptColon (PPARSER_PARMS parms);
STATIC DWORD TypeParseOptRelPath (PPARSER_PARMS parms);
//
// some commonly occurring operations as macros to improve readability
//
#define ADVANCE_TOKEN() if (RetVal = TokenAdvance(parms)) {return RetVal;}
#define TURN_ON(flag) (parms->Flags |= (PPF_##flag))
#define TURN_OFF(flag) (parms->Flags &= (~PPF_##flag))
#define PARSE_NULL() if (parms->TokenType & TOKEN_TYPE_EOS) {\
return 0;\
} else {\
return ERROR_INVALID_NAME;\
}
//
// routines
//
NET_API_STATUS
NetpwPathType(
IN LPTSTR PathName,
OUT LPDWORD PathType,
IN DWORD Flags
)
/*++
Routine Description:
NetpPathType parses the specified pathname, determining that
it is a valid pathname and determining its path type. Path type
values are defined as ITYPE_* manifest constants in ICANON.H.
If this call is remoted and the remote server returns
NERR_InvalidAPI (meaning that the server doesn't know about this
function, i.e. it's a LM 1.0D server), the work is done locally
with the OLDPATHS bits set.
Arguments:
PathName - The pathname to validate and type.
PathType - The place to store the type.
Flags - Flags to determine operation. Currently defined
values are:
rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrro
where:
r = Reserved. MBZ.
o = If set, the function uses old-style pathname rules
(128-byte paths, 8.3 components) when validating the
pathname. This flag gets set automatically on DOS
and OS/2 1.1 systems.
Return Value:
0 if successful.
The error number (> 0) if unsuccessful.
Possible error returns include:
ERROR_INVALID_PARAMETER
ERROR_INVALID_NAME
NERR_BufTooSmall
any errors returned by the API functions called by this function.
--*/
{
NET_API_STATUS RetVal;
DWORD Len;
PARSER_PARMS parms;
#ifdef CANONDBG
NetpKdPrint(("NetpwPathType\n"));
#endif
*PathType = 0;
if (Flags & INPT_FLAGS_RESERVED) {
return ERROR_INVALID_PARAMETER;
}
if (ARGUMENT_PRESENT(PathName)) {
Len = (DWORD)STRLEN(PathName);
} else {
Len = 0;
}
if (!Len || (Len > MAX_PATH - 1)) {
return ERROR_INVALID_NAME;
}
//
// Initialize parser parameter structure
//
parms.PathType = PathType;
parms.Flags = 0L;
if (Flags & INPT_FLAGS_OLDPATHS) {
parms.Flags |= PPF_8_DOT_3;
}
parms.Token = PathName;
RetVal = GetToken(
parms.Token,
&parms.TokenEnd,
&parms.TokenType,
(parms.Flags & PPF_8_DOT_3) ? GTF_8_DOT_3 : 0L
);
if (RetVal) {
return RetVal;
}
//
// Now call the parser
//
RetVal = TypeParseMain(&parms);
if (RetVal) {
return RetVal;
}
//
// If we get here, the parsing operation succeeded. We return 0
// unless the path type is still 0, in which case we return NERR_CantType.
//
//
return *PathType ? NERR_Success : NERR_CantType;
}
STATIC DWORD TokenAdvance(PPARSER_PARMS parms)
{
parms->Token = parms->TokenEnd;
return GetToken(parms->Token,
&parms->TokenEnd,
&parms->TokenType,
(parms->Flags & PPF_8_DOT_3) ? GTF_8_DOT_3 : 0L
);
}
STATIC DWORD DeviceTokenToDeviceType(DWORD TokenType)
{
DWORD DeviceType = 0;
if (TokenType & (TOKEN_TYPE_LPT | TOKEN_TYPE_PRN)) {
DeviceType = ITYPE_LPT;
} else if (TokenType & (TOKEN_TYPE_COM | TOKEN_TYPE_AUX)) {
DeviceType = ITYPE_COM;
} else if (TokenType & TOKEN_TYPE_CON) {
DeviceType = ITYPE_CON;
} else if (TokenType & TOKEN_TYPE_NUL) {
DeviceType = ITYPE_NUL;
}
return DeviceType;
}
STATIC DWORD SysnameTokenToSysnameType(DWORD TokenType)
{
DWORD SysnameType = 0;
if (TokenType & TOKEN_TYPE_MAILSLOT) {
SysnameType = ITYPE_SYS_MSLOT;
} else if (TokenType & TOKEN_TYPE_PIPE) {
SysnameType = ITYPE_SYS_PIPE;
} else if (TokenType & TOKEN_TYPE_PRINT) {
SysnameType = ITYPE_SYS_PRINT;
} else if (TokenType & TOKEN_TYPE_COMM) {
SysnameType = ITYPE_SYS_COMM;
} else if (TokenType & TOKEN_TYPE_SEM) {
SysnameType = ITYPE_SYS_SEM;
} else if (TokenType & TOKEN_TYPE_SHAREMEM) {
SysnameType = ITYPE_SYS_SHMEM;
} else if (TokenType & TOKEN_TYPE_QUEUES) {
SysnameType = ITYPE_SYS_QUEUE;
}
return SysnameType;
}
STATIC DWORD TypeParseMain(PPARSER_PARMS parms)
{
DWORD RetVal = NERR_CantType;
//
// <MAIN> --> <slash> <leadslashname> <null>
//
if (parms->TokenType & TOKEN_TYPE_SLASH) {
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
if (RetVal = TypeParseLeadSlashName(parms)) {
return RetVal;
}
PARSE_NULL();
} else {
//
// <MAIN> --> <noleadslashname> <null>
//
if (RetVal = TypeParseNoLeadSlashName(parms)) {
if (RetVal == NERR_CantType) {
//
// Return ERROR_INVALID_NAME if MATCH_OPTIONAL isn't set
//
if (!(parms->Flags & PPF_MATCH_OPTIONAL)) {
RetVal = ERROR_INVALID_NAME;
}
}
return RetVal;
}
PARSE_NULL();
}
}
STATIC DWORD TypeParseLeadSlashName(PPARSER_PARMS parms)
{
DWORD RetVal = NERR_CantType;
BOOL fSetMatchOptional = FALSE;
//
// <leadslashname> --> <slash> <uncname>
//
if (parms->TokenType & TOKEN_TYPE_SLASH) {
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
return TypeParseUNCName(parms);
} else {
//
// <leadslashname> --> <oneleadslashname>
//
//
// Turn on MATCH_OPTIONAL flag (if it isn't already on)
//
if (!(parms->Flags & PPF_MATCH_OPTIONAL)) {
fSetMatchOptional = TRUE;
TURN_ON(MATCH_OPTIONAL);
}
RetVal = TypeParseOneLeadSlashName(parms);
if (fSetMatchOptional) {
TURN_OFF(MATCH_OPTIONAL);
}
if (RetVal != NERR_CantType) {
return RetVal;
}
}
//
// <leadslashname> --> {}
//
if (RetVal == NERR_CantType) {
//
// There's nothing after the slash, but it's still an absolute path
//
*parms->PathType |= (ITYPE_PATH | ITYPE_ABSOLUTE);
RetVal = 0;
}
return RetVal;
}
STATIC DWORD TypeParseUNCName(PPARSER_PARMS parms)
{
DWORD RetVal = NERR_CantType;
if( parms->TokenType & TOKEN_TYPE_DOT ) {
//
// Take care of paths like //./stuff\stuff\..
//
ADVANCE_TOKEN();
if( parms->TokenType & TOKEN_TYPE_SLASH ) {
//
// If it starts with //./, then let anything else through
//
*parms->PathType |= ITYPE_PATH | ITYPE_ABSOLUTE | ITYPE_DPATH;
//
// Chew up the rest of the input.
//
while( TokenAdvance(parms) == 0 ) {
if( parms->TokenType & TOKEN_TYPE_EOS ) {
break;
}
}
return 0;
}
return ERROR_INVALID_NAME;
}
//
// Set the UNC type bit
//
*parms->PathType |= ITYPE_UNC;
//
// We turn off 8.3 checking for UNC names, because we don't want to
// impose this restriction on remote names.
//
TURN_OFF(8_DOT_3);
//
// <uncname> --> <computername> <uncpath>
//
if (parms->TokenType & TOKEN_TYPE_COMPUTERNAME) {
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
if (RetVal = TypeParseUNCPath(parms)) {
return RetVal;
}
} else if (parms->TokenType & TOKEN_TYPE_WILDONE) {
//
// <uncname> --> "*" <mailslotpath>
//
//
// Set the wildcard type bit
//
*parms->PathType |= ITYPE_WILD;
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
if (RetVal = TypeParseMailslotPath(parms)) {
return RetVal;
}
}
if (RetVal == 0) {
//
// HACK - Since ITYPE_PATH and ITYPE_ABSOLUTE are not for UNC paths,
// we need to turn them off here.
//
*parms->PathType &= ~(ITYPE_PATH | ITYPE_ABSOLUTE);
} else if (RetVal == NERR_CantType) {
RetVal = (parms->Flags & PPF_MATCH_OPTIONAL)
? NERR_CantType
: ERROR_INVALID_NAME;
}
return RetVal;
}
STATIC DWORD TypeParseOneLeadSlashName(PPARSER_PARMS parms)
{
DWORD RetVal = NERR_CantType;
BOOL fDevice = FALSE;
//
// <oneleadslashname> --> <sysname> <syspath>
//
if (parms->TokenType & TOKEN_TYPE_SYSNAME) {
//
// Set the appropriate Sysname type bits
//
*parms->PathType |= SysnameTokenToSysnameType(parms->TokenType);
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
RetVal = TypeParseSysPath(parms);
} else if (parms->TokenType & TOKEN_TYPE_MAILSLOT) {
//
// <oneleadslashname> --> <mailslotname> <syspath>
//
//
// Set the appropriate Mailslot type bits
//
*parms->PathType |= ITYPE_SYS_MSLOT;
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
RetVal = TypeParseSysPath(parms);
} else if (parms->TokenType & TOKEN_TYPE_DEV) {
//
// <oneleadslashname> --> <deviceprefix> <devpath>
//
//
// Set the device flag (for use below)
//
fDevice = TRUE;
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
RetVal = TypeParseDevPath(parms);
} else {
//
// <oneleadslashname> --> <relpath>
//
RetVal = TypeParseRelPath(parms);
if (RetVal == NERR_CantType) {
//
// Return ERROR_INVALID_NAME if MATCH_OPTIONAL isn't set
//
if (!(parms->Flags & PPF_MATCH_OPTIONAL)) {
RetVal = ERROR_INVALID_NAME;
}
}
}
//
// If we were able to determine the type of the object and it isn't
// a device, we know that we have an absolute path, so we turn on the
// absolute type bit.
//
if (! RetVal && ! fDevice) {
*parms->PathType |= ITYPE_ABSOLUTE;
}
return RetVal;
}
STATIC DWORD TypeParseNoLeadSlashName(PPARSER_PARMS parms)
{
DWORD RetVal = NERR_CantType;
LPTSTR PreviousToken;
DWORD ulPreviousTokenType;
DWORD ulSavedType;
//
// <noleadslashname> --> <driveletter> ":" <optslash> <optrelpath>
//
// WARNING: Since a drive letter can also be a component name, it's
// impossible to determine which production to use without
// looking at the next token (to see if it's ":"). We have
// to cheat here to get around this hole in the grammar.
//
if (parms->TokenType & TOKEN_TYPE_DRIVE) {
//
// Save the current token pointer and type (in case we need to backtrack)
//
PreviousToken = parms->Token;
ulPreviousTokenType = parms->TokenType;
ADVANCE_TOKEN();
//
// Parse ":"; restore token if it fails, otherwise proceed
//
if (! (parms->TokenType & TOKEN_TYPE_COLON)) {
parms->TokenEnd = parms->Token;
parms->Token = PreviousToken;
parms->TokenType = ulPreviousTokenType;
} else {
TURN_OFF(MATCH_OPTIONAL);
//
// We save the object type here; if it hasn't changed after
// the calls to OptSlash and OptRelPath, then we know we have
// a disk device only. If it does change, we know he have a
// drive path. In either case, we use this information to
// set the appropriate type bits.
//
ulSavedType = *parms->PathType;
ADVANCE_TOKEN();
if (RetVal = TypeParseOptSlash(parms)) {
return RetVal;
}
if (RetVal = TypeParseOptRelPath(parms)) {
return RetVal;
}
//
// Set type bits based on whether the calls to OptSlash and
// OptRelpath changed the type.
//
*parms->PathType |= (ulSavedType == *parms->PathType)
? (ITYPE_DEVICE | ITYPE_DISK)
: ITYPE_DPATH;
return 0;
}
} else if (parms->TokenType & TOKEN_TYPE_LOCALDEVICE) {
//
// <noleadslashname> --> <localdevice> <optcolon>
//
//
// Set the appropriate Device type bits
//
*parms->PathType |= ITYPE_DEVICE |
DeviceTokenToDeviceType(parms->TokenType);
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
return TypeParseOptColon(parms);
}
//
// <noleadslashname> --> <relpath>
//
RetVal = TypeParseRelPath(parms);
if (RetVal == NERR_CantType) {
//
// Return ERROR_INVALID_NAME if MATCH_OPTIONAL isn't set
//
if (!(parms->Flags & PPF_MATCH_OPTIONAL)) {
RetVal = ERROR_INVALID_NAME;
}
}
return RetVal;
}
STATIC DWORD TypeParseRelPath(PPARSER_PARMS parms)
{
DWORD RetVal = NERR_CantType;
//
// <relpath> --> <component> <abspath>
//
if (parms->TokenType & TOKEN_TYPE_COMPONENT) {
//
// Set the path bit
//
*parms->PathType |= ITYPE_PATH;
//
// Set the wildcard type bit, if appropriate
//
if (parms->TokenType & TOKEN_TYPE_WILDCARD) {
*parms->PathType |= ITYPE_WILD;
}
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
return TypeParseAbsPath(parms);
} else {
return (parms->Flags & PPF_MATCH_OPTIONAL)
? NERR_CantType
: ERROR_INVALID_NAME;
}
}
STATIC DWORD TypeParseDeviceName(PPARSER_PARMS parms)
{
DWORD RetVal = NERR_CantType;
//
// <devicename> --> <localdevice>
//
if (parms->TokenType & TOKEN_TYPE_LOCALDEVICE) {
//
// Set the appropriate Device type bits
//
*parms->PathType |= DeviceTokenToDeviceType(parms->TokenType);
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
return 0;
} else if (parms->TokenType & TOKEN_TYPE_COMPONENT) {
//
// <devicename> --> <component>
//
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
return 0;
} else {
return (parms->Flags & PPF_MATCH_OPTIONAL)
? NERR_CantType
: ERROR_INVALID_NAME;
}
}
STATIC DWORD TypeParseMailslotPath(PPARSER_PARMS parms)
{
DWORD RetVal = NERR_CantType;
//
// <mailslotpath> --> <slash> <mailslotname> <syspath>
//
if (parms->TokenType & TOKEN_TYPE_SLASH) {
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
//
// Parse <mailslotname>
//
if (! (parms->TokenType & TOKEN_TYPE_MAILSLOT)) {
return ERROR_INVALID_NAME;
}
//
// Set the appropriate Mailslot type bits
//
*parms->PathType |= ITYPE_SYS_MSLOT;
ADVANCE_TOKEN();
return TypeParseSysPath(parms);
}
//
// <mailslotpath> --> {}
//
if (RetVal == NERR_CantType) {
//
// Since there's no mailslot path, this is a UNC wildcard compname
//
*parms->PathType |= ITYPE_COMPNAME;
RetVal = 0;
}
return RetVal;
}
STATIC DWORD TypeParseAbsPath(PPARSER_PARMS parms)
{
DWORD RetVal = NERR_CantType;
//
// <abspath> --> <slash> <component> <abspath>
//
if (parms->TokenType & TOKEN_TYPE_SLASH) {
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
//
// Parse <component>
//
if (!(parms->TokenType & TOKEN_TYPE_COMPONENT)) {
return ERROR_INVALID_NAME;
}
//
// Set the wildcard type bit, if appropriate
//
if (parms->TokenType & TOKEN_TYPE_WILDCARD) {
*parms->PathType |= ITYPE_WILD;
}
ADVANCE_TOKEN();
return TypeParseAbsPath(parms);
}
//
// <abspath> --> {}
//
if (RetVal == NERR_CantType) {
RetVal = 0;
}
return RetVal;
}
STATIC DWORD TypeParseUNCPath(PPARSER_PARMS parms)
{
DWORD RetVal = NERR_CantType;
//
// <uncpath> --> <slash> <oneleadslashname>
//
if (parms->TokenType & TOKEN_TYPE_SLASH) {
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
//
// Turn off the TOKEN_TYPE_DEV flag. If we got to this function,
// it means that we are working on the share name portion of
// a UNC style name. We want to allow a share called "Dev".
//
parms->TokenType &= ~TOKEN_TYPE_DEV;
return TypeParseOneLeadSlashName(parms);
}
//
// <uncpath> --> {}
//
if (RetVal == NERR_CantType) {
//
// Since there's no UNC path, this is a UNC computername
//
*parms->PathType |= ITYPE_COMPNAME;
RetVal = 0;
}
return RetVal;
}
STATIC DWORD TypeParseSysPath(PPARSER_PARMS parms)
{
DWORD RetVal = NERR_CantType;
//
// <syspath> --> <slash> <relpath>
//
if (parms->TokenType & TOKEN_TYPE_SLASH) {
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
return TypeParseRelPath(parms);
}
//
// <syspath> --> {}
//
if (RetVal == NERR_CantType) {
//
// If there's no Syspath, turn on the Meta and Path bits
//
*parms->PathType |= (ITYPE_META | ITYPE_PATH);
RetVal = 0;
}
return RetVal;
}
STATIC DWORD TypeParseDevPath(PPARSER_PARMS parms)
{
DWORD RetVal = NERR_CantType;
//
// Set the appropriate Device type bit
//
*parms->PathType |= ITYPE_DEVICE;
//
// <devpath> --> <slash> <devicename>
//
if (parms->TokenType & TOKEN_TYPE_SLASH) {
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
return TypeParseDeviceName(parms);
}
//
// <devpath> --> {}
//
if (RetVal == NERR_CantType) {
//
// If there's no Devpath, turn on the Meta bit
//
*parms->PathType |= ITYPE_META;
RetVal = 0;
}
return RetVal;
}
STATIC DWORD TypeParseOptSlash(PPARSER_PARMS parms)
{
DWORD RetVal = NERR_CantType;
//
// <optslash> --> <slash>
//
if (parms->TokenType & TOKEN_TYPE_SLASH) {
//
// This is an absolute path; set the type bits
//
*parms->PathType |= (ITYPE_ABSOLUTE | ITYPE_PATH);
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
return 0;
}
//
// <optslash> --> {}
//
if (RetVal == NERR_CantType) {
RetVal = 0;
}
return RetVal;
}
STATIC DWORD TypeParseOptColon(PPARSER_PARMS parms)
{
DWORD RetVal = NERR_CantType;
//
// <optcolon> --> <colon>
//
if (parms->TokenType & TOKEN_TYPE_COLON) {
ADVANCE_TOKEN();
TURN_OFF(MATCH_OPTIONAL);
return 0;
}
//
// <optcolon> --> {}
//
if (RetVal == NERR_CantType) {
RetVal = 0;
}
return RetVal;
}
STATIC DWORD TypeParseOptRelPath(PPARSER_PARMS parms)
{
DWORD RetVal = NERR_CantType;
BOOL fSetMatchOptional = FALSE;
//
// <optrelpath> --> <relpath>
//
//
// Turn on MATCH_OPTIONAL flag (if it isn't already on)
//
if (!(parms->Flags & PPF_MATCH_OPTIONAL)) {
fSetMatchOptional = TRUE;
TURN_ON(MATCH_OPTIONAL);
}
RetVal = TypeParseRelPath(parms);
if (fSetMatchOptional) {
TURN_OFF(MATCH_OPTIONAL);
}
//
// <optrelpath> --> {}
//
if (RetVal == NERR_CantType) {
RetVal = 0;
}
return RetVal;
}