//------------------------------------------------------------------------- // Desc: Query parsing // Tabs: 3 // // Copyright (c) 1998-2000,2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program 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, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fqparse.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE tokenAllocSpace( char ** ppszTokenStart, char ** ppszToken, FLMUINT * puiTokenBufSize, FLMUINT uiNewTokenBufSize); FSTATIC RCODE tokenGet( const char ** ppszString, char ** ppszToken, FLMBOOL * pbQuoted, QTYPES * peType, FLMUINT * puiTokenBufSize); FSTATIC FLMBOOL tokenIsOperator( const char * pszToken, QTYPES * peOperator); FSTATIC FLMBOOL tokenIsField( const char * pszToken, F_NameTable * pNameTable, FLMUINT * puiFieldPath, QTYPES * peValueType, FLMBOOL bAllowNamesOnly, FLMBOOL bMustBeField); FSTATIC RCODE allocValueSpace( void ** ppvVal, FLMUINT * puiValBufSize, FLMUINT uiNewSize); FSTATIC RCODE tokenGetBinary( const char * pszToken, void ** ppvVal, FLMUINT * puiValLen, FLMUINT * puiValBufSize); FSTATIC RCODE tokenGetValue( QTYPES eValueType, const char * pszToken, FLMBOOL bQuoted, void ** ppvVal, QTYPES * peValType, FLMUINT * puiValLen, FLMUINT * puiValBufSize); /**************************************************************************** Desc: Allocate space for the token. ****************************************************************************/ FSTATIC RCODE tokenAllocSpace( char ** ppszTokenStart, char ** ppszToken, FLMUINT * puiTokenBufSize, FLMUINT uiNewTokenBufSize) { RCODE rc = FERR_OK; char * pszTmp; if (RC_BAD( rc = f_alloc( uiNewTokenBufSize, &pszTmp))) { goto Exit; } // Copy the current token into the new buffer. f_memcpy( pszTmp, *ppszTokenStart, *puiTokenBufSize); // Free the old buffer, if it was allocated f_free( ppszTokenStart); *ppszTokenStart = pszTmp; *ppszToken = *ppszTokenStart + *puiTokenBufSize; *puiTokenBufSize = uiNewTokenBufSize; Exit: return( rc); } /**************************************************************************** Desc: Determine if a character is a delimiter character. ****************************************************************************/ FINLINE FLMBOOL tokenIsDelimiter( FLMBYTE ucChar) { if (ucChar <= ' ') { return( TRUE); } switch (ucChar) { case '!': case '+': case '-': case '*': case '%': case '/': case '(': case ')': case '=': case '>': case '<': case '&': case '|': case '\'': case '"': return( TRUE); default: return( FALSE); } } /**************************************************************************** Desc: Get the next token from a query string. ****************************************************************************/ FSTATIC RCODE tokenGet( const char ** ppszString, char ** ppszToken, FLMBOOL * pbQuoted, QTYPES * peType, FLMUINT * puiTokenBufSize) { RCODE rc = FERR_OK; const char * pszStr = *ppszString; char * pszToken = *ppszToken; char * pszTokenStart = pszToken; FLMBYTE ucQuote; FLMUINT uiLen; // Skip leading white space *peType = NO_TYPE; *pbQuoted = FALSE; while (*pszStr && *pszStr <= ' ') { pszStr++; } switch (*pszStr) { case 0: break; case '!': case '+': case '-': case '*': case '%': case '/': case '(': case ')': { *pszToken++ = *pszStr++; break; } case '=': case '>': case '<': { *pszToken++ = *pszStr++; if (*pszStr == '=') { *pszToken++ = *pszStr++; } break; } case '&': { *pszToken++ = *pszStr++; if (*pszStr == '&') { *pszToken++ = *pszStr++; } break; } case '|': { *pszToken++ = *pszStr++; if (*pszStr == '|') { *pszToken++ = *pszStr++; } break; } case '\'': case '"': { *pbQuoted = TRUE; ucQuote = *pszStr++; uiLen = 0; while (*pszStr && *pszStr != ucQuote) { *pszToken++ = *pszStr++; uiLen++; // If we don't have room for a null terminating byte, // better reallocate. Allocate enough so that we can // get the entire token. if (uiLen == *puiTokenBufSize) { const char * pszTmp = pszStr; FLMUINT uiExtraCharsNeeded = 1; // For NULL // See how many more characters we need. while (*pszTmp && *pszTmp != ucQuote) { uiExtraCharsNeeded++; pszTmp++; } if (RC_BAD( rc = tokenAllocSpace( &pszTokenStart, &pszToken, puiTokenBufSize, *puiTokenBufSize + uiExtraCharsNeeded))) { goto Exit; } } } // If we ended on the quote, skip it. if (*pszStr == ucQuote) { pszStr++; } else { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } break; } default: { uiLen = 0; while (!tokenIsDelimiter( *pszStr)) { *pszToken++ = *pszStr++; uiLen++; // If we don't have room for a null terminating byte, // better reallocate. Allocate enough so that we can // get the entire token. if (uiLen == *puiTokenBufSize) { const char * pszTmp = pszStr; FLMUINT uiExtraCharsNeeded = 1; // For NULL // See how many more characters we need. while (!tokenIsDelimiter( *pszTmp)) { uiExtraCharsNeeded++; pszTmp++; } if (RC_BAD( rc = tokenAllocSpace( &pszTokenStart, &pszToken, puiTokenBufSize, *puiTokenBufSize + uiExtraCharsNeeded))) { goto Exit; } } } break; } } // Always null terminate the token *pszToken = 0; // Test to see if we have any function keywords we want to parse out here. if( !(*pbQuoted)) { if (f_stricmp( pszTokenStart, "field") == 0 || f_stricmp( pszTokenStart, "path") == 0) { *peType = FLM_FLD_PATH; } else if (f_stricmp( pszTokenStart, "unicode") == 0 || f_stricmp( pszTokenStart, "text") == 0) { *peType = FLM_UNICODE_VAL; } else if (f_stricmp( pszTokenStart, "unsigned") == 0) { *peType = FLM_UINT32_VAL; } else if (f_stricmp( pszTokenStart, "boolean") == 0) { *peType = FLM_BOOL_VAL; } else if (f_stricmp( pszTokenStart, "signed") == 0) { *peType = FLM_INT32_VAL; } else if (f_stricmp( pszTokenStart, "context") == 0) { *peType = FLM_REC_PTR_VAL; } else if (f_stricmp( pszTokenStart, "binary") == 0) { *peType = FLM_BINARY_VAL; } if (*peType != NO_TYPE) { FLMBYTE ucEndChar; // Skip white space - should hit a left paren. while (*pszStr && *pszStr <= ' ') { pszStr++; } // Better have a left paren here, or it is a syntax // error. if (*pszStr != '(') { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } // Skip past left paren pszStr++; // Skip white space again while (*pszStr && *pszStr <= ' ') { pszStr++; } // Better not be at end of string or have an empty value if (!(*pszStr) || *pszStr == ')') { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } // Token can be quoted inside the parens. However, it doesn't // change the type to string. The type specified takes precedence. if (*pszStr == '"' || *pszStr == '\'') { ucEndChar = *pszStr; pszStr++; } else { ucEndChar = ')'; } uiLen = 0; pszToken = pszTokenStart; while (*pszStr && *pszStr != ucEndChar) { *pszToken++ = *pszStr++; uiLen++; // If we don't have room for a null terminating byte, // better reallocate. Allocate enough so that we can // get the entire token. if (uiLen == *puiTokenBufSize) { const char * pszTmp = pszStr; FLMUINT uiExtraCharsNeeded = 1; // For NULL // See how many more characters we need. while (*pszStr && *pszStr != ucEndChar) { uiExtraCharsNeeded++; pszTmp++; } if (RC_BAD( rc = tokenAllocSpace( &pszTokenStart, &pszToken, puiTokenBufSize, *puiTokenBufSize + uiExtraCharsNeeded))) { goto Exit; } } } *pszToken = 0; // If the string is not quoted, trim off trailing white space if (ucEndChar == ')') { while (pszToken > pszTokenStart) { pszToken--; if (*pszToken <= ' ') { *pszToken = 0; } else { break; } } } // If we did not hit the end character, we have a syntax error. if (*pszStr != ucEndChar) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } // Skip past end character pszStr++; // If the end character is not a right paren, skip any white space // between it and the right paren. if (ucEndChar != ')') { while (*pszStr && *pszStr <= ' ') { pszStr++; } // If we did not hit a right paren, it is a syntax error. if (*pszStr != ')') { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } // Skip the right paren pszStr++; } } } Exit: // Return string should be positioned past the last character of the // returned token. *ppszString = pszStr; *ppszToken = pszTokenStart; return( rc); } /**************************************************************************** Desc: Determine if a token is an operator. ****************************************************************************/ FSTATIC FLMBOOL tokenIsOperator( const char * pszToken, QTYPES * peOperator) { FLMBOOL bIsOperator = TRUE; if (f_stricmp( pszToken, "(") == 0) { *peOperator = FLM_LPAREN_OP; } else if (f_stricmp( pszToken, ")") == 0) { *peOperator = FLM_RPAREN_OP; } else if (f_stricmp( pszToken, "&&") == 0 || f_stricmp( pszToken, "AND") == 0) { *peOperator = FLM_AND_OP; } else if (f_stricmp( pszToken, "||") == 0 || f_stricmp( pszToken, "OR") == 0) { *peOperator = FLM_OR_OP; } else if (f_stricmp( pszToken, "!") == 0) { *peOperator = FLM_NOT_OP; } else if (f_stricmp( pszToken, "==") == 0 || f_stricmp( pszToken, "EQ") == 0 || f_stricmp( pszToken, "=") == 0) { *peOperator = FLM_EQ_OP; } else if (f_stricmp( pszToken, "!=") == 0 || f_stricmp( pszToken, "NE") == 0) { *peOperator = FLM_NE_OP; } else if (f_stricmp( pszToken, "<=") == 0 || f_stricmp( pszToken, "LE") == 0) { *peOperator = FLM_LE_OP; } else if (f_stricmp( pszToken, "<") == 0 || f_stricmp( pszToken, "LT") == 0) { *peOperator = FLM_LT_OP; } else if (f_stricmp( pszToken, ">=") == 0 || f_stricmp( pszToken, "GE") == 0) { *peOperator = FLM_GE_OP; } else if (f_stricmp( pszToken, ">") == 0 || f_stricmp( pszToken, "GT") == 0) { *peOperator = FLM_GT_OP; } else if (f_stricmp( pszToken, "MATCH") == 0 || f_stricmp( pszToken, "MATCHES") == 0) { *peOperator = FLM_MATCH_OP; } else if (f_stricmp( pszToken, "MATCHBEGIN") == 0 || f_stricmp( pszToken, "MATCH_BEGIN") == 0) { *peOperator = FLM_MATCH_BEGIN_OP; } else if (f_stricmp( pszToken, "MATCHEND") == 0 || f_stricmp( pszToken, "MATCH_END") == 0) { *peOperator = FLM_MATCH_END_OP; } else if (f_stricmp( pszToken, "CONTAINS") == 0 || f_stricmp( pszToken, "CONTAIN") == 0) { *peOperator = FLM_CONTAINS_OP; } else if (f_stricmp( pszToken, "&") == 0 || f_stricmp( pszToken, "BITAND") == 0) { *peOperator = FLM_BITAND_OP; } else if (f_stricmp( pszToken, "|") == 0 || f_stricmp( pszToken, "BITOR") == 0) { *peOperator = FLM_AND_OP; } else if (f_stricmp( pszToken, "^") == 0 || f_stricmp( pszToken, "BITXOR") == 0) { *peOperator = FLM_AND_OP; } else if (f_stricmp( pszToken, "*") == 0) { *peOperator = FLM_MULT_OP; } else if (f_stricmp( pszToken, "/") == 0) { *peOperator = FLM_DIV_OP; } else if (f_stricmp( pszToken, "%") == 0 || f_stricmp( pszToken, "MOD") == 0) { *peOperator = FLM_MOD_OP; } else if (f_stricmp( pszToken, "+") == 0) { *peOperator = FLM_PLUS_OP; } else if (f_stricmp( pszToken, "-") == 0) { *peOperator = FLM_MINUS_OP; } else { bIsOperator = FALSE; } return( bIsOperator); } /**************************************************************************** Desc: Map a field type to a query value type. ****************************************************************************/ FINLINE QTYPES mapFieldTypeToValType( FLMUINT uiFieldType) { switch (uiFieldType) { case FLM_TEXT_TYPE: return( FLM_UNICODE_VAL); case FLM_NUMBER_TYPE: return( FLM_UINT32_VAL); case FLM_CONTEXT_TYPE: return( FLM_REC_PTR_VAL); case FLM_BINARY_TYPE: return( FLM_BINARY_VAL); } // Should never reach here - but some compilers don't like it when // all of the cases are not covered. flmAssert( 0); return( NO_TYPE); } /**************************************************************************** Desc: Determine if a token is a field. ****************************************************************************/ FSTATIC FLMBOOL tokenIsField( const char * pszToken, F_NameTable * pNameTable, FLMUINT * puiFieldPath, QTYPES * peValueType, FLMBOOL bAllowNamesOnly, FLMBOOL bMustBeField) { FLMBOOL bIsField = TRUE; FLMUINT uiFieldCount = 0; FLMUINT uiFieldNum; FLMUINT uiDictType; FLMBOOL bIsTagName; FLMBOOL bIsNum; FLMBOOL bIsDrnNum; FLMBOOL bIsTagNum; FLMUINT uiNum; FLMUINT uiTagNum; FLMUINT uiFieldType; char szNameBuf[ 128]; char * pszNameEnd; // Parse out each part of the name. while( *pszToken) { // Path components are separated by periods or spaces. They may be names or // tag numbers. pszNameEnd = &szNameBuf[ 0]; while( *pszToken && *pszToken != '.') { *pszNameEnd++ = *pszToken; pszToken++; } *pszNameEnd = 0; // If we detect a period, the bAllowNamesOnly restriction is lifted. if( *pszToken == '.') { bAllowNamesOnly = FALSE; } if( pNameTable) { bIsTagName = pNameTable->getFromTagTypeAndName( NULL, szNameBuf, FLM_FIELD_TAG, &uiFieldNum, &uiFieldType); } else { bIsTagName = FALSE; } bIsNum = tokenIsNum( szNameBuf, &uiNum); bIsDrnNum = FALSE; if( f_stricmp( szNameBuf, "DRN") == 0) { bIsDrnNum = TRUE; uiNum = FLM_RECID_FIELD; } bIsTagNum = (f_strnicmp( szNameBuf, "TAG_", 4) == 0 && tokenIsNum( &szNameBuf[ 4], &uiTagNum)) ? TRUE : FALSE; // See if the token is a field name. // // NOTE: If it is a number and also comes out as a legitimate tag name, // we have a bit of an ambiguity. In that case, we will ignore the tag // name if the bMustBeField flag is TRUE, because that means it was // specified inside a field() construct: e.g., field( 60). In these // cases, we would take the literal number, even though the string "60" // also happens to map to a tag name. if( !bIsTagName || (bIsNum && bMustBeField)) { if( bAllowNamesOnly) { bIsField = FALSE; goto Exit; } // Not a field name, see if it is a number in the proper // range for field numbers. if( bIsNum || bIsDrnNum) { uiFieldNum = uiNum; } else if( bIsTagNum) { uiFieldNum = uiTagNum; } else { bIsField = FALSE; goto Exit; } if( !uiFieldNum || uiFieldNum > 0xFFFF) { bIsField = FALSE; goto Exit; } if( uiFieldNum >= FLM_UNREGISTERED_TAGS) { // This is only valid if the field name began with "TAG_" // Otherwise, we will treat it as a value, not a field. if( !bIsTagNum) { bIsField = FALSE; goto Exit; } *peValueType = NO_TYPE; } else if( uiFieldNum == FLM_RECID_FIELD) { *peValueType = FLM_REC_PTR_VAL; } else if( pNameTable && pNameTable->getFromTagNum( uiFieldNum, NULL, NULL, 0, &uiDictType, &uiFieldType) && uiDictType == FLM_FIELD_TAG) { *peValueType = mapFieldTypeToValType( uiFieldType); } else if( bMustBeField) { // Couldn't find in name table, or name table was NULL, // so we don't know its type, but we know that this is // inside a field() construct - so we have to take it as is. *peValueType = NO_TYPE; } else { bIsField = FALSE; goto Exit; } } else { *peValueType = mapFieldTypeToValType( uiFieldType); // If the name happens to be a number and we are only allowing // names, don't use this one. if( bIsNum && bAllowNamesOnly) { bIsField = FALSE; goto Exit; } } puiFieldPath [uiFieldCount++] = uiFieldNum; // If *pszToken is non-null (a period), skip it. if( *pszToken) { pszToken++; } } // A field count of zero means we didn't get anything that // looked like a legitimate field. if( !uiFieldCount) { bIsField = FALSE; goto Exit; } // Null terminate the field path. puiFieldPath[ uiFieldCount] = 0; Exit: return( bIsField); } /**************************************************************************** Desc: Allocate buffer space for a query value. ****************************************************************************/ FSTATIC RCODE allocValueSpace( void ** ppvVal, FLMUINT * puiValBufSize, FLMUINT uiNewSize) { RCODE rc = FERR_OK; void * pvVal; if( RC_BAD( rc = f_alloc( uiNewSize, &pvVal))) { goto Exit; } f_free( ppvVal); *ppvVal = pvVal; *puiValBufSize = uiNewSize; Exit: return( rc); } /**************************************************************************** Desc: Get a unicode value from a token. ****************************************************************************/ RCODE tokenGetUnicode( const char * pszToken, void ** ppvVal, FLMUINT * puiValLen, FLMUINT * puiValBufSize) { RCODE rc = FERR_OK; FLMUINT uiLen = f_strlen( pszToken) + 1; FLMUNICODE * puzTmp; // Just make sure we have enough to cover the maximum number // of unicode characters that could be created. if( *puiValBufSize < uiLen * sizeof( FLMUNICODE)) { if( RC_BAD( rc = allocValueSpace( ppvVal, puiValBufSize, uiLen * sizeof( FLMUNICODE)))) { goto Exit; } } // Convert the ASCII to unicode. puzTmp = (FLMUNICODE *)(*ppvVal); while (*pszToken) { if (*pszToken != '~' || *(pszToken + 1) != '[') { *puzTmp++ = (FLMUNICODE)(*pszToken); pszToken++; } else { const char * pszSave = pszToken; FLMUNICODE * puzSave = puzTmp; // Skip the ~[ pszToken += 2; // Unicode characters may be specified as numbers // separated by commas and/or spaces. The list // ends when we hit a ']' character or a non-numeric // value. while (*pszToken && *pszToken != ']') { char szNumBuf[ 32]; char * pszNumEnd; FLMBOOL bIsNum; FLMUINT uiNum; // Skip white space and commas while (*pszToken && (*pszToken <= ' ' || *pszToken == ',')) { pszToken++; } // Number should start here. pszNumEnd = &szNumBuf[ 0]; while( *pszToken > ' ' && *pszToken != ',' && *pszToken != ']') { *pszNumEnd++ = *pszToken; pszToken++; } *pszNumEnd = 0; // If we ended on a non-null character, see if it is a number. // If not, we will quit processing. if( !(*pszToken)) { pszToken = pszSave; break; } bIsNum = tokenIsNum( szNumBuf, &uiNum); if( bIsNum && uiNum && uiNum <= 0xFFFE) { *puzTmp++ = (FLMUNICODE)uiNum; } else { // Resetting pszToken to pszSave so that we will know that // we did not successfully process everything in the ~[]. pszToken = pszSave; break; } } // If we hit a ']', we successfully processed whatever was between // the ~[], and we can skip the ']'. If we did not hit the ']' // we were not able to interpret everything between the ~[] as unicode // characters, so we should just go back and redo everything between // the ~[] as ascii - including the ~[]. if (*pszToken == ']') { pszToken++; } else { // UNICODE-ize the rest of the string, including the ~[ and // up to a trailing ']', if any. pszToken = pszSave; puzTmp = puzSave; while (*pszToken && *pszToken != ']') { *puzTmp++ = (FLMUNICODE)(*pszToken); pszToken++; } // Get trailing ']', if any if (*pszToken == ']') { *puzTmp++ = (FLMUNICODE)(*pszToken); pszToken++; } } } } // Null terminate *puzTmp = 0; Exit: *puiValLen = 0; return( rc); } /**************************************************************************** Desc: Get a binary value from a token. ****************************************************************************/ FSTATIC RCODE tokenGetBinary( const char * pszToken, void ** ppvVal, FLMUINT * puiValLen, FLMUINT * puiValBufSize) { RCODE rc = FERR_OK; FLMUINT uiLen = f_strlen( pszToken); FLMBYTE * pucTmp; FLMUINT uiBinaryChars; FLMUINT uiNibble; FLMBOOL bFirstNibble; // Just make sure we have enough to cover the maximum number // of binary characters that could be created. if (*puiValBufSize < uiLen / 2 + 1) { if (RC_BAD( rc = allocValueSpace( ppvVal, puiValBufSize, uiLen / 2 + 1))) { goto Exit; } } // Convert the ASCII to unicode. pucTmp = (FLMBYTE *)(*ppvVal); uiBinaryChars = 0; bFirstNibble = TRUE; while (*pszToken) { if (*pszToken >= '0' && *pszToken <= '9') { uiNibble = (FLMUINT)(*pszToken - '0'); } else if (*pszToken >= 'A' && *pszToken <= 'F') { uiNibble = (FLMUINT)(*pszToken - 'A' + 10); } else if (*pszToken >= 'a' && *pszToken <= 'f') { uiNibble = (FLMUINT)(*pszToken - 'a' + 10); } else { rc = RC_SET( FERR_BAD_FIELD_TYPE); goto Exit; } pszToken++; if (bFirstNibble) { uiBinaryChars++; *pucTmp = (FLMBYTE)(uiNibble << 4); bFirstNibble = FALSE; } else { (*pucTmp) |= (FLMBYTE)(uiNibble); bFirstNibble = TRUE; pucTmp++; } } // If we ended on an odd number of characters, shift our last character // down by four bits. if (!bFirstNibble) { (*pucTmp) >>= 4; } *puiValLen = uiBinaryChars; Exit: return( rc); } /**************************************************************************** Desc: Parse a value and return it. ****************************************************************************/ FSTATIC RCODE tokenGetValue( QTYPES eValueType, const char * pszToken, FLMBOOL bQuoted, void ** ppvVal, QTYPES * peValType, FLMUINT * puiValLen, FLMUINT * puiValBufSize) { RCODE rc = FERR_OK; FLMUINT uiNum; FLMINT32 i32Num; FLMUINT32 ui32Num; if (bQuoted || eValueType == FLM_UNICODE_VAL) { *peValType = FLM_UNICODE_VAL; if (RC_BAD( rc = tokenGetUnicode( pszToken, ppvVal, puiValLen, puiValBufSize))) { goto Exit; } } else if (eValueType == FLM_UINT32_VAL || eValueType == FLM_INT32_VAL) { FLMBOOL bNeg = FALSE; if (*pszToken == '-') { bNeg = TRUE; pszToken++; } if (tokenIsNum( pszToken, &uiNum)) { if (*puiValBufSize < sizeof( FLMINT32)) { if (RC_BAD( rc = allocValueSpace( ppvVal, puiValBufSize, sizeof( FLMINT32)))) { goto Exit; } } if (bNeg || eValueType == FLM_INT32_VAL) { i32Num = (FLMINT32)uiNum; if (bNeg) { i32Num = -i32Num; } *peValType = FLM_INT32_VAL; *((FLMINT32 *)(*ppvVal)) = i32Num; *puiValLen = sizeof( FLMINT32); } else { ui32Num = (FLMUINT32)uiNum; *peValType = FLM_UINT32_VAL; *((FLMUINT32 *)(*ppvVal)) = ui32Num; *puiValLen = sizeof( FLMUINT32); } } else { rc = RC_SET( FERR_BAD_FIELD_TYPE); goto Exit; } } else if (eValueType == FLM_REC_PTR_VAL) { if (*puiValBufSize < sizeof( FLMUINT32)) { if (RC_BAD( rc = allocValueSpace( ppvVal, puiValBufSize, sizeof( FLMUINT32)))) { goto Exit; } } if (tokenIsNum( pszToken, &uiNum)) { ui32Num = (FLMUINT32)uiNum; *peValType = FLM_REC_PTR_VAL; *((FLMUINT32 *)(*ppvVal)) = ui32Num; *puiValLen = sizeof( FLMUINT32); } else { rc = RC_SET( FERR_BAD_FIELD_TYPE); goto Exit; } } else if (eValueType == FLM_BOOL_VAL) { if (*puiValBufSize < sizeof( FLMBOOL)) { if (RC_BAD( rc = allocValueSpace( ppvVal, puiValBufSize, sizeof( FLMBOOL)))) { goto Exit; } } if (tokenIsNum( pszToken, &uiNum)) { *peValType = FLM_BOOL_VAL; *((FLMBOOL *)(*ppvVal)) = uiNum ? TRUE : FALSE; *puiValLen = sizeof( FLMBOOL); } else if (f_stricmp( pszToken, "false") == 0) { *peValType = FLM_BOOL_VAL; *((FLMBOOL *)(*ppvVal)) = FALSE; *puiValLen = sizeof( FLMBOOL); } else if (f_stricmp( pszToken, "true") == 0) { *peValType = FLM_BOOL_VAL; *((FLMBOOL *)(*ppvVal)) = TRUE; *puiValLen = sizeof( FLMBOOL); } else if (f_stricmp( pszToken, "unknown") == 0) { *peValType = FLM_BOOL_VAL; if (*ppvVal) { f_free( ppvVal); } *puiValBufSize = 0; *puiValLen = 0; } else { rc = RC_SET( FERR_BAD_FIELD_TYPE); goto Exit; } } else if (eValueType == FLM_BINARY_VAL) { *peValType = FLM_BINARY_VAL; if (RC_BAD( rc = tokenGetBinary( pszToken, ppvVal, puiValLen, puiValBufSize))) { goto Exit; } } else { // Field type is unknown. Try converting to // a number. If that doesn't work, simply // use text. if (tokenIsNum( pszToken, &uiNum)) { ui32Num = (FLMUINT32)uiNum; *peValType = FLM_UINT32_VAL; *((FLMUINT32 *)(*ppvVal)) = ui32Num; *puiValLen = sizeof( FLMUINT32); } else { *peValType = FLM_UNICODE_VAL; if (RC_BAD( rc = tokenGetUnicode( pszToken, ppvVal, puiValLen, puiValBufSize))) { goto Exit; } } } Exit: return( rc); } /*API~*********************************************************************** Desc: Parse a query criteria string and populate an HFCURSOR from it. *END************************************************************************/ RCODE FlmParseQuery( HFCURSOR hCursor, F_NameTable * pNameTable, const char * pszQueryCriteria) { RCODE rc = FERR_OK; const char * pszTmp = pszQueryCriteria; QTYPES eTokenType; QTYPES ePriorTokenType; void * pvVal = NULL; FLMUINT uiValBufSize; FLMUINT uiValLen; QTYPES eLastFldType = NO_TYPE; FLMUINT uiFieldPath [20]; char * pszToken = NULL; FLMUINT uiTokenBufSize; FLMBOOL bQuoted; FLMUINT uiNum; // Allocate space for tokens and values. These will be // reallocated as needed. uiValBufSize = 512; if (RC_BAD( rc = f_alloc( uiValBufSize, &pvVal))) { goto Exit; } uiTokenBufSize = 512; if (RC_BAD( rc = f_alloc( uiTokenBufSize, &pszToken))) { goto Exit; } ePriorTokenType = NO_TYPE; for (;;) { // Get the next token. if( RC_BAD( rc = tokenGet( &pszTmp, &pszToken, &bQuoted, &eTokenType, &uiTokenBufSize))) { goto Exit; } // If the token is empty, we are done if (!(*pszToken)) { break; } // If eTokenType is not explicit, attempt to figure it out if (eTokenType == NO_TYPE) { if (bQuoted) { eLastFldType = FLM_UNICODE_VAL; Get_Value: if (RC_BAD( rc = tokenGetValue( eLastFldType, pszToken, bQuoted, &pvVal, &eTokenType, &uiValLen, &uiValBufSize))) { goto Exit; } if (RC_BAD( rc = FlmCursorAddValue( hCursor, eTokenType, pvVal, uiValLen))) { goto Exit; } eLastFldType = NO_TYPE; } else if (tokenIsOperator( pszToken, &eTokenType)) { // If this token is a minus operator, and the prior token was // a comparison operator, arithmetic operator, or // left paren, change this token to a negative sign. if (eTokenType == FLM_MINUS_OP && (ePriorTokenType == FLM_LPAREN_OP || IS_COMPARE_OP( ePriorTokenType) || IS_ARITH_OP( ePriorTokenType))) { eTokenType = FLM_NEG_OP; } if (RC_BAD( rc = FlmCursorAddOp( hCursor, eTokenType, TRUE))) { goto Exit; } // If the operator is not a comparison operator // and not an arithmetic operator and not a negative // operator, then the last field type is now unknown. if (!IS_COMPARE_OP( eTokenType) && !IS_ARITH_OP( eTokenType) && eTokenType != FLM_NEG_OP) { eLastFldType = NO_TYPE; } } else if (tokenIsField( pszToken, pNameTable, uiFieldPath, &eLastFldType, TRUE, FALSE)) { Add_Field: eTokenType = FLM_FLD_PATH; if (RC_BAD( rc = FlmCursorAddFieldPath( hCursor, uiFieldPath, 0))) { goto Exit; } } else if (IS_COMPARE_OP( ePriorTokenType) || IS_ARITH_OP( ePriorTokenType) || ePriorTokenType == FLM_NEG_OP) { if (ePriorTokenType == FLM_NEG_OP) { eLastFldType = FLM_INT32_VAL; } goto Get_Value; } else if (tokenIsField( pszToken, pNameTable, uiFieldPath, &eLastFldType, FALSE, FALSE)) { goto Add_Field; } else if (tokenIsNum( pszToken, &uiNum)) { goto Get_Value; } else { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } } else { switch (eTokenType) { case FLM_FLD_PATH: { if (!tokenIsField( pszToken, pNameTable, uiFieldPath, &eLastFldType, FALSE, TRUE)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } goto Add_Field; } case FLM_UNICODE_VAL: case FLM_INT32_VAL: case FLM_UINT32_VAL: case FLM_REC_PTR_VAL: case FLM_BINARY_VAL: case FLM_BOOL_VAL: { eLastFldType = eTokenType; goto Get_Value; } default: { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } } } ePriorTokenType = eTokenType; } Exit: if( pszToken) { f_free( &pszToken); } if( pvVal) { f_free( &pvVal); } return( rc); }