home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 217 / DPCS0306DVD.ISO / Toolkit / Internet / FileZilla / Server / FileZilla_Server-0.9.11.exe / source / misc / MarkupSTL.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-12-03  |  38.5 KB  |  1,455 lines

  1. // Markup.cpp: implementation of the CMarkupSTL class.
  2. //
  3. // Markup Release 6.3
  4. // Copyright (C) 1999-2002 First Objective Software, Inc. All rights reserved
  5. // Go to www.firstobject.com for the latest CMarkupSTL and EDOM documentation
  6. // Use in commercial applications requires written permission
  7. // This software is provided "as is", with no warranty.
  8.  
  9. #include "stdafx.h"
  10. #include "atlconv.h"
  11. #include "MarkupSTL.h"
  12.  
  13. #if defined(_DEBUG) && !defined(MMGR)
  14. #undef THIS_FILE
  15. static char THIS_FILE[]=__FILE__;
  16. #endif
  17.  
  18. #ifdef _MBCS
  19. #pragma message( "Note: MBCS build (not UTF-8)" )
  20. // For UTF-8, remove _MBCS from project settings C/C++ preprocessor definitions
  21. #endif
  22.  
  23. // Defines for Windows CE
  24. #ifndef _tclen
  25. #define _tclen(p) 1
  26. #define _tccpy(p1,p2) *(p1)=*(p2)
  27. #endif
  28.  
  29.  
  30. void CMarkupSTL::operator=( const CMarkupSTL& markup )
  31. {
  32.     m_iPosParent = markup.m_iPosParent;
  33.     m_iPos = markup.m_iPos;
  34.     m_iPosChild = markup.m_iPosChild;
  35.     m_iPosFree = markup.m_iPosFree;
  36.     m_nNodeType = markup.m_nNodeType;
  37.     m_aPos.clear();
  38.     m_aPos= markup.m_aPos;
  39.     m_csDoc = markup.m_csDoc;
  40.     MARKUP_SETDEBUGSTATE;
  41. }
  42.  
  43. bool CMarkupSTL::SetDoc( LPCTSTR szDoc )
  44. {
  45.     // Reset indexes
  46.     m_iPosFree = 1;
  47.     ResetPos();
  48.     m_mapSavedPos.clear();
  49.  
  50.     // Set document text
  51.     if ( szDoc )
  52.         m_csDoc = szDoc;
  53.     else
  54.         m_csDoc.Empty();
  55.  
  56.     // Starting size of position array: 1 element per 64 bytes of document
  57.     // Tight fit when parsing small doc, only 0 to 2 reallocs when parsing large doc
  58.     // Start at 8 when creating new document
  59.     UINT nStartSize = m_csDoc.GetLength() / 64 + 8;
  60.     if ( m_aPos.size() < nStartSize )
  61.         m_aPos.resize( nStartSize );
  62.  
  63.     // Parse document
  64.     bool bWellFormed = false;
  65.     if ( m_csDoc.GetLength() )
  66.     {
  67.         m_aPos[0].Clear();
  68.         int iPos = x_ParseElem( 0 );
  69.         if ( iPos > 0 )
  70.         {
  71.             m_aPos[0].iElemChild = iPos;
  72.             bWellFormed = true;
  73.         }
  74.     }
  75.  
  76.     // Clear indexes if parse failed or empty document
  77.     if ( ! bWellFormed )
  78.     {
  79.         m_aPos[0].Clear();
  80.         m_iPosFree = 1;
  81.     }
  82.  
  83.     ResetPos();
  84.     return bWellFormed;
  85. };
  86.  
  87. bool CMarkupSTL::IsWellFormed()
  88. {
  89.     if ( m_aPos.size() && m_aPos[0].iElemChild )
  90.         return true;
  91.     return false;
  92. }
  93.  
  94. bool CMarkupSTL::Load( LPCTSTR szFileName )
  95. {
  96.     CStdString csDoc;
  97.     HANDLE hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
  98.     if (hFile == INVALID_HANDLE_VALUE)
  99.         return false;
  100.     //The following will not work for files larger than 2GB
  101.     int nLength = GetFileSize(hFile, NULL);
  102.  
  103. #if defined(_UNICODE)
  104.     // Allocate Buffer for UTF-8 file data
  105.     unsigned char* pBuffer = new unsigned char[nLength + 1];
  106.     DWORD numread;
  107.     if (ReadFile(hFile, pBuffer, nLength, &numread, 0))
  108.         nLength = numread;
  109.     else
  110.         nLength = 0;
  111.     pBuffer[nLength] = '\0';
  112.  
  113.     // Convert file from UTF-8 to Windows UNICODE (AKA UCS-2)
  114.     int nWideLength = MultiByteToWideChar(CP_UTF8,0,(const char*)pBuffer,nLength,NULL,0);
  115.     nLength = MultiByteToWideChar(CP_UTF8,0,(const char*)pBuffer,nLength,
  116.         csDoc.GetBuffer(nWideLength),nWideLength);
  117.     ASSERT( nLength == nWideLength );
  118.     delete [] pBuffer;
  119. #else
  120.     DWORD numread;
  121.     if (ReadFile(hFile, csDoc.GetBuffer(nLength), nLength, &numread, 0))
  122.         nLength = numread;
  123.     else
  124.         nLength = 0;
  125. #endif
  126.     csDoc.ReleaseBuffer(nLength);
  127.     CloseHandle(hFile);
  128.  
  129.     return SetDoc( csDoc );
  130. }
  131.  
  132. bool CMarkupSTL::Save( LPCTSTR szFileName )
  133. {
  134.     int nLength = m_csDoc.GetLength();
  135.     HANDLE hFile = CreateFile(szFileName, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
  136.     if (hFile == INVALID_HANDLE_VALUE)
  137.         return false;
  138.     
  139. #if defined( _UNICODE )
  140.     int nUTF8Len = WideCharToMultiByte(CP_UTF8,0,m_csDoc,nLength,NULL,0,NULL,NULL);
  141.     char* pBuffer = new char[nUTF8Len+1];
  142.     nLength = WideCharToMultiByte(CP_UTF8,0,m_csDoc,nLength,pBuffer,nUTF8Len+1,NULL,NULL);
  143.     DWORD numwritten;
  144.     WriteFile(hFile, pBuffer, nLength, &numwritten, 0);
  145.     delete pBuffer;
  146. #else
  147.     DWORD numwritten;
  148.     WriteFile(hFile, (LPCTSTR)m_csDoc, nLength, &numwritten, 0);
  149. #endif
  150.     CloseHandle(hFile);
  151.     return true;
  152. }
  153.  
  154. bool CMarkupSTL::FindElem( LPCTSTR szName )
  155. {
  156.     // Change current position only if found
  157.     //
  158.     if ( m_aPos.size() )
  159.     {
  160.         int iPos = x_FindElem( m_iPosParent, m_iPos, szName );
  161.         if ( iPos )
  162.         {
  163.             // Assign new position
  164.             x_SetPos( m_aPos[iPos].iElemParent, iPos, 0 );
  165.             return true;
  166.         }
  167.     }
  168.     return false;
  169. }
  170.  
  171. bool CMarkupSTL::FindChildElem( LPCTSTR szName )
  172. {
  173.     // Change current child position only if found
  174.     //
  175.     // Shorthand: call this with no current main position
  176.     // means find child under root element
  177.     if ( ! m_iPos )
  178.         FindElem();
  179.  
  180.     int iPosChild = x_FindElem( m_iPos, m_iPosChild, szName );
  181.     if ( iPosChild )
  182.     {
  183.         // Assign new position
  184.         int iPos = m_aPos[iPosChild].iElemParent;
  185.         x_SetPos( m_aPos[iPos].iElemParent, iPos, iPosChild );
  186.         return true;
  187.     }
  188.  
  189.     return false;
  190. }
  191.  
  192. CStdString CMarkupSTL::GetTagName() const
  193. {
  194.     // Return the tag name at the current main position
  195.     CStdString csTagName;
  196.  
  197.     if ( m_iPos )
  198.         csTagName = x_GetTagName( m_iPos );
  199.     return csTagName;
  200. }
  201.  
  202. bool CMarkupSTL::IntoElem()
  203. {
  204.     // If there is no child position and IntoElem is called it will succeed in release 6.3
  205.     // (A subsequent call to FindElem will find the first element)
  206.     // The following short-hand behavior was never part of EDOM and was misleading
  207.     // It would find a child element if there was no current child element position and go into it
  208.     // It is removed in release 6.3, this change is NOT backwards compatible!
  209.     // if ( ! m_iPosChild )
  210.     //    FindChildElem();
  211.  
  212.     if ( m_iPos && m_nNodeType == MNT_ELEMENT )
  213.     {
  214.         x_SetPos( m_iPos, m_iPosChild, 0 );
  215.         return true;
  216.     }
  217.     return false;
  218. }
  219.  
  220. bool CMarkupSTL::OutOfElem()
  221. {
  222.     // Go to parent element
  223.     if ( m_iPosParent )
  224.     {
  225.         x_SetPos( m_aPos[m_iPosParent].iElemParent, m_iPosParent, m_iPos );
  226.         return true;
  227.     }
  228.     return false;
  229. }
  230.  
  231. CStdString CMarkupSTL::GetAttribName( int n ) const
  232. {
  233.     // Return nth attribute name of main position
  234.     if ( ! m_iPos || m_nNodeType != MNT_ELEMENT )
  235.         return _T("");
  236.  
  237.     TokenPos token( m_csDoc );
  238.     token.nNext = m_aPos[m_iPos].nStartL + 1;
  239.     for ( int nAttrib=0; nAttrib<=n; ++nAttrib )
  240.         if ( ! x_FindAttrib(token) )
  241.             return _T("");
  242.  
  243.     // Return substring of document
  244.     return x_GetToken( token );
  245. }
  246.  
  247. bool CMarkupSTL::SavePos( LPCTSTR szPosName )
  248. {
  249.     // Save current element position in saved position map
  250.     if ( szPosName )
  251.     {
  252.         SavedPos savedpos;
  253.         savedpos.iPosParent = m_iPosParent;
  254.         savedpos.iPos = m_iPos;
  255.         savedpos.iPosChild = m_iPosChild;
  256.         m_mapSavedPos[szPosName]=savedpos;
  257.         return true;
  258.     }
  259.     return false;
  260. }
  261.  
  262. bool CMarkupSTL::RestorePos( LPCTSTR szPosName )
  263. {
  264.     // Restore element position if found in saved position map
  265.     if (!szPosName)
  266.         return false;
  267.  
  268.     std::map<CStdString, SavedPos>::iterator iter=m_mapSavedPos.find( szPosName );
  269.  
  270.     if ( iter!=m_mapSavedPos.end() )
  271.     {
  272.         x_SetPos( iter->second.iPosParent, iter->second.iPos, iter->second.iPosChild );
  273.         return true;
  274.     }
  275.     return false;
  276. }
  277.  
  278. bool CMarkupSTL::GetOffsets( int& nStart, int& nEnd ) const
  279. {
  280.     // Return document offsets of current main position element
  281.     // This is not part of EDOM but is used by the Markup project
  282.     if ( m_iPos )
  283.     {
  284.         nStart = m_aPos[m_iPos].nStartL;
  285.         nEnd = m_aPos[m_iPos].nEndR;
  286.         return true;
  287.     }
  288.     return false;
  289. }
  290.  
  291. CStdString CMarkupSTL::GetChildSubDoc() const
  292. {
  293.     if ( m_iPosChild )
  294.     {
  295.         int nL = m_aPos[m_iPosChild].nStartL;
  296.         int nR = m_aPos[m_iPosChild].nEndR + 1;
  297.         TokenPos token( m_csDoc );
  298.         token.nNext = nR;
  299.         if ( ! x_FindToken(token) || m_csDoc[token.nL] == _T('<') )
  300.             nR = token.nL;
  301.         return m_csDoc.Mid( nL, nR - nL );
  302.     }
  303.     return _T("");
  304. }
  305.  
  306. bool CMarkupSTL::RemoveElem()
  307. {
  308.     // Remove current main position element
  309.     if ( m_iPos && m_nNodeType == MNT_ELEMENT )
  310.     {
  311.         int iPos = x_RemoveElem( m_iPos );
  312.         x_SetPos( m_iPosParent, iPos, 0 );
  313.         return true;
  314.     }
  315.     return false;
  316. }
  317.  
  318. bool CMarkupSTL::RemoveChildElem()
  319. {
  320.     // Remove current child position element
  321.     if ( m_iPosChild )
  322.     {
  323.         int iPosChild = x_RemoveElem( m_iPosChild );
  324.         x_SetPos( m_iPosParent, m_iPos, iPosChild );
  325.         return true;
  326.     }
  327.     return false;
  328. }
  329.  
  330. //////////////////////////////////////////////////////////////////////
  331. // Private Methods
  332. //////////////////////////////////////////////////////////////////////
  333.  
  334. int CMarkupSTL::x_GetFreePos()
  335. {
  336.     //
  337.     // This returns the index of the next unused ElemPos in the array
  338.     //
  339.     if ( (unsigned int)m_iPosFree == m_aPos.size() )
  340.         m_aPos.resize( m_iPosFree + m_iPosFree / 2 );
  341.     ++m_iPosFree;
  342.     return m_iPosFree - 1;
  343. }
  344.  
  345. int CMarkupSTL::x_ReleasePos()
  346. {
  347.     //
  348.     // This decrements the index of the next unused ElemPos in the array
  349.     // allowing the element index returned by GetFreePos() to be reused
  350.     //
  351.     --m_iPosFree;
  352.     return 0;
  353. }
  354.  
  355. int CMarkupSTL::x_ParseError( LPCTSTR szError, LPCTSTR szName )
  356. {
  357.     if ( szName )
  358.         m_csError.Format( szError, szName );
  359.     else
  360.         m_csError = szError;
  361.     x_ReleasePos();
  362.     return -1;
  363. }
  364.  
  365. int CMarkupSTL::x_ParseElem( int iPosParent )
  366. {
  367.     // This is either called by SetDoc, x_AddSubDoc, or itself recursively
  368.     // m_aPos[iPosParent].nEndL is where to start parsing for the child element
  369.     // This returns the new position if a tag is found, otherwise zero
  370.     // In all cases we need to get a new ElemPos, but release it if unused
  371.     //
  372.     int iPos = x_GetFreePos();
  373.     m_aPos[iPos].nStartL = m_aPos[iPosParent].nEndL;
  374.     m_aPos[iPos].iElemParent = iPosParent;
  375.     m_aPos[iPos].iElemChild = 0;
  376.     m_aPos[iPos].iElemNext = 0;
  377.  
  378.     // Start Tag
  379.     // A loop is used to ignore all remarks tags and special tags
  380.     // i.e. <?xml version="1.0"?>, and <!-- comment here -->
  381.     // So any tag beginning with ? or ! is ignored
  382.     // Loop past ignored tags
  383.     TokenPos token( m_csDoc );
  384.     token.nNext = m_aPos[iPosParent].nEndL;
  385.     CStdString csName;
  386.     while ( csName.IsEmpty() )
  387.     {
  388.         // Look for left angle bracket of start tag
  389.         m_aPos[iPos].nStartL = token.nNext;
  390.         if ( ! x_FindChar( token.szDoc, m_aPos[iPos].nStartL, _T('<') ) )
  391.             return x_ParseError( _T("Element tag not found") );
  392.  
  393.         // Set parent's End tag to start looking from here (or later)
  394.         m_aPos[iPosParent].nEndL = m_aPos[iPos].nStartL;
  395.  
  396.         // Determine whether this is an element, or bypass other type of node
  397.         token.nNext = m_aPos[iPos].nStartL + 1;
  398.         if ( x_FindToken( token ) )
  399.         {
  400.             if ( token.bIsString )
  401.                 return x_ParseError( _T("Tag starts with quote") );
  402.             _TCHAR cFirstChar = m_csDoc[token.nL];
  403.             if ( cFirstChar == _T('?') || cFirstChar == _T('!') )
  404.             {
  405.                 token.nNext = m_aPos[iPos].nStartL;
  406.                 if ( ! x_ParseNode(token) )
  407.                     return x_ParseError( _T("Invalid node") );
  408.             }
  409.             else if ( cFirstChar != _T('/') )
  410.             {
  411.                 csName = x_GetToken( token );
  412.                 // Look for end of tag
  413.                 if ( ! x_FindChar(token.szDoc, token.nNext, _T('>')) )
  414.                     return x_ParseError( _T("End of tag not found") );
  415.             }
  416.             else
  417.                 return x_ReleasePos(); // probably end tag of parent
  418.         }
  419.         else
  420.             return x_ParseError( _T("Abrupt end within tag") );
  421.     }
  422.     m_aPos[iPos].nStartR = token.nNext;
  423.  
  424.     // Is ending mark within start tag, i.e. empty element?
  425.     if ( m_csDoc[m_aPos[iPos].nStartR-1] == _T('/') )
  426.     {
  427.         // Empty element
  428.         // Close tag left is set to ending mark, and right to open tag right
  429.         m_aPos[iPos].nEndL = m_aPos[iPos].nStartR-1;
  430.         m_aPos[iPos].nEndR = m_aPos[iPos].nStartR;
  431.     }
  432.     else // look for end tag
  433.     {
  434.         // Element probably has contents
  435.         // Determine where to start looking for left angle bracket of end tag
  436.         // This is done by recursively parsing the contents of this element
  437.         int iInner, iInnerPrev = 0;
  438.         m_aPos[iPos].nEndL = m_aPos[iPos].nStartR + 1;
  439.         while ( (iInner = x_ParseElem( iPos )) > 0 )
  440.         {
  441.             // Set links to iInner
  442.             if ( iInnerPrev )
  443.                 m_aPos[iInnerPrev].iElemNext = iInner;
  444.             else
  445.                 m_aPos[iPos].iElemChild = iInner;
  446.             iInnerPrev = iInner;
  447.  
  448.             // Set offset to reflect child
  449.             m_aPos[iPos].nEndL = m_aPos[iInner].nEndR + 1;
  450.         }
  451.         if ( iInner == -1 )
  452.             return -1;
  453.  
  454.         // Look for left angle bracket of end tag
  455.         if ( ! x_FindChar( token.szDoc, m_aPos[iPos].nEndL, _T('<') ) )
  456.             return x_ParseError( _T("End tag of %s element not found"), csName );
  457.  
  458.         // Look through tokens of end tag
  459.         token.nNext = m_aPos[iPos].nEndL + 1;
  460.         int nTokenCount = 0;
  461.         while ( x_FindToken( token ) )
  462.         {
  463.             ++nTokenCount;
  464.             if ( ! token.bIsString )
  465.             {
  466.                 // Is first token not an end slash mark?
  467.                 if ( nTokenCount == 1 && m_csDoc[token.nL] != _T('/') )
  468.                     return x_ParseError( _T("Expecting end tag of element %s"), csName );
  469.  
  470.                 else if ( nTokenCount == 2 && ! token.Match(csName) )
  471.                     return x_ParseError( _T("End tag does not correspond to %s"), csName );
  472.  
  473.                 // Else is it a right angle bracket?
  474.                 else if ( m_csDoc[token.nL] == _T('>') )
  475.                     break;
  476.             }
  477.         }
  478.  
  479.         // Was a right angle bracket not found?
  480.         if ( ! token.szDoc[token.nL] || nTokenCount < 2 )
  481.             return x_ParseError( _T("End tag not completed for element %s"), csName );
  482.         m_aPos[iPos].nEndR = token.nL;
  483.     }
  484.  
  485.     // Successfully parsed element (and contained elements)
  486.     return iPos;
  487. }
  488.  
  489. bool CMarkupSTL::x_FindChar( LPCTSTR szDoc, int& nChar, _TCHAR c )
  490. {
  491.     // static function
  492.     LPCTSTR pChar = &szDoc[nChar];
  493.     while ( *pChar && *pChar != c )
  494.         pChar += _tclen( pChar );
  495.     nChar = pChar - szDoc;
  496.     if ( ! *pChar )
  497.         return false;
  498.     /*
  499.     while ( szDoc[nChar] && szDoc[nChar] != c )
  500.         nChar += _tclen( &szDoc[nChar] );
  501.     if ( ! szDoc[nChar] )
  502.         return false;
  503.     */
  504.     return true;
  505. }
  506.  
  507. bool CMarkupSTL::x_FindToken( CMarkupSTL::TokenPos& token )
  508. {
  509.     // Starting at token.nNext, bypass whitespace and find the next token
  510.     // returns true on success, members of token point to token
  511.     // returns false on end of document, members point to end of document
  512.     LPCTSTR szDoc = token.szDoc;
  513.     int nChar = token.nNext;
  514.     token.bIsString = false;
  515.  
  516.     // By-pass leading whitespace
  517.     while ( szDoc[nChar] && _tcschr(_T(" \t\n\r"),szDoc[nChar]) )
  518.         ++nChar;
  519.     if ( ! szDoc[nChar] )
  520.     {
  521.         // No token was found before end of document
  522.         token.nL = nChar;
  523.         token.nR = nChar;
  524.         token.nNext = nChar;
  525.         return false;
  526.     }
  527.  
  528.     // Is it an opening quote?
  529.     _TCHAR cFirstChar = szDoc[nChar];
  530.     if ( cFirstChar == _T('\"') || cFirstChar == _T('\'') )
  531.     {
  532.         token.bIsString = true;
  533.  
  534.         // Move past opening quote
  535.         ++nChar;
  536.         token.nL = nChar;
  537.  
  538.         // Look for closing quote
  539.         x_FindChar( token.szDoc, nChar, cFirstChar );
  540.  
  541.         // Set right to before closing quote
  542.         token.nR = nChar - 1;
  543.  
  544.         // Set nChar past closing quote unless at end of document
  545.         if ( szDoc[nChar] )
  546.             ++nChar;
  547.     }
  548.     else
  549.     {
  550.         // Go until special char or whitespace
  551.         token.nL = nChar;
  552.         while ( szDoc[nChar] && ! _tcschr(_T(" \t\n\r<>=\\/?!"),szDoc[nChar]) )
  553.             nChar += _tclen(&szDoc[nChar]);
  554.  
  555.         // Adjust end position if it is one special char
  556.         if ( nChar == token.nL )
  557.             ++nChar; // it is a special char
  558.         token.nR = nChar - 1;
  559.     }
  560.  
  561.     // nNext points to one past last char of token
  562.     token.nNext = nChar;
  563.     return true;
  564. }
  565.  
  566. CStdString CMarkupSTL::x_GetToken( const CMarkupSTL::TokenPos& token ) const
  567. {
  568.     // The token contains indexes into the document identifying a small substring
  569.     // Build the substring from those indexes and return it
  570.     if ( token.nL > token.nR )
  571.         return _T("");
  572.     return m_csDoc.Mid( token.nL,
  573.         token.nR - token.nL + ((token.nR<m_csDoc.GetLength())? 1:0) );
  574. }
  575.  
  576. int CMarkupSTL::x_FindElem( int iPosParent, int iPos, LPCTSTR szPath )
  577. {
  578.     // If szPath is NULL or empty, go to next sibling element
  579.     // Otherwise go to next sibling element with matching path
  580.     //
  581.     if ( iPos )
  582.         iPos = m_aPos[iPos].iElemNext;
  583.     else
  584.         iPos = m_aPos[iPosParent].iElemChild;
  585.  
  586.     // Finished here if szPath not specified
  587.     if ( szPath == NULL || !szPath[0] )
  588.         return iPos;
  589.  
  590.     // Search
  591.     TokenPos token( m_csDoc );
  592.     while ( iPos )
  593.     {
  594.         // Compare tag name
  595.         token.nNext = m_aPos[iPos].nStartL + 1;
  596.         x_FindToken( token ); // Locate tag name
  597.         if ( token.Match(szPath) )
  598.             return iPos;
  599.         iPos = m_aPos[iPos].iElemNext;
  600.     }
  601.     return 0;
  602. }
  603.  
  604. int CMarkupSTL::x_ParseNode( CMarkupSTL::TokenPos& token )
  605. {
  606.     // Call this with token.nNext set to the start of the node
  607.     // This returns the node type and token.nNext set to the char after the node
  608.     // If the node is not found or an element, token.nR is not determined
  609.     // White space between elements is a text node
  610.     int nTypeFound = 0;
  611.     LPCTSTR szDoc = token.szDoc;
  612.     token.nL = token.nNext;
  613.     if ( szDoc[token.nL] == '<' )
  614.     {
  615.         // Started with <, could be:
  616.         // <!--...--> comment
  617.         // <!DOCTYPE ...> dtd
  618.         // <?target ...?> processing instruction
  619.         // <![CDATA[...]]> cdata section
  620.         // <NAME ...> element
  621.         //
  622.         if ( ! szDoc[token.nL+1] || ! szDoc[token.nL+2] )
  623.             return 0;
  624.         _TCHAR cFirstChar = szDoc[token.nL+1];
  625.         LPCTSTR szEndOfNode = NULL;
  626.         if ( cFirstChar == _T('?') )
  627.         {
  628.             nTypeFound = MNT_PROCESSING_INSTRUCTION; // processing instruction
  629.             szEndOfNode = _T("?>");
  630.         }
  631.         else if ( cFirstChar == _T('!') )
  632.         {
  633.             _TCHAR cSecondChar = szDoc[token.nL+2];
  634.             if ( cSecondChar == _T('[') )
  635.             {
  636.                 nTypeFound = MNT_CDATA_SECTION;
  637.                 szEndOfNode = _T("]]>");
  638.             }
  639.             else if ( cSecondChar == _T('-') )
  640.             {
  641.                 nTypeFound = MNT_COMMENT;
  642.                 szEndOfNode = _T("-->");
  643.             }
  644.             else
  645.             {
  646.                 // Document type requires tokenizing because of strings and brackets
  647.                 nTypeFound = 0;
  648.                 int nBrackets = 0;
  649.                 while ( x_FindToken(token) )
  650.                 {
  651.                     if ( ! token.bIsString )
  652.                     {
  653.                         _TCHAR cChar = szDoc[token.nL];
  654.                         if ( cChar == _T('[') )
  655.                             ++nBrackets;
  656.                         else if ( cChar == _T(']') )
  657.                             --nBrackets;
  658.                         else if ( nBrackets == 0 && cChar == _T('>') )
  659.                         {
  660.                             nTypeFound = MNT_DOCUMENT_TYPE;
  661.                             break;
  662.                         }
  663.                     }
  664.                 }
  665.                 if ( ! nTypeFound )
  666.                     return 0;
  667.             }
  668.         }
  669.         else if ( cFirstChar == _T('/') )
  670.         {
  671.             // End tag means no node found within parent element
  672.             return 0;
  673.         }
  674.         else
  675.         {
  676.             nTypeFound = MNT_ELEMENT;
  677.         }
  678.  
  679.         // Search for end of node if not found yet
  680.         if ( szEndOfNode )
  681.         {
  682.             LPCTSTR pEnd = _tcsstr( &szDoc[token.nNext], szEndOfNode );
  683.             if ( ! pEnd )
  684.                 return 0; // not well-formed
  685.             token.nNext = (pEnd - szDoc) + _tcslen(szEndOfNode);
  686.         }
  687.     }
  688.     else if ( szDoc[token.nL] )
  689.     {
  690.         // It is text or whitespace because it did not start with <
  691.         nTypeFound = MNT_WHITESPACE;
  692.         if ( x_FindToken(token) )
  693.         {
  694.             if ( szDoc[token.nL] == _T('<') )
  695.                 token.nNext = token.nL;
  696.             else
  697.             {
  698.                 nTypeFound = MNT_TEXT;
  699.                 x_FindChar( token.szDoc, token.nNext, _T('<') );
  700.             }
  701.         }
  702.     }
  703.     return nTypeFound;
  704. }
  705.  
  706. CStdString CMarkupSTL::x_GetTagName( int iPos ) const
  707. {
  708.     // Return the tag name at specified element
  709.     TokenPos token( m_csDoc );
  710.     token.nNext = m_aPos[iPos].nStartL + 1;
  711.     if ( ! iPos || ! x_FindToken( token ) )
  712.         return _T("");
  713.  
  714.     // Return substring of document
  715.     return x_GetToken( token );
  716. }
  717.  
  718. bool CMarkupSTL::x_FindAttrib( CMarkupSTL::TokenPos& token, LPCTSTR szAttrib ) const
  719. {
  720.     // If szAttrib is NULL find next attrib, otherwise find named attrib
  721.     // Return true if found
  722.     int nAttrib = 0;
  723.     for ( int nCount = 0; x_FindToken(token); ++nCount )
  724.     {
  725.         if ( ! token.bIsString )
  726.         {
  727.             // Is it the right angle bracket?
  728.             if ( m_csDoc[token.nL] == _T('>') || m_csDoc[token.nL] == _T('/') )
  729.                 break; // attrib not found
  730.  
  731.             // Equal sign
  732.             if ( m_csDoc[token.nL] == _T('=') )
  733.                 continue;
  734.  
  735.             // Potential attribute
  736.             if ( ! nAttrib && nCount )
  737.             {
  738.                 // Attribute name search?
  739.                 if ( ! szAttrib || ! szAttrib[0] )
  740.                     return true; // return with token at attrib name
  741.  
  742.                 // Compare szAttrib
  743.                 if ( token.Match(szAttrib) )
  744.                     nAttrib = nCount;
  745.             }
  746.         }
  747.         else if ( nAttrib && nCount == nAttrib + 2 )
  748.         {
  749.             return true;
  750.         }
  751.     }
  752.  
  753.     // Not found
  754.     return false;
  755. }
  756.  
  757. CStdString CMarkupSTL::x_GetAttrib( int iPos, LPCTSTR szAttrib ) const
  758. {
  759.     // Return the value of the attrib at specified element
  760.     if ( ! iPos || m_nNodeType != MNT_ELEMENT )
  761.         return _T("");
  762.  
  763.     TokenPos token( m_csDoc );
  764.     token.nNext = m_aPos[iPos].nStartL + 1;
  765.     if ( szAttrib && x_FindAttrib( token, szAttrib ) )
  766.         return x_TextFromDoc( token.nL, token.nR - ((token.nR<m_csDoc.GetLength())?0:1) );
  767.     return _T("");
  768. }
  769.  
  770. bool CMarkupSTL::x_SetAttrib( int iPos, LPCTSTR szAttrib, int nValue )
  771. {
  772.     // Convert integer to string and call SetChildAttrib
  773.     _TCHAR szVal[25];
  774.     _stprintf( szVal, _T("%d"), nValue );
  775.     return x_SetAttrib( iPos, szAttrib, szVal );
  776. }
  777.  
  778. bool CMarkupSTL::x_SetAttrib( int iPos, LPCTSTR szAttrib, __int64 nValue )
  779. {
  780.     // Convert integer to string and call SetChildAttrib
  781.     _TCHAR szVal[25];
  782.     _stprintf( szVal, _T("%I64d"), nValue );
  783.     return x_SetAttrib( iPos, szAttrib, szVal );
  784. }
  785.  
  786. bool CMarkupSTL::x_SetAttrib( int iPos, LPCTSTR szAttrib, LPCTSTR szValue )
  787. {
  788.     // Set attribute in iPos element
  789.     if ( ! iPos || m_nNodeType != MNT_ELEMENT )
  790.         return false;
  791.  
  792.     TokenPos token( m_csDoc );
  793.     token.nNext = m_aPos[iPos].nStartL + 1;
  794.     int nInsertAt, nReplace = 0;
  795.     CStdString csInsert;
  796.     if ( x_FindAttrib( token, szAttrib ) )
  797.     {
  798.         // Decision: for empty value leaving attrib="" instead of removing attrib
  799.         // Replace value only
  800.         csInsert = x_TextToDoc( szValue, true );
  801.         nInsertAt = token.nL;
  802.         nReplace = token.nR-token.nL+1;
  803.     }
  804.     else
  805.     {
  806.         // Insert string name value pair
  807.         CStdString csFormat;
  808.         csFormat = _T(" ");
  809.         csFormat += szAttrib;
  810.         csFormat += _T("=\"");
  811.         csFormat += x_TextToDoc( szValue, true );
  812.         csFormat += _T("\"");
  813.         csInsert = csFormat;
  814.  
  815.         // take into account whether it is an empty element
  816.         nInsertAt = m_aPos[iPos].nStartR - (m_aPos[iPos].IsEmptyElement()?1:0);
  817.     }
  818.  
  819.     x_DocChange( nInsertAt, nReplace, csInsert );
  820.     int nAdjust = csInsert.GetLength() - nReplace;
  821.     m_aPos[iPos].nStartR += nAdjust;
  822.     m_aPos[iPos].AdjustEnd( nAdjust );
  823.     x_Adjust( iPos, nAdjust );
  824.     MARKUP_SETDEBUGSTATE;
  825.     return true;
  826. }
  827.  
  828. bool CMarkupSTL::x_CreateNode( CStdString& csNode, int nNodeType, LPCTSTR szText )
  829. {
  830.     // Set csNode based on nNodeType and szData
  831.     // Return false if szData would jeopardize well-formed document
  832.     //
  833.     switch ( nNodeType )
  834.     {
  835.     case MNT_CDATA_SECTION:
  836.         if ( _tcsstr(szText,_T("]]>")) != NULL )
  837.             return false;
  838.         csNode = "<![CDATA[";
  839.         csNode += szText;
  840.         csNode += "]]>";
  841.         break;
  842.     }
  843.     return true;
  844. }
  845.  
  846. bool CMarkupSTL::x_SetData( int iPos, LPCTSTR szData, int nCDATA )
  847. {
  848.     // Set data at specified position
  849.     // if nCDATA==1, set content of element to a CDATA Section
  850.     CStdString csInsert;
  851.  
  852.     // Set data in iPos element
  853.     if ( ! iPos || m_aPos[iPos].iElemChild )
  854.         return false;
  855.  
  856.     // Build csInsert from szData based on nCDATA
  857.     // If CDATA section not valid, use parsed text (PCDATA) instead
  858.     if ( nCDATA != 0 )
  859.         if ( ! x_CreateNode(csInsert, MNT_CDATA_SECTION, szData) )
  860.             nCDATA = 0;
  861.     if ( nCDATA == 0 )
  862.         csInsert = x_TextToDoc( szData );
  863.  
  864.     // Decide where to insert
  865.     int nInsertAt, nReplace;
  866.     if ( m_aPos[iPos].IsEmptyElement() )
  867.     {
  868.         nInsertAt = m_aPos[iPos].nEndL;
  869.         nReplace = 1;
  870.  
  871.         // Pre-adjust since <NAME/> becomes <NAME>data</NAME>
  872.         CStdString csTagName = x_GetTagName( iPos );
  873.         m_aPos[iPos].nStartR -= 1;
  874.         m_aPos[iPos].nEndL -= (1 + csTagName.GetLength());
  875.         CStdString csFormat;
  876.         csFormat = _T(">");
  877.         csFormat += csInsert;
  878.         csFormat += _T("</");
  879.         csFormat += csTagName;
  880.         csInsert = csFormat;
  881.     }
  882.     else
  883.     {
  884.         nInsertAt = m_aPos[iPos].nStartR+1;
  885.         nReplace = m_aPos[iPos].nEndL - m_aPos[iPos].nStartR - 1;
  886.     }
  887.     x_DocChange( nInsertAt, nReplace, csInsert );
  888.     int nAdjust = csInsert.GetLength() - nReplace;
  889.     x_Adjust( iPos, nAdjust );
  890.     m_aPos[iPos].AdjustEnd( nAdjust );
  891.     MARKUP_SETDEBUGSTATE;
  892.     return true;
  893. }
  894.  
  895. CStdString CMarkupSTL::x_GetData( int iPos ) const
  896. {
  897.     // Return a string representing data between start and end tag
  898.     // Return empty string if there are any children elements
  899.     if ( ! m_aPos[iPos].iElemChild && ! m_aPos[iPos].IsEmptyElement() )
  900.     {
  901.         // See if it is a CDATA section
  902.         TokenPos token( m_csDoc );
  903.         token.nNext = m_aPos[iPos].nStartR+1;
  904.         if ( x_FindToken( token ) && m_csDoc[token.nL] == _T('<')
  905.                 && token.nL + 11 < m_aPos[iPos].nEndL
  906.                 && _tcsncmp( &token.szDoc[token.nL+1], _T("![CDATA["), 8 ) == 0 )
  907.         {
  908.             int nEndCDATA = m_csDoc.Find( _T("]]>"), token.nNext );
  909.             if ( nEndCDATA != -1 && nEndCDATA < m_aPos[iPos].nEndL )
  910.             {
  911.                 return m_csDoc.Mid( token.nL+9, nEndCDATA-token.nL-9 );
  912.             }
  913.         }
  914.         return x_TextFromDoc( m_aPos[iPos].nStartR+1, m_aPos[iPos].nEndL-1 );
  915.     }
  916.     return _T("");
  917. }
  918.  
  919. CStdString CMarkupSTL::x_TextToDoc( LPCTSTR szText, bool bAttrib ) const
  920. {
  921.     // Convert text as seen outside XML document to XML friendly
  922.     // replacing special characters with ampersand escape codes
  923.     // E.g. convert "6>7" to "6>7"
  924.     //
  925.     // <   less than
  926.     // &  ampersand
  927.     // >   greater than
  928.     //
  929.     // and for attributes:
  930.     //
  931.     // ' apostrophe or single quote
  932.     // " double quote
  933.     //
  934.     static _TCHAR* szaReplace[] = { _T("<"),_T("&"),_T(">"),_T("'"),_T(""") };
  935.     const _TCHAR* pFind = bAttrib?_T("<&>\'\""):_T("<&>");
  936.     CStdString csText;
  937.     const _TCHAR* pSource = szText;
  938.     int nDestSize = _tcslen(pSource);
  939.     nDestSize += nDestSize / 10 + 7;
  940.     _TCHAR* pDest = csText.GetBuffer(nDestSize);
  941.     int nLen = 0;
  942.     _TCHAR cSource = *pSource;
  943.     _TCHAR* pFound;
  944.     while ( cSource )
  945.     {
  946.         if ( nLen > nDestSize - 6 )
  947.         {
  948.             csText.ReleaseBuffer(nLen);
  949.             nDestSize *= 2;
  950.             pDest = csText.GetBuffer(nDestSize);
  951.         }
  952.         if ( (pFound=(_TCHAR *)_tcschr(pFind,cSource)) != NULL )
  953.         {
  954.             pFound = szaReplace[pFound-pFind];
  955.             _tcscpy(&pDest[nLen],pFound);
  956.             nLen += _tcslen(pFound);
  957.         }
  958.         else
  959.         {
  960.             _tccpy( &pDest[nLen], pSource );
  961.             ++nLen;
  962.         }
  963.         pSource += _tclen( pSource );
  964.         cSource = *pSource;
  965.     }
  966.     csText.ReleaseBuffer(nLen);
  967.     return csText;
  968. }
  969.  
  970. CStdString CMarkupSTL::x_TextFromDoc( int nLeft, int nRight ) const
  971. {
  972.     // Convert XML friendly text to text as seen outside XML document
  973.     // replacing ampersand escape codes with special characters
  974.     // E.g. convert "6>7" to "6>7"
  975.     //
  976.     // Conveniently the result is always the same or shorter in length
  977.     //
  978.     static _TCHAR* szaCode[] = { _T("lt;"),_T("amp;"),_T("gt;"),_T("apos;"),_T("quot;") };
  979.     static int anCodeLen[] = { 3,4,3,5,5 };
  980.     static _TCHAR* szSymbol = _T("<&>\'\"");
  981.     CStdString csText;
  982.     const _TCHAR* pSource = m_csDoc;
  983.     int nDestSize = nRight - nLeft + 1;
  984.     _TCHAR* pDest = csText.GetBuffer(nDestSize);
  985.     int nLen = 0;
  986.     int nCharLen;
  987.     int nChar = nLeft;
  988.     while ( nChar <= nRight )
  989.     {
  990.         if ( pSource[nChar] == _T('&') )
  991.         {
  992.             // Look for matching &code;
  993.             int nMatch;
  994.             for ( nMatch = 0; nMatch < 5; ++nMatch )
  995.             {
  996.                 if ( nChar <= nRight - anCodeLen[nMatch]
  997.                     && _tcsncmp(szaCode[nMatch],&pSource[nChar+1],anCodeLen[nMatch]) == 0 )
  998.                 {
  999.                     pDest[nLen++] = szSymbol[nMatch];
  1000.                     nChar += anCodeLen[nMatch] + 1;
  1001.                     break;
  1002.                 }
  1003.             }
  1004.  
  1005.             // If no match is found it means XML doc is invalid
  1006.             // no devastating harm done, ampersand code will just be left in result
  1007.             if ( nMatch == 5 )
  1008.             {
  1009.                 pDest[nLen++] = _T('&');
  1010.                 ++nChar;
  1011.             }
  1012.         }
  1013.         else
  1014.         {
  1015.             nCharLen = _tclen(&pSource[nChar]);
  1016.             _tccpy( &pDest[nLen], &pSource[nChar] );
  1017.             nLen += nCharLen;
  1018.             nChar += nCharLen;
  1019.         }
  1020.     }
  1021.     csText.ReleaseBuffer(nLen);
  1022.     return csText;
  1023. }
  1024.  
  1025. void CMarkupSTL::x_DocChange( int nLeft, int nReplace, const CStdString& csInsert )
  1026. {
  1027.     // Insert csInsert int m_csDoc at nLeft replacing nReplace chars
  1028.     // Do this with only one buffer reallocation if it grows
  1029.     //
  1030.     int nDocLength = m_csDoc.GetLength();
  1031.     int nInsLength = csInsert.GetLength();
  1032.  
  1033.     // Make sure nLeft and nReplace are within bounds
  1034.     nLeft = max( 0, min( nLeft, nDocLength ) );
  1035.     nReplace = max( 0, min( nReplace, nDocLength-nLeft ) );
  1036.  
  1037.     // Get pointer to buffer with enough room
  1038.     int nNewLength = nInsLength + nDocLength - nReplace;
  1039.     int nBufferLen = nNewLength;
  1040.     _TCHAR* pDoc = m_csDoc.GetBuffer( nBufferLen );
  1041.  
  1042.     // Move part of old doc that goes after insert
  1043.     if ( nLeft+nReplace < nDocLength )
  1044.         memmove( &pDoc[nLeft+nInsLength], &pDoc[nLeft+nReplace], (nDocLength-nLeft-nReplace)*sizeof(_TCHAR) );
  1045.  
  1046.     // Copy insert
  1047.     memcpy( &pDoc[nLeft], csInsert, nInsLength*sizeof(_TCHAR) );
  1048.  
  1049.     // Release
  1050.     m_csDoc.ReleaseBuffer( nNewLength );
  1051. }
  1052.  
  1053. void CMarkupSTL::x_Adjust( int iPos, int nShift, bool bAfterPos )
  1054. {
  1055.     // Loop through affected elements and adjust indexes
  1056.     // Algorithm:
  1057.     // 1. update children unless bAfterPos
  1058.     //    (if no children or bAfterPos is true, end tag of iPos not affected)
  1059.     // 2. update next siblings and their children
  1060.     // 3. go up until there is a next sibling of a parent and update end tags
  1061.     // 4. step 2
  1062.     int iPosTop = m_aPos[iPos].iElemParent;
  1063.     bool bPosFirst = bAfterPos; // mark as first to skip its children
  1064.     while ( iPos )
  1065.     {
  1066.         // Were we at containing parent of affected position?
  1067.         bool bPosTop = false;
  1068.         if ( iPos == iPosTop )
  1069.         {
  1070.             // Move iPosTop up one towards root
  1071.             iPosTop = m_aPos[iPos].iElemParent;
  1072.             bPosTop = true;
  1073.         }
  1074.  
  1075.         // Traverse to the next update position
  1076.         if ( ! bPosTop && ! bPosFirst && m_aPos[iPos].iElemChild )
  1077.         {
  1078.             // Depth first
  1079.             iPos = m_aPos[iPos].iElemChild;
  1080.         }
  1081.         else if ( m_aPos[iPos].iElemNext )
  1082.         {
  1083.             iPos = m_aPos[iPos].iElemNext;
  1084.         }
  1085.         else
  1086.         {
  1087.             // Look for next sibling of a parent of iPos
  1088.             // When going back up, parents have already been done except iPosTop
  1089.             while ( (iPos=m_aPos[iPos].iElemParent) != 0 && iPos != iPosTop )
  1090.                 if ( m_aPos[iPos].iElemNext )
  1091.                 {
  1092.                     iPos = m_aPos[iPos].iElemNext;
  1093.                     break;
  1094.                 }
  1095.         }
  1096.         bPosFirst = false;
  1097.  
  1098.         // Shift indexes at iPos
  1099.         if ( iPos != iPosTop )
  1100.             m_aPos[iPos].AdjustStart( nShift );
  1101.         m_aPos[iPos].AdjustEnd( nShift );
  1102.     }
  1103. }
  1104.  
  1105. void CMarkupSTL::x_LocateNew( int iPosParent, int& iPosRel, int& nOffset, int nLength, int nFlags )
  1106. {
  1107.     // Determine where to insert new element or node
  1108.     //
  1109.     bool bInsert = (nFlags&1)?true:false;
  1110.     bool bHonorWhitespace = (nFlags&2)?true:false;
  1111.  
  1112.     int nStartL;
  1113.     if ( nLength )
  1114.     {
  1115.         // Located at a non-element node
  1116.         if ( bInsert )
  1117.             nStartL = nOffset;
  1118.         else
  1119.             nStartL = nOffset + nLength;
  1120.     }
  1121.     else if ( iPosRel )
  1122.     {
  1123.         // Located at an element
  1124.         if ( bInsert ) // precede iPosRel
  1125.             nStartL = m_aPos[iPosRel].nStartL;
  1126.         else // follow iPosRel
  1127.             nStartL = m_aPos[iPosRel].nEndR + 1;
  1128.     }
  1129.     else if ( m_aPos[iPosParent].IsEmptyElement() )
  1130.     {
  1131.         // Parent has no separate end tag, so split empty element
  1132.         nStartL = m_aPos[iPosParent].nStartR;
  1133.     }
  1134.     else
  1135.     {
  1136.         if ( bInsert ) // after start tag
  1137.             nStartL = m_aPos[iPosParent].nStartR + 1;
  1138.         else // before end tag
  1139.             nStartL = m_aPos[iPosParent].nEndL;
  1140.     }
  1141.  
  1142.     // Go up to start of next node, unless its splitting an empty element
  1143.     if ( ! bHonorWhitespace && ! m_aPos[iPosParent].IsEmptyElement() )
  1144.     {
  1145.         TokenPos token( m_csDoc );
  1146.         token.nNext = nStartL;
  1147.         if ( ! x_FindToken(token) || m_csDoc[token.nL] == _T('<') )
  1148.             nStartL = token.nL;
  1149.     }
  1150.  
  1151.     // Determine iPosBefore
  1152.     int iPosBefore = 0;
  1153.     if ( iPosRel )
  1154.     {
  1155.         if ( bInsert )
  1156.         {
  1157.             // Is iPosRel past first sibling?
  1158.             int iPosPrev = m_aPos[iPosParent].iElemChild;
  1159.             if ( iPosPrev != iPosRel )
  1160.             {
  1161.                 // Find previous sibling of iPosRel
  1162.                 while ( m_aPos[iPosPrev].iElemNext != iPosRel )
  1163.                     iPosPrev = m_aPos[iPosPrev].iElemNext;
  1164.                 iPosBefore = iPosPrev;
  1165.             }
  1166.         }
  1167.         else
  1168.         {
  1169.             iPosBefore = iPosRel;
  1170.         }
  1171.     }
  1172.     else if ( m_aPos[iPosParent].iElemChild )
  1173.     {
  1174.         if ( ! bInsert )
  1175.         {
  1176.             // Find last element under iPosParent
  1177.             int iPosLast = m_aPos[iPosParent].iElemChild;
  1178.             int iPosNext = iPosLast;
  1179.             while ( iPosNext )
  1180.             {
  1181.                 iPosLast = iPosNext;
  1182.                 iPosNext = m_aPos[iPosNext].iElemNext;
  1183.             }
  1184.             iPosBefore = iPosLast;
  1185.         }
  1186.     }
  1187.  
  1188.     nOffset = nStartL;
  1189.     iPosRel = iPosBefore;
  1190. }
  1191.  
  1192. bool CMarkupSTL::x_AddElem( LPCTSTR szName, LPCTSTR szValue, bool bInsert, bool bAddChild )
  1193. {
  1194.     if ( bAddChild )
  1195.     {
  1196.         // Adding a child element under main position
  1197.         if ( ! m_iPos )
  1198.             return false;
  1199.     }
  1200.     else if ( m_iPosParent == 0 )
  1201.     {
  1202.         // Adding root element
  1203.         if ( IsWellFormed() )
  1204.             return false;
  1205.  
  1206.         // Locate after any version and DTD
  1207.         m_aPos[0].nEndL = m_csDoc.GetLength();
  1208.     }
  1209.  
  1210.     // Locate where to add element relative to current node
  1211.     int iPosParent, iPosBefore, nOffset = 0, nLength = 0;
  1212.     if ( bAddChild )
  1213.     {
  1214.         iPosParent = m_iPos;
  1215.         iPosBefore = m_iPosChild;
  1216.     }
  1217.     else
  1218.     {
  1219.         iPosParent = m_iPosParent;
  1220.         iPosBefore = m_iPos;
  1221.     }
  1222.     int nFlags = bInsert?1:0;
  1223.     x_LocateNew( iPosParent, iPosBefore, nOffset, nLength, nFlags );
  1224.     bool bEmptyParent = m_aPos[iPosParent].IsEmptyElement();
  1225.     if ( bEmptyParent )
  1226.         nOffset += 2; // include CRLF
  1227.  
  1228.     // Create element and modify positions of affected elements
  1229.     // If no szValue is specified, an empty element is created
  1230.     // i.e. either <NAME>value</NAME> or <NAME/>
  1231.     //
  1232.     int iPos = x_GetFreePos();
  1233.     m_aPos[iPos].nStartL = nOffset;
  1234.  
  1235.     // Set links
  1236.     m_aPos[iPos].iElemParent = iPosParent;
  1237.     m_aPos[iPos].iElemChild = 0;
  1238.     m_aPos[iPos].iElemNext = 0;
  1239.     if ( iPosBefore )
  1240.     {
  1241.         // Link in after iPosBefore
  1242.         m_aPos[iPos].iElemNext = m_aPos[iPosBefore].iElemNext;
  1243.         m_aPos[iPosBefore].iElemNext = iPos;
  1244.     }
  1245.     else
  1246.     {
  1247.         // First child
  1248.         m_aPos[iPos].iElemNext = m_aPos[iPosParent].iElemChild;
  1249.         m_aPos[iPosParent].iElemChild = iPos;
  1250.     }
  1251.  
  1252.     // Create string for insert
  1253.     CStdString csInsert;
  1254.     int nLenName = _tcslen(szName);
  1255.     int nLenValue = szValue? _tcslen(szValue) : 0;
  1256.     if ( ! nLenValue )
  1257.     {
  1258.         // <NAME/> empty element
  1259.         csInsert = _T("<");
  1260.         csInsert += szName;
  1261.         csInsert += _T("/>\r\n");
  1262.         m_aPos[iPos].nStartR = m_aPos[iPos].nStartL + nLenName + 2;
  1263.         m_aPos[iPos].nEndL = m_aPos[iPos].nStartR - 1;
  1264.         m_aPos[iPos].nEndR = m_aPos[iPos].nEndL + 1;
  1265.     }
  1266.     else
  1267.     {
  1268.         // <NAME>value</NAME>
  1269.         CStdString csValue = x_TextToDoc( szValue );
  1270.         nLenValue = csValue.GetLength();
  1271.         csInsert = _T("<");
  1272.         csInsert += szName;
  1273.         csInsert += _T(">");
  1274.         csInsert += csValue;
  1275.         csInsert += _T("</");
  1276.         csInsert += szName;
  1277.         csInsert += _T(">\r\n");
  1278.         m_aPos[iPos].nStartR = m_aPos[iPos].nStartL + nLenName + 1;
  1279.         m_aPos[iPos].nEndL = m_aPos[iPos].nStartR + nLenValue + 1;
  1280.         m_aPos[iPos].nEndR = m_aPos[iPos].nEndL + nLenName + 2;
  1281.     }
  1282.  
  1283.     // Insert
  1284.     int nReplace = 0, nLeft = m_aPos[iPos].nStartL;
  1285.     if ( bEmptyParent )
  1286.     {
  1287.         CStdString csParentTagName = x_GetTagName(iPosParent);
  1288.         CStdString csFormat;
  1289.         csFormat = _T(">\r\n");
  1290.         csFormat += csInsert;
  1291.         csFormat += _T("</");
  1292.         csFormat += csParentTagName;
  1293.         csInsert = csFormat;
  1294.         nLeft -= 3;
  1295.         nReplace = 1;
  1296.         // x_Adjust is going to update all affected indexes by one amount
  1297.         // This will satisfy all except the empty parent
  1298.         // Here we pre-adjust for the empty parent
  1299.         // The empty tag slash is removed
  1300.         m_aPos[iPosParent].nStartR -= 1;
  1301.         // For the newly created end tag, see the following example:
  1302.         // <A/> (len 4) becomes <A><B/></A> (len 11)
  1303.         // In x_Adjust everything will be adjusted 11 - 4 = 7
  1304.         // But the nEndL of element A should only be adjusted 5
  1305.         m_aPos[iPosParent].nEndL -= (csParentTagName.GetLength() + 1);
  1306.     }
  1307.     x_DocChange( nLeft, nReplace, csInsert );
  1308.     x_Adjust( iPos, csInsert.GetLength() - nReplace );
  1309.  
  1310.     if ( bAddChild )
  1311.         x_SetPos( m_iPosParent, iPosParent, iPos );
  1312.     else
  1313.         x_SetPos( iPosParent, iPos, 0 );
  1314.     return true;
  1315. }
  1316.  
  1317. bool CMarkupSTL::x_AddSubDoc( LPCTSTR szSubDoc, bool bInsert, bool bAddChild )
  1318. {
  1319.     // Add subdocument, parse, and modify positions of affected elements
  1320.     //
  1321.     int nOffset = 0, iPosParent, iPosBefore;
  1322.     if ( bAddChild )
  1323.     {
  1324.         // Add a subdocument under main position, after current child position
  1325.         if ( ! m_iPos )
  1326.             return false;
  1327.         iPosParent = m_iPos;
  1328.         iPosBefore = m_iPosChild;
  1329.     }
  1330.     else
  1331.     {
  1332.         iPosParent = m_iPosParent;
  1333.         iPosBefore = m_iPos;
  1334.     }
  1335.     int nFlags = bInsert?1:0;
  1336.     x_LocateNew( iPosParent, iPosBefore, nOffset, 0, nFlags );
  1337.     bool bEmptyParent = m_aPos[iPosParent].IsEmptyElement();
  1338.     if ( bEmptyParent )
  1339.         nOffset += 2; // include CRLF
  1340.  
  1341.     // if iPosBefore is NULL, insert as first element under parent
  1342.     int nParentEndLBeforeAdd = m_aPos[iPosParent].nEndL;
  1343.     int iPosFreeBeforeAdd = m_iPosFree;
  1344.  
  1345.     // Skip version tag or DTD at start of subdocument
  1346.     TokenPos token( szSubDoc );
  1347.     int nNodeType = x_ParseNode( token );
  1348.     while ( nNodeType && nNodeType != MNT_ELEMENT )
  1349.     {
  1350.         token.szDoc = &szSubDoc[token.nNext];
  1351.         token.nNext = 0;
  1352.         nNodeType = x_ParseNode( token );
  1353.     }
  1354.     CStdString csInsert = token.szDoc;
  1355.  
  1356.     // Insert subdocument
  1357.     m_aPos[iPosParent].nEndL = nOffset;
  1358.     int nReplace = 0, nLeft = nOffset;
  1359.     CStdString csParentTagName;
  1360.     if ( bEmptyParent )
  1361.     {
  1362.         csParentTagName = x_GetTagName(iPosParent);
  1363.         CStdString csFormat;
  1364.         csFormat = _T(">\r\n");
  1365.         csFormat += csInsert;
  1366.         csFormat += _T("</");
  1367.         csFormat += csParentTagName;
  1368.         csInsert = csFormat;
  1369.         m_aPos[iPosParent].nEndL = m_aPos[iPosParent].nStartR + 2;
  1370.         nLeft = m_aPos[iPosParent].nStartR - 1;
  1371.         nReplace = 1;
  1372.     }
  1373.     x_DocChange( nLeft, nReplace, csInsert );
  1374.  
  1375.     // Parse subdocument
  1376.     int iPos = x_ParseElem(iPosParent);
  1377.     m_aPos[iPosParent].nEndL = nParentEndLBeforeAdd;
  1378.     if ( iPos <= 0 )
  1379.     {
  1380.         // Abort because not well-formed
  1381.         CStdString csRevert = bEmptyParent?_T("/"):_T("");
  1382.         x_DocChange( nLeft, csInsert.GetLength(), csRevert );
  1383.         m_iPosFree = iPosFreeBeforeAdd;
  1384.         return false;
  1385.     }
  1386.     else
  1387.     {
  1388.         // Link in parent and siblings
  1389.         m_aPos[iPos].iElemParent = iPosParent;
  1390.         if ( iPosBefore )
  1391.         {
  1392.             m_aPos[iPos].iElemNext = m_aPos[iPosBefore].iElemNext;
  1393.             m_aPos[iPosBefore].iElemNext = iPos;
  1394.         }
  1395.         else
  1396.         {
  1397.             m_aPos[iPos].iElemNext = m_aPos[iPosParent].iElemChild;
  1398.             m_aPos[iPosParent].iElemChild = iPos;
  1399.         }
  1400.  
  1401.         // Make empty parent pre-adjustment
  1402.         if ( bEmptyParent )
  1403.         {
  1404.             m_aPos[iPosParent].nStartR -= 1;
  1405.             m_aPos[iPosParent].nEndL -= (csParentTagName.GetLength() + 1);
  1406.         }
  1407.  
  1408.         // Adjust, but don't adjust children of iPos (bAfterPos=true)
  1409.         x_Adjust( iPos, csInsert.GetLength() - nReplace, true );
  1410.     }
  1411.  
  1412.     // Set position to top element of subdocument
  1413.     if ( bAddChild )
  1414.         x_SetPos( m_iPosParent, iPosParent, iPos );
  1415.     else // Main
  1416.         x_SetPos( m_iPosParent, iPos, 0 );
  1417.     return true;
  1418. }
  1419.  
  1420. int CMarkupSTL::x_RemoveElem( int iPos )
  1421. {
  1422.     // Remove element and all contained elements
  1423.     // Return new position
  1424.     //
  1425.     int iPosParent = m_aPos[iPos].iElemParent;
  1426.  
  1427.     // Find previous sibling and bypass removed element
  1428.     // This leaves orphan positions in m_aPos array
  1429.     int iPosLook = m_aPos[iPosParent].iElemChild;
  1430.     int iPosPrev = 0;
  1431.     while ( iPosLook != iPos )
  1432.     {
  1433.         iPosPrev = iPosLook;
  1434.         iPosLook = m_aPos[iPosLook].iElemNext;
  1435.     }
  1436.     if ( iPosPrev )
  1437.         m_aPos[iPosPrev].iElemNext = m_aPos[iPos].iElemNext;
  1438.     else
  1439.         m_aPos[iPosParent].iElemChild = m_aPos[iPos].iElemNext;
  1440.  
  1441.     // Remove from document
  1442.     // Links have been changed to go around removed element
  1443.     // But element position and links are still valid
  1444.     int nAfterEnd = m_aPos[iPos].nEndR + 1;
  1445.     TokenPos token( m_csDoc );
  1446.     token.nNext = nAfterEnd;
  1447.     if ( ! x_FindToken(token) || token.szDoc[token.nL] == _T('<') )
  1448.         nAfterEnd = token.nL;
  1449.     int nLen = nAfterEnd - m_aPos[iPos].nStartL;
  1450.     x_DocChange( m_aPos[iPos].nStartL, nLen, CStdString() );
  1451.     x_Adjust( iPos, - nLen, true );
  1452.     return iPosPrev;
  1453. }
  1454.  
  1455.