git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@7 0109f412-320b-0410-ab79-c3e0c5ffbbe6
1465 lines
30 KiB
C++
1465 lines
30 KiB
C++
//-------------------------------------------------------------------------
|
|
// 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);
|
|
}
|