/*
* $Id: defFind.c,v 4.14 2007/07/04 20:51:11 bkorb Exp $
*
* Time-stamp: "2007-07-04 11:15:50 bkorb"
* Last Committed: $Date: 2007/07/04 20:51:11 $
*
* This module locates definitions.
*
* This file is part of AutoGen.
* AutoGen copyright (c) 1992-2007 by Bruce Korb - all rights reserved
*
* AutoGen is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AutoGen is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
struct defEntryList {
size_t allocCt;
size_t usedCt;
tDefEntry** papDefEntry;
int nestLevel;
};
typedef struct defEntryList tDefEntryList;
tSCC zNameRef[] = "Ill formed name ``%s'' in %s line %d\n";
static char zDefinitionName[ AG_PATH_MAX ];
static tDefEntry* findEntryByIndex( tDefEntry* pE, char* pzScan );
#define ILLFORMEDNAME() \
AG_ABEND( aprf( zNameRef, zDefinitionName, \
pCurTemplate->pzTplFile, pCurMacro->lineNo ));
/* = = = START-STATIC-FORWARD = = = */
/* static forward declarations maintained by :mkfwd */
static tDefEntry*
findEntryByIndex( tDefEntry* pE, char* pzScan );
static void
addResult( tDefEntry* pDE, tDefEntryList* pDEL );
static size_t
badName( char* pzD, char const* pzS, size_t srcLen );
static tDefEntry*
defEntrySearch( char* pzName, tDefCtx* pDefCtx, ag_bool* pIsIndexed );
static tDefEntry**
entryListSearch( char* pzName, tDefCtx* pDefCtx );
/* = = = END-STATIC-FORWARD = = = */
static tDefEntry*
findEntryByIndex( tDefEntry* pE, char* pzScan )
{
int idx;
/*
* '[]' means the first entry of whatever index number
*/
if (*pzScan == ']')
return pE;
/*
* '[$]' means the last entry of whatever index number
*/
if (*pzScan == '$') {
while (isspace( *++pzScan )) ;
if (*pzScan != ']')
return NULL;
if (pE->pEndTwin != NULL)
return pE->pEndTwin;
return pE;
}
/*
* '[nn]' means the specified index number
*/
if (isdigit( *pzScan )) {
char* pz;
idx = strtol( pzScan, &pz, 0 );
/*
* Skip over any trailing space and make sure we have a closer
*/
while (isspace( *pz )) pz++;
if (*pz != ']')
return NULL;
}
else {
/*
* '[XX]' means get the index from our definitions
*/
char* pzDef = pzScan;
char const* pzVal;
if (! isalpha( *pzScan ))
return NULL;
while (ISNAMECHAR( *pzScan )) pzScan++;
/*
* Temporarily remove the character under *pzScan and
* find the corresponding defined value.
*/
{
char svch = *pzScan;
*pzScan = NUL;
pzVal = getDefine( pzDef, AG_TRUE );
*pzScan = svch;
}
/*
* Skip over any trailing space and make sure we have a closer
*/
while (isspace( *pzScan )) pzScan++;
if (*pzScan != ']')
return NULL;
/*
* make sure we found a defined value
*/
if ((pzVal == NULL) || (*pzVal == NUL))
return NULL;
idx = strtol( pzVal, &pzDef, 0 );
/*
* Make sure we got a legal number
*/
if (*pzDef != NUL)
return NULL;
}
/*
* Search for the entry with the specified index.
*/
do {
if (pE->index > idx)
return NULL;
if (pE->index == idx)
break;
pE = pE->pTwin;
} while (pE != NULL);
return pE;
}
/*
* find entry support routines:
*
* addResult: place a new definition entry on the end of the
* list of found definitions (reallocating list size as needed).
*/
static void
addResult( tDefEntry* pDE, tDefEntryList* pDEL )
{
if (++(pDEL->usedCt) > pDEL->allocCt) {
pDEL->allocCt += pDEL->allocCt + 8; /* 8, 24, 56, ... */
pDEL->papDefEntry = (tDefEntry**)
AGREALOC( (void*)pDEL->papDefEntry, pDEL->allocCt * sizeof( void* ),
"added find result" );
}
pDEL->papDefEntry[ pDEL->usedCt-1 ] = pDE;
}
static size_t
badName( char* pzD, char const* pzS, size_t srcLen )
{
memcpy( (void*)pzD, (void*)pzS, srcLen );
pzD[ srcLen ] = NUL;
fprintf( pfTrace, zNameRef, pzD,
pCurTemplate->pzTplFile, pCurMacro->lineNo );
return srcLen + 1;
}
/*
* canonicalizeName: remove white space and roughly verify the syntax.
* This procedure will consume everything from the source string that
* forms a valid AutoGen compound definition name.
* We leave legally when:
* 1. the state is "CN_NAME_ENDED", AND
* 2. We stumble into a character that is not either '[' or '.'
* (always skipping white space).
* We start in CN_START.
*/
LOCAL int
canonicalizeName( char* pzD, char const* pzS, int srcLen )
{
typedef enum {
CN_START_NAME = 0, /* must find a name */
CN_NAME_ENDED, /* must find '[' or '.' or we end */
CN_INDEX, /* must find name, number, '$' or ']' */
CN_INDEX_CLOSE, /* must find ']' */
CN_INDEX_ENDED /* must find '.' or we end */
} teConState;
teConState state = CN_START_NAME;
char const* pzOri = pzS;
char* pzDst = pzD;
size_t stLen = srcLen;
/*
* Before anything, skip a leading '.' as a special hack to force
* a current context lookup.
*/
while (isspace( *pzS )) {
if (--srcLen <= 0) {
pzS = zNil;
break;
}
pzS++;
}
if (*pzS == '.') {
*(pzD++) = '.';
pzS++;
}
nextSegment:
/*
* The next segment may always start with an alpha character,
* but an index may also start with a number. The full number
* validation will happen in findEntryByIndex().
*/
while (isspace( *pzS )) {
if (--srcLen <= 0) {
pzS = zNil;
break;
}
pzS++;
}
switch (state) {
case CN_START_NAME:
if (! isalpha( *pzS ))
return badName( pzDst, pzOri, stLen );
state = CN_NAME_ENDED; /* we found the start of our first name */
break; /* fall through to name/number consumption code */
case CN_NAME_ENDED:
switch (*pzS++) {
case '[':
*(pzD++) = '[';
state = CN_INDEX;
break;
case '.':
*(pzD++) = '.';
state = CN_START_NAME;
break;
default:
/* legal exit -- we have a name already */
*pzD = NUL;
return srcLen;
}
if (--srcLen <= 0)
return badName( pzDst, pzOri, stLen );
goto nextSegment;
case CN_INDEX:
/*
* An index. Valid syntaxes are:
*
* '[' <#define-d name> ']'
* '[' ']'
* '[' '$' ']'
* '[' ']'
*
* We will check for and handle the last case right here.
* The next cycle must find the index closer (']').
*/
state = CN_INDEX_CLOSE;
/*
* Numbers and #define-d names are handled at the end of the switch.
* '$' and ']' are handled immediately below.
*/
if (isalnum( *pzS ))
break;
/*
* A solitary '$' is the highest index, whatever that happens to be
* We process that right here because down below we only accept
* name-type characters and this is not VMS.
*/
if (*pzS == '$') {
if (--srcLen < 0)
return badName( pzDst, pzOri, stLen );
*(pzD++) = *(pzS++);
goto nextSegment;
}
/* FALLTHROUGH */
case CN_INDEX_CLOSE:
/*
* Nothing else is okay.
*/
if ((*(pzD++) = *(pzS++)) != ']')
return badName( pzDst, pzOri, stLen );
if (--srcLen <= 0) {
*pzD = NUL;
return srcLen;
}
state = CN_INDEX_ENDED;
goto nextSegment;
case CN_INDEX_ENDED:
if ((*pzS != '.') || (--srcLen < 0)) {
*pzD = NUL;
return srcLen;
}
*(pzD++) = *(pzS++);
state = CN_START_NAME;
goto nextSegment;
}
/*
* The next state must be either looking for what comes after the
* end of a name, or for the close bracket after an index.
* Whatever, the next token must be a name or a number.
*/
assert((state == CN_NAME_ENDED) || (state == CN_INDEX_CLOSE));
assert( isalnum( *pzS ));
/*
* Copy the name/number. We already know the first character is valid.
* However, we must *NOT* downcase #define names...
*/
while (ISNAMECHAR( *pzS )) {
char ch = *(pzS++);
if ((state != CN_INDEX_CLOSE) && isupper( ch ))
*(pzD++) = tolower( ch );
else switch ( ch ) { /* force the separator chars to be '_' */
case '-':
case '^':
*(pzD++) = '_';
break;
default:
*(pzD++) = ch;
}
if (--srcLen <= 0) {
pzS = zNil;
break;
}
}
goto nextSegment;
}
/*
* findDefEntry
*
* Find the definition entry for the name passed in.
* It is okay to find block entries IFF they are found on the
* current level. Once you start traversing up the tree,
* the macro must be a text macro. Return an indicator saying if
* the element has been indexed (so the caller will not try
* to traverse the list of twins).
*/
static tDefEntry*
defEntrySearch( char* pzName, tDefCtx* pDefCtx, ag_bool* pIsIndexed )
{
char* pcBrace;
char breakCh;
tDefEntry* pE;
ag_bool dummy;
ag_bool noNesting = AG_FALSE;
static int nestingDepth = 0;
/*
* IF we are at the start of a search, then canonicalize the name
* we are hunting for, copying it to a modifiable buffer, and
* initialize the "indexed" boolean to false (we have not found
* an index yet).
*/
if (nestingDepth == 0) {
canonicalizeName( zDefinitionName, pzName, (int)strlen( pzName ));
pzName = zDefinitionName;
if (pIsIndexed != NULL)
*pIsIndexed = AG_FALSE;
else pIsIndexed = &dummy;
if (*pzName == '.') {
noNesting = AG_TRUE;
pzName++;
}
}
pcBrace = pzName + strcspn( pzName, "[." );
breakCh = *pcBrace;
*pcBrace = NUL;
if (breakCh == '[') *pIsIndexed = AG_TRUE;
for (;;) {
/*
* IF we are at the end of the definitions (reached ROOT),
* THEN it is time to bail out.
*/
pE = pDefCtx->pDefs;
if (pE == NULL)
return NULL;
do {
/*
* IF the name matches
* THEN break out of the double loop
*/
if (strcmp( pE->pzDefName, pzName ) == 0)
goto found_def_entry;
pE = pE->pNext;
} while (pE != NULL);
/*
* IF we are nested, then we cannot change the definition level.
* So, we did not find anything.
*/
if ((nestingDepth != 0) || noNesting)
return NULL;
/*
* Let's go try the definitions at the next higher level.
*/
pDefCtx = pDefCtx->pPrev;
if (pDefCtx == NULL)
return NULL;
} found_def_entry:;
/*
* At this point, we have found the entry that matches the supplied
* name, up to the '[' or '.' or NUL character. It *must* be one of
* those three characters.
*/
*pcBrace = breakCh;
switch (breakCh) {
case NUL:
return pE;
case '[':
/*
* We have to find a specific entry in a list.
*/
while (isspace( *++pcBrace )) ;
pE = findEntryByIndex( pE, pcBrace );
if (pE == NULL)
return pE;
/*
* We must find the closing brace, or there is an error
*/
pcBrace = strchr( pcBrace, ']' );
if (pcBrace == NULL)
ILLFORMEDNAME();
/*
* IF we are at the end of the definition,
* THEN return what we found
*/
switch (*++pcBrace) {
case NUL:
return pE;
case '.':
break;
default:
ILLFORMEDNAME();
}
/* FALLTHROUGH */
case '.':
/*
* It is a segmented value name. Set the name pointer
* to the next segment and search starting from the newly
* available set of definitions.
*/
pzName = pcBrace + 1;
break;
default:
ILLFORMEDNAME();
}
/*
* We cannot find a member of a non-block type macro definition.
*/
if (pE->valType != VALTYP_BLOCK)
return NULL;
/*
* Loop through all the twins of the entry we found until
* we find the entry we want. We ignore twins if we just
* used a subscript.
*/
nestingDepth++;
{
tDefCtx ctx = { NULL, &currDefCtx };
ctx.pDefs = pE->val.pDefEntry;
for (;;) {
tDefEntry* res;
res = defEntrySearch( pzName, &ctx, pIsIndexed );
if ((res != NULL) || (breakCh == '[')) {
nestingDepth--;
return res;
}
pE = pE->pTwin;
if (pE == NULL)
break;
ctx.pDefs = pE->val.pDefEntry;
}
}
nestingDepth--;
return NULL;
}
LOCAL tDefEntry*
findDefEntry( char* pzName, ag_bool* pIsIndexed )
{
return defEntrySearch( pzName, &currDefCtx, pIsIndexed );
}
/*
* findEntryList
*
* Find the definition entry for the name passed in. It is okay to find
* block entries IFF they are found on the current level. Once you start
* traversing up the tree, the macro must be a text macro. Return an
* indicator saying if the element has been indexed (so the caller will
* not try to traverse the list of twins).
*/
static tDefEntry**
entryListSearch( char* pzName, tDefCtx* pDefCtx )
{
static tDefEntryList defList = { 0, 0, NULL, 0 };
char* pcBrace;
char breakCh;
tDefEntry* pE;
ag_bool noNesting = AG_FALSE;
/*
* IF we are at the start of a search, then canonicalize the name
* we are hunting for, copying it to a modifiable buffer, and
* initialize the "indexed" boolean to false (we have not found
* an index yet).
*/
if (defList.nestLevel == 0) {
canonicalizeName( zDefinitionName, pzName, (int)strlen( pzName ));
pzName = zDefinitionName;
defList.usedCt = 0;
if (*pzName == '.') {
noNesting = AG_TRUE;
pzName++;
}
}
pcBrace = pzName + strcspn( pzName, "[." );
breakCh = *pcBrace;
*pcBrace = NUL;
for (;;) {
/*
* IF we are at the end of the definitions (reached ROOT),
* THEN it is time to bail out.
*/
pE = pDefCtx->pDefs;
if (pE == NULL) {
/*
* Make sure we are not nested. Once we start to nest,
* then we cannot "change definition levels"
*/
not_found:
if (defList.nestLevel != 0)
ILLFORMEDNAME();
/*
* Don't bother returning zero entry list. Just return NULL.
*/
return NULL;
}
do {
/*
* IF the name matches
* THEN go add it, plus all its twins
*/
if (strcmp( pE->pzDefName, pzName ) == 0)
goto found_def_entry;
pE = pE->pNext;
} while (pE != NULL);
/*
* IF we are nested, then we cannot change the definition level.
* Just go and return what we have found so far.
*/
if ((defList.nestLevel != 0) || noNesting)
goto returnResult;
/*
* Let's go try the definitions at the next higher level.
*/
pDefCtx = pDefCtx->pPrev;
if (pDefCtx == NULL)
goto not_found;
} found_def_entry:;
/*
* At this point, we have found the entry that matches the supplied
* name, up to the '[' or '.' or NUL character. It *must* be one of
* those three characters.
*/
*pcBrace = breakCh;
switch (breakCh) {
case NUL:
do {
addResult( pE, &defList );
pE = pE->pTwin;
} while (pE != NULL);
goto returnResult;
case '[':
/*
* We have to find a specific entry in a list.
*/
while (isspace( *++pcBrace )) ;
pE = findEntryByIndex( pE, pcBrace );
if (pE == NULL)
goto returnResult;
/*
* We must find the closing brace, or there is an error
*/
pcBrace = strchr( pcBrace, ']' );
if (pcBrace == NULL)
ILLFORMEDNAME();
/*
* IF we are at the end of the definition,
* THEN return what we found
*/
switch (*++pcBrace) {
case NUL:
goto returnResult;
case '.':
break;
default:
ILLFORMEDNAME();
}
/* FALLTHROUGH */
case '.':
/*
* It is a segmented value name. Set the name pointer
* to the next segment and search starting from the newly
* available set of definitions.
*/
pzName = pcBrace + 1;
break;
default:
ILLFORMEDNAME();
}
/*
* We cannot find a member of a non-block type macro definition.
*/
if (pE->valType != VALTYP_BLOCK)
return NULL;
/*
* Loop through all the twins of the entry. We ignore twins if we just
* used a subscript.
*/
defList.nestLevel++;
{
tDefCtx ctx = { NULL, &currDefCtx };
ctx.pDefs = pE->val.pDefEntry;
for (;;) {
(void)entryListSearch( pzName, &ctx );
if (breakCh == '[')
break;
pE = pE->pTwin;
if (pE == NULL)
break;
ctx.pDefs = pE->val.pDefEntry;
}
}
defList.nestLevel--;
returnResult:
if (defList.nestLevel == 0)
addResult( NULL, &defList );
return defList.papDefEntry;
}
LOCAL tDefEntry**
findEntryList( char* pzName )
{
return entryListSearch( pzName, &currDefCtx );
}
/*
* Local Variables:
* mode: C
* c-file-style: "stroustrup"
* indent-tabs-mode: nil
* End:
* end of agen5/defFind.c */