/*++ Copyright (C) 1998-2001 Microsoft Corporation Module Name: CONVERTER.CPP Abstract: ::= | ::= [],[] | ::= [-]0x[] | [-][] ::= | ::= 0..9 ::= | ::= 1..9 | A | B | C | D | E | F ::= []()[] ::= SP | TAB | CR | LF History: --*/ #include "precomp.h" #include #include #include #include "wbemcli.h" #include "var.h" #include "WT_Converter.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CConverter::CConverter(const char* szString, CIMType ct) { int nSize = strlen(szString); m_szString = new char[nSize + 1]; // Made to fit strcpy(m_szString, szString); m_ct = ct; // CIM_TYPE } CConverter::~CConverter() { delete m_szString; } /****************************************************************** // // Helper Functions // *******************************************************************/ UINT CConverter::SetBoundary(BOOL bNeg, ULONG *uMaxSize) ////////////////////////////////////////////////////////////////////// // // Establishs the outer boundary of a give CIM type. Returns the // maximum absolute value, including an offset if the value is // negative. (Compensating for the additional size of 2's complement // negative values) // ////////////////////////////////////////////////////////////////////// { *uMaxSize = 0; switch (m_ct) { case CIM_UINT8: *uMaxSize = 0x000000FF; break; case CIM_SINT8: *uMaxSize = (bNeg ? 0x00000080 : 0x0000007F); break; case CIM_UINT16: *uMaxSize = 0x0000FFFF; break; case CIM_SINT16: *uMaxSize = (bNeg ? 0x00008000 : 0x00007FFF); break; case CIM_UINT32: *uMaxSize = 0xFFFFFFFF; break; case CIM_SINT32: *uMaxSize = (bNeg ? 0x80000000 : 0x7FFFFFFF); break; case NULL: return ERR_NULL_CIMTYPE; default: return ERR_UNKNOWN; } return ERR_NOERROR; } BOOL CConverter::IsValidDec(char ch) ////////////////////////////////////////////////////////////////////// // // PARAMETERS : a character to be validated as decimal // RETURNS: TRUE only if the character is a valid decimal character // ////////////////////////////////////////////////////////////////////// { return (('0' <= ch) && ('9' >= ch)); } BOOL CConverter::IsValidHex(char ch) ////////////////////////////////////////////////////////////////////// // // PARAMETERS : a character to be validated as hexadecimal // RETURNS: TRUE only if the character is a valid hexadecimal character // ////////////////////////////////////////////////////////////////////// { return ((('0' <= ch) && ('9' >= ch)) || (('a' <= ch) && ('f' <= ch))); } /****************************************************************** // // Parser Functions // *******************************************************************/ char CConverter::PeekToken(char *ch) ////////////////////////////////////////////////////////////////////// // // PARAMETERS: the token pointer (by val) // RETURNS: the character following the current token pointer. Does // not increment the token pointer. // ////////////////////////////////////////////////////////////////////// { // ch is passed by value; change is local to method ch++; // Ensure lower case if (('A' <= *ch) && ('Z' >= *ch)) *ch += ('a' - 'A'); return *ch; } BOOL CConverter::GetToken(char **ch) ////////////////////////////////////////////////////////////////////// // // If the token pointer is not at the end of the string, it will be // incremented and the current token will be converted into lower // case and passed back. If the pointer is at the end of the string, // NULL will be passed and the pointer unaffected. // // PARAMETERS: the token pointer (by ref) // // RETURNS: TRUE if the token pointer is mid-string, and FALSE if it // is at the end of the string. // ////////////////////////////////////////////////////////////////////// { // Increment pointer by 1 byte if ('\0' != **ch) *ch += 1; // Ensure lower case if (('A' <= **ch) && ('Z' >= **ch)) **ch += ('a' - 'A'); // End of the line? return ('\0' != **ch); } void CConverter::ReplaceToken(char **ch) ////////////////////////////////////////////////////////////////////// // // If not at the front of the string, the token pointer will be // decremented one place towards the head of the string. // // PARAMETERS: the token pointer (by ref) // // RETURNS: void // ////////////////////////////////////////////////////////////////////// { if (*ch != m_szString) *ch -= 1; return; } BOOL CConverter::Done(char *ch) ////////////////////////////////////////////////////////////////////// // // Checks for additional non-whitespace tokens following current // token. Does not validate token. // // PARAMETERS: the token pointer (by ref) // // RETURNS: TRUE if no further non-whitespace tokens follow the // current token pointer // ////////////////////////////////////////////////////////////////////// { if ('\0' == *ch) return TRUE; while (isspace(*ch)) ch++; return ('\0' == *ch); } /****************************************************************** // // Token Functions // *******************************************************************/ UINT CConverter::Token_Sequence(CVar *pVar) ////////////////////////////////////////////////////////////////////// // // Root of parsing for single (non-array) values. Sets up token // pointer (ch), and parses one number. If tokens remain in the // input string following the parsing of the first number, then // the input string is invalid. // // If the parsing fails, the value of pVar is not changed // // PARAMETERS: a variant for the result (by ref) // // RETURNS: the result state of the parsing // ////////////////////////////////////////////////////////////////////// { CVar aVar; // A temporary result variant char *ch = m_szString; // The token pointer UINT uRes; // A generic result sink // Parse out the number uRes = Token_Number(&ch, &aVar); if (ERR_NOERROR != uRes) return uRes; // Check for remaining tokens if (!Done(ch)) return ERR_INVALID_INPUT_STRING; // Parsing ok, copy temp variant into final destiation *pVar = aVar; return ERR_NOERROR; } UINT CConverter::Token_Sequence(CVarVector *pVarVec) ////////////////////////////////////////////////////////////////////// // // Root of parsing for multiple (array) values. Sets up token // pointer (ch), and parses the input string. It starts with a // single number, and then enters the loop, verifying a seperator // between each subsequent number. Each number is added to the // variant array as it is parsed. // // If the parsing fails, the value if pVarVec is not changed // // PARAMETERS: a variant vector for the result (by ref) // // RETURNS: the result state of the parsing // ////////////////////////////////////////////////////////////////////// { CVar aVar; // A temporary result variant char *ch = m_szString; // The token pointer UINT uRes; // A generic result sink UINT uVTType; switch (m_ct) { case CIM_UINT8: uVTType = VT_UI1;break; case CIM_SINT8: case CIM_SINT16: uVTType = VT_I2;break; case CIM_UINT16: case CIM_UINT32: case CIM_SINT32: uVTType = VT_I4;break; } CVarVector aVarVec(uVTType);// A temporary result variant vector // Parse out the first number uRes = Token_Number(&ch, &aVar); if (ERR_NOERROR != uRes) return uRes; // Add to array, and clear temporary variant aVarVec.Add(aVar); // If more tokens exist, continue while (!Done(ch)) { // Verify separator uRes = Token_Separator(&ch); if (ERR_NOERROR != uRes) return uRes; // Parse out next number uRes = Token_Number(&ch, &aVar); if (ERR_NOERROR != uRes) return uRes; // Add to array, and clear temporary variant aVarVec.Add(aVar); } // Parsing ok, copy temp variant vector into final destiation *pVarVec = aVarVec; return ERR_NOERROR; } UINT CConverter::Token_WhiteSpace(char **ch) ////////////////////////////////////////////////////////////////////// // // Move token pointer to the next non-white space token // // PARAMETERS: the token pointer (by ref) // // RETURNS: the result state of the parsing // ////////////////////////////////////////////////////////////////////// { while (isspace(**ch)) GetToken(ch); return ERR_NOERROR; } UINT CConverter::Token_Separator(char **ch) ////////////////////////////////////////////////////////////////////// // // A valid separator is either white space or a comma optionally // preceeded by white space. Parese out white space. Stop when // a non-whitespace character is encountered. If a comma, then // there must be a following non-whitespace token. // // PARAMETERS: the token pointer (by ref) // // RETURNS: the result state of the parsing // ////////////////////////////////////////////////////////////////////// { BOOL bComma = FALSE; while ((isspace(**ch) || (',' == **ch)) && !bComma) { if (',' == **ch) bComma = TRUE; // If a comma exists, the string must not be done if (!GetToken(ch) && bComma) return ERR_INVALID_TOKEN; } return ERR_NOERROR; } UINT CConverter::Token_Number(char **ch, CVar *pVar) ////////////////////////////////////////////////////////////////////// // // Determines the sign and base values of the number, and then // calls either Token_HexField or Token_DecField to continue // parsing the digit fields. The numerical value returned from // the parsing is unsigned. If the value is signed and negative // the value is negated. Comments are then parsed out. // // If the parsing fails, the value of pVar does not change // // PARAMETERS: the token pointer (by ref) and a Variant representing // the number (by ref) // // RETURNS: the result state of the parsing // ////////////////////////////////////////////////////////////////////// { ULONG aVal; // Temp value returned from Token_XXXField USHORT uBase = BASE_DEC; // Base of number BOOL bNegative = FALSE; // Sign of number UINT uRes; // Generic result sink // Parse out white space uRes = Token_WhiteSpace(ch); if (ERR_NOERROR != uRes) return uRes; // Determines the sign (assumed positive) and validates against type if (**ch == '-') { if ((CIM_UINT8 == m_ct) || (CIM_UINT16 == m_ct) || (CIM_UINT32 == m_ct)) return ERR_INVALID_SIGNED_VALUE; //else bNegative = TRUE; GetToken(ch); // Get the next token to handle } // Determine Base (we have initialized as decimal) if (**ch == '0') { if (PeekToken(*ch) == 'x') // Hexadecimal! { uBase = BASE_HEX; // Modify base GetToken(ch); // Get Rid of the 'x' token GetToken(ch); // Get the next token to handle } } // Parse digit field and put result in aVal if (BASE_HEX == uBase) uRes = Token_HexField(ch, bNegative, &aVal); else if (BASE_DEC == uBase) uRes = Token_DecField(ch, bNegative, &aVal); else return ERR_UNKNOWN_BASE; if (ERR_NOERROR != uRes) return uRes; // NOTE: signed operation on unsigned value // -this may cause a problem on some platforms if (bNegative) aVal *= (-1); // Set variant pVar->SetLong(aVal); // Parse out comments uRes = Token_Comment(ch); if (ERR_NOERROR != uRes) return uRes; return ERR_NOERROR; } UINT CConverter::Token_DecField(char **ch, BOOL bNeg, ULONG *pVal) ////////////////////////////////////////////////////////////////////// // // Token_DecField first determines the maximum possible value of // the number based on the CIM type for the purpose of bonuds checking. // The digit field is parsed, withe each token being added to the // previous tally value after it has increased in magnitude. // // Prior to the tally increasing, the proposed tally is validated // using an algorithm that assumes that the current value is in // range, and verifies that the proposed value will be in range // by working back from the maximum value. This algorithm works, // given that the initial value is 0, which is guarenteed to be // within range. The difference between the current tally, and the // proposed tally is subtracted from the maximum value, and if the // result is larger than the current tally, then the propsed value // is valid. // // This algorithm assumes that we are using unsigend values. Signed // negative values are constructed as positive values, and then // converted. In this case, the maximum value is one larger than the // positive maximum value, according to the rules of 2's complement. // // If the parsing fails, the value of pVal does not change // // PARAMETERS: the token pointer (by ref), the sign flag (by val) // and a the unsigned value of the number (by ref) // // RETURNS: the result state of the parsing // ////////////////////////////////////////////////////////////////////// { ULONG uMaxSize; // Boundary value ULONG aVal = 0; // Tally value ULONG uDigitVal; // Return digit from Token_DecDigit UINT uRes; // Generic result sink // Sets the maximum value of the tally uRes = SetBoundary(bNeg, &uMaxSize); if (ERR_NOERROR != uRes) return uRes; // Pareses the first digit uRes = Token_DecDigit(ch, &uDigitVal); if (ERR_NOERROR != uRes) return uRes; // Adds to tally aVal = uDigitVal; // If more decimal tokens... while (IsValidDec(**ch)) { // Parse the token uRes = Token_DecDigit(ch, &uDigitVal); if (ERR_NOERROR != uRes) return uRes; // Test the bounds of the proposed tally if (((uMaxSize - uDigitVal) / BASE_DEC ) < aVal) return ERR_OUT_OF_RANGE; // Increase the magnitude and add the token digit value aVal = (aVal * BASE_DEC) + uDigitVal; } // Parsing ok, copy the temp to the destination *pVal = aVal; return ERR_NOERROR; } UINT CConverter::Token_HexField(char **ch, BOOL bNeg, ULONG *pVal) ////////////////////////////////////////////////////////////////////// // // See Token_DecField // ////////////////////////////////////////////////////////////////////// { ULONG uMaxSize; // Boundary value ULONG aVal = 0; // Tally value ULONG uDigitVal; // Return digit from Token_DecDigit UINT uRes; // Generic result // Sets the maximum value of the tally uRes = SetBoundary(bNeg, &uMaxSize); if (ERR_NOERROR != uRes) return uRes; // Pareses the first digit uRes = Token_HexDigit(ch, &uDigitVal); if (ERR_NOERROR != uRes) return uRes; // Adds to tally aVal = uDigitVal; // If more decimal tokens... while (IsValidHex(**ch)) { // Parse the token uRes = Token_HexDigit(ch, &uDigitVal); if (ERR_NOERROR != uRes) return uRes; // Test the bounds of next tally if (((uMaxSize - uDigitVal) / BASE_HEX ) < aVal) return ERR_OUT_OF_RANGE; // Increase the magnitude and add the token digit value aVal = (aVal * BASE_HEX) + uDigitVal; } // Parsing ok, copy the temp to the destination *pVal = aVal; return ERR_NOERROR; } UINT CConverter::Token_DecDigit(char **ch, ULONG *pVal) ////////////////////////////////////////////////////////////////////// // // Validates the token, and converts to numerical equivalent // // If the parsing fails, the value of pVal does not change // // PARAMETERS: the token pointer (by ref) and a the value // of the digit (by ref) // // RETURNS: the result state of the parsing // ////////////////////////////////////////////////////////////////////// { ULONG uVal = 0; // Temp value // Validate digit & convert if (('0' <= **ch) && ('9' >= **ch)) uVal = **ch - '0'; else if ('\0' == **ch) return ERR_NULL_TOKEN; else return ERR_INVALID_TOKEN; // Parsing ok, copy value to destination *pVal = uVal; // Move token pointer GetToken(ch); return ERR_NOERROR; } UINT CConverter::Token_HexDigit(char **ch, ULONG *pVal) ////////////////////////////////////////////////////////////////////// // // Validates the token, and converts to numerical equivalent // // If the parsing fails, the value of pVal does not change // // PARAMETERS: the token pointer (by ref) and a the value // of the digit (by ref) // // RETURNS: the result state of the parsing // ////////////////////////////////////////////////////////////////////// { ULONG uVal = 0; // Temp value // Validate digit & convert if (('a' <= **ch) && ('f' >= **ch)) uVal = 0xA + (**ch - 'a'); else if (('0' <= **ch) && ('9' >= **ch)) uVal = **ch - '0'; else if ('\0' == **ch) return ERR_NULL_TOKEN; else return ERR_INVALID_TOKEN; // Parsing ok, copy value to destination *pVal = uVal; // Move token pointer GetToken(ch); return ERR_NOERROR; } UINT CConverter::Token_Comment(char** ch) ////////////////////////////////////////////////////////////////////// // // Parses out white space and contents between braces, if they exist. // If an opening brace is encountered, all of the contents, including // the braces, are ignored. If an opening brace is encountered, a // closing brace must follow. // // NOTE: Nested comments are not allowed // // PARAMETERS: the token pointer (by ref) // // RETURNS: the result state of the parsing // ////////////////////////////////////////////////////////////////////// { // Parse out white space UINT uRes = Token_WhiteSpace(ch); if (ERR_NOERROR != uRes) return uRes; // If the token following the white space is an opening brace, // parse out the contents, and verify the existance of the // closing brace if ('(' == **ch) { while ((')' != **ch)) { if (!GetToken(ch)) return ERR_UNMATCHED_BRACE; } GetToken(ch); // Purge closing brace } return ERR_NOERROR; } /****************************************************************** // // Static Functions // *******************************************************************/ UINT CConverter::Convert(const char* szString, CIMType ct, CVar *pVar) ///////////////////////////////////////////////////////////////////// // // Convert is a static method that creates an instance of the // Converter object and converts the string to a variant. If an // error occurs, the value of pVar is not affected. // // PARAMETERS: the input string (by val), the CIM type (by val) and // the outout variant (by ref) // // RETURNS: the result state of the parsing // ///////////////////////////////////////////////////////////////////// { // Checks the CIM type is initialized if (NULL == ct) return ERR_NULL_CIMTYPE; CConverter converter(szString, ct); // The converter object CVar aVar; // Temp variant // Parse out the first number UINT uRes = converter.Token_Sequence(&aVar); // Check return code if (ERR_NOERROR != uRes) return uRes; // Parsing ok, copy temp to destination *pVar = aVar; return ERR_NOERROR; } UINT CConverter::Convert(const char* szString, CIMType ct, CVarVector *pVarVec) ///////////////////////////////////////////////////////////////////// // // Convert is a static method that creates an instance of the // Converter object and converts the string to an array of values. // If an error occurs, the value of pVarVec is not affected. // // PARAMETERS: the input string (by val), the CIM type (by val) and // the outout variant vecter (by ref) // // Returns the result state of the parsing // ///////////////////////////////////////////////////////////////////// { // Checks the CIM type is initialized if (NULL == ct) return ERR_NULL_CIMTYPE; CConverter converter(szString, ct); // The converter object CVarVector aVarVec; // Temp variant vector // Parse out the first number UINT uRes = converter.Token_Sequence(&aVarVec); // Check return code if (ERR_NOERROR != uRes) return uRes; // Parsing ok, copy temp to destination *pVarVec = aVarVec; return ERR_NOERROR; }