mirror of https://github.com/lianthony/NT4.0
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
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 */
|