Markup.cpp
Upload User: wbm1988
Upload Date: 2022-08-06
Package Size: 3696k
Code Size: 83k
Category:

Windows Develop

Development Platform:

Visual C++

  1. // Markup.cpp: implementation of the CMarkup class.
  2. //
  3. // Markup Release 9.0
  4. // Copyright (C) 1999-2007 First Objective Software, Inc. All rights reserved
  5. // Go to www.firstobject.com for the latest CMarkup and EDOM documentation
  6. // Use in commercial applications requires written permission
  7. // This software is provided "as is", with no warranty.
  8. //
  9. #include <stdio.h>
  10. #include "Markup.h"
  11. #ifdef MCD_STRERROR
  12. #include <string.h>
  13. #include <errno.h>
  14. #else
  15. #include <windows.h>
  16. #endif
  17. #if defined(_DEBUG) && ! defined(MARKUP_STL) && ! defined(MARKUP_STDC)
  18. #undef THIS_FILE
  19. static char THIS_FILE[]=__FILE__;
  20. #define new DEBUG_NEW
  21. #endif
  22. #ifdef _MBCS
  23. #pragma message( "Note: MBCS build (not UTF-8)" )
  24. // For UTF-8, remove _MBCS from project settings C/C++ preprocessor definitions
  25. #endif
  26. // Customization
  27. #define x_EOL _T("rn") // can be rn or n or empty
  28. #define x_EOLLEN (sizeof(x_EOL)/sizeof(MCD_CHAR)-1) // string length of x_EOL
  29. #define x_ATTRIBQUOTE _T(""") // can be double or single quote
  30. void CMarkup::operator=( const CMarkup& 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_iPosDeleted = markup.m_iPosDeleted;
  37. m_nNodeType = markup.m_nNodeType;
  38. m_nNodeOffset = markup.m_nNodeOffset;
  39. m_nNodeLength = markup.m_nNodeLength;
  40. m_strDoc = markup.m_strDoc;
  41. m_strError = markup.m_strError;
  42. m_nFlags = markup.m_nFlags;
  43. // Copy used part of the index array
  44. m_aPos.RemoveAll();
  45. m_aPos.nSize = m_iPosFree;
  46. if ( m_aPos.nSize < 8 )
  47. m_aPos.nSize = 8;
  48. m_aPos.nSegs = m_aPos.SegsUsed();
  49. if ( m_aPos.nSegs )
  50. {
  51. m_aPos.pSegs = (ElemPos**)(new char[m_aPos.nSegs*sizeof(char*)]);
  52. int nSegSize = 1 << m_aPos.PA_SEGBITS;
  53. for ( int nSeg=0; nSeg < m_aPos.nSegs; ++nSeg )
  54. {
  55. if ( nSeg + 1 == m_aPos.nSegs )
  56. nSegSize = m_aPos.GetSize() - (nSeg << m_aPos.PA_SEGBITS);
  57. m_aPos.pSegs[nSeg] = (ElemPos*)(new char[nSegSize*sizeof(ElemPos)]);
  58. memcpy( m_aPos.pSegs[nSeg], markup.m_aPos.pSegs[nSeg], nSegSize*sizeof(ElemPos) );
  59. }
  60. }
  61. // Copy SavedPos map
  62. m_mapSavedPos.RemoveAll();
  63. if ( markup.m_mapSavedPos.pTable )
  64. {
  65. m_mapSavedPos.AllocMapTable();
  66. for ( int nSlot=0; nSlot < SavedPosMap::SPM_SIZE; ++nSlot )
  67. {
  68. SavedPos* pCopySavedPos = markup.m_mapSavedPos.pTable[nSlot];
  69. if ( pCopySavedPos )
  70. {
  71. int nCount = 0;
  72. while ( pCopySavedPos[nCount].nSavedPosFlags & SavedPosMap::SPM_USED )
  73. {
  74. ++nCount;
  75. if ( pCopySavedPos[nCount-1].nSavedPosFlags & SavedPosMap::SPM_LAST )
  76. break;
  77. }
  78. if ( nCount )
  79. {
  80. SavedPos* pNewSavedPos = new SavedPos[nCount];
  81. for ( int nCopy=0; nCopy<nCount; ++nCopy )
  82. pNewSavedPos[nCopy] = pCopySavedPos[nCopy];
  83. pNewSavedPos[nCount-1].nSavedPosFlags |= SavedPosMap::SPM_LAST;
  84. m_mapSavedPos.pTable[nSlot] = pNewSavedPos;
  85. }
  86. }
  87. }
  88. }
  89. MARKUP_SETDEBUGSTATE;
  90. }
  91. bool CMarkup::SetDoc( MCD_PCSZ szDoc )
  92. {
  93. // Set document text
  94. if ( szDoc )
  95. m_strDoc = szDoc;
  96. else
  97. MCD_STRCLEAR(m_strDoc);
  98. MCD_STRCLEAR(m_strError);
  99. return x_ParseDoc();
  100. };
  101. bool CMarkup::SetDoc( const MCD_STR& strDoc )
  102. {
  103. m_strDoc = strDoc;
  104. MCD_STRCLEAR(m_strError);
  105. return x_ParseDoc();
  106. }
  107. bool CMarkup::IsWellFormed()
  108. {
  109. if ( m_aPos.GetSize()
  110. && ! (m_aPos[0].nFlags & MNF_ILLFORMED)
  111. && m_aPos[0].iElemChild
  112. && ! m_aPos[m_aPos[0].iElemChild].iElemNext )
  113. return true;
  114. return false;
  115. }
  116. bool CMarkup::Load( MCD_CSTR szFileName )
  117. {
  118. if ( ! ReadTextFile(szFileName, m_strDoc, &m_strError, &m_nFlags) )
  119. return false;
  120. return x_ParseDoc();
  121. }
  122. bool CMarkup::ReadTextFile( MCD_CSTR szFileName, MCD_STR& strDoc, MCD_STR* pstrError, int* pnFlags )
  123. {
  124. // Static utility method to load text file into strDoc
  125. //
  126. // Open file to read binary
  127. FILE* fp = MCD_FOPEN( szFileName, _T("rb") );
  128. if ( ! fp )
  129. {
  130. if ( pstrError )
  131. *pstrError = x_GetLastError();
  132. return false;
  133. }
  134. // Set flags to 0 unless flags argument provided
  135. int nFlags = pnFlags?*pnFlags:0;
  136. MCD_CHAR szDescBOM[20] = {0};
  137. MCD_CHAR szResult[100];
  138. MCD_STRCLEAR(strDoc);
  139. // Get file length
  140. fseek( fp, 0, SEEK_END );
  141. int nFileByteLen = ftell( fp );
  142. fseek( fp, 0, SEEK_SET );
  143. #if defined(UNICODE) // convert file to wide char
  144. int nWideLen = 0;
  145. if ( nFileByteLen )
  146. {
  147. char* pBuffer = new char[nFileByteLen];
  148. fread( pBuffer, nFileByteLen, 1, fp );
  149. /*
  150. // Alternative: use these 3 lines instead of 3 lines below using UTF8To16
  151. // For ANSI files, replace CP_UTF8 with CP_ACP in both places
  152. nWideLen = MultiByteToWideChar(CP_UTF8,0,pBuffer,nFileByteLen,NULL,0);
  153. MCD_CHAR* pUTF16Buffer = MCD_GETBUFFER(strDoc,nWideLen);
  154. MultiByteToWideChar(CP_UTF8,0,pBuffer,nFileByteLen,pUTF16Buffer,nWideLen);
  155. */
  156. // For ANSI files, replace both UTF8To16 calls with mbstowcs (arguments are the same)
  157. nWideLen = UTF8To16(NULL,pBuffer,nFileByteLen);
  158. MCD_CHAR* pUTF16Buffer = MCD_GETBUFFER(strDoc,nWideLen);
  159. UTF8To16(pUTF16Buffer,pBuffer,nFileByteLen);
  160. MCD_RELEASEBUFFER( strDoc, pUTF16Buffer, nWideLen );
  161. delete [] pBuffer;
  162. }
  163. MCD_SPRINTF( szResult, _T("%s%d bytes to %d wide chars"), szDescBOM, nFileByteLen, nWideLen );
  164. if ( pstrError )
  165. *pstrError = szResult;
  166. #else // read file directly
  167. if ( nFileByteLen )
  168. {
  169. MCD_CHAR* pUTF8Buffer = MCD_GETBUFFER(strDoc,nFileByteLen);
  170. fread( pUTF8Buffer, nFileByteLen, 1, fp );
  171. MCD_RELEASEBUFFER( strDoc, pUTF8Buffer, nFileByteLen );
  172. #if defined(_MBCS) // needs to be in memory as MBCS
  173. MCD_STR strEncoding = GetDeclaredEncoding( strDoc );
  174. if ( MCD_STRISEMPTY(strEncoding) || MCD_PSZNICMP(MCD_2PCSZ(strEncoding),_T("UTF-8"),5)==0 )
  175. strDoc = UTF8ToA( strDoc );
  176. #endif
  177. }
  178. MCD_SPRINTF( szResult, _T("%s%d bytes"), szDescBOM, nFileByteLen );
  179. if ( pstrError )
  180. *pstrError = szResult;
  181. #endif
  182. fclose( fp );
  183. if ( pnFlags )
  184. *pnFlags = nFlags;
  185. return true;
  186. }
  187. bool CMarkup::Save( MCD_CSTR szFileName )
  188. {
  189. return WriteTextFile( szFileName, m_strDoc, &m_strError, &m_nFlags );
  190. }
  191. bool CMarkup::WriteTextFile( MCD_CSTR szFileName, MCD_STR& strDoc, MCD_STR* pstrError, int* pnFlags )
  192. {
  193. // Static utility method to save strDoc to text file
  194. //
  195. // Open file to write binary
  196. bool bSuccess = true;
  197. FILE* fp = MCD_FOPEN( szFileName, _T("wb") );
  198. if ( ! fp )
  199. {
  200. if ( pstrError )
  201. *pstrError = x_GetLastError();
  202. return false;
  203. }
  204. // Set flags to 0 unless flags argument provided
  205. int nFlags = pnFlags?*pnFlags:0;
  206. MCD_CHAR szDescBOM[20] = {0};
  207. MCD_CHAR szResult[100];
  208. // Get document length
  209. int nDocLength = MCD_STRLENGTH(strDoc);
  210. #if defined( UNICODE )
  211. int nMBLen = 0;
  212. if ( nDocLength )
  213. {
  214. /*
  215. // Alternative: use these 3 lines instead of 3 lines below using UTF16To8
  216. // For ANSI files, replace CP_UTF8 with CP_ACP in both places
  217. nMBLen = WideCharToMultiByte(CP_UTF8,0,strDoc,nDocLength,NULL,0,NULL,NULL);
  218. char* pBuffer = new char[nMBLen+1];
  219. WideCharToMultiByte(CP_UTF8,0,strDoc,nDocLength,pBuffer,nMBLen+1,NULL,NULL);
  220. */
  221. // For ANSI files, replace both UTF16To8 calls with wcstombs (arguments are the same)
  222. nMBLen = UTF16To8(NULL,MCD_2PCSZ(strDoc),0);
  223. char* pBuffer = new char[nMBLen+1];
  224. UTF16To8(pBuffer,MCD_2PCSZ(strDoc),nMBLen);
  225. bSuccess = ( fwrite( pBuffer, nMBLen, 1, fp ) == 1 );
  226. delete [] pBuffer;
  227. }
  228. MCD_SPRINTF( szResult, _T("%d wide chars to %s%d bytes"), nDocLength, szDescBOM, nMBLen );
  229. if ( pstrError )
  230. *pstrError = szResult;
  231. #else // MBCS or UTF-8
  232. if ( nDocLength )
  233. {
  234. MCD_STR strDocWrite = strDoc; // reference unless converted
  235. #if defined(_MBCS) // is in memory as MBCS
  236. MCD_STR strEncoding = GetDeclaredEncoding( strDoc );
  237. if ( MCD_STRISEMPTY(strEncoding) || MCD_PSZNICMP(MCD_2PCSZ(strEncoding),_T("UTF-8"),5)==0 )
  238. strDocWrite = AToUTF8( strDoc );
  239. #endif
  240. nDocLength = MCD_STRLENGTH(strDocWrite);
  241. bSuccess = ( fwrite( MCD_2PCSZ(strDocWrite), nDocLength, 1, fp ) == 1 );
  242. }
  243. MCD_SPRINTF( szResult, _T("%s%d bytes"), szDescBOM, nDocLength );
  244. if ( pstrError )
  245. *pstrError = szResult;
  246. #endif
  247. if ( ! bSuccess && pstrError )
  248. *pstrError = x_GetLastError();
  249. fclose(fp);
  250. if ( pnFlags )
  251. *pnFlags = nFlags;
  252. return bSuccess;
  253. }
  254. bool CMarkup::FindElem( MCD_CSTR szName )
  255. {
  256. // Change current position only if found
  257. //
  258. if ( m_aPos.GetSize() )
  259. {
  260. int iPos = x_FindElem( m_iPosParent, m_iPos, szName );
  261. if ( iPos )
  262. {
  263. // Assign new position
  264. x_SetPos( m_aPos[iPos].iElemParent, iPos, 0 );
  265. return true;
  266. }
  267. }
  268. return false;
  269. }
  270. bool CMarkup::FindChildElem( MCD_CSTR szName )
  271. {
  272. // Change current child position only if found
  273. //
  274. // Shorthand: call this with no current main position
  275. // means find child under root element
  276. if ( ! m_iPos )
  277. FindElem();
  278. int iPosChild = x_FindElem( m_iPos, m_iPosChild, szName );
  279. if ( iPosChild )
  280. {
  281. // Assign new position
  282. int iPos = m_aPos[iPosChild].iElemParent;
  283. x_SetPos( m_aPos[iPos].iElemParent, iPos, iPosChild );
  284. return true;
  285. }
  286. return false;
  287. }
  288. MCD_STR CMarkup::EscapeText( MCD_CSTR szText, int nFlags )
  289. {
  290. // Convert text as seen outside XML document to XML friendly
  291. // replacing special characters with ampersand escape codes
  292. // E.g. convert "6>7" to "6&gt;7"
  293. //
  294. // &lt;   less than
  295. // &amp;  ampersand
  296. // &gt;   greater than
  297. //
  298. // and for attributes:
  299. //
  300. // &apos; apostrophe or single quote
  301. // &quot; double quote
  302. //
  303. static MCD_PCSZ szaReplace[] = { _T("&lt;"),_T("&amp;"),_T("&gt;"),_T("&apos;"),_T("&quot;") };
  304. MCD_PCSZ pFind = (nFlags&MNF_ESCAPEQUOTES)?_T("<&>'""):_T("<&>");
  305. MCD_STR strText;
  306. MCD_PCSZ pSource = szText;
  307. int nDestSize = MCD_PSZLEN(pSource);
  308. nDestSize += nDestSize / 10 + 7;
  309. MCD_BLDRESERVE(strText,nDestSize);
  310. MCD_CHAR cSource = *pSource;
  311. MCD_PCSZ pFound;
  312. int nCharLen;
  313. while ( cSource )
  314. {
  315. MCD_BLDCHECK(strText,nDestSize,6);
  316. if ( (pFound=MCD_PSZCHR(pFind,cSource)) != NULL )
  317. {
  318. bool bIgnoreAmpersand = false;
  319. if ( (nFlags&MNF_WITHREFS) && *pFound == _T('&') )
  320. {
  321. // Do not replace ampersand if it is start of any entity reference
  322. // &[#_:A-Za-zU][_:-.A-Za-z0-9U]*; where U is > 0x7f
  323. MCD_PCSZ pCheckEntity = pSource;
  324. ++pCheckEntity;
  325. MCD_CHAR c = *pCheckEntity;
  326. if ( (c>=_T('A')&&c<=_T('Z')) || (c>=_T('a')&&c<=_T('z'))
  327. || c==_T('#') || c==_T('_') || c==_T(':') || ((unsigned int)c)>0x7f )
  328. {
  329. while ( 1 )
  330. {
  331. pCheckEntity += MCD_CLEN( pCheckEntity );
  332. c = *pCheckEntity;
  333. if ( c == _T(';') )
  334. {
  335. int nEntityLen = (int)(pCheckEntity - pSource) + 1;
  336. MCD_BLDAPPENDN(strText,pSource,nEntityLen);
  337. pSource = pCheckEntity;
  338. bIgnoreAmpersand = true;
  339. }
  340. else if ( (c>=_T('A')&&c<=_T('Z')) || (c>=_T('a')&&c<=_T('z')) || (c>=_T('0')&&c<=_T('9'))
  341. || c==_T('_') || c==_T(':') || c==_T('-') || c==_T('.') || ((unsigned int)c)>0x7f )
  342. continue;
  343. break;
  344. }
  345. }
  346. }
  347. if ( ! bIgnoreAmpersand )
  348. {
  349. pFound = szaReplace[pFound-pFind];
  350. MCD_BLDAPPEND(strText,pFound);
  351. }
  352. ++pSource; // ASCII, so 1 byte
  353. }
  354. else
  355. {
  356. nCharLen = MCD_CLEN( pSource );
  357. MCD_BLDAPPENDN(strText,pSource,nCharLen);
  358. pSource += nCharLen;
  359. }
  360. cSource = *pSource;
  361. }
  362. MCD_BLDRELEASE(strText);
  363. return strText;
  364. }
  365. MCD_STR CMarkup::UnescapeText( MCD_CSTR szText, int nTextLength /*=-1*/ )
  366. {
  367. // Convert XML friendly text to text as seen outside XML document
  368. // ampersand escape codes replaced with special characters e.g. convert "6&gt;7" to "6>7"
  369. // ampersand numeric codes replaced with character e.g. convert &#60; to <
  370. // Conveniently the result is always the same or shorter in byte length
  371. //
  372. static MCD_PCSZ szaCode[] = { _T("lt;"),_T("amp;"),_T("gt;"),_T("apos;"),_T("quot;") };
  373. static int anCodeLen[] = { 3,4,3,5,5 };
  374. static MCD_PCSZ szSymbol = _T("<&>'"");
  375. MCD_STR strText;
  376. MCD_PCSZ pSource = szText;
  377. if ( nTextLength == -1 )
  378. nTextLength = MCD_PSZLEN(szText);
  379. MCD_BLDRESERVE(strText,nTextLength);
  380. int nCharLen;
  381. int nChar = 0;
  382. while ( nChar < nTextLength )
  383. {
  384. if ( pSource[nChar] == _T('&') )
  385. {
  386. bool bCodeConverted = false;
  387. // Is it a numeric character reference?
  388. if ( pSource[nChar+1] == _T('#') )
  389. {
  390. // Is it a hex number?
  391. int nBase = 10;
  392. int nNumericChar = nChar + 2;
  393. MCD_CHAR cChar = pSource[nNumericChar];
  394. if ( cChar == _T('x') )
  395. {
  396. ++nNumericChar;
  397. cChar = pSource[nNumericChar];
  398. nBase = 16;
  399. }
  400. // Look for terminating semi-colon within 7 characters
  401. int nCodeLen = 0;
  402. while ( nCodeLen < 7 && cChar && cChar != _T(';') )
  403. {
  404. // only ASCII digits 0-9, A-F, a-f expected
  405. nCodeLen += MCD_CLEN( &pSource[nNumericChar+nCodeLen] );
  406. cChar = pSource[nNumericChar + nCodeLen];
  407. }
  408. // Process unicode
  409. if ( cChar == _T(';') )
  410. {
  411. int nUnicode = MCD_PSZTOL( &pSource[nNumericChar], NULL, nBase );
  412. #if defined(UNICODE)
  413. MCD_BLDAPPEND1(strText,nUnicode);
  414. #elif defined(_MBCS)
  415. MCD_CHAR szANSI[2];
  416. int nMBLen = wctomb( szANSI, (wchar_t)nUnicode );
  417. if ( nMBLen > 0 )
  418. {
  419. MCD_BLDAPPENDN(strText,szANSI,nMBLen);
  420. }
  421. else
  422. nUnicode = 0;
  423. #else
  424. if ( nUnicode < 0x80 )
  425. MCD_BLDAPPEND1(strText,nUnicode);
  426. else if ( nUnicode < 0x800 )
  427. {
  428. // Convert to 2-byte UTF-8
  429. MCD_BLDAPPEND1(strText,((nUnicode&0x7c0)>>6)|0xc0);
  430. MCD_BLDAPPEND1(strText,(nUnicode&0x3f)|0x80);
  431. }
  432. else
  433. {
  434. // Convert to 3-byte UTF-8
  435. MCD_BLDAPPEND1(strText,((nUnicode&0xf000)>>12)|0xe0);
  436. MCD_BLDAPPEND1(strText,((nUnicode&0xfc0)>>6)|0x80);
  437. MCD_BLDAPPEND1(strText,(nUnicode&0x3f)|0x80);
  438. }
  439. #endif
  440. if ( nUnicode )
  441. {
  442. // Increment index past ampersand semi-colon
  443. nChar = nNumericChar + nCodeLen + 1;
  444. bCodeConverted = true;
  445. }
  446. }
  447. }
  448. else // does not start with #
  449. {
  450. // Look for matching &code;
  451. for ( int nMatch = 0; nMatch < 5; ++nMatch )
  452. {
  453. if ( nChar < nTextLength - anCodeLen[nMatch]
  454. && MCD_PSZNCMP(szaCode[nMatch],&pSource[nChar+1],anCodeLen[nMatch]) == 0 )
  455. {
  456. // Insert symbol and increment index past ampersand semi-colon
  457. MCD_BLDAPPEND1(strText,szSymbol[nMatch]);
  458. nChar += anCodeLen[nMatch] + 1;
  459. bCodeConverted = true;
  460. break;
  461. }
  462. }
  463. }
  464. // If the code is not converted, leave it as is
  465. if ( ! bCodeConverted )
  466. {
  467. MCD_BLDAPPEND1(strText,_T('&'));
  468. ++nChar;
  469. }
  470. }
  471. else // not &
  472. {
  473. nCharLen = MCD_CLEN(&pSource[nChar]);
  474. MCD_BLDAPPENDN(strText,&pSource[nChar],nCharLen);
  475. nChar += nCharLen;
  476. }
  477. }
  478. MCD_BLDRELEASE(strText);
  479. return strText;
  480. }
  481. int CMarkup::UTF16To8( char* pszUTF8, const wchar_t* pwszUTF16, int nUTF8Count )
  482. {
  483. // Supports the same arguments as wcstombs
  484. // the pwszUTF16 source must be a NULL-terminated UTF-16 string
  485. // if pszUTF8 is NULL, the number of bytes required is returned and nUTF8Count is ignored
  486. // otherwise pszUTF8 is filled with the result string and NULL-terminated if nUTF8Count allows
  487. // nUTF8Count is the byte size of pszUTF8 and must be large enough for the NULL if NULL desired
  488. // and the number of bytes (excluding NULL) is returned
  489. //
  490. int nUChar, nUTF8Len = 0;
  491. while ( *pwszUTF16 )
  492. {
  493. // Decode UTF-16
  494. nUChar = DecodeCharUTF16( pwszUTF16 );
  495. if ( nUChar == -1 )
  496. nUChar = '?';
  497. // Encode UTF-8
  498. if ( pszUTF8 && nUTF8Len + 4 > nUTF8Count )
  499. {
  500. int nUTF8LenSoFar = nUTF8Len;
  501. EncodeCharUTF8( nUChar, NULL, nUTF8Len );
  502. if ( nUTF8Len > nUTF8Count )
  503. return nUTF8LenSoFar;
  504. nUTF8Len = nUTF8LenSoFar;
  505. }
  506. EncodeCharUTF8( nUChar, pszUTF8, nUTF8Len );
  507. }
  508. if ( pszUTF8 && nUTF8Len < nUTF8Count )
  509. pszUTF8[nUTF8Len] = 0;
  510. return nUTF8Len;
  511. }
  512. int CMarkup::DecodeCharUTF8( const char*& pszUTF8 )
  513. {
  514. // Return Unicode code point and increment pszUTF8 past 1-4 bytes
  515. int nUChar = (unsigned char)*pszUTF8;
  516. ++pszUTF8;
  517. if ( nUChar & 0x80 )
  518. {
  519. int nExtraChars;
  520. if ( ! (nUChar & 0x20) )
  521. {
  522. nExtraChars = 1;
  523. nUChar &= 0x1f;
  524. }
  525. else if ( ! (nUChar & 0x10) )
  526. {
  527. nExtraChars = 2;
  528. nUChar &= 0x0f;
  529. }
  530. else if ( ! (nUChar & 0x08) )
  531. {
  532. nExtraChars = 3;
  533. nUChar &= 0x07;
  534. }
  535. else
  536. return -1;
  537. while ( nExtraChars-- )
  538. {
  539. if ( (*pszUTF8 & 0x80) )
  540. {
  541. nUChar = nUChar<<6;
  542. nUChar |= *pszUTF8 & 0x3f;
  543. }
  544. else
  545. return -1;
  546. ++pszUTF8;
  547. }
  548. }
  549. return nUChar;
  550. }
  551. void CMarkup::EncodeCharUTF16( int nUChar, wchar_t* pwszUTF16, int& nWideLen )
  552. {
  553. // Write UTF-16 sequence to pwszUTF16 for Unicode code point nUChar and update nWideLen
  554. // Be sure pwszUTF16 has room for up to 2 wide chars
  555. //
  556. if ( nUChar & ~0xffff )
  557. {
  558. if ( pwszUTF16 )
  559. {
  560. // Surrogate pair
  561. nUChar -= 0x10000;
  562. pwszUTF16[nWideLen++] = (wchar_t)(((nUChar>>10) & 0x3ff) | 0xd800); // W1
  563. pwszUTF16[nWideLen++] = (wchar_t)((nUChar & 0x3ff) | 0xdc00); // W2
  564. }
  565. else
  566. nWideLen += 2;
  567. }
  568. else
  569. {
  570. if ( pwszUTF16 )
  571. pwszUTF16[nWideLen++] = (wchar_t)nUChar;
  572. else
  573. ++nWideLen;
  574. }
  575. }
  576. int CMarkup::UTF8To16( wchar_t* pwszUTF16, const char* pszUTF8, int nUTF8Count )
  577. {
  578. // Supports the same arguments as mbstowcs
  579. // the pszUTF8 source must be a UTF-8 string which will be processed up to NULL-terminator or nUTF8Count
  580. // if pwszUTF16 is NULL, the number of wide chars required is returned
  581. // nUTF8Count is maximum UTF-8 bytes to convert and should include NULL if NULL desired in result
  582. // if pwszUTF16 is not NULL it is filled with the result string and it must be large enough
  583. // result will be NULL-terminated if NULL encountered in pszUTF8 before nUTF8Count
  584. // and the number of UTF-8 bytes converted is returned
  585. //
  586. const char* pszPosUTF8 = pszUTF8;
  587. int nUChar, nUTF8Len = 0, nWideLen = 0;
  588. while ( nUTF8Len < nUTF8Count )
  589. {
  590. // Decode UTF-8
  591. if ( nUTF8Len + 4 > nUTF8Count )
  592. {
  593. // Pre-examine UTF-8 character using temporary null-terminated copy
  594. // to see if this UTF-8 character boundary is within nUTF8Count
  595. char szUTF8Copy[5];
  596. const char* pszPosUTF8Copy = szUTF8Copy;
  597. int nUTF8EndCount = nUTF8Count - nUTF8Len;
  598. strncpy( szUTF8Copy, pszPosUTF8, nUTF8EndCount );
  599. szUTF8Copy[nUTF8EndCount] = '';
  600. nUChar = DecodeCharUTF8( pszPosUTF8Copy );
  601. int nUTF8EndLen = (int)(pszPosUTF8Copy - szUTF8Copy);
  602. if ( nUTF8Len + nUTF8EndLen > nUTF8Count )
  603. break;
  604. }
  605. nUChar = DecodeCharUTF8( pszPosUTF8 );
  606. nUTF8Len = (int)(pszPosUTF8 - pszUTF8);
  607. if ( ! nUChar )
  608. {
  609. if ( pwszUTF16 )
  610. pwszUTF16[nWideLen] = 0;
  611. break;
  612. }
  613. else if ( nUChar == -1 )
  614. nUChar = '?';
  615. // Encode UTF-16
  616. EncodeCharUTF16( nUChar, pwszUTF16, nWideLen );
  617. }
  618. if ( ! pwszUTF16 )
  619. return nWideLen;
  620. return nUTF8Len;
  621. }
  622. int CMarkup::DecodeCharUTF16( const wchar_t*& pwszUTF16 )
  623. {
  624. // Return Unicode code point and increment pwszUTF16 past 1 or 2 (if surrogrates) wide chars
  625. int nUChar = *pwszUTF16;
  626. if ( (nUChar & ~0x000007ff) == 0xd800 ) // W1
  627. {
  628. ++pwszUTF16;
  629. if ( ! *pwszUTF16 ) // W2
  630. return -1; // incorrect UTF-16
  631. nUChar = (((nUChar & 0x3ff) << 10) | (*pwszUTF16 & 0x3ff)) + 0x10000;
  632. }
  633. ++pwszUTF16;
  634. return nUChar;
  635. }
  636. void CMarkup::EncodeCharUTF8( int nUChar, char* pszUTF8, int& nUTF8Len )
  637. {
  638. // Write UTF-8 sequence to pszUTF8 for Unicode code point nUChar and update nUTF8Len
  639. // Be sure pszUTF8 has room for up to 4 bytes
  640. //
  641. if ( ! (nUChar & ~0x0000007f) ) // < 0x80
  642. {
  643. if ( pszUTF8 )
  644. pszUTF8[nUTF8Len++] = (char)nUChar;
  645. else
  646. ++nUTF8Len;
  647. }
  648. else if ( ! (nUChar & ~0x000007ff) ) // < 0x800
  649. {
  650. if ( pszUTF8 )
  651. {
  652. pszUTF8[nUTF8Len++] = (char)(((nUChar&0x7c0)>>6)|0xc0);
  653. pszUTF8[nUTF8Len++] = (char)((nUChar&0x3f)|0x80);
  654. }
  655. else
  656. nUTF8Len += 2;
  657. }
  658. else if ( ! (nUChar & ~0x0000ffff) ) // < 0x10000
  659. {
  660. if ( pszUTF8 )
  661. {
  662. pszUTF8[nUTF8Len++] = (char)(((nUChar&0xf000)>>12)|0xe0);
  663. pszUTF8[nUTF8Len++] = (char)(((nUChar&0xfc0)>>6)|0x80);
  664. pszUTF8[nUTF8Len++] = (char)((nUChar&0x3f)|0x80);
  665. }
  666. else
  667. nUTF8Len += 3;
  668. }
  669. else // < 0x110000
  670. {
  671. if ( pszUTF8 )
  672. {
  673. pszUTF8[nUTF8Len++] = (char)(((nUChar&0x1c0000)>>18)|0xf0);
  674. pszUTF8[nUTF8Len++] = (char)(((nUChar&0x3f000)>>12)|0x80);
  675. pszUTF8[nUTF8Len++] = (char)(((nUChar&0xfc0)>>6)|0x80);
  676. pszUTF8[nUTF8Len++] = (char)((nUChar&0x3f)|0x80);
  677. }
  678. else
  679. nUTF8Len += 4;
  680. }
  681. }
  682. #if ! defined( UNICODE )
  683. MCD_STR CMarkup::UTF8ToA( MCD_CSTR pszUTF8, int* pnFailed/*=NULL*/ )
  684. {
  685. // Converts from UTF-8 directly to locale ANSI charset
  686. // this uses wctomb which requires setlocale other than minimal "C" locale
  687. // e.g. setlocale(LC_ALL, "") enables the OS system locale settings
  688. MCD_STR strANSI;
  689. int nBufferLen = (int)strlen( pszUTF8 ) + 4;
  690. MCD_BLDRESERVE(strANSI,nBufferLen);
  691. int nUChar, nCharLen;
  692. MCD_CHAR szANSI[2];
  693. if ( pnFailed )
  694. *pnFailed = 0;
  695. MCD_PCSZ pUTF8 = pszUTF8;
  696. while ( *pUTF8 )
  697. {
  698. MCD_BLDCHECK(strANSI,nBufferLen,4); // was grow by (nBufferLen / 2 + 4)
  699. nUChar = DecodeCharUTF8( pUTF8 );
  700. if ( nUChar & ~0xffff )
  701. nCharLen = -1;
  702. else
  703. nCharLen = wctomb( szANSI, (wchar_t)nUChar );
  704. if ( nCharLen == -1 )
  705. {
  706. if ( pnFailed )
  707. ++(*pnFailed);
  708. MCD_BLDAPPEND1(strANSI,_T('?'));
  709. }
  710. else
  711. {
  712. MCD_BLDAPPENDN(strANSI,szANSI,nCharLen);
  713. }
  714. }
  715. MCD_BLDRELEASE(strANSI);
  716. return strANSI;
  717. }
  718. MCD_STR CMarkup::AToUTF8( MCD_CSTR pszANSI )
  719. {
  720. // Converts locale ANSI charset directly to UTF-8
  721. // this uses mbtowc which requires setlocale other than minimal "C" locale
  722. // e.g. setlocale(LC_ALL, "") enables the OS system locale settings
  723. MCD_STR strUTF8;
  724. int nBufferLen = (int)strlen( pszANSI ) * 2 + 4;
  725. MCD_BLDRESERVE(strUTF8,nBufferLen);
  726. int nUChar, nCharLen;
  727. wchar_t wcChar;
  728. MCD_CHAR szUTF8Char[4];
  729. MCD_PCSZ pANSI = pszANSI;
  730. while ( *pANSI )
  731. {
  732. MCD_BLDCHECK(strUTF8,nBufferLen,4);
  733. nCharLen = mbtowc( &wcChar, pANSI, 5 );
  734. if ( nCharLen < 1 )
  735. {
  736. nCharLen = 1;
  737. wcChar = (wchar_t)'?';
  738. }
  739. pANSI += nCharLen;
  740. nUChar = (int)wcChar;
  741. nCharLen = 0;
  742. EncodeCharUTF8( nUChar, szUTF8Char, nCharLen );
  743. MCD_BLDAPPENDN(strUTF8,szUTF8Char,nCharLen);
  744. }
  745. MCD_BLDRELEASE(strUTF8);
  746. return strUTF8;
  747. }
  748. #endif
  749. MCD_STR CMarkup::GetDeclaredEncoding( MCD_CSTR szDoc )
  750. {
  751. // Extract encoding attribute from XML Declaration
  752. MCD_STR strEncoding;
  753. MCD_PCSZ pStart = MCD_PSZCHR( szDoc, _T('<') );
  754. if ( pStart && pStart[1] == _T('?') )
  755. {
  756. MCD_PCSZ pEnd = MCD_PSZSTR( szDoc, _T("?>") );
  757. if ( pEnd )
  758. {
  759. MCD_STR strXMLDecl( pStart, (int)(pEnd-pStart)+2 );
  760. CMarkup xmlDecl( strXMLDecl );
  761. if ( xmlDecl.FindNode() )
  762. strEncoding = xmlDecl.GetAttrib( _T("encoding") );
  763. }
  764. }
  765. return strEncoding;
  766. }
  767. int CMarkup::FindNode( int nType )
  768. {
  769. // Change current node position only if a node is found
  770. // If nType is 0 find any node, otherwise find node of type nType
  771. // Return type of node or 0 if not found
  772. // If found node is an element, change m_iPos
  773. // Determine where in document to start scanning for node
  774. int nTypeFound = 0;
  775. int nNodeOffset = m_nNodeOffset;
  776. if ( m_nNodeType > 1 )
  777. {
  778. // By-pass current node
  779. nNodeOffset += m_nNodeLength;
  780. }
  781. else
  782. {
  783. // Set position to begin looking for node
  784. nNodeOffset = 0; // default to start of document
  785. if ( m_iPos )
  786. {
  787. // After element
  788. nNodeOffset = m_aPos[m_iPos].StartAfter();
  789. }
  790. else if ( m_iPosParent )
  791. {
  792. // Immediately after start tag of parent
  793. if ( m_aPos[m_iPosParent].IsEmptyElement() )
  794. return 0;
  795. else
  796. nNodeOffset = m_aPos[m_iPosParent].StartContent();
  797. }
  798. }
  799. // Get nodes until we find what we're looking for
  800. int iPosNew = m_iPos;
  801. TokenPos token( m_strDoc, m_nFlags );
  802. NodePos node;
  803. token.nNext = nNodeOffset;
  804. do
  805. {
  806. nNodeOffset = token.nNext;
  807. nTypeFound = x_ParseNode( token, node );
  808. if ( nTypeFound == 0 )
  809. {
  810. // Check if we have reached the end of the parent element
  811. // Otherwise it is a lone end tag
  812. if ( m_iPosParent && nNodeOffset == m_aPos[m_iPosParent].StartContent()
  813. + m_aPos[m_iPosParent].ContentLen() )
  814. return 0;
  815. nTypeFound = MNT_LONE_END_TAG;
  816. }
  817. else if ( nTypeFound < 0 )
  818. {
  819. if ( nTypeFound == -2 )
  820. return 0;
  821. // -1 is node error
  822. nTypeFound = MNT_NODE_ERROR;
  823. }
  824. else if ( nTypeFound == MNT_ELEMENT )
  825. {
  826. if ( iPosNew )
  827. iPosNew = m_aPos[iPosNew].iElemNext;
  828. else
  829. iPosNew = m_aPos[m_iPosParent].iElemChild;
  830. if ( ! iPosNew )
  831. return 0;
  832. if ( ! nType || (nType & nTypeFound) )
  833. {
  834. // Found element node, move position to this element
  835. x_SetPos( m_iPosParent, iPosNew, 0 );
  836. return m_nNodeType;
  837. }
  838. token.nNext = m_aPos[iPosNew].StartAfter();
  839. }
  840. }
  841. while ( nType && ! (nType & nTypeFound) );
  842. m_iPos = iPosNew;
  843. m_iPosChild = 0;
  844. m_nNodeOffset = nNodeOffset;
  845. m_nNodeLength = token.nNext - nNodeOffset;
  846. m_nNodeType = nTypeFound;
  847. MARKUP_SETDEBUGSTATE;
  848. return m_nNodeType;
  849. }
  850. bool CMarkup::RemoveNode()
  851. {
  852. if ( m_iPos || m_nNodeLength )
  853. {
  854. x_RemoveNode( m_iPosParent, m_iPos, m_nNodeType, m_nNodeOffset, m_nNodeLength );
  855. m_iPosChild = 0;
  856. MARKUP_SETDEBUGSTATE;
  857. return true;
  858. }
  859. return false;
  860. }
  861. MCD_STR CMarkup::GetTagName() const
  862. {
  863. // Return the tag name at the current main position
  864. MCD_STR strTagName;
  865. // This method is primarily for elements, however
  866. // it does return something for certain other nodes
  867. if ( m_nNodeLength )
  868. {
  869. switch ( m_nNodeType )
  870. {
  871. case MNT_PROCESSING_INSTRUCTION:
  872. case MNT_LONE_END_TAG:
  873. {
  874. // <?target or </tagname
  875. TokenPos token( m_strDoc, m_nFlags );
  876. token.nNext = m_nNodeOffset + 2;
  877. if ( x_FindName(token) )
  878. strTagName = x_GetToken( token );
  879. }
  880. break;
  881. case MNT_COMMENT:
  882. strTagName = _T("#comment");
  883. break;
  884. case MNT_CDATA_SECTION:
  885. strTagName = _T("#cdata-section");
  886. break;
  887. case MNT_DOCUMENT_TYPE:
  888. {
  889. // <!DOCTYPE name
  890. TokenPos token( m_strDoc, m_nFlags );
  891. token.nNext = m_nNodeOffset + 2;
  892. if ( x_FindName(token) && x_FindName(token) )
  893. strTagName = x_GetToken( token );
  894. }
  895. break;
  896. case MNT_TEXT:
  897. case MNT_WHITESPACE:
  898. strTagName = _T("#text");
  899. break;
  900. }
  901. return strTagName;
  902. }
  903. if ( m_iPos )
  904. strTagName = x_GetTagName( m_iPos );
  905. return strTagName;
  906. }
  907. bool CMarkup::IntoElem()
  908. {
  909. // If there is no child position and IntoElem is called it will succeed in release 6.3
  910. // (A subsequent call to FindElem will find the first element)
  911. // The following short-hand behavior was never part of EDOM and was misleading
  912. // It would find a child element if there was no current child element position and go into it
  913. // It is removed in release 6.3, this change is NOT backwards compatible!
  914. // if ( ! m_iPosChild )
  915. // FindChildElem();
  916. if ( m_iPos && m_nNodeType == MNT_ELEMENT )
  917. {
  918. x_SetPos( m_iPos, m_iPosChild, 0 );
  919. return true;
  920. }
  921. return false;
  922. }
  923. bool CMarkup::OutOfElem()
  924. {
  925. // Go to parent element
  926. if ( m_iPosParent )
  927. {
  928. x_SetPos( m_aPos[m_iPosParent].iElemParent, m_iPosParent, m_iPos );
  929. return true;
  930. }
  931. return false;
  932. }
  933. MCD_STR CMarkup::GetAttribName( int n ) const
  934. {
  935. // Return nth attribute name of main position
  936. TokenPos token( m_strDoc, m_nFlags );
  937. if ( m_iPos && m_nNodeType == MNT_ELEMENT )
  938. token.nNext = m_aPos[m_iPos].nStart + 1;
  939. else if ( m_nNodeLength && m_nNodeType == MNT_PROCESSING_INSTRUCTION )
  940. token.nNext = m_nNodeOffset + 2;
  941. else
  942. return _T("");
  943. if ( x_FindAttrib(token,NULL,n) )
  944. return x_GetToken( token );
  945. return _T("");
  946. }
  947. bool CMarkup::SavePos( MCD_CSTR szPosName )
  948. {
  949. // Save current element position in saved position map
  950. if ( szPosName )
  951. {
  952. SavedPos savedpos;
  953. if ( szPosName )
  954. savedpos.strName = szPosName;
  955. if ( m_iPosChild )
  956. {
  957. savedpos.iPos = m_iPosChild;
  958. savedpos.nSavedPosFlags |= SavedPosMap::SPM_CHILD;
  959. }
  960. else if ( m_iPos )
  961. {
  962. savedpos.iPos = m_iPos;
  963. savedpos.nSavedPosFlags |= SavedPosMap::SPM_MAIN;
  964. }
  965. else
  966. {
  967. savedpos.iPos = m_iPosParent;
  968. }
  969. savedpos.nSavedPosFlags |= SavedPosMap::SPM_USED;
  970. if ( ! m_mapSavedPos.pTable )
  971. m_mapSavedPos.AllocMapTable();
  972. int nSlot = m_mapSavedPos.Hash( szPosName );
  973. SavedPos* pSavedPos = m_mapSavedPos.pTable[nSlot];
  974. int nOffset = 0;
  975. if ( ! pSavedPos )
  976. {
  977. pSavedPos = new SavedPos[2];
  978. pSavedPos[1].nSavedPosFlags = SavedPosMap::SPM_LAST;
  979. m_mapSavedPos.pTable[nSlot] = pSavedPos;
  980. }
  981. else
  982. {
  983. while ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_USED )
  984. {
  985. if ( pSavedPos[nOffset].strName == (MCD_PCSZ)szPosName )
  986. break;
  987. if ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_LAST )
  988. {
  989. int nNewSize = (nOffset + 6) * 2;
  990. SavedPos* pNewSavedPos = new SavedPos[nNewSize];
  991. for ( int nCopy=0; nCopy<=nOffset; ++nCopy )
  992. pNewSavedPos[nCopy] = pSavedPos[nCopy];
  993. pNewSavedPos[nOffset].nSavedPosFlags ^= SavedPosMap::SPM_LAST;
  994. pNewSavedPos[nNewSize-1].nSavedPosFlags = SavedPosMap::SPM_LAST;
  995. delete [] pSavedPos;
  996. pSavedPos = pNewSavedPos;
  997. m_mapSavedPos.pTable[nSlot] = pSavedPos;
  998. ++nOffset;
  999. break;
  1000. }
  1001. ++nOffset;
  1002. }
  1003. }
  1004. if ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_LAST )
  1005. savedpos.nSavedPosFlags |= SavedPosMap::SPM_LAST;
  1006. pSavedPos[nOffset] = savedpos;
  1007. /*
  1008. // To review hash table balance, uncomment and watch strBalance
  1009. MCD_STR strBalance, strSlot;
  1010. for ( nSlot=0; nSlot < SavedPosMap::SPM_SIZE; ++nSlot )
  1011. {
  1012. pSavedPos = m_mapSavedPos.pTable[nSlot];
  1013. int nCount = 0;
  1014. while ( pSavedPos && pSavedPos->nSavedPosFlags & SavedPosMap::SPM_USED )
  1015. {
  1016. ++nCount;
  1017. if ( pSavedPos->nSavedPosFlags & SavedPosMap::SPM_LAST )
  1018. break;
  1019. ++pSavedPos;
  1020. }
  1021. strSlot.Format( _T("%d "), nCount );
  1022. strBalance += strSlot;
  1023. }
  1024. */
  1025. return true;
  1026. }
  1027. return false;
  1028. }
  1029. bool CMarkup::RestorePos( MCD_CSTR szPosName )
  1030. {
  1031. // Restore element position if found in saved position map
  1032. if ( szPosName && m_mapSavedPos.pTable )
  1033. {
  1034. int nSlot = m_mapSavedPos.Hash( szPosName );
  1035. SavedPos* pSavedPos = m_mapSavedPos.pTable[nSlot];
  1036. if ( pSavedPos )
  1037. {
  1038. int nOffset = 0;
  1039. while ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_USED )
  1040. {
  1041. if ( pSavedPos[nOffset].strName == (MCD_PCSZ)szPosName )
  1042. {
  1043. int i = pSavedPos[nOffset].iPos;
  1044. if ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_CHILD )
  1045. x_SetPos( m_aPos[m_aPos[i].iElemParent].iElemParent, m_aPos[i].iElemParent, i );
  1046. else if ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_MAIN )
  1047. x_SetPos( m_aPos[i].iElemParent, i, 0 );
  1048. else
  1049. x_SetPos( i, 0, 0 );
  1050. return true;
  1051. }
  1052. if ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_LAST )
  1053. break;
  1054. ++nOffset;
  1055. }
  1056. }
  1057. }
  1058. return false;
  1059. }
  1060. bool CMarkup::RemoveElem()
  1061. {
  1062. // Remove current main position element
  1063. if ( m_iPos && m_nNodeType == MNT_ELEMENT )
  1064. {
  1065. int iPos = x_RemoveElem( m_iPos );
  1066. x_SetPos( m_iPosParent, iPos, 0 );
  1067. return true;
  1068. }
  1069. return false;
  1070. }
  1071. bool CMarkup::RemoveChildElem()
  1072. {
  1073. // Remove current child position element
  1074. if ( m_iPosChild )
  1075. {
  1076. int iPosChild = x_RemoveElem( m_iPosChild );
  1077. x_SetPos( m_iPosParent, m_iPos, iPosChild );
  1078. return true;
  1079. }
  1080. return false;
  1081. }
  1082. //////////////////////////////////////////////////////////////////////
  1083. // Private Methods
  1084. //////////////////////////////////////////////////////////////////////
  1085. MCD_STR CMarkup::x_GetLastError()
  1086. {
  1087. MCD_STR strError;
  1088. #ifdef MCD_STRERROR
  1089. strError = MCD_STRERROR;
  1090. #else
  1091. MCD_CHAR pszError[100];
  1092. if ( ::FormatMessage(0x1200,0,::GetLastError(),0,pszError,100,0) > 0 )
  1093. strError = pszError;
  1094. #endif
  1095. for ( int nChar=0; nChar<MCD_STRLENGTH(strError); ++nChar )
  1096. if ( strError[nChar] == 'r' || strError[nChar] == 'n' )
  1097. {
  1098. strError = MCD_STRMID( strError, 0, nChar ); // no trailing newline
  1099. break;
  1100. }
  1101. return strError;
  1102. }
  1103. bool CMarkup::x_AllocPosArray( int nNewSize /*=0*/ )
  1104. {
  1105. // Resize m_aPos when the document is created or the array is filled
  1106. // The PosArray class is implemented using segments to reduce contiguous memory requirements
  1107. // It reduces reallocations (copying of memory) since this only occurs within one segment
  1108. // The "Grow By" algorithm ensures there are no reallocations after 2 segments
  1109. //
  1110. if ( ! nNewSize )
  1111. nNewSize = m_iPosFree + (m_iPosFree>>1); // Grow By: multiply size by 1.5
  1112. if ( m_aPos.GetSize() < nNewSize )
  1113. {
  1114. // Grow By: new size can be at most one more complete segment
  1115. int nSeg = (m_aPos.GetSize()?m_aPos.GetSize()-1:0) >> m_aPos.PA_SEGBITS;
  1116. int nNewSeg = (nNewSize-1) >> m_aPos.PA_SEGBITS;
  1117. if ( nNewSeg > nSeg + 1 )
  1118. {
  1119. nNewSeg = nSeg + 1;
  1120. nNewSize = (nNewSeg+1) << m_aPos.PA_SEGBITS;
  1121. }
  1122. // Allocate array of segments
  1123. if ( m_aPos.nSegs <= nNewSeg )
  1124. {
  1125. int nNewSegments = 4 + nNewSeg * 2;
  1126. char* pNewSegments = new char[nNewSegments*sizeof(char*)];
  1127. if ( m_aPos.SegsUsed() )
  1128. memcpy( pNewSegments, m_aPos.pSegs, m_aPos.SegsUsed()*sizeof(char*) );
  1129. if ( m_aPos.pSegs )
  1130. delete[] (char*)m_aPos.pSegs;
  1131. m_aPos.pSegs = (ElemPos**)pNewSegments;
  1132. m_aPos.nSegs = nNewSegments;
  1133. }
  1134. // Calculate segment sizes
  1135. int nSegSize = m_aPos.GetSize() - (nSeg << m_aPos.PA_SEGBITS);
  1136. int nNewSegSize = nNewSize - (nNewSeg << m_aPos.PA_SEGBITS);
  1137. // Complete first segment
  1138. int nFullSegSize = 1 << m_aPos.PA_SEGBITS;
  1139. if ( nSeg < nNewSeg && nSegSize < nFullSegSize )
  1140. {
  1141. char* pNewFirstSeg = new char[ nFullSegSize * sizeof(ElemPos) ];
  1142. if ( nSegSize )
  1143. {
  1144. // Reallocate
  1145. memcpy( pNewFirstSeg, m_aPos.pSegs[nSeg], nSegSize * sizeof(ElemPos) );
  1146. delete[] (char*)m_aPos.pSegs[nSeg];
  1147. }
  1148. m_aPos.pSegs[nSeg] = (ElemPos*)pNewFirstSeg;
  1149. }
  1150. // New segment
  1151. char* pNewSeg = new char[ nNewSegSize * sizeof(ElemPos) ];
  1152. if ( nNewSeg == nSeg && nSegSize )
  1153. {
  1154. // Reallocate
  1155. memcpy( pNewSeg, m_aPos.pSegs[nSeg], nSegSize * sizeof(ElemPos) );
  1156. delete[] (char*)m_aPos.pSegs[nSeg];
  1157. }
  1158. m_aPos.pSegs[nNewSeg] = (ElemPos*)pNewSeg;
  1159. m_aPos.nSize = nNewSize;
  1160. }
  1161. return true;
  1162. }
  1163. bool CMarkup::x_ParseDoc()
  1164. {
  1165. // Preserve pre-parse result
  1166. MCD_STR strResult = m_strError;
  1167. // Reset indexes
  1168. ResetPos();
  1169. m_mapSavedPos.RemoveAll();
  1170. // Starting size of position array: 1 element per 64 bytes of document
  1171. // Tight fit when parsing small doc, only 0 to 2 reallocs when parsing large doc
  1172. // Start at 8 when creating new document
  1173. m_iPosFree = 1;
  1174. x_AllocPosArray( MCD_STRLENGTH(m_strDoc) / 64 + 8 );
  1175. m_iPosDeleted = 0;
  1176. // Parse document
  1177. m_aPos[0].ClearVirtualParent();
  1178. if ( MCD_STRLENGTH(m_strDoc) )
  1179. {
  1180. TokenPos token( m_strDoc, m_nFlags );
  1181. int iPos = x_ParseElem( 0, token );
  1182. m_aPos[0].nLength = MCD_STRLENGTH(m_strDoc);
  1183. if ( iPos > 0 )
  1184. {
  1185. m_aPos[0].iElemChild = iPos;
  1186. if ( m_aPos[iPos].iElemNext )
  1187. m_strError = _T("Root element has sibling");
  1188. }
  1189. else
  1190. m_strError = _T("No root element");
  1191. }
  1192. else
  1193. m_strError = _T("Empty document");
  1194. ResetPos();
  1195. // Combine preserved result with parse error
  1196. if ( ! MCD_STRISEMPTY(strResult) )
  1197. {
  1198. if ( MCD_STRISEMPTY(m_strError) )
  1199. m_strError = strResult;
  1200. else
  1201. m_strError = strResult + _T(", ") + m_strError;
  1202. }
  1203. return IsWellFormed();
  1204. };
  1205. int CMarkup::x_ParseElem( int iPosParent, TokenPos& token )
  1206. {
  1207. // This is either called by x_ParseDoc or x_AddSubDoc or x_SetElemContent
  1208. // Returns index of the first element encountered or zero if no elements
  1209. //
  1210. int iElemRoot = 0;
  1211. int iPos = iPosParent;
  1212. int iVirtualParent = iPosParent;
  1213. int nRootDepth = m_aPos[iPos].Level();
  1214. token.nNext = 0;
  1215. MCD_STRCLEAR(m_strError);
  1216. // Loop through the nodes of the document
  1217. NodeStack aNodes;
  1218. aNodes.Add();
  1219. int nDepth = 0;
  1220. int nMatchDepth;
  1221. int iPosChild;
  1222. int iPosMatch;
  1223. int nTypeFound = 0;
  1224. ElemPos* pElem;
  1225. int iElemFirst, iElemLast;
  1226. while ( 1 )
  1227. {
  1228. nTypeFound = x_ParseNode( token, aNodes.Top() );
  1229. nMatchDepth = 0;
  1230. if ( nTypeFound == MNT_ELEMENT ) // start tag
  1231. {
  1232. iPos = x_GetFreePos();
  1233. if ( ! iElemRoot )
  1234. iElemRoot = iPos;
  1235. pElem = &m_aPos[iPos];
  1236. pElem->iElemParent = iPosParent;
  1237. pElem->iElemNext = 0;
  1238. if ( m_aPos[iPosParent].iElemChild )
  1239. {
  1240. iElemFirst = m_aPos[iPosParent].iElemChild;
  1241. iElemLast = m_aPos[iElemFirst].iElemPrev;
  1242. m_aPos[iElemLast].iElemNext = iPos;
  1243. pElem->iElemPrev = iElemLast;
  1244. m_aPos[iElemFirst].iElemPrev = iPos;
  1245. pElem->nFlags = 0;
  1246. }
  1247. else
  1248. {
  1249. m_aPos[iPosParent].iElemChild = iPos;
  1250. pElem->iElemPrev = iPos;
  1251. pElem->nFlags = MNF_FIRST;
  1252. }
  1253. pElem->SetLevel( nRootDepth + nDepth );
  1254. pElem->iElemChild = 0;
  1255. pElem->nStart = aNodes.Top().nStart;
  1256. pElem->SetStartTagLen( aNodes.Top().nLength );
  1257. if ( aNodes.Top().nFlags & MNF_EMPTY )
  1258. {
  1259. iPos = iPosParent;
  1260. pElem->SetEndTagLen( 0 );
  1261. pElem->nLength = aNodes.Top().nLength;
  1262. }
  1263. else
  1264. {
  1265. iPosParent = iPos;
  1266. ++nDepth;
  1267. aNodes.Add();
  1268. }
  1269. }
  1270. else if ( nTypeFound == 0 ) // end tag
  1271. {
  1272. nMatchDepth = nDepth;
  1273. iPosMatch = iPos;
  1274. while ( nMatchDepth && ! token.Match(aNodes.At(nMatchDepth-1).strMeta) )
  1275. {
  1276. /*
  1277. // Auto-switch case sensitivity
  1278. if ( ! (token.nTokenFlags & MDF_IGNORECASE ) )
  1279. {
  1280. token.nTokenFlags |= MDF_IGNORECASE;
  1281. if ( token.Match(aNodes.At(nMatchDepth-1).strMeta) )
  1282. break;
  1283. token.nTokenFlags |= MDF_IGNORECASE;
  1284. }
  1285. */
  1286. --nMatchDepth;
  1287. iPosMatch = m_aPos[iPosMatch].iElemParent;
  1288. }
  1289. if ( nMatchDepth == 0 )
  1290. {
  1291. // Not matched at all, it is a lone end tag, a non-element node
  1292. m_aPos[iVirtualParent].nFlags |= MNF_ILLFORMED;
  1293. m_aPos[iPos].nFlags |= MNF_ILLDATA;
  1294. if ( MCD_STRISEMPTY(m_strError) )
  1295. {
  1296. MCD_CHAR* szError = new MCD_CHAR[token.Length()+100];
  1297. MCD_SPRINTF( szError, _T("No start tag for end tag '%s' at offset %d"),
  1298. MCD_2PCSZ(x_GetToken(token)), aNodes.Top().nStart );
  1299. m_strError = szError;
  1300. delete [] szError;
  1301. }
  1302. }
  1303. else
  1304. {
  1305. pElem = &m_aPos[iPosMatch];
  1306. pElem->nLength = aNodes.Top().nStart - pElem->nStart + aNodes.Top().nLength;
  1307. pElem->SetEndTagLen( aNodes.Top().nLength );
  1308. }
  1309. }
  1310. else if ( nTypeFound == -1 )
  1311. {
  1312. m_aPos[iVirtualParent].nFlags |= MNF_ILLFORMED;
  1313. m_aPos[iPos].nFlags |= MNF_ILLDATA;
  1314. if ( MCD_STRISEMPTY(m_strError) )
  1315. m_strError = aNodes.Top().strMeta;
  1316. }
  1317. // Matched end tag, or end of document
  1318. if ( nMatchDepth || nTypeFound == -2 )
  1319. {
  1320. if ( nDepth > nMatchDepth )
  1321. m_aPos[iVirtualParent].nFlags |= MNF_ILLFORMED;
  1322. // Process any non-ended elements
  1323. while ( nDepth > nMatchDepth )
  1324. {
  1325. // Element with no end tag
  1326. pElem = &m_aPos[iPos];
  1327. iPosChild = pElem->iElemChild;
  1328. iPosParent = pElem->iElemParent;
  1329. pElem->SetEndTagLen( 0 );
  1330. pElem->nFlags |= MNF_NONENDED;
  1331. pElem->iElemChild = 0;
  1332. pElem->nLength = pElem->StartTagLen();
  1333. if ( pElem->nFlags & MNF_ILLDATA )
  1334. {
  1335. pElem->nFlags ^= MNF_ILLDATA;
  1336. m_aPos[iPosParent].nFlags |= MNF_ILLDATA;
  1337. }
  1338. while ( iPosChild )
  1339. {
  1340. m_aPos[iPosChild].iElemParent = iPosParent;
  1341. m_aPos[iPosChild].iElemPrev = iPos;
  1342. m_aPos[iPos].iElemNext = iPosChild;
  1343. iPos = iPosChild;
  1344. iPosChild = m_aPos[iPosChild].iElemNext;
  1345. }
  1346. iPos = iPosParent;
  1347. aNodes.Remove();
  1348. --nDepth;
  1349. // Error string
  1350. // if end tag did not match, top node is end tag that did not match pElem
  1351. // if end of document, any nodes below top have no end tag
  1352. if ( MCD_STRISEMPTY(m_strError) )
  1353. {
  1354. if ( nTypeFound == 0 )
  1355. {
  1356. MCD_CHAR* szError = new MCD_CHAR[MCD_STRLENGTH(aNodes.Top().strMeta)+token.Length()+100];
  1357. MCD_SPRINTF( szError, _T("End tag '%s' at offset %d does not match start tag '%s' at offset %d"),
  1358. MCD_2PCSZ(x_GetToken(token)), token.nL-1, MCD_2PCSZ(aNodes.Top().strMeta), pElem->nStart );
  1359. m_strError = szError;
  1360. delete [] szError;
  1361. }
  1362. else
  1363. {
  1364. MCD_CHAR* szError = new MCD_CHAR[MCD_STRLENGTH(aNodes.Top().strMeta)+100];
  1365. MCD_SPRINTF( szError, _T("Element '%s' at offset %d not ended"),
  1366. MCD_2PCSZ(aNodes.Top().strMeta), aNodes.Top().nStart );
  1367. m_strError = szError;
  1368. delete [] szError;
  1369. }
  1370. }
  1371. }
  1372. if ( nTypeFound == -2 )
  1373. break;
  1374. iPosParent = m_aPos[iPos].iElemParent;
  1375. iPos = iPosParent;
  1376. aNodes.Remove();
  1377. --nDepth;
  1378. }
  1379. }
  1380. return iElemRoot;
  1381. }
  1382. bool CMarkup::x_FindAny( MCD_PCSZ szDoc, int& nChar )
  1383. {
  1384. // Starting at nChar, find a non-whitespace char
  1385. // return false if no non-whitespace before end of document, nChar points to end
  1386. // otherwise return true and nChar points to non-whitespace char
  1387. while ( szDoc[nChar] && MCD_PSZCHR(_T(" tnr"),szDoc[nChar]) )
  1388. ++nChar;
  1389. return szDoc[nChar] != _T('');
  1390. }
  1391. bool CMarkup::x_FindName( CMarkup::TokenPos& token )
  1392. {
  1393. // Starting at token.nNext, bypass whitespace and find the next name
  1394. // returns true on success, members of token point to token
  1395. // returns false on end of document, members point to end of document
  1396. MCD_PCSZ szDoc = token.szDoc;
  1397. int nChar = token.nNext;
  1398. // By-pass leading whitespace
  1399. if ( ! x_FindAny(szDoc,nChar) )
  1400. {
  1401. // No token was found before end of document
  1402. token.nL = nChar;
  1403. token.nR = nChar - 1;
  1404. token.nNext = nChar;
  1405. return false;
  1406. }
  1407. // Go until special char or whitespace
  1408. token.nL = nChar;
  1409. while ( szDoc[nChar] && ! MCD_PSZCHR(_T(" tnr<>=\/?!"),szDoc[nChar]) )
  1410. nChar += MCD_CLEN(&szDoc[nChar]);
  1411. // Adjust end position if it is one special char
  1412. if ( nChar == token.nL )
  1413. ++nChar; // it is a special char
  1414. token.nR = nChar - 1;
  1415. // nNext points to one past last char of token
  1416. token.nNext = nChar;
  1417. return true;
  1418. }
  1419. MCD_STR CMarkup::x_GetToken( const CMarkup::TokenPos& token )
  1420. {
  1421. // The token contains indexes into the document identifying a small substring
  1422. // Build the substring from those indexes and return it
  1423. if ( token.nL > token.nR )
  1424. return _T("");
  1425. MCD_STR strToken( &token.szDoc[token.nL], token.Length() );
  1426. return strToken;
  1427. }
  1428. int CMarkup::x_FindElem( int iPosParent, int iPos, MCD_PCSZ szPath ) const
  1429. {
  1430. // If szPath is NULL or empty, go to next sibling element
  1431. // Otherwise go to next sibling element with matching path
  1432. //
  1433. if ( iPos )
  1434. iPos = m_aPos[iPos].iElemNext;
  1435. else
  1436. iPos = m_aPos[iPosParent].iElemChild;
  1437. // Finished here if szPath not specified
  1438. if ( szPath == NULL || !szPath[0] )
  1439. return iPos;
  1440. // Search
  1441. TokenPos token( m_strDoc, m_nFlags );
  1442. while ( iPos )
  1443. {
  1444. // Compare tag name
  1445. token.nNext = m_aPos[iPos].nStart + 1;
  1446. x_FindName( token ); // Locate tag name
  1447. if ( token.Match(szPath) )
  1448. return iPos;
  1449. iPos = m_aPos[iPos].iElemNext;
  1450. }
  1451. return 0;
  1452. }
  1453. int CMarkup::x_ParseNode( CMarkup::TokenPos& token, CMarkup::NodePos& node )
  1454. {
  1455. // Call this with token.nNext set to the start of the node or tag
  1456. // Upon return token.nNext points to the char after the node or tag
  1457. // 
  1458. // <!--...--> comment
  1459. // <!DOCTYPE ...> dtd
  1460. // <?target ...?> processing instruction
  1461. // <![CDATA[...]]> cdata section
  1462. // <NAME ...> element start tag
  1463. // </NAME ...> element end tag
  1464. //
  1465. // returns the nodetype or
  1466. // 0 for end tag
  1467. // -1 for bad node
  1468. // -2 for end of document
  1469. //
  1470. enum ParseBits
  1471. {
  1472. PD_OPENTAG = 1,
  1473. PD_BANG = 2,
  1474. PD_DASH = 4,
  1475. PD_BRACKET = 8,
  1476. PD_TEXTORWS = 16,
  1477. PD_DOCTYPE = 32,
  1478. PD_INQUOTE_S = 64,
  1479. PD_INQUOTE_D = 128,
  1480. PD_EQUALS = 256,
  1481. };
  1482. int nParseFlags = 0;
  1483. MCD_PCSZ szFindEnd = NULL;
  1484. int nNodeType = -1;
  1485. int nEndLen = 0;
  1486. int nName = 0;
  1487. unsigned int cDminus1 = 0, cDminus2 = 0;
  1488. #define FINDNODETYPE(e,t,n) { szFindEnd=e; nEndLen=(sizeof(e)-1)/sizeof(MCD_CHAR); nNodeType=t; if(n) nName=(int)(pDoc-token.szDoc)+n-1; }
  1489. #define FINDNODEBAD(e) { szFindEnd=_T(">"); nEndLen=1; MCD_CHAR szE[100]; MCD_SPRINTF(szE,_T("Incorrect %s at offset %d"),e,nR); node.strMeta=szE; nNodeType=-1; }
  1490. node.nStart = token.nNext;
  1491. node.nFlags = 0;
  1492. int nR = token.nNext;
  1493. MCD_PCSZ pDoc = &token.szDoc[nR];
  1494. register unsigned int cD = (unsigned int)*pDoc;
  1495. if ( ! cD )
  1496. {
  1497. node.nLength = 0;
  1498. node.nNodeType = 0;
  1499. return -2; // end of document
  1500. }
  1501. while ( 1 )
  1502. {
  1503. cD = (unsigned int)*pDoc;
  1504. if ( ! cD )
  1505. {
  1506. nR = (int)(pDoc - token.szDoc) - 1;
  1507. if ( nNodeType != MNT_WHITESPACE && nNodeType != MNT_TEXT )
  1508. {
  1509. MCD_PCSZ szType = _T("tag");
  1510. if ( (nParseFlags & PD_DOCTYPE) || nNodeType == MNT_DOCUMENT_TYPE )
  1511. szType = _T("Doctype");
  1512. else if ( nNodeType == MNT_ELEMENT )
  1513. szType = _T("Element tag");
  1514. else if ( nNodeType == 0 )
  1515. szType = _T("Element end tag");
  1516. else if ( nNodeType == MNT_CDATA_SECTION )
  1517. szType = _T("CDATA Section");
  1518. else if ( nNodeType == MNT_PROCESSING_INSTRUCTION )
  1519. szType = _T("Processing instruction");
  1520. else if ( nNodeType == MNT_COMMENT )
  1521. szType = _T("Comment");
  1522. nNodeType = -1;
  1523. MCD_CHAR szError[100];
  1524. MCD_SPRINTF( szError, _T("%s at offset %d unterminated"), szType, node.nStart );
  1525. node.strMeta = szError;
  1526. }
  1527. break;
  1528. }
  1529. if ( nName )
  1530. {
  1531. if ( MCD_PSZCHR(_T(" tnr/>"),(MCD_CHAR)cD) )
  1532. {
  1533. int nNameLen = (int)(pDoc - token.szDoc) - nName;
  1534. if ( nNodeType == 0 )
  1535. {
  1536. token.nL = nName;
  1537. token.nR = nName + nNameLen - 1;
  1538. }
  1539. else
  1540. {
  1541. MCD_STRASSIGN(node.strMeta,&token.szDoc[nName],nNameLen);
  1542. }
  1543. nName = 0;
  1544. cDminus2 = 0;
  1545. cDminus1 = 0;
  1546. }
  1547. else
  1548. {
  1549. pDoc += MCD_CLEN( pDoc );
  1550. continue;
  1551. }
  1552. }
  1553. if ( szFindEnd )
  1554. {
  1555. if ( cD == _T('>') && ! (nParseFlags & (PD_INQUOTE_S|PD_INQUOTE_D)) )
  1556. {
  1557. nR = (int)(pDoc - token.szDoc);
  1558. if ( nEndLen == 1 )
  1559. {
  1560. szFindEnd = NULL;
  1561. if ( nNodeType == MNT_ELEMENT && cDminus1 == _T('/') )
  1562. {
  1563. if ( (! cDminus2) || MCD_PSZCHR(_T(" tnr'""),(MCD_CHAR)cDminus2) )
  1564. node.nFlags |= MNF_EMPTY;
  1565. }
  1566. }
  1567. else if ( nR > nEndLen )
  1568. {
  1569. // Test for end of PI or comment
  1570. MCD_PCSZ pEnd = pDoc - nEndLen + 1;
  1571. MCD_PCSZ pFindEnd = szFindEnd;
  1572. int nLen = nEndLen;
  1573. while ( --nLen && *pEnd++ == *pFindEnd++ );
  1574. if ( nLen == 0 )
  1575. szFindEnd = NULL;
  1576. }
  1577. if ( ! szFindEnd && ! (nParseFlags & PD_DOCTYPE) )
  1578. break;
  1579. }
  1580. else if ( cD == _T('<') && (nNodeType == MNT_TEXT || nNodeType == -1) )
  1581. {
  1582. nR = (int)(pDoc - token.szDoc) - 1;
  1583. break;
  1584. }
  1585. else if ( nNodeType & MNT_ELEMENT )
  1586. {
  1587. if ( (nParseFlags & (PD_INQUOTE_S|PD_INQUOTE_D)) )
  1588. {
  1589. if ( cD == _T('"') && (nParseFlags&PD_INQUOTE_D) )
  1590. nParseFlags ^= PD_INQUOTE_D; // off
  1591. else if ( cD == _T(''') && (nParseFlags&PD_INQUOTE_S) )
  1592. nParseFlags ^= PD_INQUOTE_S; // off
  1593. }
  1594. else // not in quotes
  1595. {
  1596. // Only set INQUOTE status when preceeded by equal sign
  1597. if ( cD == _T('"') && (nParseFlags&PD_EQUALS) )
  1598. nParseFlags ^= PD_INQUOTE_D|PD_EQUALS; // D on, equals off
  1599. else if ( cD == _T(''') && (nParseFlags&PD_EQUALS) )
  1600. nParseFlags ^= PD_INQUOTE_S|PD_EQUALS; // S on, equals off
  1601. else if ( cD == _T('=') && cDminus1 != _T('=') && ! (nParseFlags&PD_EQUALS) )
  1602. nParseFlags ^= PD_EQUALS; // on
  1603. else if ( (nParseFlags&PD_EQUALS) && ! MCD_PSZCHR(_T(" tnr"),(MCD_CHAR)cD) )
  1604. nParseFlags ^= PD_EQUALS; // off
  1605. }
  1606. cDminus2 = cDminus1;
  1607. cDminus1 = cD;
  1608. }
  1609. else if ( nNodeType & MNT_DOCUMENT_TYPE )
  1610. {
  1611. if ( cD == _T('"') && ! (nParseFlags&PD_INQUOTE_S) )
  1612. nParseFlags ^= PD_INQUOTE_D; // toggle
  1613. else if ( cD == _T(''') && ! (nParseFlags&PD_INQUOTE_D) )
  1614. nParseFlags ^= PD_INQUOTE_S; // toggle
  1615. }
  1616. }
  1617. else if ( nParseFlags )
  1618. {
  1619. if ( nParseFlags & PD_TEXTORWS )
  1620. {
  1621. if ( cD == _T('<') )
  1622. {
  1623. nR = (int)(pDoc - token.szDoc) - 1;
  1624. nNodeType = MNT_WHITESPACE;
  1625. break;
  1626. }
  1627. else if ( ! MCD_PSZCHR(_T(" tnr"),(MCD_CHAR)cD) )
  1628. {
  1629. nParseFlags ^= PD_TEXTORWS;
  1630. FINDNODETYPE( _T("<"), MNT_TEXT, 0 )
  1631. }
  1632. }
  1633. else if ( nParseFlags & PD_OPENTAG )
  1634. {
  1635. nParseFlags ^= PD_OPENTAG;
  1636. if ( cD > 0x60 || ( cD > 0x40 && cD < 0x5b ) || cD == 0x5f || cD == 0x3a )
  1637. FINDNODETYPE( _T(">"), MNT_ELEMENT, 1 )
  1638. else if ( cD == _T('/') )
  1639. FINDNODETYPE( _T(">"), 0, 2 )
  1640. else if ( cD == _T('!') )
  1641. nParseFlags |= PD_BANG;
  1642. else if ( cD == _T('?') )
  1643. FINDNODETYPE( _T("?>"), MNT_PROCESSING_INSTRUCTION, 2 )
  1644. else
  1645. FINDNODEBAD( _T("tag name character") )
  1646. }
  1647. else if ( nParseFlags & PD_BANG )
  1648. {
  1649. nParseFlags ^= PD_BANG;
  1650. if ( cD == _T('-') )
  1651. nParseFlags |= PD_DASH;
  1652. else if ( cD == _T('[') && !(nParseFlags & PD_DOCTYPE) )
  1653. nParseFlags |= PD_BRACKET;
  1654. else if ( cD == _T('D') && !(nParseFlags & PD_DOCTYPE) )
  1655. nParseFlags |= PD_DOCTYPE;
  1656. else if ( MCD_PSZCHR(_T("EAN"),(MCD_CHAR)cD) ) // <!ELEMENT ATTLIST ENTITY NOTATION
  1657. FINDNODETYPE( _T(">"), MNT_DOCUMENT_TYPE, 0 )
  1658. else
  1659. FINDNODEBAD( _T("! tag") )
  1660. }
  1661. else if ( nParseFlags & PD_DASH )
  1662. {
  1663. nParseFlags ^= PD_DASH;
  1664. if ( cD == _T('-') )
  1665. FINDNODETYPE( _T("-->"), MNT_COMMENT, 0 )
  1666. else
  1667. FINDNODEBAD( _T("comment tag") )
  1668. }
  1669. else if ( nParseFlags & PD_BRACKET )
  1670. {
  1671. nParseFlags ^= PD_BRACKET;
  1672. if ( cD == _T('C') )
  1673. FINDNODETYPE( _T("]]>"), MNT_CDATA_SECTION, 0 )
  1674. else
  1675. FINDNODEBAD( _T("tag") )
  1676. }
  1677. else if ( nParseFlags & PD_DOCTYPE )
  1678. {
  1679. if ( cD == _T('<') )
  1680. nParseFlags |= PD_OPENTAG;
  1681. else if ( cD == _T('>') )
  1682. {
  1683. nR = (int)(pDoc - token.szDoc);
  1684. nNodeType = MNT_DOCUMENT_TYPE;
  1685. break;
  1686. }
  1687. }
  1688. }
  1689. else if ( cD == _T('<') )
  1690. {
  1691. nParseFlags |= PD_OPENTAG;
  1692. }
  1693. else
  1694. {
  1695. nNodeType = MNT_WHITESPACE;
  1696. if ( MCD_PSZCHR(_T(" tnr"),(MCD_CHAR)cD) )
  1697. nParseFlags |= PD_TEXTORWS;
  1698. else
  1699. FINDNODETYPE( _T("<"), MNT_TEXT, 0 )
  1700. }
  1701. pDoc += MCD_CLEN( pDoc );
  1702. }
  1703. token.nNext = nR + 1;
  1704. node.nLength = token.nNext - node.nStart;
  1705. node.nNodeType = nNodeType;
  1706. return nNodeType;
  1707. }
  1708. MCD_STR CMarkup::x_GetPath( int iPos ) const
  1709. {
  1710. MCD_STR strPath;
  1711. while ( iPos )
  1712. {
  1713. MCD_STR strTagName = x_GetTagName( iPos );
  1714. int iPosParent = m_aPos[iPos].iElemParent;
  1715. int iPosSib = 0;
  1716. int nCount = 0;
  1717. while ( iPosSib != iPos )
  1718. {
  1719. iPosSib = x_FindElem( iPosParent, iPosSib, MCD_2PCSZ(strTagName) );
  1720. ++nCount;
  1721. }
  1722. if ( nCount > 1 )
  1723. {
  1724. MCD_CHAR szPred[25];
  1725. MCD_SPRINTF( szPred, _T("[%d]"), nCount );
  1726. strPath = _T("/") + strTagName + szPred + strPath;
  1727. }
  1728. else
  1729. strPath = _T("/") + strTagName + strPath;
  1730. iPos = iPosParent;
  1731. }
  1732. return strPath;
  1733. }
  1734. MCD_STR CMarkup::x_GetTagName( int iPos ) const
  1735. {
  1736. // Return the tag name at specified element
  1737. TokenPos token( m_strDoc, m_nFlags );
  1738. token.nNext = m_aPos[iPos].nStart + 1;
  1739. if ( ! iPos || ! x_FindName( token ) )
  1740. return _T("");
  1741. // Return substring of document
  1742. return x_GetToken( token );
  1743. }
  1744. bool CMarkup::x_FindAttrib( CMarkup::TokenPos& token, MCD_PCSZ szAttrib, int n/*=0*/ )
  1745. {
  1746. // Return true if found, otherwise false and token.nNext is new insertion point
  1747. // If szAttrib is NULL find attrib n and leave token at attrib name
  1748. // If szAttrib is given, find matching attrib and leave token at value
  1749. // support non-well-formed attributes e.g. href=/advanced_search?hl=en, nowrap
  1750. // token also holds start and length of preceeding whitespace to support remove
  1751. //
  1752. int nPreSpaceStart;
  1753. int nPreSpaceLength;
  1754. int nChar;
  1755. MCD_CHAR cFirstChar;
  1756. MCD_PCSZ szDoc = token.szDoc;
  1757. int nAttrib = -1; // starts at tag name
  1758. int nFoundAttribNameR = 0;
  1759. bool bAfterEqual = false;
  1760. while ( 1 )
  1761. {
  1762. // Starting at token.nNext, bypass whitespace and find the next token
  1763. nChar = token.nNext;
  1764. nPreSpaceStart = nChar;
  1765. if ( ! x_FindAny(szDoc,nChar) )
  1766. break;
  1767. nPreSpaceLength = nChar - nPreSpaceStart;
  1768. // Is it an opening quote?
  1769. cFirstChar = szDoc[nChar];
  1770. if ( cFirstChar == _T('"') || cFirstChar == _T(''') )
  1771. {
  1772. token.nTokenFlags |= MNF_QUOTED;
  1773. // Move past opening quote
  1774. ++nChar;
  1775. token.nL = nChar;
  1776. // Look for closing quote
  1777. while ( szDoc[nChar] && szDoc[nChar] != cFirstChar )
  1778. nChar += MCD_CLEN( &szDoc[nChar] );
  1779. // Set right to before closing quote
  1780. token.nR = nChar - 1;
  1781. // Set nChar past closing quote unless at end of document
  1782. if ( szDoc[nChar] )
  1783. ++nChar;
  1784. }
  1785. else
  1786. {
  1787. token.nTokenFlags &= ~MNF_QUOTED;
  1788. // Go until special char or whitespace
  1789. token.nL = nChar;
  1790. if ( bAfterEqual )
  1791. {
  1792. while ( szDoc[nChar] && ! MCD_PSZCHR(_T(" tnr>"),szDoc[nChar]) )
  1793. nChar += MCD_CLEN( &szDoc[nChar] );
  1794. }
  1795. else
  1796. {
  1797. while ( szDoc[nChar] && ! MCD_PSZCHR(_T("= tnr>/?"),szDoc[nChar]) )
  1798. nChar += MCD_CLEN( &szDoc[nChar] );
  1799. }
  1800. // Adjust end position if it is one special char
  1801. if ( nChar == token.nL )
  1802. ++nChar; // it is a special char
  1803. token.nR = nChar - 1;
  1804. }
  1805. // nNext points to one past last char of token
  1806. token.nNext = nChar;
  1807. if ( ! bAfterEqual && ! (token.nTokenFlags&MNF_QUOTED) )
  1808. {
  1809. // Is it an equal sign?
  1810. MCD_CHAR cChar = szDoc[token.nL];
  1811. if ( cChar == _T('=') )
  1812. {
  1813. bAfterEqual = true;
  1814. continue;
  1815. }
  1816. // Is it the right angle bracket?
  1817. if ( cChar == _T('>') || cChar == _T('/') || cChar == _T('?') )
  1818. {
  1819. token.nNext = nPreSpaceStart;
  1820. break; // attrib not found
  1821. }
  1822. if ( nFoundAttribNameR )
  1823. break;
  1824. // Attribute name
  1825. if ( nAttrib != -1 )
  1826. {
  1827. if ( ! szAttrib )
  1828. {
  1829. if ( nAttrib == n )
  1830. return true; // found by number
  1831. }
  1832. else if ( token.Match(szAttrib) )
  1833. {
  1834. // Matched attrib name, go forward to value
  1835. nFoundAttribNameR = token.nR;
  1836. token.nPreSpaceStart = nPreSpaceStart;
  1837. token.nPreSpaceLength = nPreSpaceLength;
  1838. }
  1839. }
  1840. ++nAttrib;
  1841. }
  1842. else if ( nFoundAttribNameR )
  1843. break;
  1844. bAfterEqual = false;
  1845. }
  1846. if ( nFoundAttribNameR )
  1847. {
  1848. if ( ! bAfterEqual )
  1849. {
  1850. // when attribute has no value the value is the attribute name
  1851. token.nL = token.nPreSpaceStart + token.nPreSpaceLength;
  1852. token.nR = nFoundAttribNameR;
  1853. token.nNext = nFoundAttribNameR + 1;
  1854. }
  1855. return true; // found by name
  1856. }
  1857. return false; // not found
  1858. }
  1859. MCD_STR CMarkup::x_GetAttrib( int iPos, MCD_PCSZ szAttrib ) const
  1860. {
  1861. // Return the value of the attrib
  1862. TokenPos token( m_strDoc, m_nFlags );
  1863. if ( iPos && m_nNodeType == MNT_ELEMENT )
  1864. token.nNext = m_aPos[iPos].nStart + 1;
  1865. else if ( iPos == m_iPos && m_nNodeLength && m_nNodeType == MNT_PROCESSING_INSTRUCTION )
  1866. token.nNext = m_nNodeOffset + 2;
  1867. else
  1868. return _T("");
  1869. if ( szAttrib && x_FindAttrib( token, szAttrib ) )
  1870. return UnescapeText( &token.szDoc[token.nL], token.Length() );
  1871. return _T("");
  1872. }
  1873. bool CMarkup::x_SetAttrib( int iPos, MCD_PCSZ szAttrib, int nValue )
  1874. {
  1875. // Convert integer to string
  1876. MCD_CHAR szVal[25];
  1877. MCD_SPRINTF( szVal, _T("%d"), nValue );
  1878. return x_SetAttrib( iPos, szAttrib, szVal );
  1879. }
  1880. bool CMarkup::x_SetAttrib( int iPos, MCD_PCSZ szAttrib, MCD_PCSZ szValue )
  1881. {
  1882. // Set attribute in iPos element
  1883. TokenPos token( m_strDoc, m_nFlags );
  1884. if ( iPos && m_nNodeType == MNT_ELEMENT )
  1885. token.nNext = m_aPos[iPos].nStart + 1;
  1886. else if ( iPos == m_iPos && m_nNodeLength && m_nNodeType == MNT_PROCESSING_INSTRUCTION )
  1887. token.nNext = m_nNodeOffset + 2;
  1888. else
  1889. return false;
  1890. // Create insertion text depending on whether attribute already exists
  1891. // Decision: for empty value leaving attrib="" instead of removing attrib
  1892. int nReplace = 0;
  1893. int nInsertAt;
  1894. MCD_STR strInsert;
  1895. strInsert += x_ATTRIBQUOTE;
  1896. strInsert += EscapeText( szValue, MNF_ESCAPEQUOTES );
  1897. strInsert += x_ATTRIBQUOTE;
  1898. if ( x_FindAttrib( token, szAttrib ) )
  1899. {
  1900. // Replace value
  1901. nInsertAt = token.nL - ((token.nTokenFlags&MNF_QUOTED)?1:0);
  1902. nReplace = token.Length() + ((token.nTokenFlags&MNF_QUOTED)?2:0);
  1903. }
  1904. else
  1905. {
  1906. // Insert string name value pair
  1907. MCD_STR strFormat;
  1908. strFormat = _T(" ");
  1909. strFormat += szAttrib;
  1910. strFormat += _T("=");
  1911. strFormat += strInsert;
  1912. strInsert = strFormat;
  1913. nInsertAt = token.nNext;
  1914. }
  1915. x_DocChange( nInsertAt, nReplace, strInsert );
  1916. int nAdjust = MCD_STRLENGTH(strInsert) - nReplace;
  1917. if ( m_nNodeType == MNT_PROCESSING_INSTRUCTION )
  1918. {
  1919. x_AdjustForNode( m_iPosParent, m_iPos, nAdjust );
  1920. m_nNodeLength += nAdjust;
  1921. MARKUP_SETDEBUGSTATE;
  1922. return true;
  1923. }
  1924. m_aPos[iPos].AdjustStartTagLen( nAdjust );
  1925. m_aPos[iPos].nLength += nAdjust;
  1926. x_Adjust( iPos, nAdjust );
  1927. MARKUP_SETDEBUGSTATE;
  1928. return true;
  1929. }
  1930. bool CMarkup::x_CreateNode( MCD_STR& strNode, int nNodeType, MCD_PCSZ szText )
  1931. {
  1932. // Set strNode based on nNodeType and szData
  1933. // Return false if szData would jeopardize well-formed document
  1934. //
  1935. switch ( nNodeType )
  1936. {
  1937. case MNT_PROCESSING_INSTRUCTION:
  1938. strNode = _T("<?");
  1939. strNode += szText;
  1940. strNode += _T("?>");
  1941. break;
  1942. case MNT_COMMENT:
  1943. strNode = _T("<!--");
  1944. strNode += szText;
  1945. strNode += _T("-->");
  1946. break;
  1947. case MNT_ELEMENT:
  1948. strNode = _T("<");
  1949. strNode += szText;
  1950. strNode += _T("/>");
  1951. break;
  1952. case MNT_TEXT:
  1953. case MNT_WHITESPACE:
  1954. strNode = EscapeText( szText );
  1955. break;
  1956. case MNT_DOCUMENT_TYPE:
  1957. strNode = szText;
  1958. break;
  1959. case MNT_LONE_END_TAG:
  1960. return false;
  1961. case MNT_CDATA_SECTION:
  1962. if ( MCD_PSZSTR(szText,_T("]]>")) != NULL )
  1963. return false;
  1964. strNode = _T("<![CDATA[");
  1965. strNode += szText;
  1966. strNode += _T("]]>");
  1967. break;
  1968. }
  1969. return true;
  1970. }
  1971. MCD_STR CMarkup::x_EncodeCDATASection( MCD_PCSZ szData )
  1972. {
  1973. // Split CDATA Sections if there are any end delimiters
  1974. MCD_STR strData = _T("<![CDATA[");
  1975. MCD_PCSZ pszNextStart = szData;
  1976. MCD_PCSZ pszEnd = MCD_PSZSTR( szData, _T("]]>") );
  1977. while ( pszEnd )
  1978. {
  1979. strData += MCD_STR( pszNextStart, (int)(pszEnd - pszNextStart) );
  1980. strData += _T("]]]]><![CDATA[>");
  1981. pszNextStart = pszEnd + 3;
  1982. pszEnd = MCD_PSZSTR( pszNextStart, _T("]]>") );
  1983. }
  1984. strData += pszNextStart;
  1985. strData += _T("]]>");
  1986. return strData;
  1987. }
  1988. bool CMarkup::x_SetData( int iPos, int nValue )
  1989. {
  1990. // Convert integer to string
  1991. MCD_CHAR szVal[25];
  1992. MCD_SPRINTF( szVal, _T("%d"), nValue );
  1993. return x_SetData( iPos, szVal, 0 );
  1994. }
  1995. bool CMarkup::x_SetData( int iPos, MCD_PCSZ szData, int nFlags )
  1996. {
  1997. // Set data at specified position
  1998. // if nFlags==1, set content of element to a CDATA Section
  1999. MCD_STR strInsert;
  2000. if ( iPos == m_iPos && m_nNodeLength )
  2001. {
  2002. // Not an element
  2003. if ( ! x_CreateNode(strInsert, m_nNodeType, szData) )
  2004. return false;
  2005. x_DocChange( m_nNodeOffset, m_nNodeLength, strInsert );
  2006. x_AdjustForNode( m_iPosParent, iPos, MCD_STRLENGTH(strInsert) - m_nNodeLength );
  2007. m_nNodeLength = MCD_STRLENGTH(strInsert);
  2008. MARKUP_SETDEBUGSTATE;
  2009. return true;
  2010. }
  2011. // Set data in iPos element
  2012. if ( ! iPos || m_aPos[iPos].iElemChild )
  2013. return false;
  2014. // Build strInsert from szData based on nFlags
  2015. if ( nFlags & MNF_WITHCDATA )
  2016. strInsert = x_EncodeCDATASection( szData );
  2017. else
  2018. strInsert = EscapeText( szData, nFlags );
  2019. // Insert
  2020. NodePos node( MNF_WITHNOLINES|MNF_REPLACE );
  2021. node.strMeta = strInsert;
  2022. int iPosBefore = 0;
  2023. int nReplace = x_InsertNew( iPos, iPosBefore, node );
  2024. int nAdjust = MCD_STRLENGTH(node.strMeta) - nReplace;
  2025. x_Adjust( iPos, nAdjust );
  2026. m_aPos[iPos].nLength += nAdjust;
  2027. if ( m_aPos[iPos].nFlags & MNF_ILLDATA )
  2028. m_aPos[iPos].nFlags &= ~MNF_ILLDATA;
  2029. MARKUP_SETDEBUGSTATE;
  2030. return true;
  2031. }
  2032. MCD_STR CMarkup::x_GetData( int iPos ) const
  2033. {
  2034. if ( iPos == m_iPos && m_nNodeLength )
  2035. {
  2036. if ( m_nNodeType == MNT_COMMENT )
  2037. return MCD_STRMID( m_strDoc, m_nNodeOffset+4, m_nNodeLength-7 );
  2038. else if ( m_nNodeType == MNT_PROCESSING_INSTRUCTION )
  2039. return MCD_STRMID( m_strDoc, m_nNodeOffset+2, m_nNodeLength-4 );
  2040. else if ( m_nNodeType == MNT_CDATA_SECTION )
  2041. return MCD_STRMID( m_strDoc, m_nNodeOffset+9, m_nNodeLength-12 );
  2042. else if ( m_nNodeType == MNT_TEXT )
  2043. return UnescapeText( &(MCD_2PCSZ(m_strDoc))[m_nNodeOffset], m_nNodeLength );
  2044. else if ( m_nNodeType == MNT_LONE_END_TAG )
  2045. return MCD_STRMID( m_strDoc, m_nNodeOffset+2, m_nNodeLength-3 );
  2046. else
  2047. return MCD_STRMID( m_strDoc, m_nNodeOffset, m_nNodeLength );
  2048. }
  2049. // Return a string representing data between start and end tag
  2050. // Return empty string if there are any children elements
  2051. MCD_STR strData;
  2052. if ( ! m_aPos[iPos].iElemChild && ! m_aPos[iPos].IsEmptyElement() )
  2053. {
  2054. // Quick scan for any tags inside content
  2055. int nContentLen = m_aPos[iPos].ContentLen();
  2056. int nStartContent = m_aPos[iPos].StartContent();
  2057. MCD_PCSZ pszContent = &(MCD_2PCSZ(m_strDoc))[nStartContent];
  2058. MCD_PCSZ pszTag = MCD_PSZCHR( pszContent, _T('<') );
  2059. if ( pszTag && ((int)(pszTag-pszContent) < nContentLen) )
  2060. {
  2061. // Concatenate all CDATA Sections and text nodes, ignore other nodes
  2062. TokenPos token( m_strDoc, m_nFlags );
  2063. token.nNext = nStartContent;
  2064. NodePos node;
  2065. while ( token.nNext < nStartContent + nContentLen )
  2066. {
  2067. x_ParseNode( token, node );
  2068. if ( node.nNodeType == MNT_TEXT )
  2069. strData += UnescapeText( &token.szDoc[node.nStart], node.nLength );
  2070. else if ( node.nNodeType == MNT_CDATA_SECTION )
  2071. strData += MCD_STRMID( m_strDoc, node.nStart+9, node.nLength-12 );
  2072. }
  2073. }
  2074. else // no tags
  2075. strData = UnescapeText( &(MCD_2PCSZ(m_strDoc))[nStartContent], nContentLen );
  2076. }
  2077. return strData;
  2078. }
  2079. MCD_STR CMarkup::x_GetElemContent( int iPos ) const
  2080. {
  2081. if ( iPos && m_aPos[iPos].ContentLen() )
  2082. return MCD_STRMID( m_strDoc, m_aPos[iPos].StartContent(), m_aPos[iPos].ContentLen() );
  2083. return _T("");
  2084. }
  2085. bool CMarkup::x_SetElemContent( MCD_PCSZ szContent )
  2086. {
  2087. // Set data in iPos element only
  2088. if ( ! m_iPos )
  2089. return false;
  2090. if ( m_nNodeLength )
  2091. return false; // not an element
  2092. // Unlink all children
  2093. int iPos = m_iPos;
  2094. int iPosChild = m_aPos[iPos].iElemChild;
  2095. bool bHadChild = (iPosChild != 0);
  2096. while ( iPosChild )
  2097. iPosChild = x_ReleaseSubDoc( iPosChild );
  2098. if ( bHadChild )
  2099. x_CheckSavedPos();
  2100. // Parse content
  2101. bool bWellFormed = true;
  2102. TokenPos token( szContent, m_nFlags );
  2103. int iPosVirtual = x_GetFreePos();
  2104. m_aPos[iPosVirtual].ClearVirtualParent();
  2105. m_aPos[iPosVirtual].SetLevel( m_aPos[iPos].Level() + 1 );
  2106. iPosChild = x_ParseElem( iPosVirtual, token );
  2107. if ( m_aPos[iPosVirtual].nFlags & MNF_ILLFORMED )
  2108. bWellFormed = false;
  2109. m_aPos[iPos].nFlags = (m_aPos[iPos].nFlags & ~MNF_ILLDATA) | (m_aPos[iPosVirtual].nFlags & MNF_ILLDATA);
  2110. // Prepare insert and adjust offsets
  2111. NodePos node( MNF_WITHNOLINES|MNF_REPLACE );
  2112. node.strMeta = szContent;
  2113. int iPosBefore = 0;
  2114. int nReplace = x_InsertNew( iPos, iPosBefore, node );
  2115. // Adjust and link in the inserted elements
  2116. x_Adjust( iPosChild, node.nStart );
  2117. m_aPos[iPosChild].nStart += node.nStart;
  2118. m_aPos[iPos].iElemChild = iPosChild;
  2119. while ( iPosChild )
  2120. {
  2121. m_aPos[iPosChild].iElemParent = iPos;
  2122. iPosChild = m_aPos[iPosChild].iElemNext;
  2123. }
  2124. x_ReleasePos( iPosVirtual );
  2125. int nAdjust = MCD_STRLENGTH(node.strMeta) - nReplace;
  2126. x_Adjust( iPos, nAdjust, true );
  2127. m_aPos[iPos].nLength += nAdjust;
  2128. x_SetPos( m_iPosParent, m_iPos, 0 );
  2129. return bWellFormed;
  2130. }
  2131. void CMarkup::x_DocChange( int nLeft, int nReplace, const MCD_STR& strInsert )
  2132. {
  2133. // Insert strInsert int m_strDoc at nLeft replacing nReplace chars
  2134. // When creating a document, reduce reallocs by reserving string space
  2135. // If realloc needed, allow for 1.5 times the new length
  2136. //
  2137. int nDocLength = MCD_STRLENGTH(m_strDoc);
  2138. int nInsLength = MCD_STRLENGTH(strInsert);
  2139. int nNewLength = nInsLength + nDocLength - nReplace;
  2140. int nAllocLen = MCD_STRCAPACITY(m_strDoc);
  2141. #ifdef MCD_STRINSERTREPLACE // (STL)
  2142. if ( nNewLength > nAllocLen )
  2143. MCD_BLDRESERVE( m_strDoc, (nNewLength + nNewLength/2 + 128) );
  2144. MCD_STRINSERTREPLACE( m_strDoc, nLeft, nReplace, strInsert );
  2145. #else // (MFC)
  2146. int nBufferLen = nNewLength;
  2147. if ( nNewLength > nAllocLen )
  2148. nBufferLen += nBufferLen/2 + 128;
  2149. MCD_CHAR* pDoc = MCD_GETBUFFER( m_strDoc, nBufferLen );
  2150. if ( nLeft+nReplace < nDocLength )
  2151. memmove( &pDoc[nLeft+nInsLength], &pDoc[nLeft+nReplace], (nDocLength-nLeft-nReplace)*sizeof(MCD_CHAR) );
  2152. memcpy( &pDoc[nLeft], strInsert, nInsLength*sizeof(MCD_CHAR) );
  2153. MCD_RELEASEBUFFER( m_strDoc, pDoc, nNewLength );
  2154. #endif
  2155. }
  2156. void CMarkup::x_Adjust( int iPos, int nShift, bool bAfterPos /*=false*/ )
  2157. {
  2158. // Loop through affected elements and adjust indexes
  2159. // Algorithm:
  2160. // 1. update children unless bAfterPos
  2161. //    (if no children or bAfterPos is true, length of iPos not affected)
  2162. // 2. update starts of next siblings and their children
  2163. // 3. go up until there is a next sibling of a parent and update starts
  2164. // 4. step 2
  2165. int iPosTop = m_aPos[iPos].iElemParent;
  2166. bool bPosFirst = bAfterPos; // mark as first to skip its children
  2167. // Stop when we've reached the virtual parent (which has no tags)
  2168. while ( m_aPos[iPos].StartTagLen() )
  2169. {
  2170. // Were we at containing parent of affected position?
  2171. bool bPosTop = false;
  2172. if ( iPos == iPosTop )
  2173. {
  2174. // Move iPosTop up one towards root
  2175. iPosTop = m_aPos[iPos].iElemParent;
  2176. bPosTop = true;
  2177. }
  2178. // Traverse to the next update position
  2179. if ( ! bPosTop && ! bPosFirst && m_aPos[iPos].iElemChild )
  2180. {
  2181. // Depth first
  2182. iPos = m_aPos[iPos].iElemChild;
  2183. }
  2184. else if ( m_aPos[iPos].iElemNext )
  2185. {
  2186. iPos = m_aPos[iPos].iElemNext;
  2187. }
  2188. else
  2189. {
  2190. // Look for next sibling of a parent of iPos
  2191. // When going back up, parents have already been done except iPosTop
  2192. while ( 1 )
  2193. {
  2194. iPos = m_aPos[iPos].iElemParent;
  2195. if ( iPos == iPosTop )
  2196. break;
  2197. if ( m_aPos[iPos].iElemNext )
  2198. {
  2199. iPos = m_aPos[iPos].iElemNext;
  2200. break;
  2201. }
  2202. }
  2203. }
  2204. bPosFirst = false;
  2205. // Shift indexes at iPos
  2206. if ( iPos != iPosTop )
  2207. m_aPos[iPos].nStart += nShift;
  2208. else
  2209. m_aPos[iPos].nLength += nShift;
  2210. }
  2211. }
  2212. int CMarkup::x_InsertNew( int iPosParent, int& iPosRel, CMarkup::NodePos& node )
  2213. {
  2214. // Parent empty tag or tags with no content?
  2215. bool bEmptyParentTag = iPosParent && m_aPos[iPosParent].IsEmptyElement();
  2216. bool bNoContentParentTags = iPosParent && ! m_aPos[iPosParent].ContentLen();
  2217. if ( node.nLength )
  2218. {
  2219. // Located at a non-element node
  2220. if ( ! (node.nFlags & MNF_INSERT) )
  2221. node.nStart += node.nLength;
  2222. }
  2223. else if ( iPosRel )
  2224. {
  2225. // Located at an element
  2226. node.nStart = m_aPos[iPosRel].nStart;
  2227. if ( ! (node.nFlags & MNF_INSERT) ) // follow iPosRel
  2228. node.nStart += m_aPos[iPosRel].nLength;
  2229. }
  2230. else if ( bEmptyParentTag )
  2231. {
  2232. // Parent has no separate end tag, so split empty element
  2233. if ( m_aPos[iPosParent].nFlags & MNF_NONENDED )
  2234. node.nStart = m_aPos[iPosParent].StartContent();
  2235. else
  2236. node.nStart = m_aPos[iPosParent].StartContent() - 1;
  2237. }
  2238. else
  2239. {
  2240. if ( node.nFlags & (MNF_INSERT|MNF_REPLACE) )
  2241. node.nStart = m_aPos[iPosParent].StartContent();
  2242. else // before end tag
  2243. node.nStart = m_aPos[iPosParent].StartAfter() - m_aPos[iPosParent].EndTagLen();
  2244. }
  2245. // Go up to start of next node, unless its splitting an empty element
  2246. if ( ! (node.nFlags&(MNF_WITHNOLINES|MNF_REPLACE)) && ! bEmptyParentTag )
  2247. {
  2248. MCD_PCSZ szDoc = MCD_2PCSZ(m_strDoc);
  2249. int nChar = node.nStart;
  2250. if ( ! x_FindAny(szDoc,nChar) || szDoc[nChar] == _T('<') )
  2251. node.nStart = nChar;
  2252. }
  2253. // Is insert relative to element position? (i.e. not other kind of node)
  2254. if ( ! node.nLength )
  2255. {
  2256. // Modify iPosRel to reflect position before
  2257. if ( iPosRel )
  2258. {
  2259. if ( node.nFlags & MNF_INSERT )
  2260. {
  2261. if ( ! (m_aPos[iPosRel].nFlags & MNF_FIRST) )
  2262. iPosRel = m_aPos[iPosRel].iElemPrev;
  2263. else
  2264. iPosRel = 0;
  2265. }
  2266. }
  2267. else if ( ! (node.nFlags & MNF_INSERT) )
  2268. {
  2269. // If parent has a child, add after last child
  2270. if ( m_aPos[iPosParent].iElemChild )
  2271. iPosRel = m_aPos[m_aPos[iPosParent].iElemChild].iElemPrev;
  2272. }
  2273. }
  2274. // Get node length (used only by x_AddNode)
  2275. node.nLength = MCD_STRLENGTH(node.strMeta);
  2276. // Prepare end of lines
  2277. if ( (! (node.nFlags & MNF_WITHNOLINES)) && (bEmptyParentTag || bNoContentParentTags) )
  2278. node.nStart += x_EOLLEN;
  2279. if ( ! (node.nFlags & MNF_WITHNOLINES) )
  2280. node.strMeta += x_EOL;
  2281. // Calculate insert offset and replace length
  2282. int nReplace = 0;
  2283. int nInsertAt = node.nStart;
  2284. if ( bEmptyParentTag )
  2285. {
  2286. MCD_STR strTagName = x_GetTagName( iPosParent );
  2287. MCD_STR strFormat;
  2288. if ( node.nFlags & MNF_WITHNOLINES )
  2289. strFormat = _T(">");
  2290. else
  2291. strFormat = _T(">") x_EOL;
  2292. strFormat += node.strMeta;
  2293. strFormat += _T("</");
  2294. strFormat += strTagName;
  2295. node.strMeta = strFormat;
  2296. if ( m_aPos[iPosParent].nFlags & MNF_NONENDED )
  2297. {
  2298. nInsertAt = m_aPos[iPosParent].StartAfter() - 1;
  2299. nReplace = 0;
  2300. m_aPos[iPosParent].nFlags ^= MNF_NONENDED;
  2301. }
  2302. else
  2303. {
  2304. nInsertAt = m_aPos[iPosParent].StartAfter() - 2;
  2305. nReplace = 1;
  2306. m_aPos[iPosParent].AdjustStartTagLen( -1 );
  2307. }
  2308. m_aPos[iPosParent].SetEndTagLen( 3 + MCD_STRLENGTH(strTagName) );
  2309. }
  2310. else
  2311. {
  2312. if ( node.nFlags & MNF_REPLACE )
  2313. {
  2314. nInsertAt = m_aPos[iPosParent].StartContent();
  2315. nReplace = m_aPos[iPosParent].ContentLen();
  2316. }
  2317. else if ( bNoContentParentTags )
  2318. {
  2319. node.strMeta = x_EOL + node.strMeta;
  2320. nInsertAt = m_aPos[iPosParent].StartContent();
  2321. }
  2322. }
  2323. x_DocChange( nInsertAt, nReplace, node.strMeta );
  2324. return nReplace;
  2325. }
  2326. bool CMarkup::x_AddElem( MCD_PCSZ szName, int nValue, int nFlags )
  2327. {
  2328. // Convert integer to string
  2329. MCD_CHAR szVal[25];
  2330. MCD_SPRINTF( szVal, _T("%d"), nValue );
  2331. return x_AddElem( szName, szVal, nFlags );
  2332. }
  2333. bool CMarkup::x_AddElem( MCD_PCSZ szName, MCD_PCSZ szValue, int nFlags )
  2334. {
  2335. if ( nFlags & MNF_CHILD )
  2336. {
  2337. // Adding a child element under main position
  2338. if ( ! m_iPos )
  2339. return false;
  2340. }
  2341. // Locate where to add element relative to current node
  2342. NodePos node( nFlags );
  2343. int iPosParent, iPosBefore;
  2344. if ( nFlags & MNF_CHILD )
  2345. {
  2346. iPosParent = m_iPos;
  2347. iPosBefore = m_iPosChild;
  2348. }
  2349. else
  2350. {
  2351. iPosParent = m_iPosParent;
  2352. iPosBefore = m_iPos;
  2353. node.nStart = m_nNodeOffset;
  2354. node.nLength = m_nNodeLength;
  2355. }
  2356. // Cannot have data in non-ended element
  2357. if ( (nFlags&MNF_WITHNOEND) && szValue && szValue[0] )
  2358. return false;
  2359. // Allocate ElemPos structure for this element
  2360. int iPos = x_GetFreePos();
  2361. // Create string for insert
  2362. // If no szValue is specified, an empty element is created
  2363. // i.e. either <NAME>value</NAME> or <NAME/>
  2364. //
  2365. ElemPos* pElem = &m_aPos[iPos];
  2366. int nLenName = MCD_PSZLEN(szName);
  2367. if ( ! szValue || ! szValue[0] )
  2368. {
  2369. // <NAME/> empty element
  2370. node.strMeta = _T("<");
  2371. node.strMeta += szName;
  2372. if ( nFlags & MNF_WITHNOEND )
  2373. {
  2374. node.strMeta += _T(">");
  2375. pElem->SetStartTagLen( nLenName + 2 );
  2376. pElem->nLength = nLenName + 2;
  2377. }
  2378. else
  2379. {
  2380. if ( nFlags & MNF_WITHXHTMLSPACE )
  2381. {
  2382. node.strMeta += _T(" />");
  2383. pElem->SetStartTagLen( nLenName + 4 );
  2384. pElem->nLength = nLenName + 4;
  2385. }
  2386. else
  2387. {
  2388. node.strMeta += _T("/>");
  2389. pElem->SetStartTagLen( nLenName + 3 );
  2390. pElem->nLength = nLenName + 3;
  2391. }
  2392. }
  2393. pElem->SetEndTagLen( 0 );
  2394. }
  2395. else
  2396. {
  2397. // <NAME>value</NAME>
  2398. MCD_STR strValue;
  2399. if ( nFlags & MNF_WITHCDATA )
  2400. strValue = x_EncodeCDATASection( szValue );
  2401. else
  2402. strValue = EscapeText( szValue, nFlags );
  2403. int nLenValue = MCD_STRLENGTH(strValue);
  2404. node.strMeta = _T("<");
  2405. node.strMeta += szName;
  2406. node.strMeta += _T(">");
  2407. node.strMeta += strValue;
  2408. node.strMeta += _T("</");
  2409. node.strMeta += szName;
  2410. node.strMeta += _T(">");
  2411. pElem->SetEndTagLen( nLenName + 3 );
  2412. pElem->nLength = nLenName * 2 + nLenValue + 5;
  2413. pElem->SetStartTagLen( nLenName + 2 );
  2414. }
  2415. // Insert
  2416. int nReplace = x_InsertNew( iPosParent, iPosBefore, node );
  2417. pElem->nStart = node.nStart;
  2418. pElem->iElemChild = 0;
  2419. if ( nFlags & MNF_WITHNOEND )
  2420. pElem->nFlags = MNF_NONENDED;
  2421. else
  2422. pElem->nFlags = 0;
  2423. x_LinkElem( iPosParent, iPosBefore, iPos );
  2424. x_Adjust( iPos, MCD_STRLENGTH(node.strMeta) - nReplace );
  2425. if ( nFlags & MNF_CHILD )
  2426. x_SetPos( m_iPosParent, iPosParent, iPos );
  2427. else
  2428. x_SetPos( iPosParent, iPos, 0 );
  2429. return true;
  2430. }
  2431. MCD_STR CMarkup::x_GetSubDoc( int iPos ) const
  2432. {
  2433. if ( iPos )
  2434. {
  2435. int nStart = m_aPos[iPos].nStart;
  2436. int nNext = nStart + m_aPos[iPos].nLength;
  2437. MCD_PCSZ szDoc = MCD_2PCSZ(m_strDoc);
  2438. int nChar = nNext;
  2439. if ( ! x_FindAny(szDoc,nChar) || szDoc[nChar] == _T('<') )
  2440. nNext = nChar;
  2441. return MCD_STRMID( m_strDoc, nStart, nNext - nStart );
  2442. }
  2443. return _T("");
  2444. }
  2445. bool CMarkup::x_AddSubDoc( MCD_PCSZ szSubDoc, int nFlags )
  2446. {
  2447. // Add subdocument, parse, and modify positions of affected elements
  2448. //
  2449. NodePos node( nFlags );
  2450. int iPosParent, iPosBefore;
  2451. if ( nFlags & MNF_CHILD )
  2452. {
  2453. // Add a subdocument under main position, before or after child
  2454. if ( ! m_iPos )
  2455. return false;
  2456. iPosParent = m_iPos;
  2457. iPosBefore = m_iPosChild;
  2458. }
  2459. else
  2460. {
  2461. // Add a subdocument under parent position, before or after main
  2462. iPosParent = m_iPosParent;
  2463. iPosBefore = m_iPos;
  2464. node.nStart = m_nNodeOffset;
  2465. node.nLength = m_nNodeLength;
  2466. }
  2467. // Parse subdocument
  2468. bool bWellFormed = true;
  2469. TokenPos token( szSubDoc, m_nFlags );
  2470. int iPosVirtual = x_GetFreePos();
  2471. m_aPos[iPosVirtual].ClearVirtualParent();
  2472. m_aPos[iPosVirtual].SetLevel( m_aPos[iPosParent].Level() + 1 );
  2473. int iPos = x_ParseElem( iPosVirtual, token );
  2474. if ( (!iPos) || m_aPos[iPosVirtual].nFlags & MNF_ILLFORMED )
  2475. bWellFormed = false;
  2476. if ( m_aPos[iPosVirtual].nFlags & MNF_ILLDATA )
  2477. m_aPos[iPosParent].nFlags |= MNF_ILLDATA;
  2478. // Extract subdocument without leading/trailing nodes
  2479. int nExtractStart = 0;
  2480. int iPosLast = m_aPos[iPos].iElemPrev;
  2481. if ( bWellFormed )
  2482. {
  2483. nExtractStart = m_aPos[iPos].nStart;
  2484. int nExtractLength = m_aPos[iPos].nLength;
  2485. if ( iPos != iPosLast )
  2486. {
  2487. nExtractLength = m_aPos[iPosLast].nStart - nExtractStart + m_aPos[iPosLast].nLength;
  2488. bWellFormed = false; // treat as subdoc here, but return not well-formed
  2489. }
  2490. MCD_STRASSIGN(node.strMeta,&szSubDoc[nExtractStart],nExtractLength);
  2491. }
  2492. else
  2493. {
  2494. node.strMeta = szSubDoc;
  2495. node.nFlags |= MNF_WITHNOLINES;
  2496. }
  2497. // Insert
  2498. int nReplace = x_InsertNew( iPosParent, iPosBefore, node );
  2499. // Adjust and link in the inserted elements
  2500. // iPosVirtual will stop it from affecting rest of document
  2501. int nAdjust = node.nStart - nExtractStart;
  2502. if ( iPos && nAdjust )
  2503. {
  2504. x_Adjust( iPos, nAdjust );
  2505. m_aPos[iPos].nStart += nAdjust;
  2506. }
  2507. int iPosChild = iPos;
  2508. while ( iPosChild )
  2509. {
  2510. int iPosNext = m_aPos[iPosChild].iElemNext;
  2511. x_LinkElem( iPosParent, iPosBefore, iPosChild );
  2512. iPosBefore = iPosChild;
  2513. iPosChild = iPosNext;
  2514. }
  2515. x_ReleasePos( iPosVirtual );
  2516. // Now adjust remainder of document
  2517. x_Adjust( iPosLast, MCD_STRLENGTH(node.strMeta) - nReplace, true );
  2518. // Set position to top element of subdocument
  2519. if ( nFlags & MNF_CHILD )
  2520. x_SetPos( m_iPosParent, iPosParent, iPos );
  2521. else // Main
  2522. x_SetPos( m_iPosParent, iPos, 0 );
  2523. return bWellFormed;
  2524. }
  2525. int CMarkup::x_RemoveElem( int iPos )
  2526. {
  2527. // Remove element and all contained elements
  2528. // Return new position
  2529. //
  2530. if ( ! iPos )
  2531. return 0;
  2532. // Determine whether any whitespace up to next tag
  2533. int nAfterEnd = m_aPos[iPos].StartAfter();
  2534. MCD_PCSZ szDoc = MCD_2PCSZ(m_strDoc);
  2535. int nChar = nAfterEnd;
  2536. if ( ! x_FindAny(szDoc,nChar) || szDoc[nChar] == _T('<') )
  2537. nAfterEnd = nChar;
  2538. // Remove from document, adjust affected indexes, and unlink
  2539. int nLen = nAfterEnd - m_aPos[iPos].nStart;
  2540. x_DocChange( m_aPos[iPos].nStart, nLen, MCD_STR() );
  2541. x_Adjust( iPos, - nLen, true );
  2542. int iPosPrev = x_UnlinkElem( iPos );
  2543. x_CheckSavedPos();
  2544. return iPosPrev;
  2545. }
  2546. void CMarkup::x_LinkElem( int iPosParent, int iPosBefore, int iPos )
  2547. {
  2548. // Link in element, and initialize nFlags, and iElem indexes
  2549. ElemPos* pElem = &m_aPos[iPos];
  2550. pElem->iElemParent = iPosParent;
  2551. if ( iPosBefore )
  2552. {
  2553. // Link in after iPosBefore
  2554. pElem->nFlags &= ~MNF_FIRST;
  2555. pElem->iElemNext = m_aPos[iPosBefore].iElemNext;
  2556. if ( pElem->iElemNext )
  2557. m_aPos[pElem->iElemNext].iElemPrev = iPos;
  2558. else
  2559. m_aPos[m_aPos[iPosParent].iElemChild].iElemPrev = iPos;
  2560. m_aPos[iPosBefore].iElemNext = iPos;
  2561. pElem->iElemPrev = iPosBefore;
  2562. }
  2563. else
  2564. {
  2565. // Link in as first child
  2566. pElem->nFlags |= MNF_FIRST;
  2567. if ( m_aPos[iPosParent].iElemChild )
  2568. {
  2569. pElem->iElemNext = m_aPos[iPosParent].iElemChild;
  2570. pElem->iElemPrev = m_aPos[pElem->iElemNext].iElemPrev;
  2571. m_aPos[pElem->iElemNext].iElemPrev = iPos;
  2572. m_aPos[pElem->iElemNext].nFlags ^= MNF_FIRST;
  2573. }
  2574. else
  2575. {
  2576. pElem->iElemNext = 0;
  2577. pElem->iElemPrev = iPos;
  2578. }
  2579. m_aPos[iPosParent].iElemChild = iPos;
  2580. }
  2581. if ( iPosParent )
  2582. pElem->SetLevel( m_aPos[iPosParent].Level() + 1 );
  2583. }
  2584. int CMarkup::x_UnlinkElem( int iPos )
  2585. {
  2586. // Fix links to remove element and mark as deleted
  2587. // return previous position or zero if none
  2588. ElemPos* pElem = &m_aPos[iPos];
  2589. // Find previous sibling and bypass removed element
  2590. int iPosPrev = 0;
  2591. if ( pElem->nFlags & MNF_FIRST )
  2592. {
  2593. if ( pElem->iElemNext ) // set next as first child
  2594. {
  2595. m_aPos[pElem->iElemParent].iElemChild = pElem->iElemNext;
  2596. m_aPos[pElem->iElemNext].iElemPrev = pElem->iElemPrev;
  2597. m_aPos[pElem->iElemNext].nFlags |= MNF_FIRST;
  2598. }
  2599. else // no children remaining
  2600. m_aPos[pElem->iElemParent].iElemChild = 0;
  2601. }
  2602. else
  2603. {
  2604. iPosPrev = pElem->iElemPrev;
  2605. m_aPos[iPosPrev].iElemNext = pElem->iElemNext;
  2606. if ( pElem->iElemNext )
  2607. m_aPos[pElem->iElemNext].iElemPrev = iPosPrev;
  2608. else
  2609. m_aPos[m_aPos[pElem->iElemParent].iElemChild].iElemPrev = iPosPrev;
  2610. }
  2611. x_ReleaseSubDoc( iPos );
  2612. return iPosPrev;
  2613. }
  2614. int CMarkup::x_ReleasePos( int iPos )
  2615. {
  2616. int iPosNext = m_aPos[iPos].iElemNext;
  2617. m_aPos[iPos].iElemNext = m_iPosDeleted;
  2618. m_aPos[iPos].nFlags = MNF_DELETED;
  2619. m_iPosDeleted = iPos;
  2620. return iPosNext;
  2621. }
  2622. int CMarkup::x_ReleaseSubDoc( int iPos )
  2623. {
  2624. // Mark position structures as deleted by depth first traversal
  2625. // Tricky because iElemNext used in traversal is overwritten for linked list of deleted
  2626. // Return value is what iElemNext was before being overwritten
  2627. //
  2628. int iPosNext = 0, iPosTop = iPos;
  2629. while ( 1 )
  2630. {
  2631. if ( m_aPos[iPos].iElemChild )
  2632. iPos = m_aPos[iPos].iElemChild;
  2633. else
  2634. {
  2635. while ( 1 )
  2636. {
  2637. iPosNext = x_ReleasePos( iPos );
  2638. if ( iPos == iPosTop )
  2639. return iPosNext;
  2640. if ( iPosNext )
  2641. break;
  2642. iPos = m_aPos[iPos].iElemParent;
  2643. }
  2644. iPos = iPosNext;
  2645. }
  2646. }
  2647. return iPosNext;
  2648. }
  2649. void CMarkup::x_CheckSavedPos()
  2650. {
  2651. // Remove any saved positions now pointing to deleted elements
  2652. // Must be done as part of element removal before position reassigned
  2653. if ( m_mapSavedPos.pTable )
  2654. {
  2655. for ( int nSlot = 0; nSlot < SavedPosMap::SPM_SIZE; ++nSlot )
  2656. {
  2657. SavedPos* pSavedPos = m_mapSavedPos.pTable[nSlot];
  2658. if ( pSavedPos )
  2659. {
  2660. int nOffset = 0;
  2661. int nSavedPosCount = 0;
  2662. while ( 1 )
  2663. {
  2664. if ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_USED )
  2665. {
  2666. int iPos = pSavedPos[nOffset].iPos;
  2667. if ( ! (m_aPos[iPos].nFlags & MNF_DELETED) )
  2668. {
  2669. if ( nSavedPosCount < nOffset )
  2670. {
  2671. pSavedPos[nSavedPosCount] = pSavedPos[nOffset];
  2672. pSavedPos[nSavedPosCount].nSavedPosFlags &= ~SavedPosMap::SPM_LAST;
  2673. }
  2674. ++nSavedPosCount;
  2675. }
  2676. }
  2677. if ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_LAST )
  2678. {
  2679. while ( nSavedPosCount <= nOffset )
  2680. pSavedPos[nSavedPosCount++].nSavedPosFlags &= ~SavedPosMap::SPM_USED;
  2681. break;
  2682. }
  2683. ++nOffset;
  2684. }
  2685. }
  2686. }
  2687. }
  2688. }
  2689. void CMarkup::x_AdjustForNode( int iPosParent, int iPos, int nShift )
  2690. {
  2691. // Adjust affected indexes
  2692. bool bAfterPos = true;
  2693. if ( ! iPos )
  2694. {
  2695. // Change happened before or at first element under iPosParent
  2696. // If there are any children of iPosParent, adjust from there
  2697. // otherwise start at parent and adjust from there
  2698. iPos = m_aPos[iPosParent].iElemChild;
  2699. if ( iPos )
  2700. {
  2701. m_aPos[iPos].nStart += nShift;
  2702. bAfterPos = false;
  2703. }
  2704. else
  2705. {
  2706. iPos = iPosParent;
  2707. m_aPos[iPos].nLength += nShift;
  2708. }
  2709. }
  2710. x_Adjust( iPos, nShift, bAfterPos );
  2711. }
  2712. bool CMarkup::x_AddNode( int nNodeType, MCD_PCSZ szText, int nFlags )
  2713. {
  2714. // Only comments, DTDs, and processing instructions are followed by CRLF
  2715. // Other nodes are usually concerned with mixed content, so no CRLF
  2716. if ( ! (nNodeType & (MNT_PROCESSING_INSTRUCTION|MNT_COMMENT|MNT_DOCUMENT_TYPE)) )
  2717. nFlags |= MNF_WITHNOLINES;
  2718. // Add node of nNodeType after current node position
  2719. NodePos node( nFlags );
  2720. if ( ! x_CreateNode(node.strMeta, nNodeType, szText) )
  2721. return false;
  2722. // Locate where to add node relative to current node
  2723. int iPosBefore = m_iPos;
  2724. int iPosParent = m_iPosParent;
  2725. node.nStart = m_nNodeOffset;
  2726. node.nLength = m_nNodeLength;
  2727. node.nNodeType = nNodeType;
  2728. int nReplace = x_InsertNew( iPosParent, iPosBefore, node );
  2729. // If its a new element, create an ElemPos
  2730. int iPos = iPosBefore;
  2731. if ( nNodeType == MNT_ELEMENT )
  2732. {
  2733. // Set indexes
  2734. iPos = x_GetFreePos();
  2735. ElemPos* pElem = &m_aPos[iPos];
  2736. pElem->nStart = node.nStart;
  2737. pElem->SetStartTagLen( node.nLength );
  2738. pElem->SetEndTagLen( 0 );
  2739. pElem->nLength = node.nLength;
  2740. node.nStart = 0;
  2741. node.nLength = 0;
  2742. pElem->iElemChild = 0;
  2743. pElem->nFlags = 0;
  2744. x_LinkElem( iPosParent, iPosBefore, iPos );
  2745. }
  2746. // Need to adjust element positions after iPos
  2747. x_AdjustForNode( iPosParent, iPos, MCD_STRLENGTH(node.strMeta) - nReplace );
  2748. // Set current position
  2749. m_iPos = iPos;
  2750. m_iPosChild = 0;
  2751. m_nNodeOffset = node.nStart;
  2752. m_nNodeLength = node.nLength;
  2753. m_nNodeType = nNodeType;
  2754. MARKUP_SETDEBUGSTATE;
  2755. return true;
  2756. }
  2757. void CMarkup::x_RemoveNode( int iPosParent, int& iPos, int& nNodeType, int& nNodeOffset, int& nNodeLength )
  2758. {
  2759. // Remove node and return new position
  2760. //
  2761. int iPosPrev = iPos;
  2762. // Removing an element?
  2763. if ( nNodeType == MNT_ELEMENT )
  2764. {
  2765. nNodeOffset = m_aPos[iPos].nStart;
  2766. nNodeLength = m_aPos[iPos].nLength;
  2767. iPosPrev = x_UnlinkElem( iPos );
  2768. x_CheckSavedPos();
  2769. }
  2770. // Find previous node type, offset and length
  2771. int nPrevOffset = 0;
  2772. if ( iPosPrev )
  2773. nPrevOffset = m_aPos[iPosPrev].StartAfter();
  2774. else if ( iPosParent )
  2775. nPrevOffset = m_aPos[iPosParent].StartContent();
  2776. TokenPos token( m_strDoc, m_nFlags );
  2777. NodePos node;
  2778. token.nNext = nPrevOffset;
  2779. int nPrevType = 0;
  2780. while ( token.nNext < nNodeOffset )
  2781. {
  2782. nPrevOffset = token.nNext;
  2783. nPrevType = x_ParseNode( token, node );
  2784. }
  2785. int nPrevLength = nNodeOffset - nPrevOffset;
  2786. if ( ! nPrevLength )
  2787. {
  2788. // Previous node is iPosPrev element
  2789. nPrevOffset = 0;
  2790. if ( iPosPrev )
  2791. nPrevType = MNT_ELEMENT;
  2792. }
  2793. // Remove node from document
  2794.   x_DocChange( nNodeOffset, nNodeLength, MCD_STR() );
  2795. x_AdjustForNode( iPosParent, iPosPrev, - nNodeLength );
  2796. // Was removed node a lone end tag?
  2797. if ( nNodeType == MNT_LONE_END_TAG )
  2798. {
  2799. // See if we can unset parent MNF_ILLDATA flag
  2800. token.nNext = m_aPos[iPosParent].StartContent();
  2801. int nEndOfContent = token.nNext + m_aPos[iPosParent].ContentLen();
  2802. int iPosChild = m_aPos[iPosParent].iElemChild;
  2803. while ( token.nNext < nEndOfContent )
  2804. {
  2805. if ( x_ParseNode(token,node) <= 0 )
  2806. break;
  2807. if ( node.nNodeType == MNT_ELEMENT )
  2808. {
  2809. token.nNext = m_aPos[iPosChild].StartAfter();
  2810. iPosChild = m_aPos[iPosChild].iElemNext;
  2811. }
  2812. }
  2813. if ( token.nNext == nEndOfContent )
  2814. m_aPos[iPosParent].nFlags &= ~MNF_ILLDATA;
  2815. }
  2816. nNodeType = nPrevType;
  2817. nNodeOffset = nPrevOffset;
  2818. nNodeLength = nPrevLength;
  2819. iPos = iPosPrev;
  2820. }