diff --git a/harbour/ChangeLog b/harbour/ChangeLog index a58c7b2e56..e0c17970c1 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -1,3 +1,21 @@ +19991231-13:00 GMT+1 Ryszard Glab + + *source/rtl/memvars.c + * new function hb_memvarGetStrValuePtr() that return a value + of passed variable if this variable exists and if it contains + a string value (the function is called from macro text + substitution) + + *include/extend.h + * added declaration for hb_memvarGetStrValuePtr() + * added declaration of hb_macroTextValue() + + *source/vm/hvm.c + * added code to handle macro text substitution "text ¯o" + + *source/macro/macro.c + * added code to handle macro text substitution + 19991231-06:15 GMT+1 Antonio Linares * source/rtl/gt/gtwin.c * fix for a Win API wrong parameter call detected using NuMega BoundsChecker diff --git a/harbour/include/extend.h b/harbour/include/extend.h index 1e3f9831da..2fdf6b6219 100644 --- a/harbour/include/extend.h +++ b/harbour/include/extend.h @@ -407,6 +407,7 @@ extern void hb_memvarGetRefer( HB_ITEM_PTR, PHB_SYMB ); extern ULONG hb_memvarGetPrivatesBase( void ); extern void hb_memvarSetPrivatesBase( ULONG ); extern void hb_memvarNewParameter( PHB_SYMB, HB_ITEM_PTR ); +extern char * hb_memvarGetStrValuePtr( char *, ULONG * ); /* console I/O subsystem */ extern void hb_consoleInitialize( void ); @@ -455,6 +456,7 @@ typedef struct HB_MACRO_ /* a macro compiled pcode container */ extern void hb_macroGetValue( HB_ITEM_PTR ); extern void hb_macroSetValue( HB_ITEM_PTR ); +extern void hb_macroTextValue( HB_ITEM_PTR ); extern void hb_macroPushSymbol( HB_ITEM_PTR ); extern void hb_macroRun( HB_MACRO_PTR ); extern HB_MACRO_PTR hb_macroCompile( char * ); diff --git a/harbour/source/macro/macro.c b/harbour/source/macro/macro.c index 097de4cb16..1c297a372c 100644 --- a/harbour/source/macro/macro.c +++ b/harbour/source/macro/macro.c @@ -4,7 +4,7 @@ /* * Harbour Project source code: - * Compiler main file + * Macro compiler main file * * Copyright 1999 Ryszard Glab * www - http://www.harbour-project.org @@ -47,7 +47,13 @@ static BOOL hb_comp_bUseName10 = FALSE; /* names limited to 10 characters */ /* ************************************************************************* */ -/* Compile passes string into a pcode buffer +/* Compile passed string into a pcode buffer + * + * 'pMacro' - pointer to HB_MACRO structure that will hold all information + * nedded for macro compilation and evaluation + * 'szString' - a string to compile + * 'iFlag' - specifies if compiled code should generate pcodes either for push + * operation (for example: var :=¯o) or for pop operation (¯o :=var) */ static int hb_macroParse( HB_MACRO_PTR pMacro, char * szString, int iFlag ) { @@ -67,7 +73,7 @@ static int hb_macroParse( HB_MACRO_PTR pMacro, char * szString, int iFlag ) HB_TRACE(HB_TR_DEBUG, ("hb_macroParse.(%p, %s, %i)", pMacro, szString, iFlag)); pMacro->pCodeInfo->pCode = ( BYTE * ) hb_xgrab( HB_PCODE_SIZE ); - /* We have to specify if we want a push or pop operation because + /* We have to specify if we want either a push or a pop operation because * we are using different pcodes for these operations */ pMacro->Flags = iFlag; @@ -75,6 +81,12 @@ static int hb_macroParse( HB_MACRO_PTR pMacro, char * szString, int iFlag ) return hb_compParse( pMacro ); } +/* releases all memory allocated for macro evaluation + * NOTE: + * Only members of HB_MACRO structure are deallocated + * the 'pMacro' pointer is not released - it can be a pointer + * to a memory allocated on the stack. + */ void hb_macroDelete( HB_MACRO_PTR pMacro ) { HB_TRACE(HB_TR_DEBUG, ("hb_macroDelete(%p)", pMacro)); @@ -83,6 +95,8 @@ void hb_macroDelete( HB_MACRO_PTR pMacro ) hb_xfree( (void *) pMacro->pCodeInfo ); } +/* checks if a correct ITEM was passed from the virtual machine eval stack + */ static BOOL hb_macroCheckParam( HB_ITEM_PTR pItem ) { BOOL bValid = TRUE; @@ -139,37 +153,189 @@ static void hb_macroSyntaxError( HB_MACRO_PTR pMacro ) static BOOL hb_macroIsIdent( char * szString ) { char * pTmp = szString; + BOOL bIsIdent = FALSE; /* NOTE: This uses _a-zA-Z0-9 pattern to check for a valid name */ - if( *pTmp == '_' || (*pTmp >= 'A' && *pTmp <= 'Z') || (*pTmp >= 'a' && *pTmp <= 'z') ) + if( *pTmp ) { - ++pTmp; - while( *pTmp && (*pTmp == '_' || (*pTmp >= 'A' && *pTmp <= 'Z') || (*pTmp >= 'a' && *pTmp <= 'z') || (*pTmp >= '0' && *pTmp <= '9')) ) - ++pTmp; + if( ! ( pTmp[ 0 ] == '_' && pTmp[ 1 ] == 0 ) ) + { + /* this is not a "_" string + */ + if( *pTmp == '_' || (*pTmp >= 'A' && *pTmp <= 'Z') || (*pTmp >= 'a' && *pTmp <= 'z') ) + { + ++pTmp; + while( *pTmp && (*pTmp == '_' || (*pTmp >= 'A' && *pTmp <= 'Z') || (*pTmp >= 'a' && *pTmp <= 'z') || (*pTmp >= '0' && *pTmp <= '9')) ) + ++pTmp; + /* the name is valid if pTmp is at the end of a string + */ + bIsIdent = (*pTmp ? FALSE : TRUE ); + } + } } - /* the name was valid if pTmp is at the end of a string - */ - return (*pTmp ? FALSE : TRUE ); + return bIsIdent; } -/* Replace all nested macro operators +/* This replaces all '&var' or '&var.' occurences within a given string + * with the value of variable 'var' if this variable exists and contains + * a string value. The value of variable is also searched for + * occurences of macro operator and if it is found then it is expanded + * until there is no more macro operators. + * NOTE: + * this does not evaluate a macro expression - there is a simple text + * substitution only + * NOTE: + * hb_macroTextSubst returns either a pointer that points to the passed + * string if there was no macro operator in it or a pointer to a new + * allocated memory with expanded string if there was a macro operator + * in passed string. */ -static char * hb_macroTextSubst( char * szString ) +static char * hb_macroTextSubst( char * szString, ULONG *pulStringLen ) { - char * szText; - ULONG ulLen; + char * szResult; + ULONG ulCopy; + ULONG ulResLen; + ULONG ulResPos; + ULONG ulCharsLeft; + char * pHead; + char * pTail; - HB_TRACE(HB_TR_DEBUG, ("hb_macroTextSubst(%s)", szString)); + HB_TRACE(HB_TR_DEBUG, ("hb_macroTextSubst(%s, %li)", szString, *pulStringLen)); -/* TODO: Nested macro or complex macro ( e.g. ¯o¯o2.end ) - */ - ulLen = strlen( szString ) + 1; - szText = (char *) hb_xgrab( ulLen ); - memcpy( szText, szString, ulLen ); + pHead = (char *)memchr( (void *)szString, '&', *pulStringLen ); + if( pHead == NULL ) + return szString; /* no more processing is required */ - return szText; + /* initial buffer for return value */ + szResult = (char *) hb_xgrab( *pulStringLen + 1 ); + /* copy initial length of the string (it can contain null bytes) */ + ulResLen = ulCharsLeft = *pulStringLen; + /* current position within the buffer where byte will be copied */ + ulResPos = 0; + + pTail = szString; + do + { + ulCopy = pHead - pTail; + if( ulCopy ) + { + /* copy bytes located before '&' character */ + memcpy( szResult + ulResPos, pTail, ulCopy ); + ulResPos += ulCopy; + pTail = pHead; + } + + /* check if the next character can start a valid identifier + * (only _a-zA-Z are allowed) + */ + ++pHead; + if( *pHead == '_' || (*pHead >= 'A' && *pHead <= 'Z') || (*pHead >= 'a' && *pHead <= 'z') ) + { + /* extract a variable name */ + /* NOTE: the extracted name can be longer then supported maximal + * length of identifiers (HB_SYMBOL_NAME_LEN) - only the max allowed + * are used for name lookup however the whole string is replaced + */ + ULONG ulNameLen = 0; + char * pName = pHead; + + while( *pHead && (*pHead == '_' || (*pHead >= 'A' && *pHead <= 'Z') || (*pHead >= 'a' && *pHead <= 'z') || (*pHead >= '0' && *pHead <= '9')) ) + { + ++pHead; + ++ulNameLen; + } + /* pHead points now at the character that terminated a variable name */ + + /* NOTE: '_' is invalid variable name + */ + if( ! ( *pName == '_' && ulNameLen == 1 ) ) + { + /* this is not the "&_" string */ + char * szValPtr; + ULONG ulValLen; + + /* Get a pointer to the string value stored in this variable + * or NULL if variable doesn't exist or doesn't contain a string + * value. + * NOTE: This doesn't create a copy of the value then it + * shouldn't be released here. + */ + ulValLen = ulNameLen; /* the length of name */ + szValPtr = hb_memvarGetStrValuePtr( pName, &ulValLen ); + if( szValPtr ) + { + char * szValSubst; + + if( *pHead == '.' ) + { + /* we have stopped at the macro terminator '.' - skip it */ + ++pHead; + ++ulNameLen; + } + + /* Check if returned string contains nested macro operators + * and expand them if they exist + */ + szValSubst = hb_macroTextSubst( szValPtr, &ulValLen ); + + /* expand the result buffer if the substituted value is too + * long to be stored in the current buffer + */ + if( (ulResPos + ulValLen) >= ulResLen ) + { + ulResLen = ulResPos + ulValLen; + szResult = ( char * ) hb_xrealloc( szResult, ulResLen + 1 ); + } + memcpy( szResult + ulResPos, szValSubst, ulValLen ); + ulResPos += ulValLen; + if( szValSubst != szValPtr ) + { + /* a new pointer was created in hb_macroTextSubst + * (if a macro operator was expanded) - we should release it + */ + hb_xfree( szValSubst ); + } + /* move a pointer to the characters processed so far - + * replaced characters shouldn't be copied + */ + pTail = pHead; + } + + } + } + /* copy characters that were not a valid identifier */ + ulCopy = pHead - pTail; + if( ulCopy ) + { + memcpy( szResult + ulResPos, pTail, ulCopy ); + ulResPos += ulCopy; + pTail = pHead; + } + ulCharsLeft = *pulStringLen - ( pTail - szString ); + } + while( ulCharsLeft && ( pHead = (char *) memchr( (void *)pTail, '&', ulCharsLeft ) ) ); + + if( ulCharsLeft ) + { + /* copy trailing characters */ + memcpy( szResult + ulResPos, pTail, ulCharsLeft ); + ulResPos += ulCharsLeft; + } + + if( ulResPos < ulResLen ) + { + /* result string is shorter then allocated buffer - + * cut it to a required length + */ + szResult = ( char * ) hb_xrealloc( szResult, ulResPos + 1 ); + } + szResult[ ulResPos ] = 0; /* place terminating null character */ + /* return a length of result string */ + *pulStringLen = ulResPos; + + return szResult; /* a new memory buffer was allocated */ } @@ -257,16 +423,24 @@ HB_MACRO_PTR hb_macroCompile( char * szString ) return pMacro; } +/* This function handles a macro function calls, e.g. var :=¯o() + * + * 'pItem' points to a ITEM that contains a string value which after + * text substitution will return a function name + */ void hb_macroPushSymbol( HB_ITEM_PTR pItem ) { - HB_TRACE(HB_TR_DEBUG, ("hb_macroGetValue(%p)", pItem)); + HB_TRACE(HB_TR_DEBUG, ("hb_macroPushSymbol(%p)", pItem)); if( hb_macroCheckParam( pItem ) ) { HB_MACRO struMacro; char * szString; + BOOL bNewBuffer; + ULONG ulLength = pItem->item.asString.length; - szString = hb_macroTextSubst( pItem->item.asString.value ); + szString = hb_macroTextSubst( pItem->item.asString.value, &ulLength ); + bNewBuffer = ( szString != pItem->item.asString.value ); hb_stackPop(); /* remove compiled string */ if( hb_macroIsIdent( szString ) ) @@ -274,17 +448,54 @@ void hb_macroPushSymbol( HB_ITEM_PTR pItem ) HB_DYNS_PTR pDynSym; pDynSym = hb_dynsymGet( szString ); - /* NOTE: checking for valid function name is done in hb_vmDo() + /* NOTE: checking for valid function name (valid pointer) is done + * in hb_vmDo() */ hb_vmPushSymbol( pDynSym->pSymbol ); - hb_xfree( szString ); + if( bNewBuffer ) + hb_xfree( szString ); /* free space allocated in hb_macroTextSubst */ } else + { + if( bNewBuffer ) + hb_xfree( szString ); /* free space allocated in hb_macroTextSubst */ hb_macroSyntaxError( &struMacro ); + } } } +/* Macro text substitution + * + * 'pItem' points to a ITEM that contains a string value which after + * text substitution will be returned + */ +void hb_macroTextValue( HB_ITEM_PTR pItem ) +{ + HB_TRACE(HB_TR_DEBUG, ("hb_macroTextValue(%p)", pItem)); + + if( hb_macroCheckParam( pItem ) ) + { + char * szString; + ULONG ulLength = pItem->item.asString.length; + + szString = hb_macroTextSubst( pItem->item.asString.value, &ulLength ); + + if( szString != pItem->item.asString.value ) + { + /* replace the old value on the eval stack with the new one + */ + hb_itemPutCPtr( pItem, szString, ulLength ); + } + /* + * else + * leave original value on the eval stack - there was no '&' operator + * inside a string + */ + } +} + + /* ************************************************************************* */ /* returns the order + 1 of a variable if defined or zero */ diff --git a/harbour/source/rtl/memvars.c b/harbour/source/rtl/memvars.c index 867de4cf01..75fd896f36 100644 --- a/harbour/source/rtl/memvars.c +++ b/harbour/source/rtl/memvars.c @@ -87,6 +87,7 @@ struct mv_PUBLIC_var_info static void hb_memvarCreateFromItem( PHB_ITEM, BYTE, PHB_ITEM ); static void hb_memvarCreateFromDynSymbol( PHB_DYNS, BYTE, PHB_ITEM ); static void hb_memvarAddPrivate( PHB_DYNS ); +static HB_DYNS_PTR hb_memvarFindSymbol( HB_ITEM_PTR ); void hb_memvarsInit( void ) { @@ -513,7 +514,7 @@ void hb_memvarGetRefer( HB_ITEM_PTR pItem, PHB_SYMB pMemvarSymb ) hb_errInternal( 9999, "Invalid symbol item passed as memvar %s", pMemvarSymb->szName, NULL ); } -/* +/* Create a new variable declared as procedure/function PARAMETER */ void hb_memvarNewParameter( PHB_SYMB pSymbol, PHB_ITEM pValue ) { @@ -522,6 +523,49 @@ void hb_memvarNewParameter( PHB_SYMB pSymbol, PHB_ITEM pValue ) hb_memvarCreateFromDynSymbol( pSymbol->pDynSym, HB_MV_PRIVATE, pValue ); } +/* This function returns a string value of memvar variable with passed name. + * If a requested variable does not exist or contains non-string value + * then the NULL pointer is returned. + * NOTE: + * This is a helper function for macro text substitution + * var1 := "&var2.value" + */ +char * hb_memvarGetStrValuePtr( char * szVarName, ULONG *pulLen ) +{ + HB_ITEM itName; + HB_DYNS_PTR pDynVar; + char * szValue = NULL; + + HB_TRACE(HB_TR_DEBUG, ("hb_memvarGetStrValuePtr(%s, %li)", szVarName, pulLen)); + + itName.type = IT_STRING; + itName.item.asString.value = szVarName; + itName.item.asString.length = *pulLen; + pDynVar = hb_memvarFindSymbol( &itName ); + + if( pDynVar ) + { + /* there is dynamic symbol with the requested name - check if it is + * a memvar variable + */ + if( pDynVar->hMemvar ) + { + /* variable contains some data + */ + HB_ITEM_PTR pItem = &s_globalTable[ pDynVar->hMemvar ].item; + if( IS_BYREF( pItem ) ) + pItem = hb_itemUnRef( pItem ); /* it is a PARAMETER variable */ + if( IS_STRING( pItem ) ) + { + szValue = pItem->item.asString.value; + *pulLen = pItem->item.asString.length; + } + } + } + + return szValue; +} + /* * This function creates a value for memvar variable @@ -847,12 +891,19 @@ static HB_DYNS_PTR hb_memvarFindSymbol( HB_ITEM_PTR pName ) if( pName ) { - ULONG ulLen = pName->item.asString.length; + ULONG ulLen; + + /* truncate a passed name to maximal allowed symbol name + */ + if( pName->item.asString.length < HB_SYMBOL_NAME_LEN ) + ulLen = pName->item.asString.length; + else + ulLen = HB_SYMBOL_NAME_LEN; if( ulLen ) { - char * szName = ( char * ) hb_xgrab( ulLen + 1 ); char * szArg = pName->item.asString.value; + char szName[ HB_SYMBOL_NAME_LEN + 1 ]; szName[ ulLen ] = '\0'; do @@ -862,7 +913,6 @@ static HB_DYNS_PTR hb_memvarFindSymbol( HB_ITEM_PTR pName ) } while( ulLen ); pDynSym = hb_dynsymFind( szName ); - hb_xfree( szName ); } } return pDynSym; diff --git a/harbour/source/vm/hvm.c b/harbour/source/vm/hvm.c index 4b8a24f8c4..656e793e86 100644 --- a/harbour/source/vm/hvm.c +++ b/harbour/source/vm/hvm.c @@ -940,7 +940,10 @@ void hb_vmExecute( BYTE * pCode, PHB_SYMB pSymbols ) break; case HB_P_MACROTEXT: - /* macro text substitution */ + /* macro text substitution + * "text ¯o.other text" + */ + hb_macroTextValue( hb_stack.pPos - 1 ); w++; break;