Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1166 lines
29 KiB

/**********************************************************************/
/** Microsoft Windows/NT **/
/** Copyright(c) Microsoft Corp., 1991 **/
/**********************************************************************/
/*
RULE.CXX
NT Network Control Panel Applet Rule Handling Class Implementation
FILE HISTORY:
DavidHov 9/18/91 Created
DavidHov 11/07/92 Enhanced to handle INF-style
nested lists.
NOTES:
SETUP.EXE handles lists, but its internal form is highly
idiosyncratic. Here's an example of a triply-nested list:
{ "{""IRQ"",""0"",""90"",
""{""""5"""",""""9"""",""""10"""",""""11"""",""""15""""}""
}",
"{""IOADDR"",""0"",""100"",
""{""""768"""",""""512""""}""
}",
"{""MEMADDR"",""0"",""75"",
""{""""786432"""",""""819200"""",""""851968"""",
""""884736"""",""""917504"""",""""950272""""}""
}",
"{""MEMLENGTH"",""2"",""100"",
""{""""32768"""",""""65536""""}""
}"
}
The internal form is that every list element is just a quoted string,
but this has the side-effect that the number of quotes increases
at (2 ** depth) where "depth" is the nesting depth of lists!
My gorge rises at this sort of thing.
*/
#include "pch.hxx" // Precompiled header
#pragma hdrstop
#define MAXSTRING 256 // Longest parsable token string
// Token types
#define TOKWHITE 1
#define TOKLIST 2
#define TOKEOLIST 3
#define TOKNUM 4
#define TOKATOM 5
#define TOKSTRING 6
#define TOKVAR 7
#define TOKEQUALS 8
#define TOKVBAR 9
#define TOKBOGUS 10
// Character markers
#define TCHX(a) ((TCHAR)TCH(a))
#define ChComment1 TCHX('/')
#define ChComment2 TCHX('*')
#define ChOpenList TCHX('(')
#define ChCloseList TCHX(')')
#define ChInfOpenList TCHX('{')
#define ChInfCloseList TCHX('}')
#define ChInfSeparator TCHX(',')
#define ChQuote TCHX('\"')
#define ChSpace TCHX(' ')
#define ChEquals TCHX('=')
#define ChTab TCHX('\t')
#define ChEor TCHX('\r')
#define ChCr TCHX('\n')
#define EOS TCHX('\0')
#define ChDosEof TCHX('\x1a')
#define ChUscore TCHX('_')
#define ChVbar TCHX('|')
#define ChAlphaLow1 TCHX('A')
#define ChAlphaHigh1 TCHX('Z')
#define ChAlphaLow2 TCHX('a')
#define ChAlphaHigh2 TCHX('z')
#define ChNumLow TCHX('0')
#define ChNumHigh TCHX('9')
#define ChMinus TCHX('-')
/*******************************************************************
NAME: CFG_RULE_NODE::cfScanAtomic
SYNOPSIS: Attempt to scan a string of characters which
could represent an atomic SProlog symbol.
ENTRY: const TCHAR * pszData Data to scan
const TCHAR * pszDelim Delimiters allowed
TCHAR * pszBuffer Storage area; OPTIONAL
INT cchBuffLen Size of storage area;
OPTIONAL
EXIT:
RETURNS:
NOTES: If the set of characters up to the first character
which not allowed in an atom is delimited by a
character in the delimiter table, succeed and move
the characters into the result buffer.
The first character of the string is examined; if
numeric, only numbers are allowed.
HISTORY:
********************************************************************/
INT CFG_RULE_NODE :: cfScanAtomic (
const TCHAR * pszData, // Data to scan
const TCHAR * pszDelim, // Delimiters allowed
TCHAR * pszBuffer, // Storage area
INT cchBuffLen ) // Size of storage area
{
static const TCHAR * const pcszAtomic =
SZ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_") ;
static const TCHAR * const pcszNumeric = SZ("0123456789") ;
INT cchResult ;
const TCHAR * pcszTable = cfIsDigit( *pszData )
? pcszNumeric
: pcszAtomic ;
if ( pszDelim == NULL )
{
pszDelim = SZ("") ;
}
// Find the first non-atom character in the data
cchResult = ::strspnf( pszData, pcszTable ) ;
// If invalid character is not EOS, see if it's an
// expected delimiter; EOS is always an allowed delimiter.
if ( pszData[ cchResult ] != EOS )
{
if ( ::strchrf( pszDelim, pszData[ cchResult ] ) == NULL )
{
// Unexpected delimiter; we've failed
cchResult = -1 ;
}
}
// Move the data found
if ( cchResult > 0
&& pszBuffer
&& cchResult < cchBuffLen )
{
::memcpyf( pszBuffer, pszData, cchResult * sizeof(TCHAR) ) ;
pszBuffer[ cchResult ] = EOS ;
}
return cchResult ;
}
BOOL CFG_RULE_NODE :: cfIsDigit ( TCHAR ch )
{
return ch >= ChNumLow && ch <= ChNumHigh ;
}
BOOL CFG_RULE_NODE :: cfIsAlpha ( TCHAR ch )
{
return (ch >= ChAlphaLow1 && ch <= ChAlphaHigh1)
|| (ch >= ChAlphaLow2 && ch <= ChAlphaHigh2) ;
}
BOOL CFG_RULE_NODE :: cfIsAlNum ( TCHAR ch )
{
return cfIsDigit( ch ) || cfIsAlpha( ch ) ;
}
TCHAR CFG_RULE_NODE :: cfIsUpper ( TCHAR ch )
{
return ch >= ChAlphaLow1 && ch <= ChAlphaHigh1 ;
}
TCHAR CFG_RULE_NODE :: cfIsLower ( TCHAR ch )
{
return ch >= ChAlphaLow2 && ch <= ChAlphaHigh2 ;
}
TCHAR CFG_RULE_NODE :: cfToUpper ( TCHAR ch )
{
if ( cfIsLower( ch ) )
{
ch -= ChAlphaLow2 - ChAlphaLow1 ;
}
return ch ;
}
TCHAR CFG_RULE_NODE :: cfToLower ( TCHAR ch )
{
if ( cfIsUpper( ch ) )
{
ch += ChAlphaLow2 - ChAlphaLow1 ;
}
return ch ;
}
LONG CFG_RULE_NODE :: cfAtoL ( TCHAR * pszData )
{
BOOL fMinus ;
LONG lResult = 0 ;
if ( fMinus = (*pszData == ChMinus) )
pszData++ ;
while ( cfIsDigit( *pszData ) )
{
lResult *= 10 ;
lResult += *pszData++ - ChNumLow ;
}
return fMinus ? -lResult : lResult ;
}
/*******************************************************************
NAME: CFG_RULE_NODE::LinkAfter
SYNOPSIS: Link this node as the successor of the given node.
ENTRY: CFG_RULE_NODE * pcrnPrev predecessor node
EXIT:
RETURNS:
NOTES:
HISTORY:
DavidHov 10/1/91 Created
********************************************************************/
void CFG_RULE_NODE :: LinkAfter ( CFG_RULE_NODE * pcrnPrev )
{
_pcrnBack = pcrnPrev->_pcrnBack ;
_pcrnFwd = pcrnPrev ;
_pcrnBack->_pcrnFwd = this ;
pcrnPrev->_pcrnBack = this ;
}
/*******************************************************************
NAME: CFG_RULE_NODE::CFG_RULE_NODE
SYNOPSIS: Constructor for String or Atom token node
ENTRY: CFG_RULE_NODE * pcrnPrev predecessor node
HUATOM atom for token or string
CFG_RULE_NODE_TYPE specific type of token
EXIT:
RETURNS:
NOTES:
HISTORY:
DavidHov 10/1/91 Created
********************************************************************/
CFG_RULE_NODE :: CFG_RULE_NODE
( CFG_RULE_NODE * pcrnPrev,
HUATOM hUatom,
CFG_RULE_NODE_TYPE crnt ) :
_pcrnParent( pcrnPrev->_pcrnParent ),
_pcrnFwd( NULL ),
_pcrnBack( NULL ),
_ecrType( CRT_RULE ),
_ecnType( CRN_UNKNOWN ),
_pv( NULL )
{
// Link node as successor to given previous node.
LinkAfter( pcrnPrev );
SetAtom( hUatom, crnt ) ;
}
/*******************************************************************
NAME: CFG_RULE_NODE::CFG_RULE_NODE
SYNOPSIS: Constructor for numeric token node
ENTRY: CFG_RULE_NODE * pcrnPrev predecessor node
LONG lNumber numeric value
EXIT:
RETURNS:
NOTES:
HISTORY:
DavidHov 10/1/91 Created
********************************************************************/
CFG_RULE_NODE :: CFG_RULE_NODE ( CFG_RULE_NODE * pcrnPrev, LONG lNumber )
: _pcrnParent( pcrnPrev->_pcrnParent ),
_pcrnFwd( NULL ),
_pcrnBack( NULL ),
_ecrType( CRT_RULE ),
_ecnType( CRN_UNKNOWN ),
_pv( NULL )
{
// Link node as successor to given previous node.
LinkAfter( pcrnPrev );
SetNumber( lNumber ) ;
}
/*******************************************************************
NAME: CFG_RULE_NODE::CFG_RULE_NODE
SYNOPSIS: Constructor for list token. A new CFG_RULE_NODE
is automatically allocated which forms the
NIL ground for the new list.
ENTRY: CFG_RULE_NODE * pcrnPrev predecessor node
LONG lNumber numeric value
EXIT:
RETURNS:
NOTES: A NULL value for "pPrev" implies the start of a new tree.
HISTORY:
DavidHov 10/1/91 Created
********************************************************************/
CFG_RULE_NODE :: CFG_RULE_NODE
( CFG_RULE_NODE * pcrnPrev, CFG_RULE_NODE_TYPE crnt )
: _pcrnParent( NULL ),
_pcrnFwd( NULL ),
_pcrnBack( NULL ),
_ecrType( CRT_RULE ),
_ecnType( CRN_UNKNOWN ),
_pv( NULL )
{
if ( pcrnPrev )
{
_pcrnParent = pcrnPrev->_pcrnParent ;
LinkAfter( pcrnPrev );
}
else
{
_pcrnBack = _pcrnFwd = this ;
}
if ( crnt == CRN_LIST )
{
SetList( new CFG_RULE_NODE(), CRN_LIST ) ;
UIASSERT( QueryList() != NULL ) ;
QueryList()->_pcrnParent = this ;
}
else
{
SetType( crnt ) ;
}
}
/*******************************************************************
NAME: CFG_RULE_NODE::CFG_RULE_NODE
SYNOPSIS: Generic constructor for child nodes in lists
ENTRY:
EXIT:
RETURNS:
NOTES:
HISTORY:
DavidHov 10/1/91 Created
********************************************************************/
CFG_RULE_NODE :: CFG_RULE_NODE ()
: _pcrnParent( NULL ),
_pcrnFwd( NULL ),
_pcrnBack( NULL ),
_ecrType( CRT_RULE ),
_ecnType( CRN_UNKNOWN ),
_pv( NULL )
{
_pcrnFwd = this ;
_pcrnBack = this ;
SetList( NULL, CRN_NIL ) ;
}
/*******************************************************************
NAME: CFG_RULE_NODE::~CFG_RULE_NODE
SYNOPSIS: Destructor
ENTRY:
EXIT:
RETURNS:
NOTES: Destroys this node and all its children.
HISTORY:
DavidHov 10/1/91 Created
********************************************************************/
CFG_RULE_NODE :: ~ CFG_RULE_NODE ()
{
// Delete our sub-tree if we have one.
// Keep deleting until our child pointer is NULL.
if ( _ecnType == CRN_LIST )
{
while ( QueryList() )
{
delete QueryList() ;
}
}
// Delink us from our siblings. If we have a parent,
// maintain the parent's child pointer.
if ( _pcrnBack != this )
{
_pcrnFwd->_pcrnBack = _pcrnBack ;
_pcrnBack->_pcrnFwd = _pcrnFwd ;
}
// If the parent points at us, change to a sibling.
if ( _pcrnParent )
{
_pcrnParent->SetList( _pcrnBack == this
? NULL : _pcrnBack, CRN_LIST ) ;
}
}
/*******************************************************************
NAME: CFG_RULE_NODE::QueryNth
SYNOPSIS: Return the Nth element of a list. Index starts at 1;
zero is valid, but returns the CRN_NIL node.
ENTRY: int cIndex index vov pointer to return
EXIT:
RETURNS:
NOTES: Destroys this node and all its children.
HISTORY:
DavidHov 10/1/91 Created
********************************************************************/
CFG_RULE_NODE * CFG_RULE_NODE :: QueryNth ( int cIndex )
{
if ( _ecnType != CRN_LIST )
return NULL ;
CFG_RULE_NODE * prnResult = QueryList() ;
if ( prnResult == NULL || prnResult->QueryType() != CRN_NIL )
return NULL ;
while ( prnResult && cIndex )
{
prnResult = prnResult->_pcrnFwd ;
cIndex-- ;
}
return cIndex == 0 ? prnResult : NULL ;
}
/*******************************************************************
NAME: CFG_RULE_SET::CFG_RULE_SET
SYNOPSIS: Constructor of entire rule set parsed from
a block of text.
ENTRY:
EXIT:
RETURNS:
NOTES:
HISTORY:
DavidHov 10/1/91 Created
********************************************************************/
CFG_RULE_SET :: CFG_RULE_SET ()
: CFG_RULE_NODE( NULL, CRN_LIST ),
_pszData( NULL ),
_pszNext( NULL ),
_pszTok( NULL ),
_iParseResult( PARSE_INCOMPLETE ),
_ulLength( 0 )
{
_ecrType = CRT_CONTAINER ;
}
/*******************************************************************
NAME: CFG_RULE_SET::~CFG_RULE_SET
SYNOPSIS: Destructor of entire rule set parsed from
a block of text.
ENTRY:
EXIT:
RETURNS:
NOTES:
HISTORY:
DavidHov 10/1/91 Created
********************************************************************/
CFG_RULE_SET :: ~ CFG_RULE_SET ()
{
}
/*******************************************************************
NAME: CFG_RULE_SET::NextChar
SYNOPSIS: Return the next character from the buffer.
ENTRY:
EXIT: Next character as integer
RETURNS:
NOTES:
HISTORY:
DavidHov 10/1/91 Created
********************************************************************/
int CFG_RULE_SET :: NextChar ()
{
int iResult = PeekChar() ;
if ( iResult == ChDosEof )
iResult = EOS ;
if ( iResult != EOS )
_pszNext++ ;
return iResult ;
}
/*******************************************************************
NAME: CFG_RULE_SET::NextToken
SYNOPSIS: Return the type of the next token in the data buffer.
ENTRY: TCHAR * pszStr where to store the scanned tokem
int cbStr capacity of the token buffer
EXIT: Next character as integer
RETURNS:
NOTES:
HISTORY:
DavidHov 10/1/91 Created
********************************************************************/
int CFG_RULE_SET :: NextToken ( TCHAR * pszStr, int cbStr )
{
TCHAR * s,
* sMax = pszStr + cbStr - 1 ;
int next,
peek,
result ;
_pszTok = _pszNext ;
do {
result = TOKBOGUS ;
s = pszStr ;
if ( (next = NextChar() ) == EOS )
return EOS ;
if ( cfIsAlpha( next ) || next == ChUscore )
{
result = (cfIsUpper( (TCHAR) next ) || next == ChUscore)
? TOKVAR
: TOKATOM ;
for ( *s++ = (TCHAR) next ;
s < sMax
&& (cfIsAlNum( PeekChar()) || PeekChar() == ChUscore) ;
*s++ = (TCHAR) NextChar() ) ;
}
else
if ( cfIsDigit( next ) )
{
for ( *s++ = (TCHAR) next ;
s < sMax && cfIsDigit( PeekChar() ) ;
*s++ = (TCHAR) NextChar() ) ;
result = TOKNUM ;
}
else
if ( next == ChComment1 && PeekChar() == ChComment2 )
{
do
{
next = NextChar() ;
} while ( next != ChComment2 || PeekChar() != ChComment1 ) ;
NextChar() ;
result = TOKWHITE ;
} else
if ( next == ChTab || next == ChSpace
|| next == ChEor || next == ChCr )
{
while ( (peek = PeekChar()) == ChTab
|| peek == ChSpace || peek == ChEor )
NextChar();
result = TOKWHITE ;
}
else
if ( next == ChOpenList )
{
*s++ = (TCHAR) next ;
result = TOKLIST ;
}
else
if ( next == ChCloseList )
{
*s++ = (TCHAR) next ;
result = TOKEOLIST ;
} else
if ( next == ChQuote )
{
for ( next = NextChar() ;
next != ChQuote ;
next = NextChar() )
*s++ = (TCHAR) next ;
result = TOKSTRING ;
} else
if ( next == ChEquals )
{
*s++ = (TCHAR) next ;
result = TOKEQUALS ;
} else
if ( next == ChVbar )
{
*s++ = (TCHAR) next ;
result = TOKVBAR ;
}
*s = EOS ;
} while ( result == TOKWHITE && s < sMax ) ;
return s < sMax ? result : TOKBOGUS ;
}
/*******************************************************************
NAME: CFG_RULE_SET::ParseLevel
SYNOPSIS: Return the type of the next tokejn in the data buffer.
ENTRY: CFG_RULE_NODE * pcrn Node to append to
int level lexical level (list depth)
EXIT:
RETURNS: error code (e.g., PARSE_ERR_BAD_TOKEN)
NOTES:
Recursively parse a tree. The node we're pointing at is
the NIL anchor for the linked list. Newly allocated nodes
become the newest (last) element on the list.
Parse handling. This routine can parse files with either
facts, rules or both. Prolog statments are considered to
be "rules" if they contain variables or list descriptors.
HISTORY:
DavidHov 10/1/91 Created
********************************************************************/
int CFG_RULE_SET :: ParseLevel ( CFG_RULE_NODE * pcrn, int level )
{
int token,
result = PARSE_INCOMPLETE ;
CFG_RULE_NODE * newn = pcrn ;
static TCHAR string [ MAXSTRING ] ; // N.B. This is STATIC!
do {
switch ( token = NextToken( string, sizeof string ) )
{
case EOS:
result = level > 0
? PARSE_ERR_UNBAL_PAREN
: PARSE_SUCCESSFUL ;
break ;
case TOKNUM:
newn = new CFG_RULE_NODE( pcrn, cfAtoL( string ) ) ;
break ;
case TOKVAR:
// Check if variables are allowed
if ( ! (_usParseCtl & (PARSE_CTL_RSP_SYNTAX | PARSE_CTL_FULL_SYNTAX)) )
{
result = PARSE_ERR_BAD_TOKEN ;
break ;
} // Falls thru if acceptable
case TOKSTRING:
case TOKATOM:
newn = new CFG_RULE_NODE( pcrn,
HUATOM( string ),
token == TOKSTRING ? CRN_STR
: (token == TOKVAR ? CRN_VAR
: CRN_ATOM) ) ;
break ;
case TOKLIST:
{
newn = new CFG_RULE_NODE( pcrn, CRN_LIST ) ;
if ( newn == NULL )
break ;
CFG_RULE_NODE * newList = newn->QueryList() ;
if ( newList != NULL )
{
result = ParseLevel( newList, level + 1 ) ;
// If successful, continue parsing
if ( result == PARSE_SUCCESSFUL )
result = PARSE_INCOMPLETE ;
}
else
{
result = PARSE_ERR_NO_MEMORY ;
}
}
break ;
case TOKEOLIST:
result = PARSE_SUCCESSFUL ;
break ;
case TOKEQUALS:
if ( ! (_usParseCtl & PARSE_CTL_RSP_SYNTAX) )
{
result = PARSE_ERR_BAD_TOKEN ;
break ;
}
newn = new CFG_RULE_NODE( pcrn, CRN_EQUIV ) ;
break ;
case TOKVBAR:
if ( ! (_usParseCtl & PARSE_CTL_FULL_SYNTAX) )
{
result = PARSE_ERR_BAD_TOKEN ;
break ;
}
newn = new CFG_RULE_NODE( pcrn, CRN_VBAR ) ;
break ;
case TOKBOGUS:
default:
result = PARSE_ERR_BAD_TOKEN ;
break ;
}
if ( newn == NULL )
{
result = PARSE_ERR_NO_MEMORY ;
}
} while ( result == PARSE_INCOMPLETE ) ;
#if defined(DEBUG)
if ( result == PARSE_ERR_BAD_TOKEN )
{
TRACEEOL( SZ("NCPA: RULE PARSE: Bad token: ")
<< string ) ;
}
#endif
return result ;
}
/*******************************************************************
NAME: CFG_RULE_SET::Parse
SYNOPSIS: Parse a block of text, forming a tree descending from
this node.
ENTRY: TCHAR * pszText the block of text
USHORT usParseCtl constraint flags
EXIT:
RETURNS: APIERR
NOTES:
HISTORY:
********************************************************************/
APIERR CFG_RULE_SET :: Parse ( const TCHAR * pszText, USHORT usParseCtl )
{
_pszTok = _pszNext = _pszData = pszText ;
_usParseCtl = usParseCtl ;
_iParseResult = ParseLevel( QueryList(), 0 ) ;
return _iParseResult == PARSE_SUCCESSFUL
? NERR_Success
: ERROR_GEN_FAILURE ;
}
/*******************************************************************
NAME: CFG_RULE_SET::Textify
SYNOPSIS: Generate a stanard list from the given tree.
ENTRY: TEXT_BUFFER * ptxBuff Output buffer
EXIT:
RETURNS: APIERR
NOTES:
HISTORY:
********************************************************************/
APIERR CFG_RULE_SET :: Textify ( TEXT_BUFFER * ptxbBuff )
{
ptxbBuff->CopyFrom( SZ("") ) ;
UIASSERT( ! "CFG_RULE_SET::Textify() not implemented") ;
return ERROR_GEN_FAILURE ;
}
INT CFG_RULE_SET :: ConvertInfList (
const TCHAR * * ppszIn,
TCHAR * * ppszOut,
INT cLevel )
{
INT err = PARSE_INCOMPLETE ;
TCHAR ch ;
const TCHAR * pszIn = *ppszIn ;
TCHAR * pszOut = *ppszOut ;
INT cQuotes = 1 << cLevel ;
for ( ; err == PARSE_INCOMPLETE && *pszIn ; )
{
// Reduce quotes according to nesting depth
for ( INT cq = 0 ;
*pszIn == ChQuote && cq < cQuotes ;
cq++, ++pszIn ) ;
switch ( ch = *pszIn++ )
{
// Recursively parse a list.
case ChInfOpenList:
*pszOut++ = ChOpenList ;
err = ConvertInfList( & pszIn, & pszOut, cLevel + 1 ) ;
*pszOut++ = ChCloseList ;
break ;
// End of list. Exit.
case ChInfCloseList:
err = PARSE_CLOSE_LEVEL ;
break ;
// Replace INF comma separators with spaces
case ChInfSeparator:
*pszOut++ = ChSpace ;
break ;
// Preserve remaining atomic quoted strings.
case ChQuote:
*pszOut++ = ChQuote ;
while ( *pszIn && *pszIn != ChQuote )
{
*pszOut++ = *pszIn++ ;
}
if ( *pszIn == ChQuote )
{
*pszOut++ = ChQuote ;
break ;
}
// Fall thru to mark bad token
// Should never get nested EOS
case EOS:
err = PARSE_ERR_BAD_TOKEN ;
break ;
// Normal characters; just move a string of 'em
default:
{
// See if the characters can be treated atomically;
// quote is the only allowed delimiter character.
INT cchAtomic = cfScanAtomic( --pszIn, SZ("\""), NULL, 0 ) ;
// If not allowed as an atomic value, quote it.
if ( cchAtomic < 1 )
*pszOut++ = ChQuote ;
while ( *pszIn && *pszIn != ChQuote )
{
*pszOut++ = *pszIn++ ;
}
if ( cchAtomic < 1 )
*pszOut++ = ChQuote ;
}
break ;
}
}
*pszOut = 0 ;
if ( err == PARSE_INCOMPLETE )
{
if ( *pszIn == EOS )
{
err = cLevel
? PARSE_INCOMPLETE
: PARSE_SUCCESSFUL ;
}
}
else
if ( err == PARSE_CLOSE_LEVEL )
{
err = PARSE_INCOMPLETE ;
}
*ppszIn = pszIn ;
*ppszOut = pszOut ;
return err ;
}
/*******************************************************************
NAME: CFG_RULE_SET::ParseInfList
SYNOPSIS: Parse a nested INF list. This is done
by converting the list to standard form
and then parsing.
ENTRY: TCHAR * pszText the block of text
EXIT:
RETURNS: APIERR
NOTES: Due to the plethora of quote characters and commas
present in an INF list, it is impossible that more
characters will be required for the SProlog format
of the INF list.
HISTORY:
********************************************************************/
APIERR CFG_RULE_SET :: ParseInfList ( const TCHAR * pszText )
{
TCHAR * pszTemp = new TCHAR[ ::strlenf(pszText) + 2 ] ;
const TCHAR * pszIn = pszText ;
TCHAR * pszOut = pszTemp ;
if ( pszTemp == NULL )
return ERROR_NOT_ENOUGH_MEMORY ;
INT err = ConvertInfList( & pszIn, & pszOut, 0 );
if ( err == PARSE_SUCCESSFUL )
{
err = Parse( pszTemp, PARSE_CTL_FULL_SYNTAX ) ;
}
else
{
err = ERROR_GEN_FAILURE ;
}
delete pszTemp ;
return err ;
}
INT CFG_RULE_SET :: TextifyInfList (
TEXT_BUFFER * ptxbBuff,
CFG_RULE_NODE * pcrnList,
INT cLevel,
INT cBaseLevel )
{
INT err = PARSE_INCOMPLETE,
cq,
cIter ;
CFG_RULE_NODE * pcrnNext = pcrnList ;
// Set val = 2 ** pwr
#define PWR2(val,pwr) (val = 1 << pwr)
// Emit "count" quotes
#define PUMP_QUOTES(count) { for ( INT cqx = count ; \
cqx-- ; \
ptxbBuff->Cat( ChQuote ) ) ; \
}
ptxbBuff->Cat( ChInfOpenList ) ;
PWR2( cq, cLevel ) ;
for ( cIter = 0 ; pcrnNext = pcrnNext->QueryNext() ; cIter++ )
{
if ( cIter )
ptxbBuff->Cat( ChInfSeparator ) ;
PUMP_QUOTES(cq);
switch ( pcrnNext->QueryType() )
{
case CRN_LIST:
err = TextifyInfList( ptxbBuff,
pcrnNext->QueryList(),
cLevel + 1,
cBaseLevel ) ;
break ;
case CRN_ATOM:
case CRN_STR:
case CRN_VAR:
ptxbBuff->Cat( pcrnNext->QueryAtom().QueryText() ) ;
break ;
case CRN_NUM:
ptxbBuff->Cat( (int) pcrnNext->QueryNumber() ) ;
break ;
default:
err = PARSE_ERR_BAD_TOKEN ;
break ;
}
PUMP_QUOTES(cq);
if ( err != PARSE_INCOMPLETE )
break ;
}
if ( err == PARSE_INCOMPLETE )
{
ptxbBuff->Cat( ChInfCloseList ) ;
ptxbBuff->Cat( EOS ) ;
if ( cLevel == cBaseLevel )
err = PARSE_SUCCESSFUL ;
}
return err ;
}
/*******************************************************************
NAME: CFG_RULE_SET::TextifyInf
SYNOPSIS: Convert this parse tree into an INF-style
nested list.
ENTRY: TEXT_BUFFER * ptxbBuff output buffer
INT cLevel exterior lexical
nesting level
EXIT:
RETURNS: APIERR
NOTES: The "cLevel" parameter specifies how deeply
the list will be imbedded in the caller's
context. This is necessary due to the truly
perverse INF list format.
HISTORY:
********************************************************************/
APIERR CFG_RULE_SET :: TextifyInf (
TEXT_BUFFER * ptxbBuff,
INT cLevel )
{
CFG_RULE_NODE * pcrnList = QueryList() ;
ptxbBuff->CopyFrom( SZ("") ) ;
// Walk down the tree until we get to the primary list.
if ( pcrnList == NULL
|| pcrnList->QueryType() != CRN_NIL
|| (pcrnList = pcrnList->QueryNext()) == NULL
|| pcrnList->QueryType() != CRN_LIST )
{
return ERROR_GEN_FAILURE ;
}
// Recursively export the list.
INT err = TextifyInfList( ptxbBuff,
pcrnList->QueryList(),
cLevel, cLevel ) ;
return err == PARSE_SUCCESSFUL
? NERR_Success
: ERROR_GEN_FAILURE ;
}
/* End of RULE.CXX */