// -------------------------------------------------------------------------------- // Strparse.cpp // Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved // -------------------------------------------------------------------------------- #include "pch.hxx" #include "strparse.h" // -------------------------------------------------------------------------------- // FGROWNEEDED - Determines if we need to call _HrGrowDestination // -------------------------------------------------------------------------------- #define FGROWNEEDED(_cbWrite) (m_cchDest + _cbWrite + 1 > m_cbDestMax) // -------------------------------------------------------------------------------- // CStringParser::CStringParser // -------------------------------------------------------------------------------- CStringParser::CStringParser(void) { m_cRef = 1; m_codepage = CP_ACP; m_pszSource = NULL; m_cchSource = 0; m_iSource = 0; m_pszDest = NULL; m_cchDest = 0; m_cbDestMax = 0; m_dwFlags = 0; m_pszTokens = NULL; m_cCommentNest = 0; ZeroMemory(m_rgbTokTable, sizeof(m_rgbTokTable)); ZeroMemory(&m_rLiteral, sizeof(m_rLiteral)); } // -------------------------------------------------------------------------------- // CStringParser::~CStringParser // -------------------------------------------------------------------------------- CStringParser::~CStringParser(void) { if (m_pszDest && m_pszDest != m_szScratch) g_pMalloc->Free(m_pszDest); } // -------------------------------------------------------------------------------- // CStringParser::AddRef // -------------------------------------------------------------------------------- ULONG CStringParser::AddRef(void) { return ++m_cRef; } // -------------------------------------------------------------------------------- // CStringParser::AddRef // -------------------------------------------------------------------------------- ULONG CStringParser::Release(void) { if (0 != --m_cRef) return m_cRef; delete this; return 0; } // -------------------------------------------------------------------------------- // CStringParser::Init // -------------------------------------------------------------------------------- void CStringParser::Init(LPCSTR pszParseMe, ULONG cchParseMe, DWORD dwFlags) { // Invalid Args Assert(NULL == m_pszSource && NULL == m_pszDest && pszParseMe && '\0' == pszParseMe[cchParseMe]); // Save Parse Flags m_dwFlags = dwFlags; // Safe the String m_pszSource = pszParseMe; m_cchSource = cchParseMe; // Setup Dest m_pszDest = m_szScratch; m_cbDestMax = sizeof(m_szScratch); } // -------------------------------------------------------------------------------- // CStringParser::SetTokens // -------------------------------------------------------------------------------- void CStringParser::SetTokens(LPCSTR pszTokens) { // Locals LPSTR psz; // Enable the tokens in the table if (m_pszTokens) for (psz=(LPSTR)m_pszTokens; *psz != '\0'; psz++) m_rgbTokTable[(UCHAR)(*psz)] = 0x00; // New Tokens if (pszTokens) for (psz=(LPSTR)pszTokens; *psz != '\0'; psz++) m_rgbTokTable[(UCHAR)(*psz)] = 0xff; // Save new tokens m_pszTokens = pszTokens; } // -------------------------------------------------------------------------------- // CStringParser::_HrGrowDestination // -------------------------------------------------------------------------------- HRESULT CStringParser::_HrGrowDestination(ULONG cbWrite) { // Locals HRESULT hr=S_OK; ULONG cbAlloc; // We should need to grow, should have called FGROWNEEDED Assert(FGROWNEEDED(cbWrite)); // Is this the first realloc if (m_pszDest == m_szScratch) { // Validate Current Size Assert(m_cbDestMax == sizeof(m_szScratch)); // Compute New Size cbAlloc = max(m_cchSource + 1, m_cchDest + 256 + cbWrite); // Init pszValue CHECKALLOC(m_pszDest = (LPSTR)g_pMalloc->Alloc(cbAlloc)); // Copy Current Value CopyMemory(m_pszDest, m_szScratch, m_cchDest); // Set Max Val m_cbDestMax = cbAlloc; } // Otherwise, need to realloc else { // Locals LPBYTE pbTemp; // Should already be bigger than m_cchSource + 1 Assert(m_cbDestMax >= m_cchSource + 1); // Compute New Size cbAlloc = m_cbDestMax + 256 + cbWrite; // Realloc CHECKALLOC(pbTemp = (LPBYTE)g_pMalloc->Realloc((LPBYTE)m_pszDest, cbAlloc)); // Save new pointer m_pszDest = (LPSTR)pbTemp; // Save new Size m_cbDestMax = cbAlloc; } exit: // Done return hr; } // -------------------------------------------------------------------------------- // CStringParser::FIsParseSpace // -------------------------------------------------------------------------------- BOOL CStringParser::FIsParseSpace(CHAR ch, BOOL *pfCommentChar) { // Locals WORD wType; // Should not be DBCS Assert(ISFLAGSET(m_dwFlags, PSF_DBCS) ? !IsDBCSLeadByteEx(m_codepage, ch) : TRUE); // Comment Char *pfCommentChar = FALSE; // NoComments if (ISFLAGSET(m_dwFlags, PSF_NOCOMMENTS)) { // Comment Start ? if ('(' == ch) { // Increment Nested Count m_cCommentNest++; // Comment Char *pfCommentChar = TRUE; // Treat it as a space return TRUE; } // Comment End ? else if (')' == ch && m_cCommentNest) { // Decrement Nested Count m_cCommentNest--; // Comment Char *pfCommentChar = TRUE; // Treat it as a space return TRUE; } // Inside a Comment ? else if (m_cCommentNest) { // Comment Char *pfCommentChar = TRUE; // Treat it as a space return TRUE; } } // Get StringType if (' ' == ch || '\t' == ch || '\r' == ch || '\n' == ch) return(TRUE); // Not a space return(FALSE); #if 0 if (0 == GetStringTypeExA(LOCALE_USER_DEFAULT, CT_CTYPE1, &ch, 1, &wType)) wType = 0; // Return IsSpace return(ISFLAGSET(wType, C1_SPACE)); #endif } // -------------------------------------------------------------------------------- // CStringParser::ChSkip - Returns TRUE if done parsing // -------------------------------------------------------------------------------- CHAR CStringParser::ChSkipWhite(void) { // Locals CHAR ch=0; BOOL fCommentChar; // Loop while (1) { // Get Current Character ch = *(m_pszSource + m_iSource); // Are we done if ('\0' == ch) break; // Better not be done Assert(m_iSource < m_cchSource); // Look for DBCS characters if (ISFLAGSET(m_dwFlags, PSF_DBCS) && IsDBCSLeadByteEx(m_codepage, ch)) break; // Not a space if (!FIsParseSpace(ch, &fCommentChar)) break; // Goto Next Char m_iSource++; } // Done return ch; } // -------------------------------------------------------------------------------- // CStringParser::ChSkip - Returns TRUE if done parsing // -------------------------------------------------------------------------------- CHAR CStringParser::ChSkip(void) { // Locals CHAR ch = 0; // Loop while (1) { // Get Current Character ch = *(m_pszSource + m_iSource); // Are we done if ('\0' == ch) break; // Better not be done Assert(m_iSource < m_cchSource); // Look for DBCS characters if (ISFLAGSET(m_dwFlags, PSF_DBCS) && IsDBCSLeadByteEx(m_codepage, ch)) break; // Compare for a token if (0x00 == m_rgbTokTable[(UCHAR)ch]) break; // Goto Next Char m_iSource++; } // Done return ch; } // -------------------------------------------------------------------------------- // CStringParser::ChPeekNext // -------------------------------------------------------------------------------- CHAR CStringParser::ChPeekNext(ULONG cchFromCurrent) { // Locals CHAR ch=0; BOOL fCommentChar; // Past the end of the source if (m_iSource + cchFromCurrent >= m_cchSource) return '\0'; // Return the character return *(m_pszSource + m_iSource + cchFromCurrent); } // -------------------------------------------------------------------------------- // CStringParser::_HrDoubleByteIncrement // -------------------------------------------------------------------------------- HRESULT CStringParser::_HrDoubleByteIncrement(BOOL fEscape) { // Locals HRESULT hr=S_OK; // Can I copy two more bytes to pszValue if (FGROWNEEDED(2)) { // Otherwise, grow the buffer CHECKHR(hr = _HrGrowDestination(2)); } // If Not an Escape Character or the last character is an escape character, then step over it if (FALSE == fEscape || m_iSource + 1 > m_cchSource) m_pszDest[m_cchDest++] = m_pszSource[m_iSource]; // Next Character m_iSource++; // Copy Next Character if (m_iSource < m_cchSource) m_pszDest[m_cchDest++] = m_pszSource[m_iSource++]; exit: // Done return hr; } // -------------------------------------------------------------------------------- // CStringParser::ChParse // -------------------------------------------------------------------------------- CHAR CStringParser::ChParse(LPCSTR pszTokens, DWORD dwFlags) { // Save Flags DWORD dwCurrFlags=m_dwFlags; // Reset Flags m_dwFlags = dwFlags; // Set Parsing Tokens SetTokens(pszTokens); // Parse CHAR chToken = ChParse(); // Set Flags m_dwFlags = dwCurrFlags; // Return the Token return chToken; } // -------------------------------------------------------------------------------- // CStringParser::ChParse // -------------------------------------------------------------------------------- CHAR CStringParser::ChParse(CHAR chStart, CHAR chEnd, DWORD dwFlags) { // We really should have finished the last literal Assert(FALSE == m_rLiteral.fInside); // Save Flags DWORD dwCurrFlags = m_dwFlags; // Reset Flags m_dwFlags = dwFlags; // Set Parsing Tokens SetTokens(NULL); // Save Literal Info m_rLiteral.fInside = TRUE; m_rLiteral.chStart = chStart; m_rLiteral.chEnd = chEnd; m_rLiteral.cNested = 0; // Quoted String Assert('\"' == chStart ? '\"' == chEnd : TRUE); // Parse CHAR chToken = ChParse(); // Not in a literal m_rLiteral.fInside = FALSE; // Reset Flags m_dwFlags = dwCurrFlags; // Return the Token return chToken; } // -------------------------------------------------------------------------------- // CStringParser::HrAppendValue // -------------------------------------------------------------------------------- HRESULT CStringParser::HrAppendValue(CHAR ch) { // Locals HRESULT hr=S_OK; // Just copy this character if (FGROWNEEDED(1)) { // Otherwise, grow the buffer CHECKHR(hr = _HrGrowDestination(1)); } // Insert the Character m_pszDest[m_cchDest++] = ch; // There is always room for a Null, look at FGROWNEEDED and _HrGrowDestination m_pszDest[m_cchDest] = '\0'; exit: // Done return hr; } // -------------------------------------------------------------------------------- // CStringParser::ChParse // -------------------------------------------------------------------------------- CHAR CStringParser::ChParse(void) { // Locals HRESULT hr=S_OK; CHAR ch; ULONG iStart=m_iSource; LONG iLastSpace=-1; CHAR chToken; BOOL fCommentChar; BOOL fIsSpace; // Invalid Arg Assert(m_iSource <= m_cchSource && m_pszDest); // Init chToken chToken = '\0'; // No Reset if (!ISFLAGSET(m_dwFlags, PSF_NORESET)) { m_pszDest[0] = '\0'; m_cchDest = 0; } // Skip Forward Whitespace if (ISFLAGSET(m_dwFlags, PSF_NOFRONTWS) && FALSE == m_rLiteral.fInside && '\0' == ChSkipWhite()) goto TokenFound; // Save Starting Position while(1) { // Get the Next Character ch = *(m_pszSource + m_iSource); // Done if ('\0' == ch) { chToken = '\0'; goto TokenFound; } // Better not be done Assert(m_iSource < m_cchSource); // If this is a DBCS lead byte if ((ISFLAGSET(m_dwFlags, PSF_DBCS) && IsDBCSLeadByteEx(m_codepage, ch))) { // _HrDoubleByteIncrement CHECKHR(hr = _HrDoubleByteIncrement(FALSE)); iLastSpace = -1; // reset space counter // Goto Next Character continue; } // Check for escaped characters if (ISFLAGSET(m_dwFlags, PSF_ESCAPED) && '\\' == ch) { // _HrDoubleByteIncrement CHECKHR(hr = _HrDoubleByteIncrement(TRUE)); iLastSpace = -1; // reset space counter // Goto Next Character continue; } // If not inside of a comment if (0 == m_cCommentNest) { if (m_rLiteral.fInside) { // End of quoted string if (ch == m_rLiteral.chEnd) { // No nested ? if (0 == m_rLiteral.cNested) { // We found a token chToken = ch; // Walk over this item in the string m_iSource++; // Ya-hoo, we found a token hr = S_OK; // Done goto TokenFound; } // Otherwise, decrement nest else m_rLiteral.cNested--; } // Otherwise, check for nesting else if (m_rLiteral.chStart != m_rLiteral.chEnd && ch == m_rLiteral.chStart) m_rLiteral.cNested++; } // Compare for a token - m_cCommentNest is only set if PSF_NOCOMMENTS is set else if (0xff == m_rgbTokTable[(UCHAR)ch]) { // We found a token chToken = ch; // Walk over this item in the string m_iSource++; // Ya-hoo, we found a token hr = S_OK; // Done goto TokenFound; } } // Always Call fIsSpace = FIsParseSpace(ch, &fCommentChar); // Detect Spaces... if (ISFLAGSET(m_dwFlags, PSF_NOTRAILWS)) { // If not a space, then kill iLastSpace if (!fIsSpace) iLastSpace = -1; // Otherwise, if not a consecutive space else if (-1 == iLastSpace) iLastSpace = m_cchDest; } // Copy the next character if (!fCommentChar) { // Make sure we have space if (FGROWNEEDED(1)) { // Otherwise, grow the buffer CHECKHR(hr = _HrGrowDestination(1)); } // Copy the character m_pszDest[m_cchDest++] = ch; } // Goto next char m_iSource++; } TokenFound: // Determine correct end of string if (S_OK == hr && ISFLAGSET(m_dwFlags, PSF_NOTRAILWS) && FALSE == m_rLiteral.fInside) m_cchDest = (-1 == iLastSpace) ? m_cchDest : iLastSpace; // Otherwise, just insert a null Assert(m_cchDest < m_cbDestMax); // There is always room for a Null, look at FGROWNEEDED and _HrGrowDestination m_pszDest[m_cchDest] = '\0'; exit: // Failure Resets the parse to initial state if (FAILED(hr)) { m_iSource = iStart; chToken = '\0'; } // Validate Paren Nesting // AssertSz(m_cCommentNest == 0, "A string was parsed that has an un-balanced paren nesting."); // Done return chToken; }