苏州姑苏区景点地图:一个强大的XML处理类,老外写的,学习中

来源:百度文库 编辑:偶看新闻 时间:2024/04/28 06:56:11

一个强大的XML处理类,老外写的,学习中

  1. .h文件如下  
  2. // Markup.h: interface for the CMarkup class.  
  3. //  
  4. // Markup Release 11.2  
  5. // Copyright (C) 2009 First Objective Software, Inc. All rights reserved  
  6. // Go to www.firstobject.com for the latest CMarkup and EDOM documentation  
  7. // Use in commercial applications requires written permission  
  8. // This software is provided "as is", with no warranty.  
  9.   
  10. #if !defined(_MARKUP_H_INCLUDED_)  
  11. #define _MARKUP_H_INCLUDED_  
  12.   
  13. #include   
  14. #include  // memcpy, memset, strcmp...  
  15.   
  16. // Major build options  
  17. // MARKUP_WCHAR wide char (2-byte UTF-16 on Windows, 4-byte UTF-32 on Linux and OS X)  
  18. // MARKUP_MBCS ANSI/double-byte strings on Windows  
  19. // MARKUP_STL (default except VC++) use STL strings instead of MFC strings  
  20. // MARKUP_SAFESTR to use string _s functions in VC++ 2005 (_MSC_VER >= 1400)  
  21. // MARKUP_WINCONV (default for VC++) for Windows API character conversion  
  22. // MARKUP_ICONV (default for GNU) for character conversion on Linux and OS X and other platforms  
  23. // MARKUP_STDCONV to use neither WINCONV or ICONV, falls back to setlocale based conversion for ANSI  
  24. //  
  25. #if _MSC_VER > 1000 // VC++  
  26. #pragma once  
  27. #if ! defined(MARKUP_SAFESTR) // not VC++ safe strings  
  28. #pragma warning(disable:4996) // VC++ 2005 deprecated function warnings  
  29. #endif // not VC++ safe strings  
  30. #if defined(MARKUP_STL) && _MSC_VER < 1400 // STL pre VC++ 2005  
  31. #pragma warning(disable:4786) // std::string long names  
  32. #endif // VC++ 2005 STL  
  33. #else // not VC++  
  34. #if ! defined(MARKUP_STL)  
  35. #define MARKUP_STL  
  36. #endif // not STL  
  37. #if defined(__GNUC__) && ! defined(MARKUP_ICONV) && ! defined(MARKUP_STDCONV) && ! defined(MARKUP_WINCONV)  
  38. #define MARKUP_ICONV  
  39. #endif // GNUC and not ICONV not STDCONV not WINCONV  
  40. #endif // not VC++  
  41. #if (defined(_UNICODE) || defined(UNICODE)) && ! defined(MARKUP_WCHAR)  
  42. #define MARKUP_WCHAR  
  43. #endif // _UNICODE or UNICODE  
  44. #if (defined(_MBCS) || defined(MBCS)) && ! defined(MARKUP_MBCS)  
  45. #define MARKUP_MBCS  
  46. #endif // _MBCS and not MBCS  
  47. #if ! defined(MARKUP_SIZEOFWCHAR)  
  48. #if __SIZEOF_WCHAR_T__ == 4 || __WCHAR_MAX__ > 0x10000  
  49. #define MARKUP_SIZEOFWCHAR 4  
  50. #else // sizeof(wchar_t) != 4  
  51. #define MARKUP_SIZEOFWCHAR 2  
  52. #endif // sizeof(wchar_t) != 4  
  53. #endif // not MARKUP_SIZEOFWCHAR  
  54. #if ! defined(MARKUP_WINCONV) && ! defined(MARKUP_STDCONV) && ! defined(MARKUP_ICONV)  
  55. #define MARKUP_WINCONV  
  56. #endif // not WINCONV not STDCONV not ICONV  
  57. #if ! defined(MARKUP_FILEBLOCKSIZE)  
  58. #define MARKUP_FILEBLOCKSIZE 16384  
  59. #endif  
  60.   
  61. // Text type and function defines (compiler and build-option dependent)  
  62. //   
  63. #define MCD_ACP 0  
  64. #define MCD_UTF8 65001  
  65. #define MCD_UTF16 1200  
  66. #define MCD_UTF32 65005  
  67. #if defined(MARKUP_WCHAR)  
  68. #define MCD_CHAR wchar_t  
  69. #define MCD_PCSZ const wchar_t*  
  70. #define MCD_PSZLEN (int)wcslen  
  71. #define MCD_PSZCHR wcschr  
  72. #define MCD_PSZSTR wcsstr  
  73. #define MCD_PSZTOL wcstol  
  74. #define MCD_PSZNCMP wcsncmp  
  75. #if defined(MARKUP_SAFESTR) // VC++ safe strings  
  76. #define MCD_SSZ(sz) sz,(sizeof(sz)/sizeof(MCD_CHAR))  
  77. #define MCD_PSZCPY(sz,p) wcscpy_s(MCD_SSZ(sz),p)  
  78. #define MCD_PSZNCPY(sz,p,n) wcsncpy_s(MCD_SSZ(sz),p,n)  
  79. #define MCD_PSZCAT(sz,p) wcscat_s(MCD_SSZ(sz),p)  
  80. #define MCD_SPRINTF swprintf_s  
  81. #define MCD_FOPEN(f,n,m) {if(_wfopen_s(&f,n,m)!=0)f=NULL;}  
  82. #else // not VC++ safe strings  
  83. #if defined(__GNUC__)  
  84. #define MCD_SSZ(sz) sz,(sizeof(sz)/sizeof(MCD_CHAR))  
  85. #else // not GNUC  
  86. #define MCD_SSZ(sz) sz  
  87. #endif // not GNUC  
  88. #define MCD_PSZCPY wcscpy  
  89. #define MCD_PSZNCPY wcsncpy  
  90. #define MCD_PSZCAT wcscat  
  91. #define MCD_SPRINTF swprintf  
  92. #define MCD_FOPEN(f,n,m) f=_wfopen(n,m)  
  93. #endif // not VC++ safe strings  
  94. #define MCD_T(s) L ## s  
  95. #if MARKUP_SIZEOFWCHAR == 4 // sizeof(wchar_t) == 4  
  96. #define MCD_ENC MCD_T("UTF-32")  
  97. #else // sizeof(wchar_t) == 2  
  98. #define MCD_ENC MCD_T("UTF-16")  
  99. #endif  
  100. #define MCD_CLEN(p) 1  
  101. #else // not MARKUP_WCHAR  
  102. #define MCD_CHAR char  
  103. #define MCD_PCSZ const char*  
  104. #define MCD_PSZLEN (int)strlen  
  105. #define MCD_PSZCHR strchr  
  106. #define MCD_PSZSTR strstr  
  107. #define MCD_PSZTOL strtol  
  108. #define MCD_PSZNCMP strncmp  
  109. #if defined(MARKUP_SAFESTR) // VC++ safe strings  
  110. #define MCD_SSZ(sz) sz,(sizeof(sz)/sizeof(MCD_CHAR))  
  111. #define MCD_PSZCPY(sz,p) strcpy_s(MCD_SSZ(sz),p)  
  112. #define MCD_PSZNCPY(sz,p,n) strncpy_s(MCD_SSZ(sz),p,n)  
  113. #define MCD_PSZCAT(sz,p) strcat_s(MCD_SSZ(sz),p)  
  114. #define MCD_SPRINTF sprintf_s  
  115. #define MCD_FOPEN(f,n,m) {if(fopen_s(&f,n,m)!=0)f=NULL;}  
  116. #else // not VC++ safe strings  
  117. #define MCD_SSZ(sz) sz  
  118. #define MCD_PSZCPY strcpy  
  119. #define MCD_PSZNCPY strncpy  
  120. #define MCD_PSZCAT strcat  
  121. #define MCD_SPRINTF sprintf  
  122. #define MCD_FOPEN(f,n,m) f=fopen(n,m)  
  123. #endif // not VC++ safe strings  
  124. #define MCD_T(s) s  
  125. #if defined(MARKUP_MBCS) // MBCS/double byte  
  126. #define MCD_ENC MCD_T("")  
  127. #if defined(MARKUP_WINCONV)  
  128. #define MCD_CLEN(p) (int)_mbclen((const unsigned char*)p)  
  129. #else // not WINCONV  
  130. #define MCD_CLEN(p) (int)mblen(p,MB_CUR_MAX)  
  131. #endif // not WINCONV  
  132. #else // not MBCS/double byte  
  133. #define MCD_ENC MCD_T("UTF-8")  
  134. #define MCD_CLEN(p) 1  
  135. #endif // not MBCS/double byte  
  136. #endif // not MARKUP_WCHAR  
  137. #if _MSC_VER < 1000 // not VC++  
  138. #define MCD_STRERROR strerror(errno)  
  139. #endif // not VC++  
  140.   
  141. // String type and function defines (compiler and build-option dependent)  
  142. // Define MARKUP_STL to use STL strings  
  143. //  
  144. #if defined(MARKUP_STL) // STL  
  145. #include   
  146. #if defined(MARKUP_WCHAR)  
  147. #define MCD_STR std::wstring  
  148. #else // not MARKUP_WCHAR  
  149. #define MCD_STR std::string  
  150. #endif // not MARKUP_WCHAR  
  151. #define MCD_2PCSZ(s) s.c_str()  
  152. #define MCD_STRLENGTH(s) (int)s.size()  
  153. #define MCD_STRCLEAR(s) s.erase()  
  154. #define MCD_STRCLEARSIZE(s) MCD_STR t; s.swap(t)  
  155. #define MCD_STRISEMPTY(s) s.empty()  
  156. #define MCD_STRMID(s,n,l) s.substr(n,l)  
  157. #define MCD_STRASSIGN(s,p,n) s.assign(p,n)  
  158. #define MCD_STRCAPACITY(s) (int)s.capacity()  
  159. #define MCD_STRINSERTREPLACE(d,i,r,s) d.replace(i,r,s)  
  160. #define MCD_GETBUFFER(s,n) new MCD_CHAR[n+1]; s.reserve(n)  
  161. #define MCD_RELEASEBUFFER(s,p,n) s.assign(p,n); delete[]p  
  162. #define MCD_BLDRESERVE(s,n) s.reserve(n)  
  163. #define MCD_BLDCHECK(s,n,d) ;  
  164. #define MCD_BLDRELEASE(s) ;  
  165. #define MCD_BLDAPPENDN(s,p,n) s.append(p,n)  
  166. #define MCD_BLDAPPEND(s,p) s.append(p)  
  167. #define MCD_BLDAPPEND1(s,c) s+=(MCD_CHAR)(c)  
  168. #else // not STL, i.e. MFC  
  169. #include   
  170. #define MCD_STR CString  
  171. #define MCD_2PCSZ(s) ((MCD_PCSZ)s)  
  172. #define MCD_STRLENGTH(s) s.GetLength()  
  173. #define MCD_STRCLEAR(s) s.Empty()  
  174. #define MCD_STRCLEARSIZE(s) s=MCD_STR()  
  175. #define MCD_STRISEMPTY(s) s.IsEmpty()  
  176. #define MCD_STRMID(s,n,l) s.Mid(n,l)  
  177. #define MCD_STRASSIGN(s,p,n) memcpy(s.GetBuffer(n),p,(n)*sizeof(MCD_CHAR));s.ReleaseBuffer(n);  
  178. #define MCD_STRCAPACITY(s) (((CStringData*)((MCD_PCSZ)s)-1)->nAllocLength)  
  179. #define MCD_GETBUFFER(s,n) s.GetBuffer(n)  
  180. #define MCD_RELEASEBUFFER(s,p,n) s.ReleaseBuffer(n)  
  181. #define MCD_BLDRESERVE(s,n) MCD_CHAR*pD=s.GetBuffer(n); int nL=0  
  182. #define MCD_BLDCHECK(s,n,d) if(nL+(int)(d)>n){s.ReleaseBuffer(nL);n<<=2;pD=s.GetBuffer(n);}  
  183. #define MCD_BLDRELEASE(s) s.ReleaseBuffer(nL)  
  184. #define MCD_BLDAPPENDN(s,p,n) MCD_PSZNCPY(&pD[nL],p,n);nL+=n  
  185. #define MCD_BLDAPPEND(s,p) MCD_PSZCPY(&pD[nL],p);nL+=MCD_PSZLEN(p)  
  186. #define MCD_BLDAPPEND1(s,c) pD[nL++]=(MCD_CHAR)(c)  
  187. #endif // not STL  
  188. #define MCD_STRTOINT(s) MCD_PSZTOL(MCD_2PCSZ(s),NULL,10)  
  189.   
  190. // Allow function args to accept string objects as constant string pointers  
  191. struct MCD_CSTR  
  192. {  
  193.     MCD_CSTR() { pcsz=NULL; };  
  194.     MCD_CSTR( MCD_PCSZ p ) { pcsz=p; };  
  195.     MCD_CSTR( const MCD_STR& s ) { pcsz = MCD_2PCSZ(s); };  
  196.     operator MCD_PCSZ() const { return pcsz; };  
  197.     MCD_PCSZ pcsz;  
  198. };  
  199.   
  200. // On Linux and OS X, filenames are not specified in wchar_t  
  201. #if defined(MARKUP_WCHAR) && defined(__GNUC__)  
  202. #undef MCD_FOPEN  
  203. #define MCD_FOPEN(f,n,m) f=fopen(n,m)  
  204. #define MCD_T_FILENAME(s) s  
  205. #define MCD_PCSZ_FILENAME const char*  
  206. struct MCD_CSTR_FILENAME  
  207. {  
  208.     MCD_CSTR_FILENAME() { pcsz=NULL; };  
  209.     MCD_CSTR_FILENAME( MCD_PCSZ_FILENAME p ) { pcsz=p; };  
  210.     MCD_CSTR_FILENAME( const std::string& s ) { pcsz = s.c_str(); };  
  211.     operator MCD_PCSZ_FILENAME() const { return pcsz; };  
  212.     MCD_PCSZ_FILENAME pcsz;  
  213. };  
  214. #else // not WCHAR GNUC  
  215. #define MCD_CSTR_FILENAME MCD_CSTR  
  216. #define MCD_T_FILENAME MCD_T  
  217. #define MCD_PCSZ_FILENAME MCD_PCSZ  
  218. #endif // not WCHAR GNUC  
  219.   
  220. #if defined(__GNUC__)  
  221. #define MCD_FSEEK fseeko  
  222. #define MCD_FTELL ftello  
  223. #define MCD_INTFILEOFFSET off_t  
  224. #elif _MSC_VER >= 1000 && defined(MARKUP_HUGEFILE) // VC++ HUGEFILE  
  225. #if _MSC_VER < 1400 // before VC++ 2005  
  226. extern "C" int __cdecl _fseeki64(FILE *, __int64, int);  
  227. extern "C" __int64 __cdecl _ftelli64(FILE *);  
  228. #endif // before VC++ 2005  
  229. #define MCD_FSEEK _fseeki64  
  230. #define MCD_FTELL _ftelli64  
  231. #define MCD_INTFILEOFFSET __int64  
  232. #else // not GNU or VC++ HUGEFILE  
  233. #define MCD_FSEEK fseek  
  234. #define MCD_FTELL ftell  
  235. #define MCD_INTFILEOFFSET long  
  236. #endif // not GNU or VC++ HUGEFILE  
  237.   
  238. struct FilePos;  
  239. struct TokenPos;  
  240. struct NodePos;  
  241. struct PathPos;  
  242. struct SavedPosMapArray;  
  243. struct ElemPosTree;  
  244.   
  245. class CMarkup  
  246. {  
  247. public:  
  248.     CMarkup() { x_InitMarkup(); SetDoc( NULL ); };  
  249.     CMarkup( MCD_CSTR szDoc ) { x_InitMarkup(); SetDoc( szDoc ); };  
  250.     CMarkup( int nFlags ) { x_InitMarkup(); SetDoc( NULL ); m_nDocFlags = nFlags; };  
  251.     CMarkup( const CMarkup& markup ) { x_InitMarkup(); *this = markup; };  
  252.     void operator=( const CMarkup& markup );  
  253.     ~CMarkup();  
  254.   
  255.     // Navigate  
  256.     bool Load( MCD_CSTR_FILENAME szFileName );  
  257.     bool SetDoc( MCD_PCSZ pDoc );  
  258.     bool SetDoc( const MCD_STR& strDoc );  
  259.     bool IsWellFormed();  
  260.     bool FindElem( MCD_CSTR szName=NULL );  
  261.     bool FindChildElem( MCD_CSTR szName=NULL );  
  262.     bool IntoElem();  
  263.     bool OutOfElem();  
  264.     void ResetChildPos() { x_SetPos(m_iPosParent,m_iPos,0); };  
  265.     void ResetMainPos() { x_SetPos(m_iPosParent,0,0); };  
  266.     void ResetPos() { x_SetPos(0,0,0); };  
  267.     MCD_STR GetTagName() const;  
  268.     MCD_STR GetChildTagName() const { return x_GetTagName(m_iPosChild); };  
  269.     MCD_STR GetData() { return x_GetData(m_iPos); };  
  270.     MCD_STR GetChildData() { return x_GetData(m_iPosChild); };  
  271.     MCD_STR GetElemContent() const { return x_GetElemContent(m_iPos); };  
  272.     MCD_STR GetAttrib( MCD_CSTR szAttrib ) const { return x_GetAttrib(m_iPos,szAttrib); };  
  273.     MCD_STR GetChildAttrib( MCD_CSTR szAttrib ) const { return x_GetAttrib(m_iPosChild,szAttrib); };  
  274.     MCD_STR GetAttribName( int n ) const;  
  275.     int FindNode( int nType=0 );  
  276.     int GetNodeType() { return m_nNodeType; };  
  277.     bool SavePos( MCD_CSTR szPosName=MCD_T(""), int nMap = 0 );  
  278.     bool RestorePos( MCD_CSTR szPosName=MCD_T(""), int nMap = 0 );  
  279.     bool SetMapSize( int nSize, int nMap = 0 );  
  280.     MCD_STR GetError() const;  
  281.     const MCD_STR& GetResult() const { return m_strResult; };  
  282.     int GetDocFlags() const { return m_nDocFlags; };  
  283.     void SetDocFlags( int nFlags ) { m_nDocFlags = (nFlags & ~(MDF_READFILE|MDF_WRITEFILE|MDF_APPENDFILE)); };  
  284.     enum MarkupDocFlags  
  285.     {  
  286.         MDF_UTF16LEFILE = 1,  
  287.         MDF_UTF8PREAMBLE = 4,  
  288.         MDF_IGNORECASE = 8,  
  289.         MDF_READFILE = 16,  
  290.         MDF_WRITEFILE = 32,  
  291.         MDF_APPENDFILE = 64,  
  292.         MDF_UTF16BEFILE = 128  
  293.     };  
  294.     enum MarkupNodeFlags  
  295.     {  
  296.         MNF_WITHCDATA      = 0x01,  
  297.         MNF_WITHNOLINES    = 0x02,  
  298.         MNF_WITHXHTMLSPACE = 0x04,  
  299.         MNF_WITHREFS       = 0x08,  
  300.         MNF_WITHNOEND      = 0x10,  
  301.         MNF_ESCAPEQUOTES  = 0x100,  
  302.         MNF_NONENDED   = 0x100000,  
  303.         MNF_ILLDATA    = 0x200000  
  304.     };  
  305.     enum MarkupNodeType  
  306.     {  
  307.         MNT_ELEMENT                 = 1,    // 0x0001  
  308.         MNT_TEXT                    = 2,    // 0x0002  
  309.         MNT_WHITESPACE              = 4,    // 0x0004  
  310.         MNT_TEXT_AND_WHITESPACE     = 6,    // 0x0006  
  311.         MNT_CDATA_SECTION           = 8,    // 0x0008  
  312.         MNT_PROCESSING_INSTRUCTION  = 16,   // 0x0010  
  313.         MNT_COMMENT                 = 32,   // 0x0020  
  314.         MNT_DOCUMENT_TYPE           = 64,   // 0x0040  
  315.         MNT_EXCLUDE_WHITESPACE      = 123,  // 0x007b  
  316.         MNT_LONE_END_TAG            = 128,  // 0x0080  
  317.         MNT_NODE_ERROR              = 32768 // 0x8000  
  318.     };  
  319.   
  320.     // Create  
  321.     bool Save( MCD_CSTR_FILENAME szFileName );  
  322.     const MCD_STR& GetDoc() const { return m_strDoc; };  
  323.     bool AddElem( MCD_CSTR szName, MCD_CSTR szData=NULL, int nFlags=0 ) { return x_AddElem(szName,szData,nFlags); };  
  324.     bool InsertElem( MCD_CSTR szName, MCD_CSTR szData=NULL, int nFlags=0 ) { return x_AddElem(szName,szData,nFlags|MNF_INSERT); };  
  325.     bool AddChildElem( MCD_CSTR szName, MCD_CSTR szData=NULL, int nFlags=0 ) { return x_AddElem(szName,szData,nFlags|MNF_CHILD); };  
  326.     bool InsertChildElem( MCD_CSTR szName, MCD_CSTR szData=NULL, int nFlags=0 ) { return x_AddElem(szName,szData,nFlags|MNF_INSERT|MNF_CHILD); };  
  327.     bool AddElem( MCD_CSTR szName, int nValue, int nFlags=0 ) { return x_AddElem(szName,nValue,nFlags); };  
  328.     bool InsertElem( MCD_CSTR szName, int nValue, int nFlags=0 ) { return x_AddElem(szName,nValue,nFlags|MNF_INSERT); };  
  329.     bool AddChildElem( MCD_CSTR szName, int nValue, int nFlags=0 ) { return x_AddElem(szName,nValue,nFlags|MNF_CHILD); };  
  330.     bool InsertChildElem( MCD_CSTR szName, int nValue, int nFlags=0 ) { return x_AddElem(szName,nValue,nFlags|MNF_INSERT|MNF_CHILD); };  
  331.     bool AddAttrib( MCD_CSTR szAttrib, MCD_CSTR szValue ) { return x_SetAttrib(m_iPos,szAttrib,szValue); };  
  332.     bool AddChildAttrib( MCD_CSTR szAttrib, MCD_CSTR szValue ) { return x_SetAttrib(m_iPosChild,szAttrib,szValue); };  
  333.     bool AddAttrib( MCD_CSTR szAttrib, int nValue ) { return x_SetAttrib(m_iPos,szAttrib,nValue); };  
  334.     bool AddChildAttrib( MCD_CSTR szAttrib, int nValue ) { return x_SetAttrib(m_iPosChild,szAttrib,nValue); };  
  335.     bool AddSubDoc( MCD_CSTR szSubDoc ) { return x_AddSubDoc(szSubDoc,0); };  
  336.     bool InsertSubDoc( MCD_CSTR szSubDoc ) { return x_AddSubDoc(szSubDoc,MNF_INSERT); };  
  337.     MCD_STR GetSubDoc() { return x_GetSubDoc(m_iPos); };  
  338.     bool AddChildSubDoc( MCD_CSTR szSubDoc ) { return x_AddSubDoc(szSubDoc,MNF_CHILD); };  
  339.     bool InsertChildSubDoc( MCD_CSTR szSubDoc ) { return x_AddSubDoc(szSubDoc,MNF_CHILD|MNF_INSERT); };  
  340.     MCD_STR GetChildSubDoc() { return x_GetSubDoc(m_iPosChild); };  
  341.     bool AddNode( int nType, MCD_CSTR szText ) { return x_AddNode(nType,szText,0); };  
  342.     bool InsertNode( int nType, MCD_CSTR szText ) { return x_AddNode(nType,szText,MNF_INSERT); };  
  343.   
  344.     // Modify  
  345.     bool RemoveElem();  
  346.     bool RemoveChildElem();  
  347.     bool RemoveNode();  
  348.     bool SetAttrib( MCD_CSTR szAttrib, MCD_CSTR szValue, int nFlags=0 ) { return x_SetAttrib(m_iPos,szAttrib,szValue,nFlags); };  
  349.     bool SetChildAttrib( MCD_CSTR szAttrib, MCD_CSTR szValue, int nFlags=0 ) { return x_SetAttrib(m_iPosChild,szAttrib,szValue,nFlags); };  
  350.     bool SetAttrib( MCD_CSTR szAttrib, int nValue, int nFlags=0 ) { return x_SetAttrib(m_iPos,szAttrib,nValue,nFlags); };  
  351.     bool SetChildAttrib( MCD_CSTR szAttrib, int nValue, int nFlags=0 ) { return x_SetAttrib(m_iPosChild,szAttrib,nValue,nFlags); };  
  352.     bool SetData( MCD_CSTR szData, int nFlags=0 ) { return x_SetData(m_iPos,szData,nFlags); };  
  353.     bool SetChildData( MCD_CSTR szData, int nFlags=0 ) { return x_SetData(m_iPosChild,szData,nFlags); };  
  354.     bool SetData( int nValue ) { return x_SetData(m_iPos,nValue); };  
  355.     bool SetChildData( int nValue ) { return x_SetData(m_iPosChild,nValue); };  
  356.     bool SetElemContent( MCD_CSTR szContent ) { return x_SetElemContent(szContent); };  
  357.   
  358.   
  359.     // Utility  
  360.     static bool ReadTextFile( MCD_CSTR_FILENAME szFileName, MCD_STR& strDoc, MCD_STR* pstrResult=NULL, int* pnDocFlags=NULL, MCD_STR* pstrEncoding=NULL );  
  361.     static bool WriteTextFile( MCD_CSTR_FILENAME szFileName, const MCD_STR& strDoc, MCD_STR* pstrResult=NULL, int* pnDocFlags=NULL, MCD_STR* pstrEncoding=NULL );  
  362.     static MCD_STR EscapeText( MCD_CSTR szText, int nFlags = 0 );  
  363.     static MCD_STR UnescapeText( MCD_CSTR szText, int nTextLength = -1 );  
  364.     static int UTF16To8( char *pszUTF8, const unsigned short* pwszUTF16, int nUTF8Count );  
  365.     static int UTF8To16( unsigned short* pwszUTF16, const char* pszUTF8, int nUTF8Count );  
  366.     static MCD_STR UTF8ToA( MCD_CSTR pszUTF8, int* pnFailed = NULL );  
  367.     static MCD_STR AToUTF8( MCD_CSTR pszANSI );  
  368.     static void EncodeCharUTF8( int nUChar, char* pszUTF8, int& nUTF8Len );  
  369.     static int DecodeCharUTF8( const char*& pszUTF8, const char* pszUTF8End = NULL );  
  370.     static void EncodeCharUTF16( int nUChar, unsigned short* pwszUTF16, int& nUTF16Len );  
  371.     static int DecodeCharUTF16( const unsigned short*& pwszUTF16, const unsigned short* pszUTF16End = NULL );  
  372.     static bool DetectUTF8( const char* pText, int nTextLen, int* pnNonASCII = NULL, bool* bErrorAtEnd = NULL );  
  373.     static MCD_STR GetDeclaredEncoding( MCD_CSTR szDoc );  
  374.     static int GetEncodingCodePage( MCD_CSTR pszEncoding );  
  375.   
  376. protected:  
  377.   
  378. #if defined(_DEBUG)  
  379.     MCD_PCSZ m_pDebugCur;  
  380.     MCD_PCSZ m_pDebugPos;  
  381. #endif // DEBUG  
  382.   
  383.     MCD_STR m_strDoc;  
  384.     MCD_STR m_strResult;  
  385.   
  386.     int m_iPosParent;  
  387.     int m_iPos;  
  388.     int m_iPosChild;  
  389.     int m_iPosFree;  
  390.     int m_iPosDeleted;  
  391.     int m_nNodeType;  
  392.     int m_nNodeOffset;  
  393.     int m_nNodeLength;  
  394.     int m_nDocFlags;  
  395.   
  396.     FilePos* m_pFilePos;  
  397.     SavedPosMapArray* m_pSavedPosMaps;  
  398.     ElemPosTree* m_pElemPosTree;  
  399.   
  400.     enum MarkupNodeFlagsInternal  
  401.     {  
  402.         MNF_INSERT     = 0x002000,  
  403.         MNF_CHILD      = 0x004000  
  404.     };  
  405.   
  406. #if defined(_DEBUG) // DEBUG   
  407.     void x_SetDebugState();  
  408. #define MARKUP_SETDEBUGSTATE x_SetDebugState()  
  409. #else // not DEBUG  
  410. #define MARKUP_SETDEBUGSTATE  
  411. #endif // not DEBUG  
  412.   
  413.     void x_InitMarkup();  
  414.     void x_SetPos( int iPosParent, int iPos, int iPosChild );  
  415.     int x_GetFreePos();  
  416.     bool x_AllocElemPos( int nNewSize = 0 );  
  417.     int x_GetParent( int i );  
  418.     bool x_ParseDoc();  
  419.     int x_ParseElem( int iPos, TokenPos& token );  
  420.     int x_FindElem( int iPosParent, int iPos, PathPos& path ) const;  
  421.     MCD_STR x_GetPath( int iPos ) const;  
  422.     MCD_STR x_GetTagName( int iPos ) const;  
  423.     MCD_STR x_GetData( int iPos );  
  424.     MCD_STR x_GetAttrib( int iPos, MCD_PCSZ pAttrib ) const;  
  425.     static MCD_STR x_EncodeCDATASection( MCD_PCSZ szData );  
  426.     bool x_AddElem( MCD_PCSZ pName, MCD_PCSZ pValue, int nFlags );  
  427.     bool x_AddElem( MCD_PCSZ pName, int nValue, int nFlags );  
  428.     MCD_STR x_GetSubDoc( int iPos );  
  429.     bool x_AddSubDoc( MCD_PCSZ pSubDoc, int nFlags );  
  430.     bool x_SetAttrib( int iPos, MCD_PCSZ pAttrib, MCD_PCSZ pValue, int nFlags=0 );  
  431.     bool x_SetAttrib( int iPos, MCD_PCSZ pAttrib, int nValue, int nFlags=0 );  
  432.     bool x_AddNode( int nNodeType, MCD_PCSZ pText, int nNodeFlags );  
  433.     void x_RemoveNode( int iPosParent, int& iPos, int& nNodeType, int& nNodeOffset, int& nNodeLength );  
  434.     static bool x_CreateNode( MCD_STR& strNode, int nNodeType, MCD_PCSZ pText );  
  435.     int x_InsertNew( int iPosParent, int& iPosRel, NodePos& node );  
  436.     void x_AdjustForNode( int iPosParent, int iPos, int nShift );  
  437.     void x_Adjust( int iPos, int nShift, bool bAfterPos = false );  
  438.     void x_LinkElem( int iPosParent, int iPosBefore, int iPos );  
  439.     int x_UnlinkElem( int iPos );  
  440.     int x_UnlinkPrevElem( int iPosParent, int iPosBefore, int iPos );  
  441.     int x_ReleaseSubDoc( int iPos );  
  442.     int x_ReleasePos( int iPos );  
  443.     void x_CheckSavedPos();  
  444.     bool x_SetData( int iPos, MCD_PCSZ szData, int nFlags );  
  445.     bool x_SetData( int iPos, int nValue );  
  446.     int x_RemoveElem( int iPos );  
  447.     MCD_STR x_GetElemContent( int iPos ) const;  
  448.     bool x_SetElemContent( MCD_PCSZ szContent );  
  449.     void x_DocChange( int nLeft, int nReplace, const MCD_STR& strInsert );  
  450. };  
  451.   
  452. #endif // !defined(_MARKUP_H_INCLUDED_)  
  453. .cpp文件如下:  
  454. // Markup.cpp: implementation of the CMarkup class.  
  455. //  
  456. // Markup Release 11.2  
  457. // Copyright (C) 2009 First Objective Software, Inc. All rights reserved  
  458. // Go to www.firstobject.com for the latest CMarkup and EDOM documentation  
  459. // Use in commercial applications requires written permission  
  460. // This software is provided "as is", with no warranty.  
  461. //  
  462. #include   
  463. #include "Markup.h"  
  464.   
  465. #if defined(MCD_STRERROR) // C error routine  
  466. #include   
  467. #endif // C error routine  
  468.   
  469. #if defined (MARKUP_ICONV)  
  470. #include   
  471. #endif  
  472.   
  473. #if defined(MARKUP_STL) && ( defined(MARKUP_WINCONV) || (! defined(MCD_STRERROR)))  
  474. #include  // for MultiByteToWideChar, WideCharToMultiByte, FormatMessage  
  475. #endif // need windows.h when STL and (not setlocale or not strerror), MFC afx.h includes it already   
  476.   
  477. #if defined(MARKUP_MBCS) // MBCS/double byte  
  478. #pragma message( "Note: MBCS build (not UTF-8)" )  
  479. // For UTF-8, remove MBCS from project settings C/C++ preprocessor definitions  
  480. #if defined (MARKUP_WINCONV)  
  481. #include  // for VC++ _mbclen  
  482. #endif // WINCONV  
  483. #endif // MBCS/double byte  
  484.   
  485. #if defined(_DEBUG) && _MSC_VER > 1000 // VC++ DEBUG  
  486. #undef THIS_FILE  
  487. static char THIS_FILE[]=__FILE__;  
  488. #if defined(DEBUG_NEW)  
  489. #define new DEBUG_NEW  
  490. #endif // DEBUG_NEW  
  491. #endif // VC++ DEBUG  
  492.   
  493. // Customization  
  494. #define x_EOL MCD_T("/r/n") // can be /r/n or /n or empty  
  495. #define x_EOLLEN (sizeof(x_EOL)/sizeof(MCD_CHAR)-1) // string length of x_EOL  
  496. #define x_ATTRIBQUOTE '/"' // can be double or single quote  
  497.   
  498.   
  499. // Disable "while ( 1 )" warning in VC++ 2002  
  500. #if _MSC_VER >= 1300 // VC++ 2002 (7.0)  
  501. #pragma warning(disable:4127)  
  502. #endif // VC++ 2002 (7.0)  
  503.   
  504. //////////////////////////////////////////////////////////////////////  
  505. // Internal static utility functions  
  506. //  
  507. void x_StrInsertReplace( MCD_STR& str, int nLeft, int nReplace, const MCD_STR& strInsert )  
  508. {  
  509.     // Insert strInsert into str at nLeft replacing nReplace chars  
  510.     // Reduce reallocs on growing string by reserving string space  
  511.     // If realloc needed, allow for 1.5 times the new length  
  512.     //  
  513.     int nStrLength = MCD_STRLENGTH(str);  
  514.     int nInsLength = MCD_STRLENGTH(strInsert);  
  515.     int nNewLength = nInsLength + nStrLength - nReplace;  
  516.     int nAllocLen = MCD_STRCAPACITY(str);  
  517. #if defined(MCD_STRINSERTREPLACE) // STL, replace method  
  518.     if ( nNewLength > nAllocLen )  
  519.         MCD_BLDRESERVE( str, (nNewLength + nNewLength/2 + 128) );  
  520.     MCD_STRINSERTREPLACE( str, nLeft, nReplace, strInsert );  
  521. #else // MFC, no replace method  
  522.     int nBufferLen = nNewLength;  
  523.     if ( nNewLength > nAllocLen )  
  524.         nBufferLen += nBufferLen/2 + 128;  
  525.     MCD_CHAR* pDoc = MCD_GETBUFFER( str, nBufferLen );  
  526.     if ( nInsLength != nReplace && nLeft+nReplace < nStrLength )  
  527.         memmove( &pDoc[nLeft+nInsLength], &pDoc[nLeft+nReplace], (nStrLength-nLeft-nReplace)*sizeof(MCD_CHAR) );  
  528.     if ( nInsLength )  
  529.         memcpy( &pDoc[nLeft], strInsert, nInsLength*sizeof(MCD_CHAR) );  
  530.     MCD_RELEASEBUFFER( str, pDoc, nNewLength );  
  531. #endif // MFC, no replace method  
  532. }  
  533.   
  534. int x_Hash( MCD_PCSZ p, int nSize )  
  535. {  
  536.     unsigned int n=0;  
  537.     while (*p)  
  538.         n += (unsigned int)(*p++);  
  539.     return n % nSize;  
  540. }  
  541.   
  542. MCD_STR x_IntToStr( int n )  
  543. {  
  544.     MCD_CHAR sz[25];  
  545.     MCD_SPRINTF(MCD_SSZ(sz),MCD_T("%d"),n);  
  546.     MCD_STR s=sz;  
  547.     return s;  
  548. }  
  549.   
  550. enum MarkupResultCode  
  551. {  
  552.     MRC_COUNT    = 1,  
  553.     MRC_TYPE     = 2,  
  554.     MRC_NUMBER   = 4,  
  555.     MRC_ENCODING = 8,  
  556.     MRC_LENGTH   = 16,  
  557.     MRC_MODIFY   = 32,  
  558.     MRC_MSG      = 64  
  559. };  
  560.   
  561. void x_AddResult( MCD_STR& strResult, MCD_CSTR pszID, MCD_CSTR pszVal = NULL, int nResultCode = 0, int n = -1, int n2 = -1 )  
  562. {  
  563.     // Call this to append an error result to strResult, discard if accumulating too large  
  564.     if ( MCD_STRLENGTH(strResult) < 1000 )  
  565.     {  
  566.         // Use a temporary CMarkup object but keep strResult in a string to minimize memory footprint  
  567.         CMarkup mResult( strResult );  
  568.         if ( nResultCode & MRC_MODIFY )  
  569.             mResult.FindElem( pszID );  
  570.         else  
  571.             mResult.AddElem( pszID, MCD_T(""), CMarkup::MNF_WITHNOLINES );  
  572.         if ( pszVal.pcsz )  
  573.         {  
  574.             if ( nResultCode & MRC_TYPE )  
  575.                 mResult.SetAttrib( MCD_T("type"), pszVal );  
  576.             else if ( nResultCode & MRC_ENCODING )  
  577.                 mResult.SetAttrib( MCD_T("encoding"), pszVal );  
  578.             else if ( nResultCode & MRC_MSG )  
  579.                 mResult.SetAttrib( MCD_T("msg"), pszVal );  
  580.             else  
  581.                 mResult.SetAttrib( MCD_T("tagname"), pszVal );  
  582.         }  
  583.         if ( nResultCode & MRC_NUMBER )  
  584.             mResult.SetAttrib( MCD_T("n"), n );  
  585.         else if ( nResultCode & MRC_COUNT )  
  586.             mResult.SetAttrib( MCD_T("count"), n );  
  587.         else if ( nResultCode & MRC_LENGTH )  
  588.             mResult.SetAttrib( MCD_T("length"), n );  
  589.         else if ( n != -1 )  
  590.             mResult.SetAttrib( MCD_T("offset"), n );  
  591.         if ( n2 != -1 )  
  592.             mResult.SetAttrib( MCD_T("offset2"), n2 );  
  593.         strResult = mResult.GetDoc();  
  594.     }  
  595. }  
  596.   
  597. //////////////////////////////////////////////////////////////////////  
  598. // Encoding conversion struct and methods  
  599. //  
  600. struct TextEncoding  
  601. {  
  602.     TextEncoding( MCD_CSTR pszFromEncoding, const void* pFromBuffer, int nFromBufferLen )  
  603.     {  
  604.         m_strFromEncoding = pszFromEncoding;  
  605.         m_pFrom = pFromBuffer;  
  606.         m_nFromLen = nFromBufferLen;  
  607.         m_nFailedChars = 0;  
  608.         m_nToCount = 0;  
  609.     };  
  610.     int PerformConversion( void* pTo, MCD_CSTR pszToEncoding = NULL );  
  611.     bool FindRaggedEnd( int& nTruncBeforeBytes );  
  612. #if defined(MARKUP_ICONV)  
  613.     static const char* IConvName( char* szEncoding, MCD_CSTR pszEncoding );  
  614.     int IConv( void* pTo, int nToCharSize, int nFromCharSize );  
  615. #endif // ICONV  
  616. #if ! defined(MARKUP_WCHAR)  
  617.     static bool CanConvert( MCD_CSTR pszToEncoding, MCD_CSTR pszFromEncoding );  
  618. #endif // WCHAR  
  619.     MCD_STR m_strToEncoding;  
  620.     MCD_STR m_strFromEncoding;  
  621.     const void* m_pFrom;  
  622.     int m_nFromLen;  
  623.     int m_nToCount;  
  624.     int m_nFailedChars;  
  625. };  
  626.   
  627. // Encoding names  
  628. // This is a precompiled ASCII hash table for speed and minimum memory requirement  
  629. // Each entry consists of a 2 digit name length, 5 digit code page, and the encoding name  
  630. // Each table slot can have multiple entries, table size 155 was chosen for even distribution  
  631. //  
  632. MCD_PCSZ EncodingNameTable[155] =  
  633. {  
  634.     MCD_T("0800949ksc_5601"),MCD_T("1920932cseucpkdfmtjapanese0920003x-cp20003"),  
  635.     MCD_T("1250221_iso-2022-jp0228591l10920004x-cp20004"),  
  636.     MCD_T("0228592l20920005x-cp20005"),  
  637.     MCD_T("0228593l30600850ibm8501000858ccsid00858"),  
  638.     MCD_T("0228594l40600437ibm4370701201ucs-2be0600860ibm860"),  
  639.     MCD_T("0600852ibm8520501250ms-ee0600861ibm8610228599l50751932cp51932"),  
  640.     MCD_T("0600862ibm8620620127ibm3670700858cp008581010021x-mac-thai0920261x-cp20261"),  
  641.     MCD_T("0600737ibm7370500869cp-gr1057003x-iscii-be0600863ibm863"),  
  642.     MCD_T("0750221ms502210628591ibm8190600855ibm8550600864ibm864"),  
  643.     MCD_T("0600775ibm7751057002x-iscii-de0300949uhc0228605l91028591iso-ir-1000600865ibm865"),  
  644.     MCD_T("1028594iso-ir-1101028592iso-ir-1010600866ibm8660500861cp-is0600857ibm857"),  
  645.     MCD_T("0950227x-cp50227"),  
  646.     MCD_T("0320866koi1628598csisolatinhebrew1057008x-iscii-ka"),  
  647.     MCD_T("1000950big5-hkscs1220106x-ia5-german0600869ibm869"),  
  648.     MCD_T("1057009x-iscii-ma0701200ucs-2le0712001utf32be0920269x-cp20269"),  
  649.     MCD_T("0800708asmo-7080500437cspc81765000unicode-1-1-utf-70612000utf-320920936x-cp20936"),  
  650.     MCD_T("1200775ebcdic-cp-be0628598hebrew0701201utf16be1765001unicode-1-1-utf-81765001unicode-2-0-utf-80551932x-euc"),  
  651.     MCD_T("1028595iso-ir-1441028597iso-ir-1260728605latin-90601200utf-161057011x-iscii-pa"),  
  652.     MCD_T("1028596iso-ir-1271028593iso-ir-1090751932ms51932"),  
  653.     MCD_T("0801253ms-greek0600949korean1050225iso2022-kr1128605iso_8859-150920949x-cp20949"),  
  654.     MCD_T("1200775ebcdic-cp-ch1028598iso-ir-1381057006x-iscii-as1450221iso-2022-jp-ms"),  
  655.     MCD_T("1057004x-iscii-ta1028599iso-ir-148"),  
  656.     MCD_T("1000949iso-ir-1490820127us-ascii"),MCD_T(""),  
  657.     MCD_T("1000936gb_2312-801900850cspc850multilingual0712000utf32le"),  
  658.     MCD_T("1057005x-iscii-te1300949csksc560119871965000x-unicode-2-0-utf-7"),  
  659.     MCD_T("0701200utf16le1965001x-unicode-2-0-utf-80928591iso8859-1"),  
  660.     MCD_T("0928592iso8859-21420002x_chinese-eten0520866koi8r1000932x-ms-cp932"),  
  661.     MCD_T("1320000x-chinese-cns1138598iso8859-8-i1057010x-iscii-gu0928593iso8859-3"),  
  662.     MCD_T("0928594iso8859-4"),MCD_T("0928595iso8859-51150221csiso2022jp"),  
  663.     MCD_T("0928596iso8859-60900154csptcp154"),  
  664.     MCD_T("0928597iso8859-70900932shift_jis1400154cyrillic-asian"),  
  665.     MCD_T("0928598iso8859-81057007x-iscii-or1150225csiso2022kr"),  
  666.     MCD_T("0721866koi8-ru0928599iso8859-9"),MCD_T("0910000macintosh"),MCD_T(""),  
  667.     MCD_T(""),MCD_T(""),  
  668.     MCD_T("1210004x-mac-arabic0800936gb2312800628598visual1520108x-ia5-norwegian"),  
  669.     MCD_T(""),MCD_T("0829001x-europa"),MCD_T(""),MCD_T("1510079x-mac-icelandic"),  
  670.     MCD_T("0800932sjis-win1128591csisolatin1"),MCD_T("1128592csisolatin2"),  
  671.     MCD_T("1400949ks_c_5601-19871128593csisolatin3"),MCD_T("1128594csisolatin4"),  
  672.     MCD_T("0400950big51128595csisolatin51400949ks_c_5601-1989"),  
  673.     MCD_T("0500775cp5001565000csunicode11utf7"),MCD_T("0501361johab"),  
  674.     MCD_T("1100932windows-9321100437codepage437"),  
  675.     MCD_T("1800862cspc862latinhebrew1310081x-mac-turkish"),MCD_T(""),  
  676.     MCD_T("0701256ms-arab0800775csibm5000500154cp154"),  
  677.     MCD_T("1100936windows-9360520127ascii"),  
  678.     MCD_T("1528597csisolatingreek1100874windows-874"),MCD_T("0500850cp850"),  
  679.     MCD_T("0700720dos-7200500950cp9500500932cp9320500437cp4370500860cp8601650222_iso-2022-jp$sio"),  
  680.     MCD_T("0500852cp8520500861cp8610700949ksc56010812001utf-32be"),  
  681.     MCD_T("0528597greek0500862cp8620520127cp3670500853cp853"),  
  682.     MCD_T("0500737cp7371150220iso-2022-jp0801201utf-16be0500863cp863"),  
  683.     MCD_T("0500936cp9360528591cp8194520932extended_unix_code_packed_format_for_japanese0500855cp8550500864cp864"),  
  684.     MCD_T("0500775cp7750500874cp8740800860csibm8600500865cp865"),  
  685.     MCD_T("0500866cp8660800861csibm8611150225iso-2022-kr0500857cp8571101201unicodefffe"),  
  686.     MCD_T("0700862dos-8620701255ms-hebr0500858cp858"),  
  687.     MCD_T("1210005x-mac-hebrew0500949cp9490800863csibm863"),  
  688.     MCD_T("0500869cp8691600437cspc8codepage4370700874tis-6200800855csibm8550800864csibm864"),  
  689.     MCD_T("0800950x-x-big50420866koi80800932ms_kanji0700874dos-8740800865csibm865"),  
  690.     MCD_T("0800866csibm8661210003x-mac-korean0800857csibm8570812000utf-32le"),  
  691.     MCD_T(""),MCD_T("0500932ms9320801200utf-16le1028591iso-8859-10500154pt154"),  
  692.     MCD_T("1028592iso-8859-20620866koi8-r0800869csibm869"),  
  693.     MCD_T("1500936csiso58gb2312800828597elot_9281238598iso-8859-8-i1028593iso-8859-30820127iso-ir-6"),  
  694.     MCD_T("1028594iso-8859-4"),  
  695.     MCD_T("0800852cspcp8520500936ms9361028595iso-8859-50621866koi8-u0701252ms-ansi"),  
  696.     MCD_T("1028596iso-8859-60220127us2400858pc-multilingual-850+euro"),  
  697.     MCD_T("1028597iso-8859-71028603iso8859-13"),  
  698.     MCD_T("1320000x-chinese_cns1028598iso-8859-8"),  
  699.     MCD_T("1828595csisolatincyrillic1028605iso8859-151028599iso-8859-9"),  
  700.     MCD_T("0465001utf8"),MCD_T("1510017x-mac-ukrainian"),MCD_T(""),  
  701.     MCD_T("0828595cyrillic"),MCD_T("0900936gb2312-80"),MCD_T(""),  
  702.     MCD_T("0720866cskoi8r1528591iso_8859-1:1987"),MCD_T("1528592iso_8859-2:1987"),  
  703.     MCD_T("1354936iso-4873:1986"),MCD_T("0700932sjis-ms1528593iso_8859-3:1988"),  
  704.     MCD_T("1528594iso_8859-4:19880600936gb23120701251ms-cyrl"),  
  705.     MCD_T("1528596iso_8859-6:19871528595iso_8859-5:1988"),  
  706.     MCD_T("1528597iso_8859-7:1987"),  
  707.     MCD_T("1201250windows-12501300932shifft_jis-ms"),  
  708.     MCD_T("0810029x-mac-ce1201251windows-12511528598iso_8859-8:19880900949ks_c_56011110000csmacintosh"),  
  709.     MCD_T("0601200cp12001201252windows-1252"),  
  710.     MCD_T("1052936hz-gb-23121201253windows-12531400949ks_c_5601_19871528599iso_8859-9:19890601201cp1201"),  
  711.     MCD_T("1201254windows-1254"),MCD_T("1000936csgb2312801201255windows-1255"),  
  712.     MCD_T("1201256windows-12561100932windows-31j"),  
  713.     MCD_T("1201257windows-12570601250cp12500601133cp1133"),  
  714.     MCD_T("0601251cp12511201258windows-12580601125cp1125"),  
  715.     MCD_T("0701254ms-turk0601252cp1252"),MCD_T("0601253cp12530601361cp1361"),  
  716.     MCD_T("0800949ks-c56010601254cp1254"),MCD_T("0651936euc-cn0601255cp1255"),  
  717.     MCD_T("0601256cp1256"),MCD_T("0601257cp12570600950csbig50800858ibm00858"),  
  718.     MCD_T("0601258cp1258"),MCD_T("0520105x-ia5"),  
  719.     MCD_T("0801250x-cp12501110006x-mac-greek0738598logical"),  
  720.     MCD_T("0801251x-cp1251"),MCD_T(""),  
  721.     MCD_T("1410001x-mac-japanese1200932cswindows31j"),  
  722.     MCD_T("0700936chinese0720127csascii0620932euc-jp"),  
  723.     MCD_T("0851936x-euc-cn0501200ucs-2"),MCD_T("0628597greek8"),  
  724.     MCD_T("0651949euc-kr"),MCD_T(""),MCD_T("0628591latin1"),  
  725.     MCD_T("0628592latin21100874iso-8859-11"),  
  726.     MCD_T("0628593latin31420127ansi_x3.4-19681420127ansi_x3.4-19861028591iso_8859-1"),  
  727.     MCD_T("0628594latin41028592iso_8859-20701200unicode1128603iso-8859-13"),  
  728.     MCD_T("1028593iso_8859-30628599latin51410082x-mac-croatian"),  
  729.     MCD_T("1028594iso_8859-41128605iso-8859-150565000utf-70851932x-euc-jp"),  
  730.     MCD_T("1300775cspc775baltic1028595iso_8859-50565001utf-80512000utf32"),  
  731.     MCD_T("1028596iso_8859-61710002x-mac-chinesetrad0601252x-ansi"),  
  732.     MCD_T("1028597iso_8859-70628605latin90501200utf160700154ptcp1541410010x-mac-romanian"),  
  733.     MCD_T("0900936iso-ir-581028598iso_8859-8"),MCD_T("1028599iso_8859-9"),  
  734.     MCD_T("1350221iso2022-jp-ms0400932sjis"),MCD_T("0751949cseuckr"),  
  735.     MCD_T("1420002x-chinese-eten"),MCD_T("1410007x-mac-cyrillic"),  
  736.     MCD_T("1000932shifft_jis"),MCD_T("0828596ecma-114"),MCD_T(""),  
  737.     MCD_T("0900932shift-jis"),MCD_T("0701256cp1256 1320107x-ia5-swedish"),  
  738.     MCD_T("0828597ecma-118"),  
  739.     MCD_T("1628596csisolatinarabic1710008x-mac-chinesesimp0600932x-sjis"),MCD_T(""),  
  740.     MCD_T("0754936gb18030"),MCD_T("1350221windows-502210712000cp12000"),  
  741.     MCD_T("0628596arabic0500936cn-gb0900932sjis-open0712001cp12001"),MCD_T(""),  
  742.     MCD_T(""),MCD_T("0700950cn-big50920127iso646-us1001133ibm-cp1133"),MCD_T(""),  
  743.     MCD_T("0800936csgb23120900949ks-c-56010310000mac"),  
  744.     MCD_T("1001257winbaltrim0750221cp502211020127iso-ir-6us"),  
  745.     MCD_T("1000932csshiftjis"),MCD_T("0300936gbk0765001cp65001"),  
  746.     MCD_T("1620127iso_646.irv:19911351932windows-519320920001x-cp20001")  
  747. };  
  748.   
  749. int x_GetEncodingCodePage( MCD_CSTR pszEncoding )  
  750. {  
  751.     // redo for completeness, the iconv set, UTF-32, and uppercase  
  752.   
  753.     // Lookup strEncoding in EncodingNameTable and return Windows code page  
  754.     int nCodePage = -1;  
  755.     int nEncLen = MCD_PSZLEN( pszEncoding );  
  756.     if ( ! nEncLen )  
  757.         nCodePage = MCD_ACP;  
  758.     else if ( MCD_PSZNCMP(pszEncoding,MCD_T("UTF-32"),6) == 0 )  
  759.         nCodePage = MCD_UTF32;  
  760.     else if ( nEncLen < 100 )  
  761.     {  
  762.         MCD_CHAR szEncodingLower[100];  
  763.         for ( int nEncChar=0; nEncChar
  764.         {  
  765.             MCD_CHAR cEncChar = pszEncoding[nEncChar];  
  766.             szEncodingLower[nEncChar] = (cEncChar>='A' && cEncChar<='Z')? (MCD_CHAR)(cEncChar+('a'-'A')) : cEncChar;  
  767.         }  
  768.         szEncodingLower[nEncLen] = '/0';  
  769.         MCD_PCSZ pEntry = EncodingNameTable[x_Hash(szEncodingLower,sizeof(EncodingNameTable)/sizeof(MCD_PCSZ))];  
  770.         while ( *pEntry )  
  771.         {  
  772.             // e.g. entry: 0565001utf-8 means length 05, code page 65001, encoding name utf-8  
  773.             int nEntryLen = (*pEntry - '0') * 10;  
  774.             ++pEntry;  
  775.             nEntryLen += (*pEntry - '0');  
  776.             ++pEntry;  
  777.             MCD_PCSZ pCodePage = pEntry;  
  778.             pEntry += 5;  
  779.             if ( nEntryLen == nEncLen && MCD_PSZNCMP(szEncodingLower,pEntry,nEntryLen) == 0 )  
  780.             {  
  781.                 // Convert digits to integer up to code name which always starts with alpha   
  782.                 nCodePage = MCD_PSZTOL( pCodePage, NULL, 10 );  
  783.                 break;  
  784.             }  
  785.             pEntry += nEntryLen;  
  786.         }  
  787.     }  
  788.     return nCodePage;  
  789. }  
  790.   
  791. #if ! defined(MARKUP_WCHAR)  
  792. bool TextEncoding::CanConvert( MCD_CSTR pszToEncoding, MCD_CSTR pszFromEncoding )  
  793. {  
  794.     // Return true if MB to MB conversion is possible  
  795. #if defined(MARKUP_ICONV)  
  796.     // iconv_open should fail if either encoding not supported or one is alias for other  
  797.     char szTo[100], szFrom[100];  
  798.     iconv_t cd = iconv_open( IConvName(szTo,pszToEncoding), IConvName(szFrom,pszFromEncoding) );  
  799.     if ( cd == (iconv_t)-1 )  
  800.         return false;  
  801.     iconv_close(cd);  
  802. #else  
  803.     int nToCP = x_GetEncodingCodePage( pszToEncoding );  
  804.     int nFromCP = x_GetEncodingCodePage( pszFromEncoding );  
  805.     if ( nToCP == -1 || nFromCP == -1 )  
  806.         return false;  
  807. #if defined(MARKUP_WINCONV)  
  808.     if ( nToCP == MCD_ACP || nFromCP == MCD_ACP ) // either ACP ANSI?  
  809.     {  
  810.         int nACP = GetACP();  
  811.         if ( nToCP == MCD_ACP )  
  812.             nToCP = nACP;  
  813.         if ( nFromCP == MCD_ACP )  
  814.             nFromCP = nACP;  
  815.     }  
  816. #else // no conversion API, but we can do AToUTF8 and UTF8ToA  
  817.     if ( nToCP != MCD_UTF8 && nFromCP != MCD_UTF8 ) // either UTF-8?  
  818.         return false;  
  819. #endif // no conversion API  
  820.     if ( nToCP == nFromCP )  
  821.         return false;  
  822. #endif // not ICONV  
  823.     return true;  
  824. }  
  825. #endif // not WCHAR  
  826.   
  827. #if defined(MARKUP_ICONV)  
  828. const char* TextEncoding::IConvName( char* szEncoding, MCD_CSTR pszEncoding )  
  829. {  
  830.     // Make upper case char-based name from strEncoding which consists only of characters in the ASCII range  
  831.     int nEncChar = 0;  
  832.     while ( pszEncoding[nEncChar] )  
  833.     {  
  834.         char cEncChar = (char)pszEncoding[nEncChar];  
  835.         szEncoding[nEncChar] = (cEncChar>='a' && cEncChar<='z')? (cEncChar-('a'-'A')) : cEncChar;  
  836.         ++nEncChar;  
  837.     }  
  838.     if ( nEncChar == 6 && strncmp(szEncoding,"UTF-16",6) == 0 )  
  839.     {  
  840.         szEncoding[nEncChar++] = 'B';  
  841.         szEncoding[nEncChar++] = 'E';  
  842.     }  
  843.     szEncoding[nEncChar] = '/0';  
  844.     return szEncoding;  
  845. }  
  846.   
  847. int TextEncoding::IConv( void* pTo, int nToCharSize, int nFromCharSize )  
  848. {  
  849.     // Converts from m_pFrom to pTo  
  850.     char szTo[100], szFrom[100];  
  851.     iconv_t cd = iconv_open( IConvName(szTo,m_strToEncoding), IConvName(szFrom,m_strFromEncoding) );  
  852.     int nToLenBytes = 0;  
  853.     if ( cd != (iconv_t)-1 )  
  854.     {  
  855.         size_t nFromLenRemaining = (size_t)m_nFromLen * nFromCharSize;  
  856.         size_t nToCountRemaining = (size_t)m_nToCount * nToCharSize;  
  857.         size_t nToCountRemainingBefore;  
  858.         char* pToChar = (char*)pTo;  
  859.         char* pFromChar = (char*)m_pFrom;  
  860.         char* pToTempBuffer = NULL;  
  861.         const size_t nTempBufferSize = 2048;  
  862.         size_t nResult;  
  863.         if ( ! pTo )  
  864.         {  
  865.             pToTempBuffer = new char[nTempBufferSize];  
  866.             pToChar = pToTempBuffer;  
  867.             nToCountRemaining = nTempBufferSize;  
  868.         }  
  869.         while ( nFromLenRemaining )  
  870.         {  
  871.             nToCountRemainingBefore = nToCountRemaining;  
  872.             nResult = iconv( cd, &pFromChar, &nFromLenRemaining, &pToChar, &nToCountRemaining );  
  873.             nToLenBytes += (int)(nToCountRemainingBefore - nToCountRemaining);  
  874.             if ( nResult == (size_t)-1 )  
  875.             {  
  876.                 int nErrno = errno;  
  877.                 if ( nErrno == EILSEQ  )  
  878.                 {  
  879.                     // Bypass bad char, question mark denotes problem in source string  
  880.                     pFromChar += nFromCharSize;  
  881.                     nFromLenRemaining -= nFromCharSize;  
  882.                     if ( nToCharSize == 1 )  
  883.                         *pToChar = '?';  
  884.                     else if ( nToCharSize == 2 )  
  885.                         *((unsigned short*)pToChar) = (unsigned short)'?';  
  886.                     else if ( nToCharSize == 4 )  
  887.                         *((unsigned int*)pToChar) = (unsigned int)'?';  
  888.                     pToChar += nToCharSize;  
  889.                     nToCountRemaining -= nToCharSize;  
  890.                 }  
  891.                 else if ( nErrno == EINVAL )  
  892.                     break; // incomplete character or shift sequence at end of input  
  893.                 // E2BIG : output buffer full should only happen when using a temp buffer  
  894.             }  
  895.             else  
  896.                 m_nFailedChars += nResult;  
  897.             if ( pToTempBuffer && nToCountRemaining < 10 )  
  898.             {  
  899.                 nToCountRemaining = nTempBufferSize;  
  900.                 pToChar = pToTempBuffer;  
  901.             }  
  902.         }  
  903.         if ( pToTempBuffer )  
  904.             delete[] pToTempBuffer;  
  905.         iconv_close(cd);  
  906.     }  
  907.     return nToLenBytes / nToCharSize;  
  908. }  
  909. #endif  
  910.   
  911. #if defined(MARKUP_WINCONV)  
  912. bool x_NoDefaultChar( int nCP )  
  913. {  
  914.     // WideCharToMultiByte fails if lpUsedDefaultChar is non-NULL for these code pages:  
  915.     return (bool)(nCP == 65000 || nCP == 65001 || nCP == 50220 || nCP == 50221 || nCP == 50222 || nCP == 50225 ||  
  916.             nCP == 50227 || nCP == 50229 || nCP == 52936 || nCP == 54936 || (nCP >= 57002 && nCP <= 57011) );  
  917. }  
  918. #endif  
  919.   
  920. int TextEncoding::PerformConversion( void* pTo, MCD_CSTR pszToEncoding/*=NULL*/ )  
  921. {  
  922.     // If pTo is not NULL, it must be large enough to hold result, length of result is returned  
  923.     // m_nFailedChars will be set to >0 if characters not supported in strToEncoding  
  924.     int nToLen = 0;  
  925.     if ( pszToEncoding.pcsz )  
  926.         m_strToEncoding = pszToEncoding;  
  927.     int nToCP = x_GetEncodingCodePage( m_strToEncoding );  
  928.     if ( nToCP == -1 )  
  929.         nToCP = MCD_ACP;  
  930.     int nFromCP = x_GetEncodingCodePage( m_strFromEncoding );  
  931.     if ( nFromCP == -1 )  
  932.         nFromCP = MCD_ACP;  
  933.     m_nFailedChars = 0;  
  934.   
  935. #if ! defined(MARKUP_WINCONV) && ! defined(MARKUP_ICONV)  
  936.     // Only non-Unicode encoding supported is locale charset, must call setlocale  
  937.     if ( nToCP != MCD_UTF8 && nToCP != MCD_UTF16 && nToCP != MCD_UTF32 )  
  938.         nToCP = MCD_ACP;  
  939.     if ( nFromCP != MCD_UTF8 && nFromCP != MCD_UTF16 && nFromCP != MCD_UTF32 )  
  940.         nFromCP = MCD_ACP;  
  941.     if ( nFromCP == MCD_ACP )  
  942.     {  
  943.         const char* pA = (const char*)m_pFrom;  
  944.         int nALenRemaining = m_nFromLen;  
  945.         int nCharLen;  
  946.         wchar_t wcChar;  
  947.         char* pU = (char*)pTo;  
  948.         while ( nALenRemaining )  
  949.         {  
  950.             nCharLen = mbtowc( &wcChar, pA, nALenRemaining );  
  951.             if ( nCharLen < 1 )  
  952.             {  
  953.                 wcChar = (wchar_t)'?';  
  954.                 nCharLen = 1;  
  955.             }  
  956.             pA += nCharLen;  
  957.             nALenRemaining -= nCharLen;  
  958.             if ( nToCP == MCD_UTF8 )  
  959.                 CMarkup::EncodeCharUTF8( (int)wcChar, pU, nToLen );  
  960.             else if ( nToCP == MCD_UTF16 )  
  961.                 CMarkup::EncodeCharUTF16( (int)wcChar, (unsigned short*)pU, nToLen );  
  962.             else // UTF32  
  963.             {  
  964.                 if ( pU )  
  965.                     ((unsigned int*)pU)[nToLen] = (unsigned int)wcChar;  
  966.                 ++nToLen;  
  967.             }  
  968.         }  
  969.     }  
  970.     else if ( nToCP == MCD_ACP )  
  971.     {  
  972.         union pUnicodeUnion { const char* p8; const unsigned short* p16; const unsigned int* p32; } pU;  
  973.         pU.p8 = (const char*)m_pFrom;  
  974.         const char* pUEnd = pU.p8 + m_nFromLen;  
  975.         if ( nFromCP == MCD_UTF16 )  
  976.             pUEnd = (char*)( pU.p16 + m_nFromLen );  
  977.         else if ( nFromCP == MCD_UTF32 )  
  978.             pUEnd = (char*)( pU.p32 + m_nFromLen );  
  979.         int nCharLen;  
  980.         char* pA = (char*)pTo;  
  981.         char szA[8];  
  982.         int nUChar;  
  983.         while ( pU.p8 != pUEnd )  
  984.         {  
  985.             if ( nFromCP == MCD_UTF8 )  
  986.                 nUChar = CMarkup::DecodeCharUTF8( pU.p8, pUEnd );  
  987.             else if ( nFromCP == MCD_UTF16 )  
  988.                 nUChar = CMarkup::DecodeCharUTF16( pU.p16, (const unsigned short*)pUEnd );  
  989.             else // UTF32  
  990.                 nUChar = *(pU.p32)++;  
  991.             if ( nUChar == -1 )  
  992.                 nCharLen = -2;  
  993.             else if ( nUChar & ~0xffff )  
  994.                 nCharLen = -1;  
  995.             else  
  996.                 nCharLen = wctomb( pA?pA:szA, (wchar_t)nUChar );  
  997.             if ( nCharLen < 0 )  
  998.             {  
  999.                 if ( nCharLen == -1 )  
  1000.                     ++m_nFailedChars;  
  1001.                 nCharLen = 1;  
  1002.                 if ( pA )  
  1003.                     *pA = '?';  
  1004.             }  
  1005.             if ( pA )  
  1006.                 pA += nCharLen;  
  1007.             nToLen += nCharLen;  
  1008.         }  
  1009.     }  
  1010. #endif // not WINCONV and not ICONV  
  1011.   
  1012.     if ( nFromCP == MCD_UTF32 )  
  1013.     {  
  1014.         const unsigned int* p32 = (const unsigned int*)m_pFrom;  
  1015.         const unsigned int* p32End = p32 + m_nFromLen;  
  1016.         if ( nToCP == MCD_UTF8 )  
  1017.         {  
  1018.             char* p8 = (char*)pTo;  
  1019.             while ( p32 != p32End )  
  1020.                 CMarkup::EncodeCharUTF8( *p32++, p8, nToLen );  
  1021.         }  
  1022.         else if ( nToCP == MCD_UTF16 )  
  1023.         {  
  1024.             unsigned short* p16 = (unsigned short*)pTo;  
  1025.             while ( p32 != p32End )  
  1026.                 CMarkup::EncodeCharUTF16( (int)*p32++, p16, nToLen );  
  1027.         }  
  1028.         else // to ANSI  
  1029.         {  
  1030.             // WINCONV not supported for 32To8, since only used for sizeof(wchar_t) == 4  
  1031. #if defined(MARKUP_ICONV)  
  1032.             nToLen = IConv( pTo, 1, 4 );  
  1033. #endif // ICONV  
  1034.         }  
  1035.     }  
  1036.     else if ( nFromCP == MCD_UTF16 )  
  1037.     {  
  1038.         // UTF16To8 will be deprecated since weird output buffer size sensitivity not worth implementing here  
  1039.         const unsigned short* p16 = (const unsigned short*)m_pFrom;  
  1040.         const unsigned short* p16End = p16 + m_nFromLen;  
  1041.         int nUChar;  
  1042.         if ( nToCP == MCD_UTF32 )  
  1043.         {  
  1044.             unsigned int* p32 = (unsigned int*)pTo;  
  1045.             while ( p16 != p16End )  
  1046.             {  
  1047.                 nUChar = CMarkup::DecodeCharUTF16( p16, p16End );  
  1048.                 if ( nUChar == -1 )  
  1049.                     nUChar = '?';  
  1050.                 if ( p32 )  
  1051.                     p32[nToLen] = (unsigned int)nUChar;  
  1052.                 ++nToLen;  
  1053.             }  
  1054.         }  
  1055. #if defined(MARKUP_WINCONV)  
  1056.         else // to UTF-8 or other multi-byte  
  1057.         {  
  1058.             nToLen = WideCharToMultiByte(nToCP,0,(const wchar_t*)m_pFrom,m_nFromLen,(char*)pTo,  
  1059.                     m_nToCount?m_nToCount+1:0,NULL,x_NoDefaultChar(nToCP)?NULL:&m_nFailedChars);  
  1060.         }  
  1061. #else // not WINCONV  
  1062.         else if ( nToCP == MCD_UTF8 )  
  1063.         {  
  1064.             char* p8 = (char*)pTo;  
  1065.             while ( p16 != p16End )  
  1066.             {  
  1067.                 nUChar = CMarkup::DecodeCharUTF16( p16, p16End );  
  1068.                 if ( nUChar == -1 )  
  1069.                     nUChar = '?';  
  1070.                 CMarkup::EncodeCharUTF8( nUChar, p8, nToLen );  
  1071.             }  
  1072.         }  
  1073.         else // to ANSI  
  1074.         {  
  1075. #if defined(MARKUP_ICONV)  
  1076.             nToLen = IConv( pTo, 1, 2 );  
  1077. #endif // ICONV  
  1078.         }  
  1079. #endif // not WINCONV  
  1080.     }  
  1081.     else if ( nToCP == MCD_UTF16  ) // to UTF-16 from UTF-8/ANSI  
  1082.     {  
  1083. #if defined(MARKUP_WINCONV)  
  1084.         nToLen = MultiByteToWideChar(nFromCP,0,(const char*)m_pFrom,m_nFromLen,(wchar_t*)pTo,m_nToCount);  
  1085. #else // not WINCONV  
  1086.         if ( nFromCP == MCD_UTF8 )  
  1087.         {  
  1088.             const char* p8 = (const char*)m_pFrom;  
  1089.             const char* p8End = p8 + m_nFromLen;  
  1090.             int nUChar;  
  1091.             unsigned short* p16 = (unsigned short*)pTo;  
  1092.             while ( p8 != p8End )  
  1093.             {  
  1094.                 nUChar = CMarkup::DecodeCharUTF8( p8, p8End );  
  1095.                 if ( nUChar == -1 )  
  1096.                     nUChar = '?';  
  1097.                 if ( p16 )  
  1098.                     p16[nToLen] = (unsigned short)nUChar;  
  1099.                 ++nToLen;  
  1100.             }  
  1101.         }  
  1102.         else // from ANSI  
  1103.         {  
  1104. #if defined(MARKUP_ICONV)  
  1105.             nToLen = IConv( pTo, 2, 1 );  
  1106. #endif // ICONV  
  1107.         }  
  1108. #endif // not WINCONV  
  1109.     }  
  1110.     else if ( nToCP == MCD_UTF32  ) // to UTF-32 from UTF-8/ANSI  
  1111.     {  
  1112.         if ( nFromCP == MCD_UTF8 )  
  1113.         {  
  1114.             const char* p8 = (const char*)m_pFrom;  
  1115.             const char* p8End = p8 + m_nFromLen;  
  1116.             int nUChar;  
  1117.             unsigned int* p32 = (unsigned int*)pTo;  
  1118.             while ( p8 != p8End )  
  1119.             {  
  1120.                 nUChar = CMarkup::DecodeCharUTF8( p8, p8End );  
  1121.                 if ( nUChar == -1 )  
  1122.                     nUChar = '?';  
  1123.                 if ( p32 )  
  1124.                     p32[nToLen] = (unsigned int)nUChar;  
  1125.                 ++nToLen;  
  1126.             }  
  1127.         }  
  1128.         else // from ANSI  
  1129.         {  
  1130.             // WINCONV not supported for ATo32, since only used for sizeof(wchar_t) == 4  
  1131. #if defined(MARKUP_ICONV)  
  1132.             // nToLen = IConv( pTo, 4, 1 );  
  1133.             // Linux: had trouble getting IConv to leave the BOM off of the UTF-32 output stream  
  1134.             // So converting via UTF-16 with native endianness  
  1135.             unsigned short* pwszUTF16 = new unsigned short[m_nFromLen];  
  1136.             MCD_STR strToEncoding = m_strToEncoding;  
  1137.             m_strToEncoding = MCD_T("UTF-16BE");  
  1138.             short nEndianTest = 1;  
  1139.             if ( ((char*)&nEndianTest)[0] ) // Little-endian?  
  1140.                 m_strToEncoding = MCD_T("UTF-16LE");  
  1141.             m_nToCount = m_nFromLen;  
  1142.             int nUTF16Len = IConv( pwszUTF16, 2, 1 );  
  1143.             m_strToEncoding = strToEncoding;  
  1144.             const unsigned short* p16 = (const unsigned short*)pwszUTF16;  
  1145.             const unsigned short* p16End = p16 + nUTF16Len;  
  1146.             int nUChar;  
  1147.             unsigned int* p32 = (unsigned int*)pTo;  
  1148.             while ( p16 != p16End )  
  1149.             {  
  1150.                 nUChar = CMarkup::DecodeCharUTF16( p16, p16End );  
  1151.                 if ( nUChar == -1 )  
  1152.                     nUChar = '?';  
  1153.                 if ( p32 )  
  1154.                     *p32++ = (unsigned int)nUChar;  
  1155.                 ++nToLen;  
  1156.             }  
  1157.             delete[] pwszUTF16;  
  1158. #endif // ICONV  
  1159.         }  
  1160.     }  
  1161.     else  
  1162.     {  
  1163. #if defined(MARKUP_ICONV)  
  1164.         nToLen = IConv( pTo, 1, 1 );  
  1165. #elif defined(MARKUP_WINCONV)  
  1166.         wchar_t* pwszUTF16 = new wchar_t[m_nFromLen];  
  1167.         int nUTF16Len = MultiByteToWideChar(nFromCP,0,(const char*)m_pFrom,m_nFromLen,pwszUTF16,m_nFromLen);  
  1168.         nToLen = WideCharToMultiByte(nToCP,0,pwszUTF16,nUTF16Len,(char*)pTo,m_nToCount,NULL,  
  1169.             x_NoDefaultChar(nToCP)?NULL:&m_nFailedChars);  
  1170.         delete[] pwszUTF16;  
  1171. #endif // WINCONV  
  1172.     }  
  1173.   
  1174.     // Store the length in case this is called again after allocating output buffer to fit  
  1175.     m_nToCount = nToLen;  
  1176.     return nToLen;  
  1177. }  
  1178.   
  1179. bool TextEncoding::FindRaggedEnd( int& nTruncBeforeBytes )  
  1180. {  
  1181.     // Check for ragged end UTF-16 or multi-byte according to m_strToEncoding, expects at least 40 bytes to work with  
  1182.     bool bSuccess = true;  
  1183.     nTruncBeforeBytes = 0;  
  1184.     int nCP = x_GetEncodingCodePage( m_strFromEncoding );  
  1185.     if ( nCP == MCD_UTF16 )  
  1186.     {  
  1187.         unsigned short* pUTF16Buffer = (unsigned short*)m_pFrom;  
  1188.         const unsigned short* pUTF16Last = &pUTF16Buffer[m_nFromLen-1];  
  1189.         if ( CMarkup::DecodeCharUTF16(pUTF16Last,&pUTF16Buffer[m_nFromLen]) == -1 )  
  1190.             nTruncBeforeBytes = 2;  
  1191.     }  
  1192.     else // UTF-8, SBCS DBCS  
  1193.     {  
  1194.         if ( nCP == MCD_UTF8 )  
  1195.         {  
  1196.             char* pUTF8Buffer = (char*)m_pFrom;  
  1197.             char* pUTF8End = &pUTF8Buffer[m_nFromLen];  
  1198.             int nLast = m_nFromLen - 1;  
  1199.             const char* pUTF8Last = &pUTF8Buffer[nLast];  
  1200.             while ( nLast > 0 && CMarkup::DecodeCharUTF8(pUTF8Last,pUTF8End) == -1 )  
  1201.                 pUTF8Last = &pUTF8Buffer[--nLast];  
  1202.             nTruncBeforeBytes = (int)(pUTF8End - pUTF8Last);  
  1203.         }  
  1204.         else  
  1205.         {  
  1206.             // Do a conversion-based test unless we can determine it is not multi-byte  
  1207.             // If m_strEncoding="" default code page then GetACP can tell us the code page, otherwise just do the test  
  1208. #if defined(MARKUP_WINCONV)  
  1209.             if ( nCP == 0 )  
  1210.                 nCP = GetACP();  
  1211. #endif  
  1212.             int nMultibyteCharsToTest = 2;  
  1213.             switch ( nCP )  
  1214.             {  
  1215.                 case 54936:  
  1216.                     nMultibyteCharsToTest = 4;  
  1217.                 case 932: case 51932: case 20932: case 50220: case 50221: case 50222: case 10001: // Japanese  
  1218.                 case 949: case 51949: case 50225: case 1361: case 10003: case 20949: // Korean  
  1219.                 case 874: case 20001: case 20004: case 10021: case 20003: // Taiwan  
  1220.                 case 50930: case 50939: case 50931: case 50933: case 20833: case 50935: case 50937: // EBCDIC  
  1221.                 case 936: case 51936: case 20936: case 52936: // Chinese  
  1222.                 case 950: case 50227: case 10008: case 20000: case 20002: case 10002: // Chinese  
  1223.                     nCP = 0;  
  1224.                     break;  
  1225.             }  
  1226.             if ( nMultibyteCharsToTest > m_nFromLen )  
  1227.                 nMultibyteCharsToTest = m_nFromLen;  
  1228.             if ( nCP == 0 && nMultibyteCharsToTest )  
  1229.             {  
  1230.                 /* 
  1231.                 1. convert the piece to Unicode with MultiByteToWideChar  
  1232.                 2. Identify at least two Unicode code point boundaries at the end of  
  1233.                 the converted piece by stepping backwards from the end and re-  
  1234.                 converting the final 2 bytes, 3 bytes, 4 bytes etc, comparing the  
  1235.                 converted end string to the end of the entire converted piece to find  
  1236.                 a valid code point boundary.  
  1237.                 3. Upon finding a code point boundary, I still want to make sure it  
  1238.                 will convert the same separately on either side of the divide as it  
  1239.                 does together, so separately convert the first byte and the remaining  
  1240.                 bytes and see if the result together is the same as the whole end, if  
  1241.                 not try the first two bytes and the remaining bytes. etc., until I  
  1242.                 find a useable dividing point. If none found, go back to step 2 and  
  1243.                 get a longer end string to try.  
  1244.                 */  
  1245.                 m_strToEncoding = MCD_T("UTF-16");  
  1246.                 m_nToCount = m_nFromLen*2;  
  1247.                 unsigned short* pUTF16Buffer = new unsigned short[m_nToCount];  
  1248.                 int nUTF16Len = PerformConversion( (void*)pUTF16Buffer );  
  1249.                 int nOriginalByteLen = m_nFromLen;  
  1250.   
  1251.                 // Guaranteed to have at least MARKUP_FILEBLOCKSIZE/2 bytes to work with  
  1252.                 const int nMaxBytesToTry = 40;  
  1253.                 unsigned short wsz16End[nMaxBytesToTry*2];  
  1254.                 unsigned short wsz16EndDivided[nMaxBytesToTry*2];  
  1255.                 const char* pszOriginalBytes = (const char*)m_pFrom;  
  1256.                 int nBoundariesFound = 0;  
  1257.                 bSuccess = false;  
  1258.                 while ( nTruncBeforeBytes < nMaxBytesToTry && ! bSuccess )  
  1259.                 {  
  1260.                     ++nTruncBeforeBytes;  
  1261.                     m_pFrom = &pszOriginalBytes[nOriginalByteLen-nTruncBeforeBytes];  
  1262.                     m_nFromLen = nTruncBeforeBytes;  
  1263.                     m_nToCount = nMaxBytesToTry*2;  
  1264.                     int nEndUTF16Len = PerformConversion( (void*)wsz16End );  
  1265.                     if ( nEndUTF16Len && memcmp(wsz16End,&pUTF16Buffer[nUTF16Len-nEndUTF16Len],nEndUTF16Len*2) == 0 )  
  1266.                     {  
  1267.                         ++nBoundariesFound;  
  1268.                         if ( nBoundariesFound > 2 )  
  1269.                         {  
  1270.                             int nDivideAt = 1;  
  1271.                             while ( nDivideAt < nTruncBeforeBytes )  
  1272.                             {  
  1273.                                 m_pFrom = &pszOriginalBytes[nOriginalByteLen-nTruncBeforeBytes];  
  1274.                                 m_nFromLen = nDivideAt;  
  1275.                                 m_nToCount = nMaxBytesToTry*2;  
  1276.                                 int nDividedUTF16Len = PerformConversion( (void*)wsz16EndDivided );  
  1277.                                 if ( nDividedUTF16Len )  
  1278.                                 {  
  1279.                                     m_pFrom = &pszOriginalBytes[nOriginalByteLen-nTruncBeforeBytes+nDivideAt];  
  1280.                                     m_nFromLen = nTruncBeforeBytes-nDivideAt;  
  1281.                                     m_nToCount = nMaxBytesToTry*2-nDividedUTF16Len;  
  1282.                                     nDividedUTF16Len += PerformConversion( (void*)&wsz16EndDivided[nDividedUTF16Len] );  
  1283.                                     if ( m_nToCount && nEndUTF16Len == nDividedUTF16Len && memcmp(wsz16End,wsz16EndDivided,nEndUTF16Len) == 0 )  
  1284.                                     {  
  1285.                                         nTruncBeforeBytes -= nDivideAt;  
  1286.                                         bSuccess = true;  
  1287.                                         break;  
  1288.                                     }  
  1289.                                 }  
  1290.                                 ++nDivideAt;  
  1291.                             }  
  1292.                         }  
  1293.                     }  
  1294.                 }  
  1295.                 delete [] pUTF16Buffer;  
  1296.             }  
  1297.         }  
  1298.     }  
  1299.     return bSuccess;  
  1300. }  
  1301.   
  1302. bool x_EndianSwapRequired( int nDocFlags )  
  1303. {  
  1304.     short nWord = 1;  
  1305.     char cFirstByte = ((char*)&nWord)[0];  
  1306.     if ( cFirstByte ) // LE  
  1307.     {  
  1308.         if ( nDocFlags & CMarkup::MDF_UTF16BEFILE )  
  1309.             return true;  
  1310.     }  
  1311.     else if ( nDocFlags & CMarkup::MDF_UTF16LEFILE )  
  1312.         return true;  
  1313.     return false;  
  1314. }  
  1315.   
  1316. void x_EndianSwapUTF16( unsigned short* pBuffer, int nCharLen )  
  1317. {  
  1318.     unsigned short cChar;  
  1319.     while ( nCharLen-- )  
  1320.     {  
  1321.         cChar = pBuffer[nCharLen];  
  1322.         pBuffer[nCharLen] = (unsigned short)((cChar<<8) | (cChar>>8));  
  1323.     }  
  1324. }  
  1325.   
  1326. //////////////////////////////////////////////////////////////////////  
  1327. // Element position indexes  
  1328. // This is the primary means of storing the layout of the document  
  1329. //  
  1330. struct ElemPos  
  1331. {  
  1332.     ElemPos() {};  
  1333.     ElemPos( const ElemPos& pos ) { *this = pos; };  
  1334.     int StartTagLen() const { return nStartTagLen; };  
  1335.     void SetStartTagLen( int n ) { nStartTagLen = n; };  
  1336.     void AdjustStartTagLen( int n ) { nStartTagLen += n; };  
  1337.     int EndTagLen() const { return nEndTagLen; };  
  1338.     void SetEndTagLen( int n ) { nEndTagLen = n; };  
  1339.     bool IsEmptyElement() { return (StartTagLen()==nLength)?true:false; };  
  1340.     int StartContent() const { return nStart + StartTagLen(); };  
  1341.     int ContentLen() const { return nLength - StartTagLen() - EndTagLen(); };  
  1342.     int StartAfter() const { return nStart + nLength; };  
  1343.     int Level() const { return nFlags & 0xffff; };  
  1344.     void SetLevel( int nLev ) { nFlags = (nFlags & ~0xffff) | nLev; };  
  1345.     void ClearVirtualParent() { memset(this,0,sizeof(ElemPos)); };  
  1346.     void SetEndTagLenUnparsed() { SetEndTagLen(1); };  
  1347.     bool IsUnparsed() { return EndTagLen() == 1; };  
  1348.   
  1349.     // Memory size: 8 32-bit integers == 32 bytes  
  1350.     int nStart;  
  1351.     int nLength;  
  1352.     unsigned int nStartTagLen : 22; // 4MB limit for start tag  
  1353.     unsigned int nEndTagLen : 10; // 1K limit for end tag  
  1354.     int nFlags; // 16 bits flags, 16 bits level 65536 depth limit  
  1355.     int iElemParent;  
  1356.     int iElemChild; // first child  
  1357.     int iElemNext; // next sibling  
  1358.     int iElemPrev; // if this is first, iElemPrev points to last  
  1359. };  
  1360.   
  1361. enum MarkupNodeFlagsInternal2  
  1362. {  
  1363.     MNF_REPLACE    = 0x001000,  
  1364.     MNF_QUOTED     = 0x008000,  
  1365.     MNF_EMPTY      = 0x010000,  
  1366.     MNF_DELETED    = 0x020000,  
  1367.     MNF_FIRST      = 0x080000,  
  1368.     MNF_PUBLIC     = 0x300000,  
  1369.     MNF_ILLFORMED  = 0x800000,  
  1370.     MNF_USER      = 0xf000000  
  1371. };  
  1372.   
  1373. struct ElemPosTree  
  1374. {  
  1375.     ElemPosTree() { Clear(); };  
  1376.     ~ElemPosTree() { Release(); };  
  1377.     enum { PA_SEGBITS = 16, PA_SEGMASK = 0xffff };  
  1378.     void ReleaseElemPosTree() { Release(); Clear(); };  
  1379.     void Release() { for (int n=0;n
  1380.     void Clear() { m_nSegs=0; m_nSize=0; m_pSegs=NULL; };  
  1381.     int GetSize() const { return m_nSize; };  
  1382.     int SegsUsed() const { return ((m_nSize-1)>>PA_SEGBITS) + 1; };  
  1383.     ElemPos& GetRefElemPosAt(int i) const { return m_pSegs[i>>PA_SEGBITS][i&PA_SEGMASK]; };  
  1384.     void CopyElemPosTree( ElemPosTree* pOtherTree, int n );  
  1385.     void GrowElemPosTree( int nNewSize );  
  1386. private:  
  1387.     ElemPos** m_pSegs;  
  1388.     int m_nSize;  
  1389.     int m_nSegs;  
  1390. };  
  1391.   
  1392. void ElemPosTree::CopyElemPosTree( ElemPosTree* pOtherTree, int n )  
  1393. {  
  1394.     ReleaseElemPosTree();  
  1395.     m_nSize = n;  
  1396.     if ( m_nSize < 8 )  
  1397.         m_nSize = 8;  
  1398.     m_nSegs = SegsUsed();  
  1399.     if ( m_nSegs )  
  1400.     {  
  1401.         m_pSegs = (ElemPos**)(new char[m_nSegs*sizeof(char*)]);  
  1402.         int nSegSize = 1 << PA_SEGBITS;  
  1403.         for ( int nSeg=0; nSeg < m_nSegs; ++nSeg )  
  1404.         {  
  1405.             if ( nSeg + 1 == m_nSegs )  
  1406.                 nSegSize = m_nSize - (nSeg << PA_SEGBITS);  
  1407.             m_pSegs[nSeg] = (ElemPos*)(new char[nSegSize*sizeof(ElemPos)]);  
  1408.             memcpy( m_pSegs[nSeg], pOtherTree->m_pSegs[nSeg], nSegSize*sizeof(ElemPos) );  
  1409.         }  
  1410.     }  
  1411. }  
  1412.   
  1413. void ElemPosTree::GrowElemPosTree( int nNewSize )  
  1414. {  
  1415.     // Called by x_AllocElemPos when the document is created or the array is filled  
  1416.     // The ElemPosTree class is implemented using segments to reduce contiguous memory requirements  
  1417.     // It reduces reallocations (copying of memory) since this only occurs within one segment  
  1418.     // The "Grow By" algorithm ensures there are no reallocations after 2 segments  
  1419.     //  
  1420.     // Grow By: new size can be at most one more complete segment  
  1421.     int nSeg = (m_nSize?m_nSize-1:0) >> PA_SEGBITS;  
  1422.     int nNewSeg = (nNewSize-1) >> PA_SEGBITS;  
  1423.     if ( nNewSeg > nSeg + 1 )  
  1424.     {  
  1425.         nNewSeg = nSeg + 1;  
  1426.         nNewSize = (nNewSeg+1) << PA_SEGBITS;  
  1427.     }  
  1428.   
  1429.     // Allocate array of segments  
  1430.     if ( m_nSegs <= nNewSeg )  
  1431.     {  
  1432.         int nNewSegments = 4 + nNewSeg * 2;  
  1433.         char* pNewSegments = new char[nNewSegments*sizeof(char*)];  
  1434.         if ( SegsUsed() )  
  1435.             memcpy( pNewSegments, m_pSegs, SegsUsed()*sizeof(char*) );  
  1436.         if ( m_pSegs )  
  1437.             delete[] (char*)m_pSegs;  
  1438.         m_pSegs = (ElemPos**)pNewSegments;  
  1439.         m_nSegs = nNewSegments;  
  1440.     }  
  1441.   
  1442.     // Calculate segment sizes  
  1443.     int nSegSize = m_nSize - (nSeg << PA_SEGBITS);  
  1444.     int nNewSegSize = nNewSize - (nNewSeg << PA_SEGBITS);  
  1445.   
  1446.     // Complete first segment  
  1447.     int nFullSegSize = 1 << PA_SEGBITS;  
  1448.     if ( nSeg < nNewSeg && nSegSize < nFullSegSize )  
  1449.     {  
  1450.         char* pNewFirstSeg = new char[ nFullSegSize * sizeof(ElemPos) ];  
  1451.         if ( nSegSize )  
  1452.         {  
  1453.             // Reallocate  
  1454.             memcpy( pNewFirstSeg, m_pSegs[nSeg], nSegSize * sizeof(ElemPos) );  
  1455.             delete[] (char*)m_pSegs[nSeg];  
  1456.         }  
  1457.         m_pSegs[nSeg] = (ElemPos*)pNewFirstSeg;  
  1458.     }  
  1459.   
  1460.     // New segment  
  1461.     char* pNewSeg = new char[ nNewSegSize * sizeof(ElemPos) ];  
  1462.     if ( nNewSeg == nSeg && nSegSize )  
  1463.     {  
  1464.         // Reallocate  
  1465.         memcpy( pNewSeg, m_pSegs[nSeg], nSegSize * sizeof(ElemPos) );  
  1466.         delete[] (char*)m_pSegs[nSeg];  
  1467.     }  
  1468.     m_pSegs[nNewSeg] = (ElemPos*)pNewSeg;  
  1469.     m_nSize = nNewSize;  
  1470. }  
  1471.   
  1472. #define ELEM(i) m_pElemPosTree->GetRefElemPosAt(i)  
  1473.   
  1474. //////////////////////////////////////////////////////////////////////  
  1475. // NodePos stores information about an element or node during document creation and parsing  
  1476. //  
  1477. struct NodePos  
  1478. {  
  1479.     NodePos() {};  
  1480.     NodePos( int n ) { nNodeFlags=n; nNodeType=0; nStart=0; nLength=0; };  
  1481.     int nNodeType;  
  1482.     int nStart;  
  1483.     int nLength;  
  1484.     int nNodeFlags;  
  1485.     MCD_STR strMeta;  
  1486. };  
  1487.   
  1488. //////////////////////////////////////////////////////////////////////  
  1489. // Token struct and tokenizing functions  
  1490. // TokenPos handles parsing operations on a constant text pointer   
  1491. //  
  1492. struct TokenPos  
  1493. {  
  1494.     TokenPos( MCD_CSTR sz, int n, FilePos* p=NULL ) { Clear(); m_pDocText=sz; m_nTokenFlags=n; m_pReaderFilePos=p; };  
  1495.     void Clear() { m_nL=0; m_nR=-1; m_nNext=0; };  
  1496.     int Length() const { return m_nR - m_nL + 1; };  
  1497.     MCD_PCSZ GetTokenPtr() const { return &m_pDocText[m_nL]; };  
  1498.     MCD_STR GetTokenText() const { return MCD_STR( GetTokenPtr(), Length() ); };  
  1499.     int WhitespaceToTag( int n ) { m_nNext = n; if (FindAny()&&m_pDocText[m_nNext]!='<') { m_nNext=n; m_nR=n-1; } return m_nNext; };  
  1500.     void ForwardUntil( MCD_PCSZ szStopChars ) { while ( m_pDocText[m_nNext] && ! MCD_PSZCHR(szStopChars,m_pDocText[m_nNext]) ) m_nNext += MCD_CLEN(&m_pDocText[m_nNext]); }  
  1501.     bool FindAny()  
  1502.     {  
  1503.         // Go to non-whitespace or end  
  1504.         while ( m_pDocText[m_nNext] && MCD_PSZCHR(MCD_T(" /t/n/r"),m_pDocText[m_nNext]) )  
  1505.             ++m_nNext;  
  1506.         m_nL = m_nNext;  
  1507.         m_nR = m_nNext-1;  
  1508.         return m_pDocText[m_nNext]!='/0';  
  1509.     };  
  1510.     bool FindName()  
  1511.     {  
  1512.         if ( ! FindAny() ) // go to first non-whitespace  
  1513.             return false;  
  1514.         ForwardUntil(MCD_T(" /t/n/r<>=///?!/"';"));  
  1515.         if ( m_nNext == m_nL )  
  1516.             ++m_nNext; // it is a special char  
  1517.         m_nR = m_nNext - 1;  
  1518.         return true;  
  1519.     }  
  1520.     static int StrNIACmp( MCD_PCSZ p1, MCD_PCSZ p2, int n )  
  1521.     {  
  1522.         // string compare ignore case  
  1523.         bool bNonAsciiFound = false;  
  1524.         MCD_CHAR c1, c2;  
  1525.         while ( n-- )  
  1526.         {  
  1527.             c1 = *p1++;  
  1528.             c2 = *p2++;  
  1529.             if ( c1 != c2 )  
  1530.             {  
  1531.                 if ( bNonAsciiFound )  
  1532.                     return c1 - c2;  
  1533.                 if ( c1 >= 'a' && c1 <= 'z' )  
  1534.                     c1 = (MCD_CHAR)( c1 - ('a'-'A') );  
  1535.                 if ( c2 >= 'a' && c2 <= 'z' )  
  1536.                     c2 = (MCD_CHAR)( c2 - ('a'-'A') );  
  1537.                 if ( c1 != c2 )  
  1538.                     return c1 - c2;  
  1539.             }  
  1540.             else if ( (unsigned int)c1 > 127 )  
  1541.                 bNonAsciiFound = true;  
  1542.         }  
  1543.         return 0;  
  1544.     }  
  1545.   
  1546.     bool Match( MCD_CSTR szName )  
  1547.     {  
  1548.         int nLen = Length();  
  1549.         if ( m_nTokenFlags & CMarkup::MDF_IGNORECASE )  
  1550.             return ( (StrNIACmp( GetTokenPtr(), szName, nLen ) == 0)  
  1551.                 && ( szName[nLen] == '/0' || MCD_PSZCHR(MCD_T(" =/[]"),szName[nLen]) ) );  
  1552.         else  
  1553.             return ( (MCD_PSZNCMP( GetTokenPtr(), szName, nLen ) == 0)  
  1554.                 && ( szName[nLen] == '/0' || MCD_PSZCHR(MCD_T(" =/[]"),szName[nLen]) ) );  
  1555.     };  
  1556.     bool FindAttrib( MCD_PCSZ pAttrib, int n = 0 );  
  1557.     int ParseNode( NodePos& node );  
  1558.     int m_nL;  
  1559.     int m_nR;  
  1560.     int m_nNext;  
  1561.     MCD_PCSZ m_pDocText;  
  1562.     int m_nTokenFlags;  
  1563.     int m_nPreSpaceStart;  
  1564.     int m_nPreSpaceLength;  
  1565.     FilePos* m_pReaderFilePos;  
  1566. };  
  1567.   
  1568. bool TokenPos::FindAttrib( MCD_PCSZ pAttrib, int n/*=0*/ )  
  1569. {  
  1570.     // Return true if found, otherwise false and token.m_nNext is new insertion point  
  1571.     // If pAttrib is NULL find attrib n and leave token at attrib name  
  1572.     // If pAttrib is given, find matching attrib and leave token at value  
  1573.     // support non-well-formed attributes e.g. href=/advanced_search?hl=en, nowrap  
  1574.     // token also holds start and length of preceeding whitespace to support remove  
  1575.     //  
  1576.     int nTempPreSpaceStart;  
  1577.     int nTempPreSpaceLength;  
  1578.     MCD_CHAR cFirstChar;  
  1579.     int nAttrib = -1; // starts at tag name  
  1580.     int nFoundAttribNameR = 0;  
  1581.     bool bAfterEqual = false;  
  1582.     while ( 1 )  
  1583.     {  
  1584.         // Starting at m_nNext, bypass whitespace and find the next token  
  1585.         nTempPreSpaceStart = m_nNext;  
  1586.         if ( ! FindAny() )  
  1587.             break;  
  1588.         nTempPreSpaceLength = m_nNext - nTempPreSpaceStart;  
  1589.   
  1590.         // Is it an opening quote?  
  1591.         cFirstChar = m_pDocText[m_nNext];  
  1592.         if ( cFirstChar == '/"' || cFirstChar == '/'' )  
  1593.         {  
  1594.             m_nTokenFlags |= MNF_QUOTED;  
  1595.   
  1596.             // Move past opening quote  
  1597.             ++m_nNext;  
  1598.             m_nL = m_nNext;  
  1599.   
  1600.             // Look for closing quote  
  1601.             while ( m_pDocText[m_nNext] && m_pDocText[m_nNext] != cFirstChar )  
  1602.                 m_nNext += MCD_CLEN( &m_pDocText[m_nNext] );  
  1603.   
  1604.             // Set right to before closing quote  
  1605.             m_nR = m_nNext - 1;  
  1606.   
  1607.             // Set m_nNext past closing quote unless at end of document  
  1608.             if ( m_pDocText[m_nNext] )  
  1609.                 ++m_nNext;  
  1610.         }  
  1611.         else  
  1612.         {  
  1613.             m_nTokenFlags &= ~MNF_QUOTED;  
  1614.   
  1615.             // Go until special char or whitespace  
  1616.             m_nL = m_nNext;  
  1617.             if ( bAfterEqual )  
  1618.                 ForwardUntil(MCD_T(" /t/n/r>"));  
  1619.             else  
  1620.                 ForwardUntil(MCD_T("= /t/n/r>/?"));  
  1621.   
  1622.             // Adjust end position if it is one special char  
  1623.             if ( m_nNext == m_nL )  
  1624.                 ++m_nNext; // it is a special char  
  1625.             m_nR = m_nNext - 1;  
  1626.         }  
  1627.   
  1628.         if ( ! bAfterEqual && ! (m_nTokenFlags&MNF_QUOTED) )  
  1629.         {  
  1630.             // Is it an equal sign?  
  1631.             MCD_CHAR cChar = m_pDocText[m_nL];  
  1632.             if ( cChar == '=' )  
  1633.             {  
  1634.                 bAfterEqual = true;  
  1635.                 continue;  
  1636.             }  
  1637.   
  1638.             // Is it the right angle bracket?  
  1639.             if ( cChar == '>' || cChar == '/' || cChar == '?' )  
  1640.             {  
  1641.                 m_nNext = nTempPreSpaceStart;  
  1642.                 break; // attrib not found  
  1643.             }  
  1644.   
  1645.             if ( nFoundAttribNameR )  
  1646.                 break;  
  1647.   
  1648.             // Attribute name  
  1649.             if ( nAttrib != -1 )  
  1650.             {  
  1651.                 if ( ! pAttrib )  
  1652.                 {  
  1653.                     if ( nAttrib == n )  
  1654.                         return true; // found by number  
  1655.                 }  
  1656.                 else if ( Match(pAttrib) )  
  1657.                 {  
  1658.                     // Matched attrib name, go forward to value  
  1659.                     nFoundAttribNameR = m_nR;  
  1660.                     m_nPreSpaceStart = nTempPreSpaceStart;  
  1661.                     m_nPreSpaceLength = nTempPreSpaceLength;  
  1662.                 }  
  1663.             }  
  1664.             ++nAttrib;  
  1665.         }  
  1666.         else if ( nFoundAttribNameR )  
  1667.             break;  
  1668.         bAfterEqual = false;  
  1669.     }  
  1670.   
  1671.     if ( nFoundAttribNameR )  
  1672.     {  
  1673.         if ( ! bAfterEqual )  
  1674.         {  
  1675.             // when attribute has no value the value is the attribute name  
  1676.             m_nL = m_nPreSpaceStart + m_nPreSpaceLength;  
  1677.             m_nR = nFoundAttribNameR;  
  1678.             m_nNext = nFoundAttribNameR + 1;  
  1679.         }  
  1680.         return true; // found by name  
  1681.     }  
  1682.     return false; // not found  
  1683. }  
  1684.   
  1685. //////////////////////////////////////////////////////////////////////  
  1686. // Element tag stack: an array of TagPos structs to track nested elements  
  1687. // This is used during parsing to match end tags with corresponding start tags  
  1688. // For x_ParseElem only ElemStack::iTop is used with PushIntoLevel, PopOutOfLevel, and Current  
  1689. // For file mode then the full capabilities are used to track counts of sibling tag names for path support  
  1690. //  
  1691. struct TagPos  
  1692. {  
  1693.     TagPos() { Init(); };  
  1694.     void SetTagName( MCD_PCSZ pName, int n ) { MCD_STRASSIGN(strTagName,pName,n); };  
  1695.     void Init( int i=0, int n=1 ) { nCount=1; nTagNames=n; iNext=i; iPrev=0; nSlot=-1; iSlotPrev=0; iSlotNext=0; };  
  1696.     void IncCount() { if (nCount) ++nCount; };  
  1697.     MCD_STR strTagName;  
  1698.     int nCount;  
  1699.     int nTagNames;  
  1700.     int iParent;  
  1701.     int iNext;  
  1702.     int iPrev;  
  1703.     int nSlot;  
  1704.     int iSlotNext;  
  1705.     int iSlotPrev;  
  1706. };  
  1707.   
  1708. struct ElemStack  
  1709. {  
  1710.     enum { LS_TABLESIZE = 23 };  
  1711.     ElemStack() { iTop=0; iUsed=0; iPar=0; nLevel=0; nSize=0; pL=NULL; Alloc(7); pL[0].Init(); InitTable(); };  
  1712.     ~ElemStack() { if (pL) delete [] pL; };  
  1713.     TagPos& Current() { return pL[iTop]; };  
  1714.     void InitTable() { memset(anTable,0,sizeof(int)*LS_TABLESIZE); };  
  1715.     TagPos& NextParent( int& i ) { int iCur=i; i=pL[i].iParent; return pL[iCur]; };  
  1716.     TagPos& GetRefTagPosAt( int i ) { return pL[i]; };  
  1717.     void Push( MCD_PCSZ pName, int n ) { ++iUsed; if (iUsed==nSize) Alloc(nSize*2); pL[iUsed].SetTagName(pName,n); pL[iUsed].iParent=iPar; iTop=iUsed; };  
  1718.     void IntoLevel() { iPar = iTop; ++nLevel; };  
  1719.     void OutOfLevel() { if (iPar!=iTop) Pop(); iPar = pL[iTop].iParent; --nLevel; };  
  1720.     void PushIntoLevel( MCD_PCSZ pName, int n ) { ++iTop; if (iTop==nSize) Alloc(nSize*2); pL[iTop].SetTagName(pName,n); };  
  1721.     void PopOutOfLevel() { --iTop; };  
  1722.     void Pop() { iTop = iPar; while (iUsed && pL[iUsed].iParent==iPar) { if (pL[iUsed].nSlot!=-1) Unslot(pL[iUsed]); --iUsed; } };  
  1723.     void Slot( int n ) { pL[iUsed].nSlot=n; int i=anTable[n]; anTable[n]=iUsed; pL[iUsed].iSlotNext=i; if (i) pL[i].iSlotPrev=iUsed; };  
  1724.     void Unslot( TagPos& lp ) { int n=lp.iSlotNext,p=lp.iSlotPrev; if (n) pL[n].iSlotPrev=p; if (p) pL[p].iSlotNext=n; else anTable[lp.nSlot]=n; };  
  1725.     static int CalcSlot( MCD_PCSZ pName, int n, bool bIC );  
  1726.     void PushTagAndCount( TokenPos& token );  
  1727.     int iTop;  
  1728.     int nLevel;  
  1729.     int iPar;  
  1730. protected:  
  1731.     void Alloc( int nNewSize ) { TagPos* pLNew = new TagPos[nNewSize]; Copy(pLNew); nSize=nNewSize; };  
  1732.     void Copy( TagPos* pLNew ) { for(int n=0;n
  1733.     TagPos* pL;  
  1734.     int iUsed;  
  1735.     int nSize;  
  1736.     int anTable[LS_TABLESIZE];  
  1737. };  
  1738.   
  1739. int ElemStack::CalcSlot( MCD_PCSZ pName, int n, bool bIC )  
  1740. {  
  1741.     // If bIC (ASCII ignore case) then return an ASCII case insensitive hash  
  1742.     unsigned int nHash = 0;  
  1743.     MCD_PCSZ pEnd = pName + n;  
  1744.     while ( pName != pEnd )  
  1745.     {  
  1746.         nHash += (unsigned int)(*pName);  
  1747.         if ( bIC && *pName >= 'A' && *pName <= 'Z' )  
  1748.             nHash += ('a'-'A');  
  1749.         ++pName;  
  1750.     }  
  1751.     return nHash%LS_TABLESIZE;  
  1752. }  
  1753.   
  1754. void ElemStack::PushTagAndCount( TokenPos& token )  
  1755. {  
  1756.     // Check for a matching tag name at the top level and set current if found or add new one  
  1757.     // Calculate hash of tag name, support ignore ASCII case for MDF_IGNORECASE  
  1758.     int nSlot = -1;  
  1759.     int iNext = 0;  
  1760.     MCD_PCSZ pTagName = token.GetTokenPtr();  
  1761.     if ( iTop != iPar )  
  1762.     {  
  1763.         // See if tag name is already used, first try previous sibling (almost always)  
  1764.         iNext = iTop;  
  1765.         if ( token.Match(Current().strTagName) )  
  1766.         {  
  1767.             iNext = -1;  
  1768.             Current().IncCount();  
  1769.         }  
  1770.         else  
  1771.         {  
  1772.             nSlot = CalcSlot( pTagName, token.Length(), (token.m_nTokenFlags & CMarkup::MDF_IGNORECASE)?true:false );  
  1773.             int iLookup = anTable[nSlot];  
  1774.             while ( iLookup )  
  1775.             {  
  1776.                 TagPos& tag = pL[iLookup];  
  1777.                 if ( tag.iParent == iPar && token.Match(tag.strTagName) )  
  1778.                 {  
  1779.                     pL[tag.iPrev].iNext = tag.iNext;  
  1780.                     if ( tag.iNext )  
  1781.                         pL[tag.iNext].iPrev = tag.iPrev;  
  1782.                     tag.nTagNames = Current().nTagNames;  
  1783.                     tag.iNext = iTop;  
  1784.                     tag.IncCount();  
  1785.                     iTop = iLookup;  
  1786.                     iNext = -1;  
  1787.                     break;  
  1788.                 }  
  1789.                 iLookup = tag.iSlotNext;  
  1790.             }  
  1791.         }  
  1792.     }  
  1793.     if ( iNext != -1 )  
  1794.     {  
  1795.         // Turn off in the rare case where a document uses unique tag names like record1, record2, etc, more than 256  
  1796.         int nTagNames = 0;  
  1797.         if ( iNext )  
  1798.             nTagNames = Current().nTagNames;  
  1799.         if ( nTagNames == 256 )  
  1800.         {  
  1801.             MCD_STRASSIGN( (Current().strTagName), pTagName, (token.Length()) );  
  1802.             Current().nCount = 0;  
  1803.             Unslot( Current() );  
  1804.         }  
  1805.         else  
  1806.         {  
  1807.             Push( pTagName, token.Length() );  
  1808.             Current().Init( iNext, nTagNames+1 );  
  1809.         }  
  1810.         if ( nSlot == -1 )  
  1811.             nSlot = CalcSlot( pTagName, token.Length(), (token.m_nTokenFlags & CMarkup::MDF_IGNORECASE)?true:false );  
  1812.         Slot( nSlot );  
  1813.     }  
  1814. }  
  1815.   
  1816. //////////////////////////////////////////////////////////////////////  
  1817. // FilePos is created for a file while it is open  
  1818. // In file mode the file stays open between CMarkup calls and is stored in m_pFilePos  
  1819. //  
  1820. struct FilePos  
  1821. {  
  1822.     FilePos()  
  1823.     {  
  1824.         m_fp=NULL; m_nDocFlags=0; m_nFileByteLen=0; m_nFileByteOffset=0; m_nOpFileByteLen=0; m_nBlockSizeBasis=MARKUP_FILEBLOCKSIZE;  
  1825.         m_nFileCharUnitSize=0; m_nOpFileTextLen=0; m_pstrBuffer=NULL; m_nReadBufferStart=0; m_nReadBufferRemoved=0; m_nReadGatherStart=-1;  
  1826.     };  
  1827.     bool FileOpen( MCD_CSTR_FILENAME szFileName );  
  1828.     bool FileRead( void* pBuffer );  
  1829.     bool FileReadText( MCD_STR& strDoc );  
  1830.     bool FileCheckRaggedEnd( void* pBuffer );  
  1831.     bool FileReadNextBuffer();  
  1832.     void FileGatherStart( int nStart );  
  1833.     int FileGatherEnd( MCD_STR& strSubDoc );  
  1834.     bool FileWrite( void* pBuffer, const void* pConstBuffer = NULL );  
  1835.     bool FileWriteText( const MCD_STR& strDoc, int nWriteStrLen = -1 );  
  1836.     bool FileFlush( MCD_STR& strBuffer, int nWriteStrLen = -1, bool bFflush = false );  
  1837.     bool FileClose();  
  1838.     void FileSpecifyEncoding( MCD_STR* pstrEncoding );  
  1839.     bool FileAtTop();  
  1840.     bool FileErrorAddResult();  
  1841.   
  1842.     FILE* m_fp;  
  1843.     int m_nDocFlags;  
  1844.     int m_nOpFileByteLen;  
  1845.     int m_nBlockSizeBasis;  
  1846.     MCD_INTFILEOFFSET m_nFileByteLen;  
  1847.     MCD_INTFILEOFFSET m_nFileByteOffset;  
  1848.     int m_nFileCharUnitSize;  
  1849.     int m_nOpFileTextLen;  
  1850.     MCD_STR m_strIOResult;  
  1851.     MCD_STR m_strEncoding;  
  1852.     MCD_STR* m_pstrBuffer;  
  1853.     ElemStack m_elemstack;  
  1854.     int m_nReadBufferStart;  
  1855.     int m_nReadBufferRemoved;  
  1856.     int m_nReadGatherStart;  
  1857.     MCD_STR m_strReadGatherMarkup;  
  1858. };  
  1859.   
  1860. struct BomTableStruct { const char* pszBom; int nBomLen; MCD_PCSZ pszBomEnc; int nBomFlag; } BomTable[] =  
  1861. {  
  1862.     { "/xef/xbb/xbf", 3, MCD_T("UTF-8"), CMarkup::MDF_UTF8PREAMBLE },  
  1863.     { "/xff/xfe", 2, MCD_T("UTF-16LE"), CMarkup::MDF_UTF16LEFILE },  
  1864.     { "/xfe/xff", 2, MCD_T("UTF-16BE"), CMarkup::MDF_UTF16BEFILE },  
  1865.     { NULL,0,NULL,0 }  
  1866. };  
  1867.   
  1868. bool FilePos::FileErrorAddResult()  
  1869. {  
  1870.     // strerror has difficulties cross-platform  
  1871.     // VC++ leaves MCD_STRERROR undefined and uses FormatMessage  
  1872.     // Non-VC++ use strerror (even for MARKUP_WCHAR and convert)  
  1873.     // additional notes:  
  1874.     // _WIN32_WCE (Windows CE) has no strerror (Embedded VC++ uses FormatMessage)   
  1875.     // _MSC_VER >= 1310 (VC++ 2003/7.1) has _wcserror (but not used)  
  1876.     //  
  1877.     const int nErrorBufferSize = 100;  
  1878.     int nErr = 0;  
  1879.     MCD_CHAR szError[nErrorBufferSize+1];  
  1880. #if defined(MCD_STRERROR) // C error routine  
  1881.     nErr = (int)errno;  
  1882. #if defined(MARKUP_WCHAR)  
  1883.     char szMBError[nErrorBufferSize+1];  
  1884.     strncpy( szMBError, MCD_STRERROR, nErrorBufferSize );  
  1885.     szMBError[nErrorBufferSize] = '/0';  
  1886.     TextEncoding textencoding( MCD_T(""), (const void*)szMBError, strlen(szMBError) );  
  1887.     textencoding.m_nToCount = nErrorBufferSize;  
  1888.     int nWideLen = textencoding.PerformConversion( (void*)szError, MCD_ENC );  
  1889.     szError[nWideLen] = '/0';  
  1890. #else  
  1891.     MCD_PSZNCPY( szError, MCD_STRERROR, nErrorBufferSize );  
  1892.     szError[nErrorBufferSize] = '/0';  
  1893. #endif  
  1894. #else // no C error routine, use Windows API  
  1895.     DWORD dwErr = ::GetLastError();  
  1896.     if ( ::FormatMessage(0x1200,0,dwErr,0,szError,nErrorBufferSize,0) < 1 )  
  1897.         szError[0] = '/0';  
  1898.     nErr = (int)dwErr;  
  1899. #endif // no C error routine  
  1900.     MCD_STR strError = szError;  
  1901.     for ( int nChar=0; nChar
  1902.         if ( strError[nChar] == '/r' || strError[nChar] == '/n' )  
  1903.         {  
  1904.             strError = MCD_STRMID( strError, 0, nChar ); // no trailing newline  
  1905.             break;  
  1906.         }  
  1907.     x_AddResult( m_strIOResult, MCD_T("file_error"), strError, MRC_MSG|MRC_NUMBER, nErr );  
  1908.     return false;  
  1909. }  
  1910.   
  1911. void FilePos::FileSpecifyEncoding( MCD_STR* pstrEncoding )  
  1912. {  
  1913.     // In ReadTextFile, WriteTextFile and Open, the pstrEncoding argument can override or return the detected encoding  
  1914.     if ( pstrEncoding && m_strEncoding != *pstrEncoding )  
  1915.     {  
  1916.         if ( m_nFileCharUnitSize == 1 && *pstrEncoding != MCD_T("")  )  
  1917.             m_strEncoding = *pstrEncoding; // override the encoding  
  1918.         else // just report the encoding  
  1919.             *pstrEncoding = m_strEncoding;  
  1920.     }  
  1921. }  
  1922.   
  1923. bool FilePos::FileAtTop()  
  1924. {  
  1925.     // Return true if in the first block of file mode, max BOM < 5 bytes  
  1926.     if ( ((m_nDocFlags & CMarkup::MDF_READFILE) && m_nFileByteOffset < (MCD_INTFILEOFFSET)m_nOpFileByteLen + 5 )  
  1927.             || ((m_nDocFlags & CMarkup::MDF_WRITEFILE) && m_nFileByteOffset < 5) )  
  1928.         return true;  
  1929.     return false;  
  1930. }  
  1931.   
  1932. bool FilePos::FileOpen( MCD_CSTR_FILENAME szFileName )  
  1933. {  
  1934.     MCD_STRCLEAR( m_strIOResult );  
  1935.   
  1936.     // Open file  
  1937.     MCD_PCSZ_FILENAME pMode = MCD_T_FILENAME("rb");  
  1938.     if ( m_nDocFlags & CMarkup::MDF_APPENDFILE )  
  1939.         pMode = MCD_T_FILENAME("ab");  
  1940.     else if ( m_nDocFlags & CMarkup::MDF_WRITEFILE )  
  1941.         pMode = MCD_T_FILENAME("wb");  
  1942.     m_fp = NULL;  
  1943.     MCD_FOPEN( m_fp, szFileName, pMode );  
  1944.     if ( ! m_fp )  
  1945.         return FileErrorAddResult();  
  1946.   
  1947.     // Prepare file  
  1948.     bool bSuccess = true;  
  1949.     int nBomLen = 0;  
  1950.     m_nFileCharUnitSize = 1; // unless UTF-16 BOM  
  1951.     if ( m_nDocFlags & CMarkup::MDF_READFILE )  
  1952.     {  
  1953.         // Get file length  
  1954.         MCD_FSEEK( m_fp, 0, SEEK_END );  
  1955.         m_nFileByteLen = MCD_FTELL( m_fp );  
  1956.         MCD_FSEEK( m_fp, 0, SEEK_SET );  
  1957.   
  1958.         // Read the top of the file to check BOM and encoding  
  1959.         int nReadTop = 1024;  
  1960.         if ( m_nFileByteLen < nReadTop )  
  1961.             nReadTop = (int)m_nFileByteLen;  
  1962.         if ( nReadTop )  
  1963.         {  
  1964.             char* pFileTop = new char[nReadTop];  
  1965.             if ( nReadTop )  
  1966.                 bSuccess = ( fread( pFileTop, nReadTop, 1, m_fp ) == 1 );  
  1967.             if ( bSuccess )  
  1968.             {  
  1969.                 // Check for Byte Order Mark (preamble)  
  1970.                 int nBomCheck = 0;  
  1971.                 m_nDocFlags &= ~( CMarkup::MDF_UTF16LEFILE | CMarkup::MDF_UTF8PREAMBLE );  
  1972.                 while ( BomTable[nBomCheck].pszBom )  
  1973.                 {  
  1974.                     while ( nBomLen < BomTable[nBomCheck].nBomLen )  
  1975.                     {  
  1976.                         if ( nBomLen >= nReadTop || pFileTop[nBomLen] != BomTable[nBomCheck].pszBom[nBomLen] )  
  1977.                             break;  
  1978.                         ++nBomLen;  
  1979.                     }  
  1980.                     if ( nBomLen == BomTable[nBomCheck].nBomLen )  
  1981.                     {  
  1982.                         m_nDocFlags |= BomTable[nBomCheck].nBomFlag;  
  1983.                         if ( nBomLen == 2 )  
  1984.                             m_nFileCharUnitSize = 2;  
  1985.                         m_strEncoding = BomTable[nBomCheck].pszBomEnc;  
  1986.                         break;  
  1987.                     }  
  1988.                     ++nBomCheck;  
  1989.                     nBomLen = 0;  
  1990.                 }  
  1991.                 if ( nReadTop > nBomLen )  
  1992.                     MCD_FSEEK( m_fp, nBomLen, SEEK_SET );  
  1993.   
  1994.                 // Encoding check  
  1995.                 if ( ! nBomLen )  
  1996.                 {  
  1997.                     MCD_STR strDeclCheck;  
  1998. #if defined(MARKUP_WCHAR) // WCHAR  
  1999.                     TextEncoding textencoding( MCD_T("UTF-8"), (const void*)pFileTop, nReadTop );  
  2000.                     MCD_CHAR* pWideBuffer = MCD_GETBUFFER(strDeclCheck,nReadTop);  
  2001.                     textencoding.m_nToCount = nReadTop;  
  2002.                     int nDeclWideLen = textencoding.PerformConversion( (void*)pWideBuffer, MCD_ENC );  
  2003.                     MCD_RELEASEBUFFER(strDeclCheck,pWideBuffer,nDeclWideLen);  
  2004. #else // not WCHAR  
  2005.                     MCD_STRASSIGN(strDeclCheck,pFileTop,nReadTop);  
  2006. #endif // not WCHAR  
  2007.                     m_strEncoding = CMarkup::GetDeclaredEncoding( strDeclCheck );  
  2008.                 }  
  2009.                 // Assume markup files starting with < sign are UTF-8 if otherwise unknown  
  2010.                 if ( MCD_STRISEMPTY(m_strEncoding) && pFileTop[0] == '<' )  
  2011.                     m_strEncoding = MCD_T("UTF-8");  
  2012.             }  
  2013.             delete [] pFileTop;  
  2014.         }  
  2015.     }  
  2016.     else if ( m_nDocFlags & CMarkup::MDF_WRITEFILE )  
  2017.     {  
  2018.         if ( m_nDocFlags & CMarkup::MDF_APPENDFILE )  
  2019.         {  
  2020.             // fopen for append does not move the file pointer to the end until first I/O operation  
  2021.             MCD_FSEEK( m_fp, 0, SEEK_END );  
  2022.             m_nFileByteLen = MCD_FTELL( m_fp );  
  2023.         }  
  2024.         int nBomCheck = 0;  
  2025.         while ( BomTable[nBomCheck].pszBom )  
  2026.         {  
  2027.             if ( m_nDocFlags & BomTable[nBomCheck].nBomFlag )  
  2028.             {  
  2029.                 nBomLen = BomTable[nBomCheck].nBomLen;  
  2030.                 if ( nBomLen == 2 )  
  2031.                     m_nFileCharUnitSize = 2;  
  2032.                 m_strEncoding = BomTable[nBomCheck].pszBomEnc;  
  2033.                 if ( m_nFileByteLen ) // append  
  2034.                     nBomLen = 0;  
  2035.                 else // write BOM  
  2036.                     bSuccess = ( fwrite(BomTable[nBomCheck].pszBom,nBomLen,1,m_fp) == 1 );  
  2037.                 break;  
  2038.             }  
  2039.             ++nBomCheck;  
  2040.         }  
  2041.     }  
  2042.     if ( ! bSuccess )  
  2043.         return FileErrorAddResult();  
  2044.   
  2045.     if ( m_nDocFlags & CMarkup::MDF_APPENDFILE )  
  2046.         m_nFileByteOffset = m_nFileByteLen;  
  2047.     else  
  2048.         m_nFileByteOffset = (MCD_INTFILEOFFSET)nBomLen;  
  2049.     if ( nBomLen )  
  2050.         x_AddResult( m_strIOResult, MCD_T("bom") );  
  2051.     return bSuccess;  
  2052. }  
  2053.   
  2054. bool FilePos::FileRead( void* pBuffer )  
  2055. {  
  2056.     bool bSuccess = ( fread( pBuffer,m_nOpFileByteLen,1,m_fp) == 1 );  
  2057.     m_nOpFileTextLen = m_nOpFileByteLen / m_nFileCharUnitSize;  
  2058.     if ( bSuccess )  
  2059.     {  
  2060.         m_nFileByteOffset += m_nOpFileByteLen;  
  2061.         x_AddResult( m_strIOResult, MCD_T("read"), m_strEncoding, MRC_ENCODING|MRC_LENGTH, m_nOpFileTextLen );  
  2062.   
  2063.         // Microsoft components can produce apparently valid docs with some nulls at ends of values  
  2064.         int nNullCount = 0;  
  2065.         int nNullCheckCharsRemaining = m_nOpFileTextLen;  
  2066.         char* pAfterNull = NULL;  
  2067.         char* pNullScan = (char*)pBuffer;  
  2068.         bool bSingleByteChar = m_nFileCharUnitSize == 1;  
  2069.         while ( nNullCheckCharsRemaining-- )  
  2070.         {  
  2071.             if ( bSingleByteChar? (! *pNullScan) : (! (*(unsigned short*)pNullScan)) )  
  2072.             {  
  2073.                 if ( pAfterNull && pNullScan != pAfterNull )  
  2074.                     memmove( pAfterNull - (nNullCount*m_nFileCharUnitSize), pAfterNull, pNullScan - pAfterNull );  
  2075.                 pAfterNull = pNullScan  + m_nFileCharUnitSize;  
  2076.                 ++nNullCount;  
  2077.             }  
  2078.             pNullScan += m_nFileCharUnitSize;  
  2079.         }  
  2080.         if ( pAfterNull && pNullScan != pAfterNull )  
  2081.             memmove( pAfterNull - (nNullCount*m_nFileCharUnitSize), pAfterNull, pNullScan - pAfterNull );  
  2082.         if ( nNullCount )  
  2083.         {  
  2084.             x_AddResult( m_strIOResult, MCD_T("nulls_removed"), NULL, MRC_COUNT, nNullCount );  
  2085.             m_nOpFileTextLen -= nNullCount;  
  2086.         }  
  2087.   
  2088.         // Big endian/little endian conversion  
  2089.         if ( m_nFileCharUnitSize > 1 && x_EndianSwapRequired(m_nDocFlags) )  
  2090.         {  
  2091.             x_EndianSwapUTF16( (unsigned short*)pBuffer, m_nOpFileTextLen );  
  2092.             x_AddResult( m_strIOResult, MCD_T("endian_swap") );  
  2093.         }  
  2094.     }  
  2095.     if ( ! bSuccess )  
  2096.         FileErrorAddResult();  
  2097.     return bSuccess;  
  2098. }  
  2099.   
  2100. bool FilePos::FileCheckRaggedEnd( void* pBuffer )  
  2101. {  
  2102.     // In file read mode, piece of file text in memory must end on a character boundary  
  2103.     // This check must happen after the encoding has been decided, so after UTF-8 autodetection  
  2104.     // If ragged, adjust file position, m_nOpFileTextLen and m_nOpFileByteLen  
  2105.     int nTruncBeforeBytes = 0;  
  2106.     TextEncoding textencoding( m_strEncoding, pBuffer, m_nOpFileTextLen );  
  2107.     if ( ! textencoding.FindRaggedEnd(nTruncBeforeBytes) )  
  2108.     {  
  2109.         // Input must be garbled? decoding error before potentially ragged end, add error result and continue  
  2110.         MCD_STR strEncoding = m_strEncoding;  
  2111.         if ( MCD_STRISEMPTY(strEncoding) )  
  2112.             strEncoding = MCD_T("ANSI");  
  2113.         x_AddResult( m_strIOResult, MCD_T("truncation_error"), strEncoding, MRC_ENCODING );  
  2114.     }  
  2115.     else if ( nTruncBeforeBytes )  
  2116.     {  
  2117.         nTruncBeforeBytes *= -1;  
  2118.         m_nFileByteOffset += nTruncBeforeBytes;  
  2119.         MCD_FSEEK( m_fp, m_nFileByteOffset, SEEK_SET );  
  2120.         m_nOpFileByteLen += nTruncBeforeBytes;  
  2121.         m_nOpFileTextLen += nTruncBeforeBytes / m_nFileCharUnitSize;  
  2122.         x_AddResult( m_strIOResult, MCD_T("read"), NULL, MRC_MODIFY|MRC_LENGTH, m_nOpFileTextLen );  
  2123.     }  
  2124.     return true;  
  2125. }  
  2126.   
  2127. bool FilePos::FileReadText( MCD_STR& strDoc )  
  2128. {  
  2129.     bool bSuccess = true;  
  2130.     MCD_STRCLEAR( m_strIOResult );  
  2131.     if ( ! m_nOpFileByteLen )  
  2132.     {  
  2133.         x_AddResult( m_strIOResult, MCD_T("read"), m_strEncoding, MRC_ENCODING|MRC_LENGTH, 0 );  
  2134.         return bSuccess;  
  2135.     }  
  2136.   
  2137.     // Only read up to end of file (a single read byte length cannot be over the capacity of int)  
  2138.     bool bCheckRaggedEnd = true;  
  2139.     MCD_INTFILEOFFSET nBytesRemaining = m_nFileByteLen - m_nFileByteOffset;  
  2140.     if ( (MCD_INTFILEOFFSET)m_nOpFileByteLen >= nBytesRemaining )  
  2141.     {  
  2142.         m_nOpFileByteLen = (int)nBytesRemaining;  
  2143.         bCheckRaggedEnd = false;  
  2144.     }  
  2145.   
  2146.     if ( m_nDocFlags & (CMarkup::MDF_UTF16LEFILE | CMarkup::MDF_UTF16BEFILE) )  
  2147.     {  
  2148.         int nUTF16Len = m_nOpFileByteLen / 2;  
  2149. #if defined(MARKUP_WCHAR) // WCHAR  
  2150.         int nBufferSizeForGrow = nUTF16Len + nUTF16Len/100; // extra 1%  
  2151. #if MARKUP_SIZEOFWCHAR == 4 // sizeof(wchar_t) == 4  
  2152.         unsigned short* pUTF16Buffer = new unsigned short[nUTF16Len+1];  
  2153.         bSuccess = FileRead( pUTF16Buffer );  
  2154.         if ( bSuccess )  
  2155.         {  
  2156.             if ( bCheckRaggedEnd )  
  2157.                 FileCheckRaggedEnd( (void*)pUTF16Buffer );  
  2158.             TextEncoding textencoding( MCD_T("UTF-16"), (const void*)pUTF16Buffer, m_nOpFileTextLen );  
  2159.             textencoding.m_nToCount = nBufferSizeForGrow;  
  2160.             MCD_CHAR* pUTF32Buffer = MCD_GETBUFFER(strDoc,nBufferSizeForGrow);  
  2161.             int nUTF32Len = textencoding.PerformConversion( (void*)pUTF32Buffer, MCD_T("UTF-32") );  
  2162.             MCD_RELEASEBUFFER(strDoc,pUTF32Buffer,nUTF32Len);  
  2163.             x_AddResult( m_strIOResult, MCD_T("converted_to"), MCD_T("UTF-32"), MRC_ENCODING|MRC_LENGTH, nUTF32Len );  
  2164.         }  
  2165. #else // sizeof(wchar_t) == 2  
  2166.         MCD_CHAR* pUTF16Buffer = MCD_GETBUFFER(strDoc,nBufferSizeForGrow);  
  2167.         bSuccess = FileRead( pUTF16Buffer );  
  2168.         if ( bSuccess && bCheckRaggedEnd )  
  2169.             FileCheckRaggedEnd( (void*)pUTF16Buffer );  
  2170.         MCD_RELEASEBUFFER(strDoc,pUTF16Buffer,m_nOpFileTextLen);  
  2171. #endif // sizeof(wchar_t) == 2  
  2172. #else // not WCHAR  
  2173.         // Convert file from UTF-16; it needs to be in memory as UTF-8 or MBCS  
  2174.         unsigned short* pUTF16Buffer = new unsigned short[nUTF16Len+1];  
  2175.         bSuccess = FileRead( pUTF16Buffer );  
  2176.         if ( bSuccess && bCheckRaggedEnd )  
  2177.             FileCheckRaggedEnd( (void*)pUTF16Buffer );  
  2178.         TextEncoding textencoding( MCD_T("UTF-16"), (const void*)pUTF16Buffer, m_nOpFileTextLen );  
  2179.         int nMBLen = textencoding.PerformConversion( NULL, MCD_ENC );  
  2180.         int nBufferSizeForGrow = nMBLen + nMBLen/100; // extra 1%  
  2181.         MCD_CHAR* pMBBuffer = MCD_GETBUFFER(strDoc,nBufferSizeForGrow);  
  2182.         textencoding.PerformConversion( (void*)pMBBuffer );  
  2183.         delete [] pUTF16Buffer;  
  2184.         MCD_RELEASEBUFFER(strDoc,pMBBuffer,nMBLen);  
  2185.         x_AddResult( m_strIOResult, MCD_T("converted_to"), MCD_ENC, MRC_ENCODING|MRC_LENGTH, nMBLen );  
  2186.         if ( textencoding.m_nFailedChars )  
  2187.             x_AddResult( m_strIOResult, MCD_T("conversion_loss") );  
  2188. #endif // not WCHAR  
  2189.     }  
  2190.     else // single or multibyte file (i.e. not UTF-16)  
  2191.     {  
  2192. #if defined(MARKUP_WCHAR) // WCHAR  
  2193.         char* pBuffer = new char[m_nOpFileByteLen];  
  2194.         bSuccess = FileRead( pBuffer );  
  2195.         if ( MCD_STRISEMPTY(m_strEncoding) )  
  2196.         {  
  2197.             int nNonASCII;  
  2198.             bool bErrorAtEnd;  
  2199.             if ( CMarkup::DetectUTF8(pBuffer,m_nOpFileByteLen,&nNonASCII,&bErrorAtEnd) || (bCheckRaggedEnd && bErrorAtEnd) )  
  2200.             {  
  2201.                 m_strEncoding = MCD_T("UTF-8");  
  2202.                 x_AddResult( m_strIOResult, MCD_T("read"), m_strEncoding, MRC_MODIFY|MRC_ENCODING );  
  2203.             }  
  2204.             x_AddResult( m_strIOResult, MCD_T("utf8_detection") );  
  2205.         }  
  2206.         if ( bSuccess && bCheckRaggedEnd )  
  2207.             FileCheckRaggedEnd( (void*)pBuffer );  
  2208.         TextEncoding textencoding( m_strEncoding, (const void*)pBuffer, m_nOpFileTextLen );  
  2209.         int nWideLen = textencoding.PerformConversion( NULL, MCD_ENC );  
  2210.         int nBufferSizeForGrow = nWideLen + nWideLen/100; // extra 1%  
  2211.         MCD_CHAR* pWideBuffer = MCD_GETBUFFER(strDoc,nBufferSizeForGrow);  
  2212.         textencoding.PerformConversion( (void*)pWideBuffer );  
  2213.         MCD_RELEASEBUFFER( strDoc, pWideBuffer, nWideLen );  
  2214.         delete [] pBuffer;  
  2215.         x_AddResult( m_strIOResult, MCD_T("converted_to"), MCD_ENC, MRC_ENCODING|MRC_LENGTH, nWideLen );  
  2216. #else // not WCHAR  
  2217.         // After loading a file with unknown multi-byte encoding  
  2218.         bool bAssumeUnknownIsNative = false;  
  2219.         if ( MCD_STRISEMPTY(m_strEncoding) )  
  2220.         {  
  2221.             bAssumeUnknownIsNative = true;  
  2222.             m_strEncoding = MCD_ENC;  
  2223.         }  
  2224.         if ( TextEncoding::CanConvert(MCD_ENC,m_strEncoding) )  
  2225.         {  
  2226.             char* pBuffer = new char[m_nOpFileByteLen];  
  2227.             bSuccess = FileRead( pBuffer );  
  2228.             if ( bSuccess && bCheckRaggedEnd )  
  2229.                 FileCheckRaggedEnd( (void*)pBuffer );  
  2230.             TextEncoding textencoding( m_strEncoding, (const void*)pBuffer, m_nOpFileTextLen );  
  2231.             int nMBLen = textencoding.PerformConversion( NULL, MCD_ENC );  
  2232.             int nBufferSizeForGrow = nMBLen + nMBLen/100; // extra 1%  
  2233.             MCD_CHAR* pMBBuffer = MCD_GETBUFFER(strDoc,nBufferSizeForGrow);  
  2234.             textencoding.PerformConversion( (void*)pMBBuffer );  
  2235.             MCD_RELEASEBUFFER( strDoc, pMBBuffer, nMBLen );  
  2236.             delete [] pBuffer;  
  2237.             x_AddResult( m_strIOResult, MCD_T("converted_to"), MCD_ENC, MRC_ENCODING|MRC_LENGTH, nMBLen );  
  2238.             if ( textencoding.m_nFailedChars )  
  2239.                 x_AddResult( m_strIOResult, MCD_T("conversion_loss") );  
  2240.         }  
  2241.         else // load directly into string  
  2242.         {  
  2243.             int nBufferSizeForGrow = m_nOpFileByteLen + m_nOpFileByteLen/100; // extra 1%  
  2244.             MCD_CHAR* pBuffer = MCD_GETBUFFER(strDoc,nBufferSizeForGrow);  
  2245.             bSuccess = FileRead( pBuffer );  
  2246.             bool bConvertMB = false;  
  2247.             if ( bAssumeUnknownIsNative )  
  2248.             {  
  2249.                 // Might need additional conversion if we assumed an encoding  
  2250.                 int nNonASCII;  
  2251.                 bool bErrorAtEnd;  
  2252.                 bool bIsUTF8 = CMarkup::DetectUTF8( pBuffer, m_nOpFileByteLen, &nNonASCII, &bErrorAtEnd ) || (bCheckRaggedEnd && bErrorAtEnd);  
  2253.                 MCD_STR strDetectedEncoding = bIsUTF8? MCD_T("UTF-8"): MCD_T("");  
  2254.                 if ( nNonASCII && m_strEncoding != strDetectedEncoding ) // only need to convert non-ASCII  
  2255.                     bConvertMB = true;  
  2256.                 m_strEncoding = strDetectedEncoding;  
  2257.                 if ( bIsUTF8 )  
  2258.                     x_AddResult( m_strIOResult, MCD_T("read"), m_strEncoding, MRC_MODIFY|MRC_ENCODING );  
  2259.             }  
  2260.             if ( bSuccess && bCheckRaggedEnd )  
  2261.                 FileCheckRaggedEnd( (void*)pBuffer );  
  2262.             MCD_RELEASEBUFFER( strDoc, pBuffer, m_nOpFileTextLen );  
  2263.             if ( bConvertMB )  
  2264.             {  
  2265.                 TextEncoding textencoding( m_strEncoding, MCD_2PCSZ(strDoc), m_nOpFileTextLen );  
  2266.                 int nMBLen = textencoding.PerformConversion( NULL, MCD_ENC );  
  2267.                 nBufferSizeForGrow = nMBLen + nMBLen/100; // extra 1%  
  2268.                 MCD_STR strConvDoc;  
  2269.                 pBuffer = MCD_GETBUFFER(strConvDoc,nBufferSizeForGrow);  
  2270.                 textencoding.PerformConversion( (void*)pBuffer );  
  2271.                 MCD_RELEASEBUFFER( strConvDoc, pBuffer, nMBLen );  
  2272.                 strDoc = strConvDoc;  
  2273.                 x_AddResult( m_strIOResult, MCD_T("converted_to"), MCD_ENC, MRC_ENCODING|MRC_LENGTH, nMBLen );  
  2274.                 if ( textencoding.m_nFailedChars )  
  2275.                     x_AddResult( m_strIOResult, MCD_T("conversion_loss") );  
  2276.             }  
  2277.             if ( bAssumeUnknownIsNative )  
  2278.                 x_AddResult( m_strIOResult, MCD_T("utf8_detection") );  
  2279.         }  
  2280. #endif // not WCHAR  
  2281.     }  
  2282.     return bSuccess;  
  2283. }  
  2284.   
  2285. bool FilePos::FileWrite( void* pBuffer, const void* pConstBuffer /*=NULL*/ )  
  2286. {  
  2287.     m_nOpFileByteLen = m_nOpFileTextLen * m_nFileCharUnitSize;  
  2288.     if ( ! pConstBuffer )  
  2289.         pConstBuffer = pBuffer;  
  2290.     unsigned short* pTempEndianBuffer = NULL;  
  2291.     if ( x_EndianSwapRequired(m_nDocFlags) )  
  2292.     {  
  2293.         if ( ! pBuffer )  
  2294.         {  
  2295.             pTempEndianBuffer = new unsigned short[m_nOpFileTextLen];  
  2296.             memcpy( pTempEndianBuffer, pConstBuffer, m_nOpFileTextLen * 2 );  
  2297.             pBuffer = pTempEndianBuffer;  
  2298.             pConstBuffer = pTempEndianBuffer;  
  2299.         }  
  2300.         x_EndianSwapUTF16( (unsigned short*)pBuffer, m_nOpFileTextLen );  
  2301.         x_AddResult( m_strIOResult, MCD_T("endian_swap") );  
  2302.     }  
  2303.     bool bSuccess = ( fwrite( pConstBuffer, m_nOpFileByteLen, 1, m_fp ) == 1 );  
  2304.     if ( pTempEndianBuffer )  
  2305.         delete [] pTempEndianBuffer;  
  2306.     if ( bSuccess )  
  2307.     {  
  2308.         m_nFileByteOffset += m_nOpFileByteLen;  
  2309.         x_AddResult( m_strIOResult, MCD_T("write"), m_strEncoding, MRC_ENCODING|MRC_LENGTH, m_nOpFileTextLen );  
  2310.     }  
  2311.     else  
  2312.         FileErrorAddResult();  
  2313.     return bSuccess;  
  2314. }  
  2315.   
  2316. bool FilePos::FileWriteText( const MCD_STR& strDoc, int nWriteStrLen/*=-1*/ )  
  2317. {  
  2318.     bool bSuccess = true;  
  2319.     MCD_STRCLEAR( m_strIOResult );  
  2320.     MCD_PCSZ pDoc = MCD_2PCSZ(strDoc);  
  2321.     if ( nWriteStrLen == -1 )  
  2322.         nWriteStrLen = MCD_STRLENGTH(strDoc);  
  2323.     if ( ! nWriteStrLen )  
  2324.     {  
  2325.         x_AddResult( m_strIOResult, MCD_T("write"), m_strEncoding, MRC_ENCODING|MRC_LENGTH, 0 );  
  2326.         return bSuccess;  
  2327.     }  
  2328.   
  2329.     if ( m_nDocFlags & (CMarkup::MDF_UTF16LEFILE | CMarkup::MDF_UTF16BEFILE) )  
  2330.     {  
  2331. #if defined(MARKUP_WCHAR) // WCHAR  
  2332. #if MARKUP_SIZEOFWCHAR == 4 // sizeof(wchar_t) == 4  
  2333.         TextEncoding textencoding( MCD_T("UTF-32"), (const void*)pDoc, nWriteStrLen );  
  2334.         m_nOpFileTextLen = textencoding.PerformConversion( NULL, MCD_T("UTF-16") );  
  2335.         unsigned short* pUTF16Buffer = new unsigned short[m_nOpFileTextLen];  
  2336.         textencoding.PerformConversion( (void*)pUTF16Buffer );  
  2337.         x_AddResult( m_strIOResult, MCD_T("converted_from"), MCD_T("UTF-32"), MRC_ENCODING|MRC_LENGTH, nWriteStrLen );  
  2338.         bSuccess = FileWrite( pUTF16Buffer );  
  2339.         delete [] pUTF16Buffer;  
  2340. #else // sizeof(wchar_t) == 2  
  2341.         m_nOpFileTextLen = nWriteStrLen;  
  2342.         bSuccess = FileWrite( NULL, pDoc );  
  2343. #endif  
  2344. #else // not WCHAR  
  2345.         TextEncoding textencoding( MCD_ENC, (const void*)pDoc, nWriteStrLen );  
  2346.         m_nOpFileTextLen = textencoding.PerformConversion( NULL, MCD_T("UTF-16") );  
  2347.         unsigned short* pUTF16Buffer = new unsigned short[m_nOpFileTextLen];  
  2348.         textencoding.PerformConversion( (void*)pUTF16Buffer );  
  2349.         x_AddResult( m_strIOResult, MCD_T("converted_from"), MCD_ENC, MRC_ENCODING|MRC_LENGTH, nWriteStrLen );  
  2350.         bSuccess = FileWrite( pUTF16Buffer );  
  2351.         delete [] pUTF16Buffer;  
  2352. #endif // not WCHAR  
  2353.     }  
  2354.     else // single or multibyte file (i.e. not UTF-16)  
  2355.     {  
  2356. #if ! defined(MARKUP_WCHAR) // not WCHAR  
  2357.         if ( ! TextEncoding::CanConvert(m_strEncoding,MCD_ENC) )  
  2358.         {  
  2359.             // Same or unsupported multi-byte to multi-byte, so save directly from string  
  2360.             m_nOpFileTextLen = nWriteStrLen;  
  2361.             bSuccess = FileWrite( NULL, pDoc );  
  2362.             return bSuccess;  
  2363.         }  
  2364. #endif // not WCHAR  
  2365.         TextEncoding textencoding( MCD_ENC, (const void*)pDoc, nWriteStrLen );  
  2366.         m_nOpFileTextLen = textencoding.PerformConversion( NULL, m_strEncoding );  
  2367.         char* pMBBuffer = new char[m_nOpFileTextLen];  
  2368.         textencoding.PerformConversion( (void*)pMBBuffer );  
  2369.         x_AddResult( m_strIOResult, MCD_T("converted_from"), MCD_ENC, MRC_ENCODING|MRC_LENGTH, nWriteStrLen );  
  2370.         if ( textencoding.m_nFailedChars )  
  2371.             x_AddResult( m_strIOResult, MCD_T("conversion_loss") );  
  2372.         bSuccess = FileWrite( pMBBuffer );  
  2373.         delete [] pMBBuffer;  
  2374.     }  
  2375.   
  2376.     return bSuccess;  
  2377. }  
  2378.   
  2379. bool FilePos::FileClose()  
  2380. {  
  2381.     if ( m_fp )  
  2382.     {  
  2383.         if ( fclose(m_fp) )  
  2384.             FileErrorAddResult();  
  2385.         m_fp = NULL;  
  2386.         m_nDocFlags &= ~(CMarkup::MDF_WRITEFILE|CMarkup::MDF_READFILE|CMarkup::MDF_APPENDFILE);  
  2387.         return true;  
  2388.     }  
  2389.     return false;  
  2390. }  
  2391.   
  2392. bool FilePos::FileReadNextBuffer()  
  2393. {  
  2394.     // If not end of file, returns amount to subtract from offsets  
  2395.     if ( m_nFileByteOffset < m_nFileByteLen )  
  2396.     {  
  2397.         // Prepare to put this node at beginning  
  2398.         MCD_STR& str = *m_pstrBuffer;  
  2399.         int nDocLength = MCD_STRLENGTH( str );  
  2400.         int nRemove = m_nReadBufferStart;  
  2401.         m_nReadBufferRemoved = nRemove;  
  2402.   
  2403.         // Gather  
  2404.         if ( m_nReadGatherStart != -1 )  
  2405.         {  
  2406.             if ( m_nReadBufferStart > m_nReadGatherStart )  
  2407.             {  
  2408.                 // In case it is a large subdoc, reduce reallocs by using x_StrInsertReplace  
  2409.                 MCD_STR strAppend = MCD_STRMID( str, m_nReadGatherStart, m_nReadBufferStart - m_nReadGatherStart );  
  2410.                 x_StrInsertReplace( m_strReadGatherMarkup, MCD_STRLENGTH(m_strReadGatherMarkup), 0, strAppend );  
  2411.             }  
  2412.             m_nReadGatherStart = 0;  
  2413.         }  
  2414.   
  2415.         // Increase capacity if already at the beginning keeping more than half  
  2416.         int nKeepLength = nDocLength - nRemove;  
  2417.         int nEstimatedKeepBytes = m_nBlockSizeBasis * nKeepLength / nDocLength; // block size times fraction of doc kept  
  2418.         if ( nRemove == 0 || nKeepLength > nDocLength / 2 )  
  2419.             m_nBlockSizeBasis *= 2;  
  2420.         if ( nRemove )  
  2421.             x_StrInsertReplace( str, 0, nRemove, MCD_STR() );  
  2422.         MCD_STR strRead;  
  2423.         m_nOpFileByteLen = m_nBlockSizeBasis - nEstimatedKeepBytes;  
  2424.         m_nOpFileByteLen += 4 - m_nOpFileByteLen % 4;  
  2425.         FileReadText( strRead );  
  2426.         x_StrInsertReplace( str, nKeepLength, 0, strRead );  
  2427.         m_nReadBufferStart = 0; // next time just elongate/increase capacity  
  2428.         return true;  
  2429.     }  
  2430.     return false;  
  2431. }  
  2432.   
  2433. void FilePos::FileGatherStart( int nStart )  
  2434. {  
  2435.     m_nReadGatherStart = nStart;  
  2436. }  
  2437.   
  2438. int FilePos::FileGatherEnd( MCD_STR& strMarkup )  
  2439. {  
  2440.     int nStart = m_nReadGatherStart;  
  2441.     m_nReadGatherStart = -1;  
  2442.     strMarkup = m_strReadGatherMarkup;  
  2443.     MCD_STRCLEAR( m_strReadGatherMarkup );  
  2444.     return nStart;  
  2445. }  
  2446.   
  2447. bool FilePos::FileFlush( MCD_STR& strBuffer, int nWriteStrLen/*=-1*/, bool bFflush/*=false*/ )  
  2448. {  
  2449.     bool bSuccess = true;  
  2450.     MCD_STRCLEAR( m_strIOResult );  
  2451.     if ( nWriteStrLen == -1 )  
  2452.         nWriteStrLen = MCD_STRLENGTH( strBuffer );  
  2453.     if ( nWriteStrLen )  
  2454.     {  
  2455.         if ( (! m_nFileByteOffset) && MCD_STRISEMPTY(m_strEncoding) && ! MCD_STRISEMPTY(strBuffer) )  
  2456.         {  
  2457.             m_strEncoding = CMarkup::GetDeclaredEncoding( strBuffer );  
  2458.             if ( MCD_STRISEMPTY(m_strEncoding) )  
  2459.                 m_strEncoding = MCD_T("UTF-8");  
  2460.         }  
  2461.         bSuccess = FileWriteText( strBuffer, nWriteStrLen );  
  2462.         if ( bSuccess )  
  2463.             x_StrInsertReplace( strBuffer, 0, nWriteStrLen, MCD_STR() );  
  2464.     }  
  2465.     if ( bFflush && bSuccess )  
  2466.     {  
  2467.         if ( fflush(m_fp) )  
  2468.             bSuccess = FileErrorAddResult();  
  2469.     }  
  2470.     return bSuccess;  
  2471. }  
  2472.   
  2473. //////////////////////////////////////////////////////////////////////  
  2474. // PathPos encapsulates parsing of the path string used in Find methods  
  2475. //  
  2476. struct PathPos  
  2477. {  
  2478.     PathPos( MCD_PCSZ pszPath, bool b ) { p=pszPath; bReader=b; i=0; iPathAttribName=0; iSave=0; nPathType=0; if (!ParsePath()) nPathType=-1; };  
  2479.     int GetTypeAndInc() { i=-1; if (p) { if (p[0]=='/') { if (p[1]=='/') i=2; else i=1; } else if (p[0]) i=0; } nPathType=i+1; return nPathType; };  
  2480.     int GetNumAndInc() { int n=0; while (p[i]>='0'&&p[i]<='9') n=n*10+(int)p[i++]-(int)'0'; return n; };  
  2481.     MCD_PCSZ GetValAndInc() { ++i; MCD_CHAR cEnd=']'; if (p[i]=='/''||p[i]=='/"') cEnd=p[i++]; int iVal=i; IncWord(cEnd); nLen=i-iVal; if (cEnd!=']') ++i; return &p[iVal]; };  
  2482.     int GetValOrWordLen() { return nLen; };  
  2483.     MCD_CHAR GetChar() { return p[i]; };  
  2484.     bool IsAtPathEnd() { return ((!p[i])||(iPathAttribName&&i+2>=iPathAttribName))?true:false; };   
  2485.     MCD_PCSZ GetPtr() { return &p[i]; };  
  2486.     void SaveOffset() { iSave=i; };  
  2487.     void RevertOffset() { i=iSave; };  
  2488.     void RevertOffsetAsName() { i=iSave; nPathType=1; };  
  2489.     MCD_PCSZ GetWordAndInc() { int iWord=i; IncWord(); nLen=i-iWord; return &p[iWord]; };  
  2490.     void IncWord() { while (p[i]&&!MCD_PSZCHR(MCD_T(" =/[]"),p[i])) i+=MCD_CLEN(&p[i]); };  
  2491.     void IncWord( MCD_CHAR c ) { while (p[i]&&p[i]!=c) i+=MCD_CLEN(&p[i]); };  
  2492.     void IncChar() { ++i; };  
  2493.     void Inc( int n ) { i+=n; };  
  2494.     bool IsAnywherePath() { return nPathType == 3; };  
  2495.     bool IsAbsolutePath() { return nPathType == 2; };  
  2496.     bool IsPath() { return nPathType > 0; };  
  2497.     bool ValidPath() { return nPathType != -1; };  
  2498.     MCD_PCSZ GetPathAttribName() { if (iPathAttribName) return &p[iPathAttribName]; return NULL; };  
  2499.     bool AttribPredicateMatch( TokenPos& token );  
  2500. private:  
  2501.     bool ParsePath();  
  2502.     int nPathType; // -1 invalid, 0 empty, 1 name, 2 absolute path, 3 anywhere path  
  2503.     bool bReader;  
  2504.     MCD_PCSZ p;  
  2505.     int i;  
  2506.     int iPathAttribName;  
  2507.     int iSave;  
  2508.     int nLen;  
  2509. };  
  2510.   
  2511. bool PathPos::ParsePath()  
  2512. {  
  2513.     // Determine if the path seems to be in a valid format before attempting to find  
  2514.     if ( GetTypeAndInc() )  
  2515.     {  
  2516.         SaveOffset();  
  2517.         while ( 1 )  
  2518.         {  
  2519.             if ( ! GetChar() )  
  2520.                 return false;  
  2521.             IncWord(); // Tag name  
  2522.             if ( GetChar() == '[' ) // predicate  
  2523.             {  
  2524.                 IncChar(); // [  
  2525.                 if ( GetChar() >= '1' && GetChar() <= '9' )  
  2526.                     GetNumAndInc();  
  2527.                 else // attrib or child tag name  
  2528.                 {  
  2529.                     if ( GetChar() == '@' )  
  2530.                     {  
  2531.                         IncChar(); // @  
  2532.                         IncWord(); // attrib name  
  2533.                         if ( GetChar() == '=' )  
  2534.                             GetValAndInc();  
  2535.                     }  
  2536.                     else  
  2537.                     {  
  2538.                         if ( bReader )  
  2539.                             return false;  
  2540.                         IncWord();  
  2541.                     }  
  2542.                 }  
  2543.                 if ( GetChar() != ']' )  
  2544.                     return false;  
  2545.                 IncChar(); // ]  
  2546.             }  
  2547.   
  2548.             // Another level of path  
  2549.             if ( GetChar() == '/' )  
  2550.             {  
  2551.                 if ( IsAnywherePath() )  
  2552.                     return false; // multiple levels not supported for // path  
  2553.                 IncChar();  
  2554.                 if ( GetChar() == '@' )  
  2555.                 {  
  2556.                     // FindGetData and FindSetData support paths ending in attribute  
  2557.                     IncChar(); // @  
  2558.                     iPathAttribName = i;  
  2559.                     IncWord(); // attrib name  
  2560.                     if ( GetChar() )  
  2561.                         return false; // it should have ended with attribute name  
  2562.                     break;  
  2563.                 }  
  2564.             }  
  2565.             else  
  2566.             {  
  2567.                 if ( GetChar() )  
  2568.                     return false; // not a slash, so it should have ended here  
  2569.                 break;  
  2570.             }  
  2571.         }  
  2572.         RevertOffset();  
  2573.     }  
  2574.     return true;  
  2575. }  
  2576.   
  2577. bool PathPos::AttribPredicateMatch( TokenPos& token )  
  2578. {  
  2579.     // Support attribute predicate matching in regular and file read mode  
  2580.     // token.m_nNext must already be set to node.nStart + 1 or ELEM(i).nStart + 1  
  2581.     IncChar(); // @  
  2582.     if ( token.FindAttrib(GetPtr()) )  
  2583.     {  
  2584.         IncWord();  
  2585.         if ( GetChar() == '=' )  
  2586.         {  
  2587.             MCD_PCSZ pszVal = GetValAndInc();  
  2588.             MCD_STR strPathValue = CMarkup::UnescapeText( pszVal, GetValOrWordLen() );  
  2589.             MCD_STR strAttribValue = CMarkup::UnescapeText( token.GetTokenPtr(), token.Length() );  
  2590.             if ( strPathValue != strAttribValue )  
  2591.                 return false;  
  2592.         }  
  2593.         return true;  
  2594.     }  
  2595.     return false;  
  2596. }  
  2597.   
  2598. //////////////////////////////////////////////////////////////////////  
  2599. // A map is a table of SavedPos structs  
  2600. //  
  2601. struct SavedPos  
  2602. {  
  2603.     // SavedPos is an entry in the SavedPosMap hash table  
  2604.     SavedPos() { nSavedPosFlags=0; iPos=0; };  
  2605.     MCD_STR strName;  
  2606.     int iPos;  
  2607.     enum { SPM_MAIN = 1, SPM_CHILD = 2, SPM_USED = 4, SPM_LAST = 8 };  
  2608.     int nSavedPosFlags;  
  2609. };  
  2610.   
  2611. struct SavedPosMap  
  2612. {  
  2613.     // SavedPosMap is only created if SavePos/RestorePos are used  
  2614.     SavedPosMap( int nSize ) { nMapSize=nSize; pTable = new SavedPos*[nSize]; memset(pTable,0,nSize*sizeof(SavedPos*)); };  
  2615.     ~SavedPosMap() { if (pTable) { for (int n=0;n
  2616.     SavedPos** pTable;  
  2617.     int nMapSize;  
  2618. };  
  2619.   
  2620. struct SavedPosMapArray  
  2621. {  
  2622.     // SavedPosMapArray keeps pointers to SavedPosMap instances  
  2623.     SavedPosMapArray() { m_pMaps = NULL; };  
  2624.     ~SavedPosMapArray() { ReleaseMaps(); };  
  2625.     void ReleaseMaps() { SavedPosMap**p = m_pMaps; if (p) { while (*p) delete *p++; delete[] m_pMaps; m_pMaps=NULL; } };  
  2626.     bool GetMap( SavedPosMap*& pMap, int nMap, int nMapSize = 7 );  
  2627.     void CopySavedPosMaps( SavedPosMapArray* pOtherMaps );  
  2628.     SavedPosMap** m_pMaps; // NULL terminated array  
  2629. };  
  2630.   
  2631. bool SavedPosMapArray::GetMap( SavedPosMap*& pMap, int nMap, int nMapSize /*=7*/ )  
  2632. {  
  2633.     // Find or create map, returns true if map(s) created  
  2634.     SavedPosMap** pMapsExisting = m_pMaps;  
  2635.     int nMapIndex = 0;  
  2636.     if ( pMapsExisting )  
  2637.     {  
  2638.         // Length of array is unknown, so loop through maps  
  2639.         while ( nMapIndex <= nMap )  
  2640.         {  
  2641.             pMap = pMapsExisting[nMapIndex];  
  2642.             if ( ! pMap )  
  2643.                 break;  
  2644.             if ( nMapIndex == nMap )  
  2645.                 return false; // not created  
  2646.             ++nMapIndex;  
  2647.         }  
  2648.         nMapIndex = 0;  
  2649.     }  
  2650.   
  2651.     // Create map(s)  
  2652.     // If you access map 1 before map 0 created, then 2 maps will be created  
  2653.     m_pMaps = new SavedPosMap*[nMap+2];  
  2654.     if ( pMapsExisting )  
  2655.     {  
  2656.         while ( pMapsExisting[nMapIndex] )  
  2657.         {  
  2658.             m_pMaps[nMapIndex] = pMapsExisting[nMapIndex];  
  2659.             ++nMapIndex;  
  2660.         }  
  2661.         delete[] pMapsExisting;  
  2662.     }  
  2663.     while ( nMapIndex <= nMap )  
  2664.     {  
  2665.         m_pMaps[nMapIndex] = new SavedPosMap( nMapSize );  
  2666.         ++nMapIndex;  
  2667.     }  
  2668.     m_pMaps[nMapIndex] = NULL;  
  2669.     pMap = m_pMaps[nMap];  
  2670.     return true; // map(s) created  
  2671. }  
  2672.   
  2673. void SavedPosMapArray::CopySavedPosMaps( SavedPosMapArray* pOtherMaps )  
  2674. {  
  2675.     ReleaseMaps();  
  2676.     if ( pOtherMaps->m_pMaps )  
  2677.     {  
  2678.         int nMap = 0;  
  2679.         SavedPosMap* pMap = NULL;  
  2680.         while ( pOtherMaps->m_pMaps[nMap] )  
  2681.         {  
  2682.             SavedPosMap* pMapSrc = pOtherMaps->m_pMaps[nMap];  
  2683.             GetMap( pMap, nMap, pMapSrc->nMapSize );  
  2684.             for ( int nSlot=0; nSlot < pMap->nMapSize; ++nSlot )  
  2685.             {  
  2686.                 SavedPos* pCopySavedPos = pMapSrc->pTable[nSlot];  
  2687.                 if ( pCopySavedPos )  
  2688.                 {  
  2689.                     int nCount = 0;  
  2690.                     while ( pCopySavedPos[nCount].nSavedPosFlags & SavedPos::SPM_USED )  
  2691.                     {  
  2692.                         ++nCount;  
  2693.                         if ( pCopySavedPos[nCount-1].nSavedPosFlags & SavedPos::SPM_LAST )  
  2694.                             break;  
  2695.                     }  
  2696.                     if ( nCount )  
  2697.                     {  
  2698.                         SavedPos* pNewSavedPos = new SavedPos[nCount];  
  2699.                         for ( int nCopy=0; nCopy
  2700.                             pNewSavedPos[nCopy] = pCopySavedPos[nCopy];  
  2701.                         pNewSavedPos[nCount-1].nSavedPosFlags |= SavedPos::SPM_LAST;  
  2702.                         pMap->pTable[nSlot] = pNewSavedPos;  
  2703.                     }  
  2704.                 }  
  2705.             }  
  2706.             ++nMap;  
  2707.         }  
  2708.     }  
  2709. }  
  2710.   
  2711. //////////////////////////////////////////////////////////////////////  
  2712. // Core parser function  
  2713. //  
  2714. int TokenPos::ParseNode( NodePos& node )  
  2715. {  
  2716.     // Call this with m_nNext set to the start of the node or tag  
  2717.     // Upon return m_nNext points to the char after the node or tag  
  2718.     // m_nL and m_nR are set to name location if it is a tag with a name  
  2719.     // node members set to node location, strMeta used for parse error  
  2720.     //   
  2721.     //  comment  
  2722.     //  dtd  
  2723.     //  processing instruction  
  2724.     // "), CMarkup::MNT_COMMENT )  
  2725.                 else  
  2726.                     FINDNODEBAD( MCD_T("comment_tag_syntax") )  
  2727.             }  
  2728.             else if ( nParseFlags & PD_BRACKET )  
  2729.             {  
  2730.                 nParseFlags ^= PD_BRACKET;  
  2731.                 if ( cD == 'C' )  
  2732.                     FINDNODETYPE( MCD_T("]]>"), CMarkup::MNT_CDATA_SECTION )  
  2733.                 else  
  2734.                     FINDNODEBAD( MCD_T("cdata_section_syntax") )  
  2735.             }  
  2736.             else if ( nParseFlags & PD_DOCTYPE )  
  2737.             {  
  2738.                 if ( cD == '<' )  
  2739.                     nParseFlags |= PD_OPENTAG;  
  2740.                 else if ( cD == '>' )  
  2741.                 {  
  2742.                     m_nNext = (int)(pD - m_pDocText) + 1;  
  2743.                     nNodeType = CMarkup::MNT_DOCUMENT_TYPE;  
  2744.                     break;  
  2745.                 }  
  2746.             }  
  2747.         }  
  2748.         else if ( cD == '<' )  
  2749.         {  
  2750.             nParseFlags |= PD_OPENTAG;  
  2751.         }  
  2752.         else  
  2753.         {  
  2754.             nNodeType = CMarkup::MNT_WHITESPACE;  
  2755.             if ( MCD_PSZCHR(MCD_T(" /t/n/r"),(MCD_CHAR)cD) )  
  2756.                 nParseFlags |= PD_TEXTORWS;  
  2757.             else  
  2758.                 FINDNODETYPE( MCD_T("<"), CMarkup::MNT_TEXT )  
  2759.         }  
  2760.         pD += MCD_CLEN( pD );  
  2761.     }  
  2762.     node.nLength = m_nNext - node.nStart;  
  2763.     node.nNodeType = nNodeType;  
  2764.     return nNodeType;  
  2765. }  
  2766.   
  2767. //////////////////////////////////////////////////////////////////////  
  2768. // CMarkup public methods  
  2769. //  
  2770. CMarkup::~CMarkup()  
  2771. {  
  2772.     delete m_pSavedPosMaps;  
  2773.     delete m_pElemPosTree;  
  2774. }  
  2775.   
  2776. void CMarkup::operator=( const CMarkup& markup )  
  2777. {  
  2778.     // Copying not supported during file mode because of file pointer  
  2779.     if ( (m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE)) || (markup.m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE)) )  
  2780.         return;  
  2781.     m_iPosParent = markup.m_iPosParent;  
  2782.     m_iPos = markup.m_iPos;  
  2783.     m_iPosChild = markup.m_iPosChild;  
  2784.     m_iPosFree = markup.m_iPosFree;  
  2785.     m_iPosDeleted = markup.m_iPosDeleted;  
  2786.     m_nNodeType = markup.m_nNodeType;  
  2787.     m_nNodeOffset = markup.m_nNodeOffset;  
  2788.     m_nNodeLength = markup.m_nNodeLength;  
  2789.     m_strDoc = markup.m_strDoc;  
  2790.     m_strResult = markup.m_strResult;  
  2791.     m_nDocFlags = markup.m_nDocFlags;  
  2792.     m_pElemPosTree->CopyElemPosTree( markup.m_pElemPosTree, m_iPosFree );  
  2793.     m_pSavedPosMaps->CopySavedPosMaps( markup.m_pSavedPosMaps );  
  2794.     MARKUP_SETDEBUGSTATE;  
  2795. }  
  2796.   
  2797. bool CMarkup::SetDoc( MCD_PCSZ pDoc )  
  2798. {  
  2799.     // pDoc is markup text, not a filename!  
  2800.     if ( m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE) )  
  2801.         return false;  
  2802.     // Set document text  
  2803.     if ( pDoc )  
  2804.         m_strDoc = pDoc;  
  2805.     else  
  2806.     {  
  2807.         MCD_STRCLEARSIZE( m_strDoc );  
  2808.         m_pElemPosTree->ReleaseElemPosTree();  
  2809.     }  
  2810.   
  2811.     MCD_STRCLEAR(m_strResult);  
  2812.     return x_ParseDoc();  
  2813. }  
  2814.   
  2815. bool CMarkup::SetDoc( const MCD_STR& strDoc )  
  2816. {  
  2817.     // strDoc is markup text, not a filename!  
  2818.     if ( m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE) )  
  2819.         return false;  
  2820.     m_strDoc = strDoc;  
  2821.     MCD_STRCLEAR(m_strResult);  
  2822.     return x_ParseDoc();  
  2823. }  
  2824.   
  2825. bool CMarkup::IsWellFormed()  
  2826. {  
  2827.     if ( m_nDocFlags & MDF_WRITEFILE )  
  2828.         return true;  
  2829.     if ( m_nDocFlags & MDF_READFILE )  
  2830.     {  
  2831.         if ( ! (ELEM(0).nFlags & MNF_ILLFORMED) )  
  2832.             return true;  
  2833.     }  
  2834.     else if ( m_pElemPosTree->GetSize()  
  2835.             && ! (ELEM(0).nFlags & MNF_ILLFORMED)  
  2836.             && ELEM(0).iElemChild  
  2837.             && ! ELEM(ELEM(0).iElemChild).iElemNext )  
  2838.         return true;  
  2839.     return false;  
  2840. }  
  2841.   
  2842. MCD_STR CMarkup::GetError() const  
  2843. {  
  2844.     // For backwards compatibility, return a readable English string built from m_strResult  
  2845.     // In release 11.0 you can use GetResult and examine result in XML format  
  2846.     CMarkup mResult( m_strResult );  
  2847.     MCD_STR strError;  
  2848.     int nSyntaxErrors = 0;  
  2849.     while ( mResult.FindElem() )  
  2850.     {  
  2851.         MCD_STR strItem;  
  2852.         MCD_STR strID = mResult.GetTagName();  
  2853.   
  2854.         // Parse result  
  2855.         if ( strID == MCD_T("root_has_sibling") )  
  2856.             strItem = MCD_T("root element has sibling");  
  2857.         else if ( strID == MCD_T("no_root_element") )  
  2858.             strItem = MCD_T("no root element");  
  2859.         else if ( strID == MCD_T("lone_end_tag") )  
  2860.             strItem = MCD_T("lone end tag '") + mResult.GetAttrib(MCD_T("tagname")) + MCD_T("' at offset ")  
  2861.                 + mResult.GetAttrib(MCD_T("offset"));  
  2862.         else if ( strID == MCD_T("unended_start_tag") )  
  2863.             strItem = MCD_T("start tag '") + mResult.GetAttrib(MCD_T("tagname")) + MCD_T("' at offset ")  
  2864.                 + mResult.GetAttrib(MCD_T("offset")) + MCD_T(" expecting end tag at offset ") + mResult.GetAttrib(MCD_T("offset2"));  
  2865.         else if ( strID == MCD_T("first_tag_syntax") )  
  2866.             strItem = MCD_T("tag syntax error at offset ") + mResult.GetAttrib(MCD_T("offset"))  
  2867.                 + MCD_T(" expecting tag name / ! or ?");  
  2868.         else if ( strID == MCD_T("exclamation_tag_syntax") )  
  2869.             strItem = MCD_T("tag syntax error at offset ") + mResult.GetAttrib(MCD_T("offset"))  
  2870.                 + MCD_T(" expecting 'DOCTYPE' [ or -");  
  2871.         else if ( strID == MCD_T("doctype_tag_syntax") )  
  2872.             strItem = MCD_T("tag syntax error at offset ") + mResult.GetAttrib(MCD_T("offset"))  
  2873.                 + MCD_T(" expecting markup declaration"); // ELEMENT ATTLIST ENTITY NOTATION  
  2874.         else if ( strID == MCD_T("comment_tag_syntax") )  
  2875.             strItem = MCD_T("tag syntax error at offset ") + mResult.GetAttrib(MCD_T("offset"))  
  2876.                 + MCD_T(" expecting - to begin comment");  
  2877.         else if ( strID == MCD_T("cdata_section_syntax") )  
  2878.             strItem = MCD_T("tag syntax error at offset ") + mResult.GetAttrib(MCD_T("offset"))  
  2879.                 + MCD_T(" expecting 'CDATA'");  
  2880.         else if ( strID == MCD_T("unterminated_tag_syntax") )  
  2881.             strItem = MCD_T("unterminated tag at offset ") + mResult.GetAttrib(MCD_T("offset"));  
  2882.   
  2883.         // Report only the first syntax or well-formedness error  
  2884.         if ( ! MCD_STRISEMPTY(strItem) )  
  2885.         {  
  2886.             ++nSyntaxErrors;  
  2887.             if ( nSyntaxErrors > 1 )  
  2888.                 continue;  
  2889.         }  
  2890.   
  2891.         // I/O results  
  2892.         if ( strID == MCD_T("file_error") )  
  2893.             strItem = mResult.GetAttrib(MCD_T("msg"));  
  2894.         else if ( strID == MCD_T("bom") )  
  2895.             strItem = MCD_T("BOM +");  
  2896.         else if ( strID == MCD_T("read") || strID == MCD_T("write") || strID == MCD_T("converted_to") || strID == MCD_T("converted_from") )  
  2897.         {  
  2898.             if ( strID == MCD_T("converted_to") )  
  2899.                 strItem = MCD_T("to ");  
  2900.             MCD_STR strEncoding = mResult.GetAttrib( MCD_T("encoding") );  
  2901.             if ( ! MCD_STRISEMPTY(strEncoding) )  
  2902.                 strItem += strEncoding + MCD_T(" ");  
  2903.             strItem += MCD_T("length ") + mResult.GetAttrib(MCD_T("length"));  
  2904.             if ( strID == MCD_T("converted_from") )  
  2905.                 strItem += MCD_T(" to");  
  2906.         }  
  2907.         else if ( strID == MCD_T("nulls_removed") )  
  2908.             strItem = MCD_T("removed ") + mResult.GetAttrib(MCD_T("count")) + MCD_T(" nulls");  
  2909.         else if ( strID == MCD_T("conversion_loss") )  
  2910.             strItem = MCD_T("(chars lost in conversion!)");  
  2911.         else if ( strID == MCD_T("utf8_detection") )  
  2912.             strItem = MCD_T("(used UTF-8 detection)");  
  2913.         else if ( strID == MCD_T("endian_swap") )  
  2914.             strItem = MCD_T("endian swap");  
  2915.         else if ( strID == MCD_T("truncation_error") )  
  2916.             strItem = MCD_T("encoding ") + mResult.GetAttrib(MCD_T("encoding")) + MCD_T(" adjustment error");  
  2917.   
  2918.         // Concatenate result item to error string  
  2919.         if ( ! MCD_STRISEMPTY(strItem) )  
  2920.         {  
  2921.             if ( ! MCD_STRISEMPTY(strError) )  
  2922.                 strError += MCD_T(" ");  
  2923.             strError += strItem;  
  2924.         }  
  2925.     }  
  2926.     return strError;  
  2927. }  
  2928.   
  2929. bool CMarkup::Load( MCD_CSTR_FILENAME szFileName )  
  2930. {  
  2931.     if ( m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE) )  
  2932.         return false;  
  2933.     if ( ! ReadTextFile(szFileName, m_strDoc, &m_strResult, &m_nDocFlags) )  
  2934.         return false;  
  2935.     return x_ParseDoc();  
  2936. }  
  2937.   
  2938. bool CMarkup::ReadTextFile( MCD_CSTR_FILENAME szFileName, MCD_STR& strDoc, MCD_STR* pstrResult, int* pnDocFlags, MCD_STR* pstrEncoding )  
  2939. {  
  2940.     // Static utility method to load text file into strDoc  
  2941.     //  
  2942.     FilePos file;  
  2943.     file.m_nDocFlags = (pnDocFlags?*pnDocFlags:0) | MDF_READFILE;  
  2944.     bool bSuccess = file.FileOpen( szFileName );  
  2945.     if ( pstrResult )  
  2946.         *pstrResult = file.m_strIOResult;  
  2947.     MCD_STRCLEAR(strDoc);  
  2948.     if ( bSuccess )  
  2949.     {  
  2950.         file.FileSpecifyEncoding( pstrEncoding );  
  2951.         file.m_nOpFileByteLen = (int)((MCD_INTFILEOFFSET)(file.m_nFileByteLen - file.m_nFileByteOffset));  
  2952.         bSuccess = file.FileReadText( strDoc );  
  2953.         file.FileClose();  
  2954.         if ( pstrResult )  
  2955.             *pstrResult += file.m_strIOResult;  
  2956.         if ( pnDocFlags )  
  2957.             *pnDocFlags = file.m_nDocFlags;  
  2958.     }  
  2959.     return bSuccess;  
  2960. }  
  2961.   
  2962. bool CMarkup::Save( MCD_CSTR_FILENAME szFileName )  
  2963. {  
  2964.     if ( m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE) )  
  2965.         return false;  
  2966.     return WriteTextFile( szFileName, m_strDoc, &m_strResult, &m_nDocFlags );  
  2967. }  
  2968.   
  2969. bool CMarkup::WriteTextFile( MCD_CSTR_FILENAME szFileName, const MCD_STR& strDoc, MCD_STR* pstrResult, int* pnDocFlags, MCD_STR* pstrEncoding )  
  2970. {  
  2971.     // Static utility method to save strDoc to text file  
  2972.     //  
  2973.     FilePos file;  
  2974.     file.m_nDocFlags = (pnDocFlags?*pnDocFlags:0) | MDF_WRITEFILE;  
  2975.     bool bSuccess = file.FileOpen( szFileName );  
  2976.     if ( pstrResult )  
  2977.         *pstrResult = file.m_strIOResult;  
  2978.     if ( bSuccess )  
  2979.     {  
  2980.         if ( MCD_STRISEMPTY(file.m_strEncoding) && ! MCD_STRISEMPTY(strDoc) )  
  2981.         {  
  2982.             file.m_strEncoding = GetDeclaredEncoding( strDoc );  
  2983.             if ( MCD_STRISEMPTY(file.m_strEncoding) )  
  2984.                 file.m_strEncoding = MCD_T("UTF-8"); // to do: MDF_ANSIFILE  
  2985.         }  
  2986.         file.FileSpecifyEncoding( pstrEncoding );  
  2987.         bSuccess = file.FileWriteText( strDoc );  
  2988.         file.FileClose();  
  2989.         if ( pstrResult )  
  2990.             *pstrResult += file.m_strIOResult;  
  2991.         if ( pnDocFlags )  
  2992.             *pnDocFlags = file.m_nDocFlags;  
  2993.     }  
  2994.     return bSuccess;  
  2995. }  
  2996.   
  2997. bool CMarkup::FindElem( MCD_CSTR szName )  
  2998. {  
  2999.     if ( m_nDocFlags & MDF_WRITEFILE )  
  3000.         return false;  
  3001.     if ( m_pElemPosTree->GetSize() )  
  3002.     {  
  3003.         // Change current position only if found  
  3004.         PathPos path( szName, false );  
  3005.         int iPos = x_FindElem( m_iPosParent, m_iPos, path );  
  3006.         if ( iPos )  
  3007.         {  
  3008.             // Assign new position  
  3009.             x_SetPos( ELEM(iPos).iElemParent, iPos, 0 );  
  3010.             return true;  
  3011.         }  
  3012.     }  
  3013.     return false;  
  3014. }  
  3015.   
  3016. bool CMarkup::FindChildElem( MCD_CSTR szName )  
  3017. {  
  3018.     if ( m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE) )  
  3019.         return false;  
  3020.     // Shorthand: if no current main position, find first child under parent element  
  3021.     if ( ! m_iPos )  
  3022.         FindElem();  
  3023.     // Change current child position only if found  
  3024.     PathPos path( szName, false );  
  3025.     int iPosChild = x_FindElem( m_iPos, m_iPosChild, path );  
  3026.     if ( iPosChild )  
  3027.     {  
  3028.         // Assign new position  
  3029.         int iPos = ELEM(iPosChild).iElemParent;  
  3030.         x_SetPos( ELEM(iPos).iElemParent, iPos, iPosChild );  
  3031.         return true;  
  3032.     }  
  3033.     return false;  
  3034. }  
  3035.   
  3036. MCD_STR CMarkup::EscapeText( MCD_CSTR szText, int nFlags )  
  3037. {  
  3038.     // Convert text as seen outside XML document to XML friendly  
  3039.     // replacing special characters with ampersand escape codes  
  3040.     // E.g. convert "6>7" to "6>7"  
  3041.     //  
  3042.     // <   less than  
  3043.     // &  ampersand  
  3044.     // >   greater than  
  3045.     //  
  3046.     // and for attributes:  
  3047.     //  
  3048.     // ' apostrophe or single quote  
  3049.     // " double quote  
  3050.     //  
  3051.     static MCD_PCSZ apReplace[] = { MCD_T("<"),MCD_T("&"),MCD_T(">"),MCD_T("'"),MCD_T(""") };  
  3052.     MCD_PCSZ pFind = (nFlags&MNF_ESCAPEQUOTES)?MCD_T("<&>/'/""):MCD_T("<&>");  
  3053.     MCD_STR strText;  
  3054.     MCD_PCSZ pSource = szText;  
  3055.     int nDestSize = MCD_PSZLEN(pSource);  
  3056.     nDestSize += nDestSize / 10 + 7;  
  3057.     MCD_BLDRESERVE(strText,nDestSize);  
  3058.     MCD_CHAR cSource = *pSource;  
  3059.     MCD_PCSZ pFound;  
  3060.     int nCharLen;  
  3061.     while ( cSource )  
  3062.     {  
  3063.         MCD_BLDCHECK(strText,nDestSize,6);  
  3064.         if ( (pFound=MCD_PSZCHR(pFind,cSource)) != NULL )  
  3065.         {  
  3066.             bool bIgnoreAmpersand = false;  
  3067.             if ( (nFlags&MNF_WITHREFS) && *pFound == '&' )  
  3068.             {  
  3069.                 // Do not replace ampersand if it is start of any entity reference  
  3070.                 // &[#_:A-Za-zU][_:-.A-Za-z0-9U]*; where U is > 0x7f  
  3071.                 MCD_PCSZ pCheckEntity = pSource;  
  3072.                 ++pCheckEntity;  
  3073.                 MCD_CHAR c = *pCheckEntity;  
  3074.                 if ( (c>='A'&&c<='Z') || (c>='a'&&c<='z')  
  3075.                         || c=='#' || c=='_' || c==':' || ((unsigned int)c)>0x7f )  
  3076.                 {  
  3077.                     while ( 1 )  
  3078.                     {  
  3079.                         pCheckEntity += MCD_CLEN( pCheckEntity );  
  3080.                         c = *pCheckEntity;  
  3081.                         if ( c == ';' )  
  3082.                         {  
  3083.                             int nEntityLen = (int)(pCheckEntity - pSource) + 1;  
  3084.                             MCD_BLDAPPENDN(strText,pSource,nEntityLen);  
  3085.                             pSource = pCheckEntity;  
  3086.                             bIgnoreAmpersand = true;  
  3087.                         }  
  3088.                         else if ( (c>='A'&&c<='Z') || (c>='a'&&c<='z') || (c>='0'&&c<='9')  
  3089.                                 || c=='_' || c==':' || c=='-' || c=='.' || ((unsigned int)c)>0x7f )  
  3090.                             continue;  
  3091.                         break;  
  3092.                     }  
  3093.                 }  
  3094.             }  
  3095.             if ( ! bIgnoreAmpersand )  
  3096.             {  
  3097.                 pFound = apReplace[pFound-pFind];  
  3098.                 MCD_BLDAPPEND(strText,pFound);  
  3099.             }  
  3100.             ++pSource; // ASCII, so 1 byte  
  3101.         }  
  3102.         else  
  3103.         {  
  3104.             nCharLen = MCD_CLEN( pSource );  
  3105.             MCD_BLDAPPENDN(strText,pSource,nCharLen);  
  3106.             pSource += nCharLen;  
  3107.         }  
  3108.         cSource = *pSource;  
  3109.     }  
  3110.   
  3111.     MCD_BLDRELEASE(strText);  
  3112.     return strText;  
  3113. }  
  3114.   
  3115. // Predefined character entities  
  3116. // By default UnescapeText will decode standard HTML entities as well as the 5 in XML  
  3117. // To unescape only the 5 standard XML entities, use this short table instead:  
  3118. // MCD_PCSZ PredefEntityTable[4] =  
  3119. // { MCD_T("20060lt"),MCD_T("40034quot"),MCD_T("30038amp"),MCD_T("20062gt40039apos") };  
  3120. //  
  3121. // This is a precompiled ASCII hash table for speed and minimum memory requirement  
  3122. // Each entry consists of a 1 digit code name length, 4 digit code point, and the code name  
  3123. // Each table slot can have multiple entries, table size 130 was chosen for even distribution  
  3124. //  
  3125. MCD_PCSZ PredefEntityTable[130] =  
  3126. {  
  3127.     MCD_T("60216oslash60217ugrave60248oslash60249ugrave"),  
  3128.     MCD_T("50937omega60221yacute58968lceil50969omega60253yacute"),  
  3129.     MCD_T("50916delta50206icirc50948delta50238icirc68472weierp"),MCD_T("40185sup1"),  
  3130.     MCD_T("68970lfloor40178sup2"),  
  3131.     MCD_T("50922kappa60164curren50954kappa58212mdash40179sup3"),  
  3132.     MCD_T("59830diams58211ndash"),MCD_T("68855otimes58969rceil"),  
  3133.     MCD_T("50338oelig50212ocirc50244ocirc50339oelig58482trade"),  
  3134.     MCD_T("50197aring50931sigma50229aring50963sigma"),  
  3135.     MCD_T("50180acute68971rfloor50732tilde"),MCD_T("68249lsaquo"),  
  3136.     MCD_T("58734infin68201thinsp"),MCD_T("50161iexcl"),  
  3137.     MCD_T("50920theta50219ucirc50952theta50251ucirc"),MCD_T("58254oline"),  
  3138.     MCD_T("58260frasl68727lowast"),MCD_T("59827clubs60191iquest68250rsaquo"),  
  3139.     MCD_T("58629crarr50181micro"),MCD_T("58222bdquo"),MCD_T(""),  
  3140.     MCD_T("58243prime60177plusmn58242prime"),MCD_T("40914beta40946beta"),MCD_T(""),  
  3141.     MCD_T(""),MCD_T(""),MCD_T("50171laquo50215times"),MCD_T("40710circ"),  
  3142.     MCD_T("49001lang"),MCD_T("58220ldquo40175macr"),  
  3143.     MCD_T("40182para50163pound48476real"),MCD_T(""),MCD_T("58713notin50187raquo"),  
  3144.     MCD_T("48773cong50223szlig50978upsih"),  
  3145.     MCD_T("58776asymp58801equiv49002rang58218sbquo"),  
  3146.     MCD_T("50222thorn48659darr48595darr40402fnof58221rdquo50254thorn"),  
  3147.     MCD_T("40162cent58722minus"),MCD_T("58707exist40170ordf"),MCD_T(""),  
  3148.     MCD_T("40921iota58709empty48660harr48596harr40953iota"),MCD_T(""),  
  3149.     MCD_T("40196auml40228auml48226bull40167sect48838sube"),MCD_T(""),  
  3150.     MCD_T("48656larr48592larr58853oplus"),MCD_T("30176deg58216lsquo40186ordm"),  
  3151.     MCD_T("40203euml40039apos40235euml48712isin40160nbsp"),  
  3152.     MCD_T("40918zeta40950zeta"),MCD_T("38743and48195emsp48719prod"),  
  3153.     MCD_T("30935chi38745cap30967chi48194ensp"),  
  3154.     MCD_T("40207iuml40239iuml48706part48869perp48658rarr48594rarr"),  
  3155.     MCD_T("38736ang48836nsub58217rsquo"),MCD_T(""),  
  3156.     MCD_T("48901sdot48657uarr48593uarr"),MCD_T("40169copy48364euro"),  
  3157.     MCD_T("30919eta30951eta"),MCD_T("40214ouml40246ouml48839supe"),MCD_T(""),  
  3158.     MCD_T(""),MCD_T("30038amp30174reg"),MCD_T("48733prop"),MCD_T(""),  
  3159.     MCD_T("30208eth30934phi40220uuml30240eth30966phi40252uuml"),MCD_T(""),MCD_T(""),  
  3160.     MCD_T(""),MCD_T("40376yuml40255yuml"),MCD_T(""),MCD_T("40034quot48204zwnj"),  
  3161.     MCD_T("38746cup68756there4"),MCD_T("30929rho30961rho38764sim"),  
  3162.     MCD_T("30932tau38834sub30964tau"),MCD_T("38747int38206lrm38207rlm"),  
  3163.     MCD_T("30936psi30968psi30165yen"),MCD_T(""),MCD_T("28805ge30168uml"),  
  3164.     MCD_T("30982piv"),MCD_T(""),MCD_T("30172not"),MCD_T(""),MCD_T("28804le"),  
  3165.     MCD_T("30173shy"),MCD_T("39674loz28800ne38721sum"),MCD_T(""),MCD_T(""),  
  3166.     MCD_T("38835sup"),MCD_T("28715ni"),MCD_T(""),MCD_T("20928pi20960pi38205zwj"),  
  3167.     MCD_T(""),MCD_T("60923lambda20062gt60955lambda"),MCD_T(""),MCD_T(""),  
  3168.     MCD_T("60199ccedil60231ccedil"),MCD_T(""),MCD_T("20060lt"),  
  3169.     MCD_T("20926xi28744or20958xi"),MCD_T("20924mu20956mu"),MCD_T("20925nu20957nu"),  
  3170.     MCD_T("68225dagger68224dagger"),MCD_T("80977thetasym"),MCD_T(""),MCD_T(""),  
  3171.     MCD_T(""),MCD_T("78501alefsym"),MCD_T(""),MCD_T(""),MCD_T(""),  
  3172.     MCD_T("60193aacute60195atilde60225aacute60227atilde"),MCD_T(""),  
  3173.     MCD_T("70927omicron60247divide70959omicron"),MCD_T("60192agrave60224agrave"),  
  3174.     MCD_T("60201eacute60233eacute60962sigmaf"),MCD_T("70917epsilon70949epsilon"),  
  3175.     MCD_T(""),MCD_T("60200egrave60232egrave"),MCD_T("60205iacute60237iacute"),  
  3176.     MCD_T(""),MCD_T(""),MCD_T("60204igrave68230hellip60236igrave"),  
  3177.     MCD_T("60166brvbar"),  
  3178.     MCD_T("60209ntilde68704forall58711nabla60241ntilde69824spades"),  
  3179.     MCD_T("60211oacute60213otilde60189frac1260183middot60243oacute60245otilde"),  
  3180.     MCD_T(""),MCD_T("50184cedil60188frac14"),  
  3181.     MCD_T("50198aelig50194acirc60210ograve50226acirc50230aelig60242ograve"),  
  3182.     MCD_T("50915gamma60190frac3450947gamma58465image58730radic"),  
  3183.     MCD_T("60352scaron60353scaron"),MCD_T("60218uacute69829hearts60250uacute"),  
  3184.     MCD_T("50913alpha50202ecirc70933upsilon50945alpha50234ecirc70965upsilon"),  
  3185.     MCD_T("68240permil")  
  3186. };  
  3187.   
  3188. MCD_STR CMarkup::UnescapeText( MCD_CSTR szText, int nTextLength /*=-1*/ )  
  3189. {  
  3190.     // Convert XML friendly text to text as seen outside XML document  
  3191.     // ampersand escape codes replaced with special characters e.g. convert "6>7" to "6>7"  
  3192.     // ampersand numeric codes replaced with character e.g. convert < to <  
  3193.     // Conveniently the result is always the same or shorter in byte length  
  3194.     //  
  3195.     MCD_STR strText;  
  3196.     MCD_PCSZ pSource = szText;  
  3197.     if ( nTextLength == -1 )  
  3198.         nTextLength = MCD_PSZLEN(szText);  
  3199.     MCD_BLDRESERVE(strText,nTextLength);  
  3200.     MCD_CHAR szCodeName[10];  
  3201.     int nCharLen;  
  3202.     int nChar = 0;  
  3203.     while ( nChar < nTextLength )  
  3204.     {  
  3205.         if ( pSource[nChar] == '&' )  
  3206.         {  
  3207.             // Get corresponding unicode code point  
  3208.             int nUnicode = 0;  
  3209.   
  3210.             // Look for terminating semi-colon within 9 ASCII characters  
  3211.             int nCodeLen = 0;  
  3212.             MCD_CHAR cCodeChar = pSource[nChar+1];  
  3213.             while ( nCodeLen < 9 && ((unsigned int)cCodeChar) < 128 && cCodeChar != ';' )  
  3214.             {  
  3215.                 if ( cCodeChar >= 'A' && cCodeChar <= 'Z') // upper case?  
  3216.                     cCodeChar += ('a' - 'A'); // make lower case  
  3217.                 szCodeName[nCodeLen] = cCodeChar;  
  3218.                 ++nCodeLen;  
  3219.                 cCodeChar = pSource[nChar+1+nCodeLen];  
  3220.             }  
  3221.             if ( cCodeChar == ';' ) // found semi-colon?  
  3222.             {  
  3223.                 // Decode szCodeName  
  3224.                 szCodeName[nCodeLen] = '/0';  
  3225.                 if ( *szCodeName == '#' ) // numeric character reference?  
  3226.                 {  
  3227.                     // Is it a hex number?  
  3228.                     int nBase = 10; // decimal  
  3229.                     int nNumberOffset = 1; // after #  
  3230.                     if ( szCodeName[1] == 'x' )  
  3231.                     {  
  3232.                         nNumberOffset = 2; // after #x  
  3233.                         nBase = 16; // hex  
  3234.                     }  
  3235.                     nUnicode = MCD_PSZTOL( &szCodeName[nNumberOffset], NULL, nBase );  
  3236.                 }  
  3237.                 else // does not start with #  
  3238.                 {  
  3239.                     // Look for matching code name in PredefEntityTable  
  3240.                     MCD_PCSZ pEntry = PredefEntityTable[x_Hash(szCodeName,sizeof(PredefEntityTable)/sizeof(MCD_PCSZ))];  
  3241.                     while ( *pEntry )  
  3242.                     {  
  3243.                         // e.g. entry: 40039apos means length 4, code point 0039, code name apos  
  3244.                         int nEntryLen = (*pEntry - '0');  
  3245.                         ++pEntry;  
  3246.                         MCD_PCSZ pCodePoint = pEntry;  
  3247.                         pEntry += 4;  
  3248.                         if ( nEntryLen == nCodeLen && MCD_PSZNCMP(szCodeName,pEntry,nEntryLen) == 0 )  
  3249.                         {  
  3250.                             // Convert digits to integer up to code name which always starts with alpha   
  3251.                             nUnicode = MCD_PSZTOL( pCodePoint, NULL, 10 );  
  3252.                             break;  
  3253.                         }  
  3254.                         pEntry += nEntryLen;  
  3255.                     }  
  3256.                 }  
  3257.             }  
  3258.   
  3259.             // If a code point found, encode it into text  
  3260.             if ( nUnicode )  
  3261.             {  
  3262.                 MCD_CHAR szChar[5];  
  3263.                 nCharLen = 0;  
  3264. #if defined(MARKUP_WCHAR) // WCHAR  
  3265. #if MARKUP_SIZEOFWCHAR == 4 // sizeof(wchar_t) == 4  
  3266.                 szChar[0] = (MCD_CHAR)nUnicode;  
  3267.                 nCharLen = 1;  
  3268. #else // sizeof(wchar_t) == 2  
  3269.                 EncodeCharUTF16( nUnicode, (unsigned short*)szChar, nCharLen );  
  3270. #endif  
  3271. #elif defined(MARKUP_MBCS) // MBCS/double byte  
  3272. #if defined(MARKUP_WINCONV)  
  3273.                 int nUsedDefaultChar = 0;  
  3274.                 wchar_t wszUTF16[2];  
  3275.                 EncodeCharUTF16( nUnicode, (unsigned short*)wszUTF16, nCharLen );  
  3276.                 nCharLen = WideCharToMultiByte( CP_ACP, 0, wszUTF16, nCharLen, szChar, 5, NULL, &nUsedDefaultChar );  
  3277.                 if ( nUsedDefaultChar || nCharLen <= 0 )  
  3278.                     nUnicode = 0;  
  3279. #else // not WINCONV  
  3280.                 wchar_t wcUnicode = (wchar_t)nUnicode;  
  3281.                 nCharLen = wctomb( szChar, wcUnicode );  
  3282.                 if ( nCharLen <= 0 )  
  3283.                     nUnicode = 0;  
  3284. #endif // not WINCONV  
  3285. #else // not WCHAR and not MBCS/double byte  
  3286.                 EncodeCharUTF8( nUnicode, szChar, nCharLen );  
  3287. #endif // not WCHAR and not MBCS/double byte  
  3288.                 // Increment index past ampersand semi-colon  
  3289.                 if ( nUnicode ) // must check since MBCS case can clear it  
  3290.                 {  
  3291.                     MCD_BLDAPPENDN(strText,szChar,nCharLen);  
  3292.                     nChar += nCodeLen + 2;  
  3293.                 }  
  3294.             }  
  3295.             if ( ! nUnicode )  
  3296.             {  
  3297.                 // If the code is not converted, leave it as is  
  3298.                 MCD_BLDAPPEND1(strText,'&');  
  3299.                 ++nChar;  
  3300.             }  
  3301.         }  
  3302.         else // not &  
  3303.         {  
  3304.             nCharLen = MCD_CLEN(&pSource[nChar]);  
  3305.             MCD_BLDAPPENDN(strText,&pSource[nChar],nCharLen);  
  3306.             nChar += nCharLen;  
  3307.         }  
  3308.     }  
  3309.     MCD_BLDRELEASE(strText);  
  3310.     return strText;  
  3311. }  
  3312.   
  3313. bool CMarkup::DetectUTF8( const char* pText, int nTextLen, int* pnNonASCII/*=NULL*/, bool* bErrorAtEnd/*=NULL*/ )  
  3314. {  
  3315.     // return true if ASCII or all non-ASCII byte sequences are valid UTF-8 pattern:  
  3316.     // ASCII   0xxxxxxx  
  3317.     // 2-byte  110xxxxx 10xxxxxx  
  3318.     // 3-byte  1110xxxx 10xxxxxx 10xxxxxx  
  3319.     // 4-byte  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx  
  3320.     // *pnNonASCII is set (if pnNonASCII is not NULL) to the number of non-ASCII UTF-8 sequences  
  3321.     // or if an invalid UTF-8 sequence is found, to 1 + the valid non-ASCII sequences up to the invalid sequence  
  3322.     // *bErrorAtEnd is set (if bErrorAtEnd is not NULL) to true if the UTF-8 was cut off at the end in mid valid sequence  
  3323.     int nUChar;  
  3324.     if ( pnNonASCII )  
  3325.         *pnNonASCII = 0;  
  3326.     const char* pTextEnd = pText + nTextLen;  
  3327.     while ( *pText && pText != pTextEnd )  
  3328.     {  
  3329.         if ( (unsigned char)(*pText) & 0x80 )  
  3330.         {  
  3331.             if ( pnNonASCII )  
  3332.                 ++(*pnNonASCII);  
  3333.             nUChar = DecodeCharUTF8( pText, pTextEnd );  
  3334.             if ( nUChar == -1 )  
  3335.             {  
  3336.                 if ( bErrorAtEnd )  
  3337.                     *bErrorAtEnd = (pTextEnd == pText)? true:false;  
  3338.                 return false;  
  3339.             }  
  3340.         }  
  3341.         else  
  3342.             ++pText;  
  3343.     }  
  3344.     if ( bErrorAtEnd )  
  3345.         *bErrorAtEnd = false;  
  3346.     return true;  
  3347. }  
  3348.   
  3349. int CMarkup::DecodeCharUTF8( const char*& pszUTF8, const char* pszUTF8End/*=NULL*/ )  
  3350. {  
  3351.     // Return Unicode code point and increment pszUTF8 past 1-4 bytes  
  3352.     // pszUTF8End can be NULL if pszUTF8 is null terminated  
  3353.     int nUChar = (unsigned char)*pszUTF8;  
  3354.     ++pszUTF8;  
  3355.     if ( nUChar & 0x80 )  
  3356.     {  
  3357.         int nExtraChars;  
  3358.         if ( ! (nUChar & 0x20) )  
  3359.         {  
  3360.             nExtraChars = 1;  
  3361.             nUChar &= 0x1f;  
  3362.         }  
  3363.         else if ( ! (nUChar & 0x10) )  
  3364.         {  
  3365.             nExtraChars = 2;  
  3366.             nUChar &= 0x0f;  
  3367.         }  
  3368.         else if ( ! (nUChar & 0x08) )  
  3369.         {  
  3370.             nExtraChars = 3;  
  3371.             nUChar &= 0x07;  
  3372.         }  
  3373.         else  
  3374.             return -1;  
  3375.         while ( nExtraChars-- )  
  3376.         {  
  3377.             if ( pszUTF8 == pszUTF8End || ! (*pszUTF8 & 0x80) )  
  3378.                 return -1;  
  3379.             nUChar = nUChar<<6;  
  3380.             nUChar |= *pszUTF8 & 0x3f;  
  3381.             ++pszUTF8;  
  3382.         }  
  3383.     }  
  3384.     return nUChar;  
  3385. }  
  3386.   
  3387. void CMarkup::EncodeCharUTF16( int nUChar, unsigned short* pwszUTF16, int& nUTF16Len )  
  3388. {  
  3389.     // Write UTF-16 sequence to pwszUTF16 for Unicode code point nUChar and update nUTF16Len  
  3390.     // Be sure pwszUTF16 has room for up to 2 wide chars  
  3391.     if ( nUChar & ~0xffff )  
  3392.     {  
  3393.         if ( pwszUTF16 )  
  3394.         {  
  3395.             // Surrogate pair  
  3396.             nUChar -= 0x10000;  
  3397.             pwszUTF16[nUTF16Len++] = (unsigned short)(((nUChar>>10) & 0x3ff) | 0xd800); // W1  
  3398.             pwszUTF16[nUTF16Len++] = (unsigned short)((nUChar & 0x3ff) | 0xdc00); // W2  
  3399.         }  
  3400.         else  
  3401.             nUTF16Len += 2;  
  3402.     }  
  3403.     else  
  3404.     {  
  3405.         if ( pwszUTF16 )  
  3406.             pwszUTF16[nUTF16Len++] = (unsigned short)nUChar;  
  3407.         else  
  3408.             ++nUTF16Len;  
  3409.     }  
  3410. }  
  3411.   
  3412. int CMarkup::DecodeCharUTF16( const unsigned short*& pwszUTF16, const unsigned short* pszUTF16End/*=NULL*/ )  
  3413. {  
  3414.     // Return Unicode code point and increment pwszUTF16 past 1 or 2 (if surrogrates) UTF-16 code points  
  3415.     // pszUTF16End can be NULL if pszUTF16 is zero terminated  
  3416.     int nUChar = *pwszUTF16;  
  3417.     ++pwszUTF16;  
  3418.     if ( (nUChar & ~0x000007ff) == 0xd800 ) // W1  
  3419.     {  
  3420.         if ( pwszUTF16 == pszUTF16End || ! (*pwszUTF16) ) // W2  
  3421.             return -1; // incorrect UTF-16  
  3422.         nUChar = (((nUChar & 0x3ff) << 10) | (*pwszUTF16 & 0x3ff)) + 0x10000;  
  3423.         ++pwszUTF16;  
  3424.     }  
  3425.     return nUChar;  
  3426. }  
  3427.   
  3428. void CMarkup::EncodeCharUTF8( int nUChar, char* pszUTF8, int& nUTF8Len )  
  3429. {  
  3430.     // Write UTF-8 sequence to pszUTF8 for Unicode code point nUChar and update nUTF8Len  
  3431.     // Be sure pszUTF8 has room for up to 4 bytes  
  3432.     if ( ! (nUChar & ~0x0000007f) ) // < 0x80  
  3433.     {  
  3434.         if ( pszUTF8 )  
  3435.             pszUTF8[nUTF8Len++] = (char)nUChar;  
  3436.         else  
  3437.             ++nUTF8Len;  
  3438.     }  
  3439.     else if ( ! (nUChar & ~0x000007ff) ) // < 0x800  
  3440.     {  
  3441.         if ( pszUTF8 )  
  3442.         {  
  3443.             pszUTF8[nUTF8Len++] = (char)(((nUChar&0x7c0)>>6)|0xc0);  
  3444.             pszUTF8[nUTF8Len++] = (char)((nUChar&0x3f)|0x80);  
  3445.         }  
  3446.         else  
  3447.             nUTF8Len += 2;  
  3448.     }  
  3449.     else if ( ! (nUChar & ~0x0000ffff) ) // < 0x10000  
  3450.     {  
  3451.         if ( pszUTF8 )  
  3452.         {  
  3453.             pszUTF8[nUTF8Len++] = (char)(((nUChar&0xf000)>>12)|0xe0);  
  3454.             pszUTF8[nUTF8Len++] = (char)(((nUChar&0xfc0)>>6)|0x80);  
  3455.             pszUTF8[nUTF8Len++] = (char)((nUChar&0x3f)|0x80);  
  3456.         }  
  3457.         else  
  3458.             nUTF8Len += 3;  
  3459.     }  
  3460.     else // < 0x110000  
  3461.     {  
  3462.         if ( pszUTF8 )  
  3463.         {  
  3464.             pszUTF8[nUTF8Len++] = (char)(((nUChar&0x1c0000)>>18)|0xf0);  
  3465.             pszUTF8[nUTF8Len++] = (char)(((nUChar&0x3f000)>>12)|0x80);  
  3466.             pszUTF8[nUTF8Len++] = (char)(((nUChar&0xfc0)>>6)|0x80);  
  3467.             pszUTF8[nUTF8Len++] = (char)((nUChar&0x3f)|0x80);  
  3468.         }  
  3469.         else  
  3470.             nUTF8Len += 4;  
  3471.     }  
  3472. }  
  3473.   
  3474. int CMarkup::UTF16To8( char* pszUTF8, const unsigned short* pwszUTF16, int nUTF8Count )  
  3475. {  
  3476.     // Supports the same arguments as wcstombs  
  3477.     // the pwszUTF16 source must be a NULL-terminated UTF-16 string  
  3478.     // if pszUTF8 is NULL, the number of bytes required is returned and nUTF8Count is ignored  
  3479.     // otherwise pszUTF8 is filled with the result string and NULL-terminated if nUTF8Count allows  
  3480.     // nUTF8Count is the byte size of pszUTF8 and must be large enough for the NULL if NULL desired  
  3481.     // and the number of bytes (excluding NULL) is returned  
  3482.     //  
  3483.     int nUChar, nUTF8Len = 0;  
  3484.     while ( *pwszUTF16 )  
  3485.     {  
  3486.         // Decode UTF-16  
  3487.         nUChar = DecodeCharUTF16( pwszUTF16, NULL );  
  3488.         if ( nUChar == -1 )  
  3489.             nUChar = '?';  
  3490.   
  3491.         // Encode UTF-8  
  3492.         if ( pszUTF8 && nUTF8Len + 4 > nUTF8Count )  
  3493.         {  
  3494.             int nUTF8LenSoFar = nUTF8Len;  
  3495.             EncodeCharUTF8( nUChar, NULL, nUTF8Len );  
  3496.             if ( nUTF8Len > nUTF8Count )  
  3497.                 return nUTF8LenSoFar;  
  3498.             nUTF8Len = nUTF8LenSoFar;  
  3499.         }  
  3500.         EncodeCharUTF8( nUChar, pszUTF8, nUTF8Len );  
  3501.     }  
  3502.     if ( pszUTF8 && nUTF8Len < nUTF8Count )  
  3503.         pszUTF8[nUTF8Len] = 0;  
  3504.     return nUTF8Len;  
  3505. }  
  3506.   
  3507. int CMarkup::UTF8To16( unsigned short* pwszUTF16, const char* pszUTF8, int nUTF8Count )  
  3508. {  
  3509.     // Supports the same arguments as mbstowcs  
  3510.     // the pszUTF8 source must be a UTF-8 string which will be processed up to NULL-terminator or nUTF8Count  
  3511.     // if pwszUTF16 is NULL, the number of UTF-16 chars required is returned  
  3512.     // nUTF8Count is maximum UTF-8 bytes to convert and should include NULL if NULL desired in result  
  3513.     // if pwszUTF16 is not NULL it is filled with the result string and it must be large enough  
  3514.     // result will be NULL-terminated if NULL encountered in pszUTF8 before nUTF8Count  
  3515.     // and the number of UTF-8 bytes converted is returned  
  3516.     //  
  3517.     const char* pszPosUTF8 = pszUTF8;  
  3518.     const char* pszUTF8End = pszUTF8 + nUTF8Count;  
  3519.     int nUChar, nUTF8Len = 0, nUTF16Len = 0;  
  3520.     while ( pszPosUTF8 != pszUTF8End )  
  3521.     {  
  3522.         nUChar = DecodeCharUTF8( pszPosUTF8, pszUTF8End );  
  3523.         if ( ! nUChar )  
  3524.         {  
  3525.             if ( pwszUTF16 )  
  3526.                 pwszUTF16[nUTF16Len] = 0;  
  3527.             break;  
  3528.         }  
  3529.         else if ( nUChar == -1 )  
  3530.             nUChar = '?';  
  3531.   
  3532.         // Encode UTF-16  
  3533.         EncodeCharUTF16( nUChar, pwszUTF16, nUTF16Len );  
  3534.     }  
  3535.     nUTF8Len = (int)(pszPosUTF8 - pszUTF8);  
  3536.     if ( ! pwszUTF16 )  
  3537.         return nUTF16Len;  
  3538.     return nUTF8Len;  
  3539. }  
  3540.   
  3541. #if ! defined(MARKUP_WCHAR) // not WCHAR  
  3542. MCD_STR CMarkup::UTF8ToA( MCD_CSTR pszUTF8, int* pnFailed/*=NULL*/ )  
  3543. {  
  3544.     // Converts from UTF-8 to locale ANSI charset  
  3545.     MCD_STR strANSI;  
  3546.     int nMBLen = (int)MCD_PSZLEN( pszUTF8 );  
  3547.     if ( pnFailed )  
  3548.         *pnFailed = 0;  
  3549.     if ( nMBLen )  
  3550.     {  
  3551.         TextEncoding textencoding( MCD_T("UTF-8"), (const void*)pszUTF8, nMBLen );  
  3552.         textencoding.m_nToCount = nMBLen;  
  3553.         MCD_CHAR* pANSIBuffer = MCD_GETBUFFER(strANSI,textencoding.m_nToCount);  
  3554.         nMBLen = textencoding.PerformConversion( (void*)pANSIBuffer );  
  3555.         MCD_RELEASEBUFFER(strANSI,pANSIBuffer,nMBLen);  
  3556.         if ( pnFailed )  
  3557.             *pnFailed = textencoding.m_nFailedChars;  
  3558.     }  
  3559.     return strANSI;  
  3560. }  
  3561.   
  3562. MCD_STR CMarkup::AToUTF8( MCD_CSTR pszANSI )  
  3563. {  
  3564.     // Converts locale ANSI charset to UTF-8  
  3565.     MCD_STR strUTF8;  
  3566.     int nMBLen = (int)MCD_PSZLEN( pszANSI );  
  3567.     if ( nMBLen )  
  3568.     {  
  3569.         TextEncoding textencoding( MCD_T(""), (const void*)pszANSI, nMBLen );  
  3570.         textencoding.m_nToCount = nMBLen * 4;  
  3571.         MCD_CHAR* pUTF8Buffer = MCD_GETBUFFER(strUTF8,textencoding.m_nToCount);  
  3572.         nMBLen = textencoding.PerformConversion( (void*)pUTF8Buffer, MCD_T("UTF-8") );  
  3573.         MCD_RELEASEBUFFER(strUTF8,pUTF8Buffer,nMBLen);  
  3574.     }  
  3575.     return strUTF8;  
  3576. }  
  3577. #endif // not WCHAR  
  3578.   
  3579. MCD_STR CMarkup::GetDeclaredEncoding( MCD_CSTR szDoc )  
  3580. {  
  3581.     // Extract encoding attribute from XML Declaration, or HTML meta charset  
  3582.     MCD_STR strEncoding;  
  3583.     TokenPos token( szDoc, MDF_IGNORECASE );  
  3584.     NodePos node;  
  3585.     bool bHtml = false;  
  3586.     int nTypeFound = 0;  
  3587.     while ( nTypeFound >= 0 )  
  3588.     {  
  3589.         nTypeFound = token.ParseNode( node );  
  3590.         int nNext = token.m_nNext;  
  3591.         if ( nTypeFound == MNT_PROCESSING_INSTRUCTION && node.nStart == 0 )  
  3592.         {  
  3593.             token.m_nNext = node.nStart + 2; // after 
  3594.             if ( token.FindName() && token.Match(MCD_T("xml")) )  
  3595.             {  
  3596.                 // e.g.   
  3597.                 if ( token.FindAttrib(MCD_T("encoding")) )  
  3598.                     strEncoding = token.GetTokenText();  
  3599.                 break;  
  3600.             }  
  3601.         }  
  3602.         else if ( nTypeFound == 0 ) // end tag  
  3603.         {  
  3604.             // Check for end of HTML head  
  3605.             token.m_nNext = node.nStart + 2; // after 
  3606.             if ( token.FindName() && token.Match(MCD_T("head")) )  
  3607.                 break;  
  3608.         }  
  3609.         else if ( nTypeFound == MNT_ELEMENT )  
  3610.         {  
  3611.             token.m_nNext = node.nStart + 1; // after <  
  3612.             token.FindName();  
  3613.             if ( ! bHtml )  
  3614.             {  
  3615.                 if ( ! token.Match(MCD_T("html")) )  
  3616.                     break;  
  3617.                 bHtml = true;  
  3618.             }  
  3619.             else if ( token.Match(MCD_T("meta")) )  
  3620.             {  
  3621.                 // e.g.   
  3622.                 int nAttribOffset = node.nStart + 1;  
  3623.                 token.m_nNext = nAttribOffset;  
  3624.                 if ( token.FindAttrib(MCD_T("http-equiv")) && token.Match(MCD_T("Content-Type")) )  
  3625.                 {  
  3626.                     token.m_nNext = nAttribOffset;  
  3627.                     if ( token.FindAttrib(MCD_T("content")) )  
  3628.                     {  
  3629.                         int nContentEndOffset = token.m_nNext;  
  3630.                         token.m_nNext = token.m_nL;  
  3631.                         while ( token.m_nNext < nContentEndOffset && token.FindName() )  
  3632.                         {  
  3633.                             if ( token.Match(MCD_T("charset")) && token.FindName() && token.Match(MCD_T("=")) )  
  3634.                             {  
  3635.                                 token.FindName();  
  3636.                                 strEncoding = token.GetTokenText();  
  3637.                                 break;  
  3638.                             }  
  3639.                         }  
  3640.                     }  
  3641.                     break;  
  3642.                 }  
  3643.             }  
  3644.         }  
  3645.         token.m_nNext = nNext;  
  3646.     }  
  3647.     return strEncoding;  
  3648. }  
  3649.   
  3650. int CMarkup::GetEncodingCodePage( MCD_CSTR pszEncoding )  
  3651. {  
  3652.     return x_GetEncodingCodePage( pszEncoding );  
  3653. }  
  3654.   
  3655. int CMarkup::FindNode( int nType )  
  3656. {  
  3657.     // Change current node position only if a node is found  
  3658.     // If nType is 0 find any node, otherwise find node of type nType  
  3659.     // Return type of node or 0 if not found  
  3660.   
  3661.     // Determine where in document to start scanning for node  
  3662.     int nNodeOffset = m_nNodeOffset;  
  3663.     if ( m_nNodeType > MNT_ELEMENT )  
  3664.     {  
  3665.         // By-pass current node  
  3666.         nNodeOffset += m_nNodeLength;  
  3667.     }  
  3668.     else // element or no current main position  
  3669.     {  
  3670.         // Set position to begin looking for node  
  3671.         if ( m_iPos )  
  3672.         {  
  3673.             // After element  
  3674.             nNodeOffset = ELEM(m_iPos).StartAfter();  
  3675.         }  
  3676.         else if ( m_iPosParent )  
  3677.         {  
  3678.             // Immediately after start tag of parent  
  3679.             if ( ELEM(m_iPosParent).IsEmptyElement() )  
  3680.                 return 0;  
  3681.             else  
  3682.                 nNodeOffset = ELEM(m_iPosParent).StartContent();  
  3683.         }  
  3684.     }  
  3685.   
  3686.     // Get nodes until we find what we're looking for  
  3687.     int nTypeFound = 0;  
  3688.     int iPosNew = m_iPos;  
  3689.     TokenPos token( m_strDoc, m_nDocFlags );  
  3690.     NodePos node;  
  3691.     token.m_nNext = nNodeOffset;  
  3692.     do  
  3693.     {  
  3694.         nNodeOffset = token.m_nNext;  
  3695.         nTypeFound = token.ParseNode( node );  
  3696.         if ( nTypeFound == 0 )  
  3697.         {  
  3698.             // Check if we have reached the end of the parent element  
  3699.             if ( m_iPosParent && nNodeOffset == ELEM(m_iPosParent).StartContent()  
  3700.                     + ELEM(m_iPosParent).ContentLen() )  
  3701.                 return 0;  
  3702.             nTypeFound = MNT_LONE_END_TAG; // otherwise it is a lone end tag  
  3703.         }  
  3704.         else if ( nTypeFound < 0 )  
  3705.         {  
  3706.             if ( nTypeFound == -2 ) // end of document  
  3707.                 return 0;  
  3708.             // -1 is node error  
  3709.             nTypeFound = MNT_NODE_ERROR;  
  3710.         }  
  3711.         else if ( nTypeFound == MNT_ELEMENT )  
  3712.         {  
  3713.             if ( iPosNew )  
  3714.                 iPosNew = ELEM(iPosNew).iElemNext;  
  3715.             else  
  3716.                 iPosNew = ELEM(m_iPosParent).iElemChild;  
  3717.             if ( ! iPosNew )  
  3718.                 return 0;  
  3719.             if ( ! nType || (nType & nTypeFound) )  
  3720.             {  
  3721.                 // Found element node, move position to this element  
  3722.                 x_SetPos( m_iPosParent, iPosNew, 0 );  
  3723.                 return m_nNodeType;  
  3724.             }  
  3725.             token.m_nNext = ELEM(iPosNew).StartAfter();  
  3726.         }  
  3727.     }  
  3728.     while ( nType && ! (nType & nTypeFound) );  
  3729.   
  3730.     m_iPos = iPosNew;  
  3731.     m_iPosChild = 0;  
  3732.     m_nNodeOffset = node.nStart;  
  3733.     m_nNodeLength = node.nLength;  
  3734.     m_nNodeType = nTypeFound;  
  3735.     MARKUP_SETDEBUGSTATE;  
  3736.     return m_nNodeType;  
  3737. }  
  3738.   
  3739. bool CMarkup::RemoveNode()  
  3740. {  
  3741.     if ( m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE) )  
  3742.         return false;  
  3743.     if ( m_iPos || m_nNodeLength )  
  3744.     {  
  3745.         x_RemoveNode( m_iPosParent, m_iPos, m_nNodeType, m_nNodeOffset, m_nNodeLength );  
  3746.         m_iPosChild = 0;  
  3747.         MARKUP_SETDEBUGSTATE;  
  3748.         return true;  
  3749.     }  
  3750.     return false;  
  3751. }  
  3752.   
  3753. MCD_STR CMarkup::GetTagName() const  
  3754. {  
  3755.     // Return the tag name at the current main position  
  3756.     MCD_STR strTagName;  
  3757.   
  3758.     // This method is primarily for elements, however  
  3759.     // it does return something for certain other nodes  
  3760.     if ( m_nNodeLength )  
  3761.     {  
  3762.         switch ( m_nNodeType )  
  3763.         {  
  3764.         case MNT_PROCESSING_INSTRUCTION:  
  3765.         case MNT_LONE_END_TAG:  
  3766.             {  
  3767.                 // 
  3768.                 TokenPos token( m_strDoc, m_nDocFlags );  
  3769.                 token.m_nNext = m_nNodeOffset + 2;  
  3770.                 if ( token.FindName() )  
  3771.                     strTagName = token.GetTokenText();  
  3772.             }  
  3773.             break;  
  3774.         case MNT_COMMENT:  
  3775.             strTagName = MCD_T("#comment");  
  3776.             break;  
  3777.         case MNT_CDATA_SECTION:  
  3778.             strTagName = MCD_T("#cdata-section");  
  3779.             break;  
  3780.         case MNT_DOCUMENT_TYPE:  
  3781.             {  
  3782.                 // 
  3783.                 TokenPos token( m_strDoc, m_nDocFlags );  
  3784.                 token.m_nNext = m_nNodeOffset + 2;  
  3785.                 if ( token.FindName() && token.FindName() )  
  3786.                     strTagName = token.GetTokenText();  
  3787.             }  
  3788.             break;  
  3789.         case MNT_TEXT:  
  3790.         case MNT_WHITESPACE:  
  3791.             strTagName = MCD_T("#text");  
  3792.             break;  
  3793.         }  
  3794.         return strTagName;  
  3795.     }  
  3796.   
  3797.     if ( m_iPos )  
  3798.         strTagName = x_GetTagName( m_iPos );  
  3799.     return strTagName;  
  3800. }  
  3801.   
  3802. bool CMarkup::IntoElem()  
  3803. {  
  3804.     // Make current element the parent  
  3805.     if ( m_iPos && m_nNodeType == MNT_ELEMENT )  
  3806.     {  
  3807.         x_SetPos( m_iPos, m_iPosChild, 0 );  
  3808.         return true;  
  3809.     }  
  3810.     return false;  
  3811. }  
  3812.   
  3813. bool CMarkup::OutOfElem()  
  3814. {  
  3815.     // Go to parent element  
  3816.     if ( m_iPosParent )  
  3817.     {  
  3818.         x_SetPos( ELEM(m_iPosParent).iElemParent, m_iPosParent, m_iPos );  
  3819.         return true;  
  3820.     }  
  3821.     return false;  
  3822. }  
  3823.   
  3824. MCD_STR CMarkup::GetAttribName( int n ) const  
  3825. {  
  3826.     // Return nth attribute name of main position  
  3827.     TokenPos token( m_strDoc, m_nDocFlags );  
  3828.     if ( m_iPos && m_nNodeType == MNT_ELEMENT )  
  3829.         token.m_nNext = ELEM(m_iPos).nStart + 1;  
  3830.     else if ( m_nNodeLength && m_nNodeType == MNT_PROCESSING_INSTRUCTION )  
  3831.         token.m_nNext = m_nNodeOffset + 2;  
  3832.     else  
  3833.         return MCD_T("");  
  3834.     if ( token.FindAttrib(NULL,n) )  
  3835.         return token.GetTokenText();  
  3836.     return MCD_T("");  
  3837. }  
  3838.   
  3839. bool CMarkup::SavePos( MCD_CSTR szPosName /*=""*/, int nMap /*=0*/ )  
  3840. {  
  3841.     if ( m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE) )  
  3842.         return false;  
  3843.     // Save current element position in saved position map  
  3844.     if ( szPosName )  
  3845.     {  
  3846.         SavedPosMap* pMap;  
  3847.         m_pSavedPosMaps->GetMap( pMap, nMap );  
  3848.         SavedPos savedpos;  
  3849.         if ( szPosName )  
  3850.             savedpos.strName = szPosName;  
  3851.         if ( m_iPosChild )  
  3852.         {  
  3853.             savedpos.iPos = m_iPosChild;  
  3854.             savedpos.nSavedPosFlags |= SavedPos::SPM_CHILD;  
  3855.         }  
  3856.         else if ( m_iPos )  
  3857.         {  
  3858.             savedpos.iPos = m_iPos;  
  3859.             savedpos.nSavedPosFlags |= SavedPos::SPM_MAIN;  
  3860.         }  
  3861.         else  
  3862.         {  
  3863.             savedpos.iPos = m_iPosParent;  
  3864.         }  
  3865.         savedpos.nSavedPosFlags |= SavedPos::SPM_USED;  
  3866.   
  3867.         int nSlot = x_Hash( szPosName, pMap->nMapSize);  
  3868.         SavedPos* pSavedPos = pMap->pTable[nSlot];  
  3869.         int nOffset = 0;  
  3870.         if ( ! pSavedPos )  
  3871.         {  
  3872.             pSavedPos = new SavedPos[2];  
  3873.             pSavedPos[1].nSavedPosFlags = SavedPos::SPM_LAST;  
  3874.             pMap->pTable[nSlot] = pSavedPos;  
  3875.         }  
  3876.         else  
  3877.         {  
  3878.             while ( pSavedPos[nOffset].nSavedPosFlags & SavedPos::SPM_USED )  
  3879.             {  
  3880.                 if ( pSavedPos[nOffset].strName == (MCD_PCSZ)szPosName )  
  3881.                     break;  
  3882.                 if ( pSavedPos[nOffset].nSavedPosFlags & SavedPos::SPM_LAST )  
  3883.                 {  
  3884.                     int nNewSize = (nOffset + 6) * 2;  
  3885.                     SavedPos* pNewSavedPos = new SavedPos[nNewSize];  
  3886.                     for ( int nCopy=0; nCopy<=nOffset; ++nCopy )  
  3887.                         pNewSavedPos[nCopy] = pSavedPos[nCopy];  
  3888.                     pNewSavedPos[nOffset].nSavedPosFlags ^= SavedPos::SPM_LAST;  
  3889.                     pNewSavedPos[nNewSize-1].nSavedPosFlags = SavedPos::SPM_LAST;  
  3890.                     delete [] pSavedPos;  
  3891.                     pSavedPos = pNewSavedPos;  
  3892.                     pMap->pTable[nSlot] = pSavedPos;  
  3893.                     ++nOffset;  
  3894.                     break;  
  3895.                 }  
  3896.                 ++nOffset;  
  3897.             }  
  3898.         }  
  3899.         if ( pSavedPos[nOffset].nSavedPosFlags & SavedPos::SPM_LAST )  
  3900.             savedpos.nSavedPosFlags |= SavedPos::SPM_LAST;  
  3901.         pSavedPos[nOffset] = savedpos;  
  3902.   
  3903.         /* 
  3904.         // To review hash table balance, uncomment and watch strBalance 
  3905.         MCD_STR strBalance, strSlot; 
  3906.         for ( nSlot=0; nSlot < pMap->nMapSize; ++nSlot ) 
  3907.         { 
  3908.             pSavedPos = pMap->pTable[nSlot]; 
  3909.             int nCount = 0; 
  3910.             while ( pSavedPos && pSavedPos->nSavedPosFlags & SavedPos::SPM_USED ) 
  3911.             { 
  3912.                 ++nCount; 
  3913.                 if ( pSavedPos->nSavedPosFlags & SavedPos::SPM_LAST ) 
  3914.                     break; 
  3915.                 ++pSavedPos; 
  3916.             } 
  3917.             strSlot.Format( MCD_T("%d "), nCount ); 
  3918.             strBalance += strSlot; 
  3919.         } 
  3920.         */  
  3921.         return true;  
  3922.     }  
  3923.     return false;  
  3924. }  
  3925.   
  3926. bool CMarkup::RestorePos( MCD_CSTR szPosName /*=""*/, int nMap /*=0*/ )  
  3927. {  
  3928.     if ( m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE) )  
  3929.         return false;  
  3930.     // Restore element position if found in saved position map  
  3931.     if ( szPosName )  
  3932.     {  
  3933.         SavedPosMap* pMap;  
  3934.         m_pSavedPosMaps->GetMap( pMap, nMap );  
  3935.         int nSlot = x_Hash( szPosName, pMap->nMapSize );  
  3936.         SavedPos* pSavedPos = pMap->pTable[nSlot];  
  3937.         if ( pSavedPos )  
  3938.         {  
  3939.             int nOffset = 0;  
  3940.             while ( pSavedPos[nOffset].nSavedPosFlags & SavedPos::SPM_USED )  
  3941.             {  
  3942.                 if ( pSavedPos[nOffset].strName == (MCD_PCSZ)szPosName )  
  3943.                 {  
  3944.                     int i = pSavedPos[nOffset].iPos;  
  3945.                     if ( pSavedPos[nOffset].nSavedPosFlags & SavedPos::SPM_CHILD )  
  3946.                         x_SetPos( ELEM(ELEM(i).iElemParent).iElemParent, ELEM(i).iElemParent, i );  
  3947.                     else if ( pSavedPos[nOffset].nSavedPosFlags & SavedPos::SPM_MAIN )  
  3948.                         x_SetPos( ELEM(i).iElemParent, i, 0 );  
  3949.                     else  
  3950.                         x_SetPos( i, 0, 0 );  
  3951.                     return true;  
  3952.                 }  
  3953.                 if ( pSavedPos[nOffset].nSavedPosFlags & SavedPos::SPM_LAST )  
  3954.                     break;  
  3955.                 ++nOffset;  
  3956.             }  
  3957.         }  
  3958.     }  
  3959.     return false;  
  3960. }  
  3961.   
  3962. bool CMarkup::SetMapSize( int nSize, int nMap /*=0*/ )  
  3963. {  
  3964.     if ( m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE) )  
  3965.         return false;  
  3966.     // Set saved position map hash table size before using it  
  3967.     // Returns false if map already exists  
  3968.     // Some prime numbers: 53, 101, 211, 503, 1009, 2003, 10007, 20011, 50021, 100003, 200003, 500009  
  3969.     SavedPosMap* pNewMap;  
  3970.     return m_pSavedPosMaps->GetMap( pNewMap, nMap, nSize );  
  3971. }  
  3972.   
  3973. bool CMarkup::RemoveElem()  
  3974. {  
  3975.     if ( m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE) )  
  3976.         return false;  
  3977.     // Remove current main position element  
  3978.     if ( m_iPos && m_nNodeType == MNT_ELEMENT )  
  3979.     {  
  3980.         int iPos = x_RemoveElem( m_iPos );  
  3981.         x_SetPos( m_iPosParent, iPos, 0 );  
  3982.         return true;  
  3983.     }  
  3984.     return false;  
  3985. }  
  3986.   
  3987. bool CMarkup::RemoveChildElem()  
  3988. {  
  3989.     if ( m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE) )  
  3990.         return false;  
  3991.     // Remove current child position element  
  3992.     if ( m_iPosChild )  
  3993.     {  
  3994.         int iPosChild = x_RemoveElem( m_iPosChild );  
  3995.         x_SetPos( m_iPosParent, m_iPos, iPosChild );  
  3996.         return true;  
  3997.     }  
  3998.     return false;  
  3999. }  
  4000.   
  4001.   
  4002. //////////////////////////////////////////////////////////////////////  
  4003. // CMarkup private methods  
  4004. //  
  4005. void CMarkup::x_InitMarkup()  
  4006. {  
  4007.     // Only called from CMarkup constructors  
  4008.     m_pFilePos = NULL;  
  4009.     m_pSavedPosMaps = new SavedPosMapArray;  
  4010.     m_pElemPosTree = new ElemPosTree;  
  4011.   
  4012.     // To always ignore case, define MARKUP_IGNORECASE  
  4013. #if defined(MARKUP_IGNORECASE) // ignore case  
  4014.     m_nDocFlags = MDF_IGNORECASE;  
  4015. #else // not ignore case  
  4016.     m_nDocFlags = 0;  
  4017. #endif // not ignore case  
  4018. }  
  4019.   
  4020. int CMarkup::x_GetParent( int i )  
  4021. {  
  4022.     return ELEM(i).iElemParent;  
  4023. }  
  4024.   
  4025. void CMarkup::x_SetPos( int iPosParent, int iPos, int iPosChild )  
  4026. {  
  4027.     m_iPosParent = iPosParent;  
  4028.     m_iPos = iPos;  
  4029.     m_iPosChild = iPosChild;  
  4030.     m_nNodeOffset = 0;  
  4031.     m_nNodeLength = 0;  
  4032.     m_nNodeType = iPos?MNT_ELEMENT:0;  
  4033.     MARKUP_SETDEBUGSTATE;  
  4034. }  
  4035.   
  4036. #if defined(_DEBUG) // DEBUG   
  4037. void CMarkup::x_SetDebugState()  
  4038. {  
  4039.     // Set m_pDebugCur and m_pDebugPos to point into document  
  4040.     MCD_PCSZ pD = MCD_2PCSZ(m_strDoc);  
  4041.   
  4042.     // Node (non-element) position is determined differently in file mode  
  4043.     if ( m_nNodeLength || (m_nNodeOffset && !m_pFilePos)  
  4044.             || (m_pFilePos && (!m_iPos) && (!m_iPosParent) && ! m_pFilePos->FileAtTop()) )  
  4045.     {  
  4046.         if ( ! m_nNodeLength )  
  4047.             m_pDebugCur = MCD_T("main position offset"); // file mode only  
  4048.         else  
  4049.             m_pDebugCur = MCD_T("main position node");  
  4050.         m_pDebugPos = &pD[m_nNodeOffset];  
  4051.     }  
  4052.     else  
  4053.     {  
  4054.         if ( m_iPosChild )  
  4055.         {  
  4056.             m_pDebugCur = MCD_T("child position element");  
  4057.             m_pDebugPos = &pD[ELEM(m_iPosChild).nStart];  
  4058.         }  
  4059.         else if ( m_iPos )  
  4060.         {  
  4061.             m_pDebugCur = MCD_T("main position element");  
  4062.             m_pDebugPos = &pD[ELEM(m_iPos).nStart];  
  4063.         }  
  4064.         else if ( m_iPosParent )  
  4065.         {  
  4066.             m_pDebugCur = MCD_T("parent position element");  
  4067.             m_pDebugPos = &pD[ELEM(m_iPosParent).nStart];  
  4068.         }  
  4069.         else  
  4070.         {  
  4071.             m_pDebugCur = MCD_T("top of document");  
  4072.             m_pDebugPos = pD;  
  4073.         }  
  4074.     }  
  4075. }  
  4076. #endif // DEBUG  
  4077.   
  4078. int CMarkup::x_GetFreePos()  
  4079. {  
  4080.     if ( m_iPosFree == m_pElemPosTree->GetSize() )  
  4081.         x_AllocElemPos();  
  4082.     return m_iPosFree++;  
  4083. }  
  4084.   
  4085. bool CMarkup::x_AllocElemPos( int nNewSize /*=0*/ )  
  4086. {  
  4087.     // Resize m_aPos when the document is created or the array is filled  
  4088.     if ( ! nNewSize )  
  4089.         nNewSize = m_iPosFree + (m_iPosFree>>1); // Grow By: multiply size by 1.5  
  4090.     if ( m_pElemPosTree->GetSize() < nNewSize )  
  4091.         m_pElemPosTree->GrowElemPosTree( nNewSize );  
  4092.     return true;  
  4093. }  
  4094.   
  4095. bool CMarkup::x_ParseDoc()  
  4096. {  
  4097.     // Reset indexes  
  4098.     ResetPos();  
  4099.     m_pSavedPosMaps->ReleaseMaps();  
  4100.   
  4101.     // Starting size of position array: 1 element per 64 bytes of document  
  4102.     // Tight fit when parsing small doc, only 0 to 2 reallocs when parsing large doc  
  4103.     // Start at 8 when creating new document  
  4104.     int nDocLen = MCD_STRLENGTH(m_strDoc);  
  4105.     m_iPosFree = 1;  
  4106.     x_AllocElemPos( nDocLen / 64 + 8 );  
  4107.     m_iPosDeleted = 0;  
  4108.   
  4109.     // Parse document  
  4110.     ELEM(0).ClearVirtualParent();  
  4111.     if ( nDocLen )  
  4112.     {  
  4113.         TokenPos token( m_strDoc, m_nDocFlags );  
  4114.         int iPos = x_ParseElem( 0, token );  
  4115.         ELEM(0).nLength = nDocLen;  
  4116.         if ( iPos > 0 )  
  4117.         {  
  4118.             ELEM(0).iElemChild = iPos;  
  4119.             if ( ELEM(iPos).iElemNext )  
  4120.                 x_AddResult( m_strResult, MCD_T("root_has_sibling") );  
  4121.         }  
  4122.         else  
  4123.             x_AddResult( m_strResult, MCD_T("no_root_element") );  
  4124.     }  
  4125.   
  4126.     ResetPos();  
  4127.     return IsWellFormed();  
  4128. }  
  4129.   
  4130. int CMarkup::x_ParseElem( int iPosParent, TokenPos& token )  
  4131. {  
  4132.     // This is either called by x_ParseDoc or x_AddSubDoc or x_SetElemContent  
  4133.     // Returns index of the first element encountered or zero if no elements  
  4134.     //  
  4135.     int iPosRoot = 0;  
  4136.     int iPos = iPosParent;  
  4137.     int iVirtualParent = iPosParent;  
  4138.     int nRootDepth = ELEM(iPos).Level();  
  4139.     int nMatchLevel;  
  4140.     int iPosMatch;  
  4141.     int iTag;  
  4142.     int nTypeFound;  
  4143.     int iPosFirst;  
  4144.     int iPosLast;  
  4145.     ElemPos* pElem;  
  4146.     ElemPos* pElemParent;  
  4147.     ElemPos* pElemChild;  
  4148.   
  4149.     // Loop through the nodes of the document  
  4150.     ElemStack elemstack;  
  4151.     NodePos node;  
  4152.     token.m_nNext = 0;  
  4153.     while ( 1 )  
  4154.     {  
  4155.         nTypeFound = token.ParseNode( node );  
  4156.         nMatchLevel = 0;  
  4157.         if ( nTypeFound == MNT_ELEMENT ) // start tag  
  4158.         {  
  4159.             iPos = x_GetFreePos();  
  4160.             if ( ! iPosRoot )  
  4161.                 iPosRoot = iPos;  
  4162.             pElem = &ELEM(iPos);  
  4163.             pElem->iElemParent = iPosParent;  
  4164.             pElem->iElemNext = 0;  
  4165.             pElemParent = &ELEM(iPosParent);  
  4166.             if ( pElemParent->iElemChild )  
  4167.             {  
  4168.                 iPosFirst = pElemParent->iElemChild;  
  4169.                 pElemChild = &ELEM(iPosFirst);  
  4170.                 iPosLast = pElemChild->iElemPrev;  
  4171.                 ELEM(iPosLast).iElemNext = iPos;  
  4172.                 pElem->iElemPrev = iPosLast;  
  4173.                 pElemChild->iElemPrev = iPos;  
  4174.                 pElem->nFlags = 0;  
  4175.             }  
  4176.             else  
  4177.             {  
  4178.                 pElemParent->iElemChild = iPos;  
  4179.                 pElem->iElemPrev = iPos;  
  4180.                 pElem->nFlags = MNF_FIRST;  
  4181.             }  
  4182.             pElem->SetLevel( nRootDepth + elemstack.iTop );  
  4183.             pElem->iElemChild = 0;  
  4184.             pElem->nStart = node.nStart;  
  4185.             pElem->SetStartTagLen( node.nLength );  
  4186.             if ( node.nNodeFlags & MNF_EMPTY )  
  4187.             {  
  4188.                 iPos = iPosParent;  
  4189.                 pElem->SetEndTagLen( 0 );  
  4190.                 pElem->nLength = node.nLength;  
  4191.             }  
  4192.             else  
  4193.             {  
  4194.                 iPosParent = iPos;  
  4195.                 elemstack.PushIntoLevel( token.GetTokenPtr(), token.Length() );  
  4196.             }  
  4197.         }  
  4198.         else if ( nTypeFound == 0 ) // end tag  
  4199.         {  
  4200.             iPosMatch = iPos;  
  4201.             iTag = elemstack.iTop;  
  4202.             nMatchLevel = iTag;  
  4203.             while ( nMatchLevel && ! token.Match(elemstack.GetRefTagPosAt(iTag--).strTagName) )  
  4204.             {  
  4205.                 --nMatchLevel;  
  4206.                 iPosMatch = ELEM(iPosMatch).iElemParent;  
  4207.             }  
  4208.             if ( nMatchLevel == 0 )  
  4209.             {  
  4210.                 // Not matched at all, it is a lone end tag, a non-element node  
  4211.                 ELEM(iVirtualParent).nFlags |= MNF_ILLFORMED;  
  4212.                 ELEM(iPos).nFlags |= MNF_ILLDATA;  
  4213.                 x_AddResult( m_strResult, MCD_T("lone_end_tag"), token.GetTokenText(), 0, node.nStart );  
  4214.             }  
  4215.             else  
  4216.             {  
  4217.                 pElem = &ELEM(iPosMatch);  
  4218.                 pElem->nLength = node.nStart - pElem->nStart + node.nLength;  
  4219.                 pElem->SetEndTagLen( node.nLength );  
  4220.             }  
  4221.         }  
  4222.         else if ( nTypeFound == -1 )  
  4223.         {  
  4224.             ELEM(iVirtualParent).nFlags |= MNF_ILLFORMED;  
  4225.             ELEM(iPos).nFlags |= MNF_ILLDATA;  
  4226.             m_strResult += node.strMeta;  
  4227.         }  
  4228.   
  4229.         // Matched end tag, or end of document  
  4230.         if ( nMatchLevel || nTypeFound == -2 )  
  4231.         {  
  4232.             if ( elemstack.iTop > nMatchLevel )  
  4233.                 ELEM(iVirtualParent).nFlags |= MNF_ILLFORMED;  
  4234.   
  4235.             // Process any non-ended elements  
  4236.             while ( elemstack.iTop > nMatchLevel )  
  4237.             {  
  4238.                 // Element with no end tag  
  4239.                 pElem = &ELEM(iPos);  
  4240.                 int iPosChild = pElem->iElemChild;  
  4241.                 iPosParent = pElem->iElemParent;  
  4242.                 pElem->SetEndTagLen( 0 );  
  4243.                 pElem->nFlags |= MNF_NONENDED;  
  4244.                 pElem->iElemChild = 0;  
  4245.                 pElem->nLength = pElem->StartTagLen();  
  4246.                 if ( pElem->nFlags & MNF_ILLDATA )  
  4247.                 {  
  4248.                     pElem->nFlags ^= MNF_ILLDATA;  
  4249.                     ELEM(iPosParent).nFlags |= MNF_ILLDATA;  
  4250.                 }  
  4251.                 while ( iPosChild )  
  4252.                 {  
  4253.                     ELEM(iPosChild).iElemParent = iPosParent;  
  4254.                     ELEM(iPosChild).iElemPrev = iPos;  
  4255.                     ELEM(iPos).iElemNext = iPosChild;  
  4256.                     iPos = iPosChild;  
  4257.                     iPosChild = ELEM(iPosChild).iElemNext;  
  4258.                 }  
  4259.   
  4260.                 // If end tag did not match, top node is end tag that did not match pElem  
  4261.                 // if end of document, any nodes below top have no end tag  
  4262.                 // second offset represents location where end tag was expected but end of document or other end tag was found   
  4263.                 // end tag that was found is token.GetTokenText() but not reported in error  
  4264.                 int nOffset2 = (nTypeFound==0)? token.m_nL-1: MCD_STRLENGTH(m_strDoc);  
  4265.                 x_AddResult( m_strResult, MCD_T("unended_start_tag"), elemstack.Current().strTagName, 0, pElem->nStart, nOffset2 );  
  4266.   
  4267.                 iPos = iPosParent;  
  4268.                 elemstack.PopOutOfLevel();  
  4269.             }  
  4270.             if ( nTypeFound == -2 )  
  4271.                 break;  
  4272.             iPosParent = ELEM(iPos).iElemParent;  
  4273.             iPos = iPosParent;  
  4274.             elemstack.PopOutOfLevel();  
  4275.         }  
  4276.     }  
  4277.     return iPosRoot;  
  4278. }  
  4279.   
  4280. int CMarkup::x_FindElem( int iPosParent, int iPos, PathPos& path ) const  
  4281. {  
  4282.     // If pPath is NULL or empty, go to next sibling element  
  4283.     // Otherwise go to next sibling element with matching path  
  4284.     //  
  4285.     if ( ! path.ValidPath() )  
  4286.         return 0;  
  4287.   
  4288.     // Paths other than simple tag name are only supported in the developer version  
  4289.     if ( path.IsAnywherePath() || path.IsAbsolutePath() )  
  4290.         return 0;  
  4291.   
  4292.     if ( iPos )  
  4293.         iPos = ELEM(iPos).iElemNext;  
  4294.     else  
  4295.         iPos = ELEM(iPosParent).iElemChild;  
  4296.   
  4297.     // Finished here if pPath not specified  
  4298.     if ( ! path.IsPath() )  
  4299.         return iPos;  
  4300.   
  4301.     // Search  
  4302.     TokenPos token( m_strDoc, m_nDocFlags );  
  4303.     while ( iPos )  
  4304.     {  
  4305.         // Compare tag name  
  4306.         token.m_nNext = ELEM(iPos).nStart + 1;  
  4307.         token.FindName(); // Locate tag name  
  4308.         if ( token.Match(path.GetPtr()) )  
  4309.             return iPos;  
  4310.         iPos = ELEM(iPos).iElemNext;  
  4311.     }  
  4312.     return 0;  
  4313.   
  4314. }  
  4315.   
  4316. MCD_STR CMarkup::x_GetPath( int iPos ) const  
  4317. {  
  4318.     // In file mode, iPos is an index into m_pFilePos->m_elemstack or zero  
  4319.     MCD_STR strPath;  
  4320.     while ( iPos )  
  4321.     {  
  4322.         MCD_STR strTagName;  
  4323.         int iPosParent;  
  4324.         int nCount = 0;  
  4325.         if ( m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE) )  
  4326.         {  
  4327.             TagPos& tag = m_pFilePos->m_elemstack.GetRefTagPosAt(iPos);  
  4328.             strTagName = tag.strTagName;  
  4329.             nCount = tag.nCount;  
  4330.             iPosParent = tag.iParent;  
  4331.         }  
  4332.         else  
  4333.         {  
  4334.             strTagName = x_GetTagName( iPos );  
  4335.             PathPos path( MCD_2PCSZ(strTagName), false );  
  4336.             iPosParent = ELEM(iPos).iElemParent;  
  4337.             int iPosSib = 0;  
  4338.             while ( iPosSib != iPos )  
  4339.             {  
  4340.                 path.RevertOffset();  
  4341.                 iPosSib = x_FindElem( iPosParent, iPosSib, path );  
  4342.                 ++nCount;  
  4343.             }  
  4344.         }  
  4345.         if ( nCount == 1 )  
  4346.             strPath = MCD_T("/") + strTagName + strPath;  
  4347.         else  
  4348.         {  
  4349.             MCD_CHAR szPred[25];  
  4350.             MCD_SPRINTF( MCD_SSZ(szPred), MCD_T("[%d]"), nCount );  
  4351.             strPath = MCD_T("/") + strTagName + szPred + strPath;  
  4352.         }  
  4353.         iPos = iPosParent;  
  4354.     }  
  4355.     return strPath;  
  4356. }  
  4357.   
  4358. MCD_STR CMarkup::x_GetTagName( int iPos ) const  
  4359. {  
  4360.     // Return the tag name at specified element  
  4361.     TokenPos token( m_strDoc, m_nDocFlags );  
  4362.     token.m_nNext = ELEM(iPos).nStart + 1;  
  4363.     if ( ! iPos || ! token.FindName() )  
  4364.         return MCD_T("");  
  4365.   
  4366.     // Return substring of document  
  4367.     return token.GetTokenText();  
  4368. }  
  4369.   
  4370. MCD_STR CMarkup::x_GetAttrib( int iPos, MCD_PCSZ pAttrib ) const  
  4371. {  
  4372.     // Return the value of the attrib  
  4373.     TokenPos token( m_strDoc, m_nDocFlags );  
  4374.     if ( iPos && m_nNodeType == MNT_ELEMENT )  
  4375.         token.m_nNext = ELEM(iPos).nStart + 1;  
  4376.     else if ( iPos == m_iPos && m_nNodeLength && m_nNodeType == MNT_PROCESSING_INSTRUCTION )  
  4377.         token.m_nNext = m_nNodeOffset + 2;  
  4378.     else  
  4379.         return MCD_T("");  
  4380.   
  4381.     if ( pAttrib && token.FindAttrib(pAttrib) )  
  4382.         return UnescapeText( token.GetTokenPtr(), token.Length() );  
  4383.     return MCD_T("");  
  4384. }  
  4385.   
  4386. bool CMarkup::x_SetAttrib( int iPos, MCD_PCSZ pAttrib, int nValue, int nFlags /*=0*/ )  
  4387. {  
  4388.     // Convert integer to string  
  4389.     MCD_CHAR szVal[25];  
  4390.     MCD_SPRINTF( MCD_SSZ(szVal), MCD_T("%d"), nValue );  
  4391.     return x_SetAttrib( iPos, pAttrib, szVal, nFlags );  
  4392. }  
  4393.   
  4394. bool CMarkup::x_SetAttrib( int iPos, MCD_PCSZ pAttrib, MCD_PCSZ pValue, int nFlags /*=0*/ )  
  4395. {  
  4396.     if ( m_nDocFlags & MDF_READFILE )  
  4397.         return false;  
  4398.     int nNodeStart = 0;  
  4399.     if ( iPos && m_nNodeType == MNT_ELEMENT )  
  4400.         nNodeStart = ELEM(iPos).nStart;  
  4401.     else if ( iPos == m_iPos && m_nNodeLength && m_nNodeType == MNT_PROCESSING_INSTRUCTION )  
  4402.         nNodeStart = m_nNodeOffset;  
  4403.     else  
  4404.         return false;  
  4405.   
  4406.     // Create insertion text depending on whether attribute already exists  
  4407.     // Decision: for empty value leaving attrib="" instead of removing attrib  
  4408.     TokenPos token( m_strDoc, m_nDocFlags );  
  4409.     token.m_nNext = nNodeStart + ((m_nNodeType == MNT_ELEMENT)?1:2);   
  4410.     int nReplace = 0;  
  4411.     int nInsertAt;  
  4412.     MCD_STR strEscapedValue = EscapeText( pValue, MNF_ESCAPEQUOTES|nFlags );  
  4413.     int nEscapedValueLen = MCD_STRLENGTH( strEscapedValue );  
  4414.     MCD_STR strInsert;  
  4415.     if ( token.FindAttrib(pAttrib) )  
  4416.     {  
  4417.         // Replace value  
  4418.         MCD_BLDRESERVE( strInsert, nEscapedValueLen + 2 );  
  4419.         MCD_BLDAPPEND1( strInsert, x_ATTRIBQUOTE );  
  4420.         MCD_BLDAPPENDN( strInsert, MCD_2PCSZ(strEscapedValue), nEscapedValueLen );  
  4421.         MCD_BLDAPPEND1( strInsert, x_ATTRIBQUOTE );  
  4422.         MCD_BLDRELEASE( strInsert );  
  4423.         nInsertAt = token.m_nL - ((token.m_nTokenFlags&MNF_QUOTED)?1:0);  
  4424.         nReplace = token.Length() + ((token.m_nTokenFlags&MNF_QUOTED)?2:0);  
  4425.     }  
  4426.     else  
  4427.     {  
  4428.         // Insert string name value pair  
  4429.         int nAttribNameLen = MCD_PSZLEN( pAttrib );  
  4430.         MCD_BLDRESERVE( strInsert, nAttribNameLen + nEscapedValueLen + 4 );  
  4431.         MCD_BLDAPPEND1( strInsert, ' ' );  
  4432.         MCD_BLDAPPENDN( strInsert, pAttrib, nAttribNameLen );  
  4433.         MCD_BLDAPPEND1( strInsert, '=' );  
  4434.         MCD_BLDAPPEND1( strInsert, x_ATTRIBQUOTE );  
  4435.         MCD_BLDAPPENDN( strInsert, MCD_2PCSZ(strEscapedValue), nEscapedValueLen );  
  4436.         MCD_BLDAPPEND1( strInsert, x_ATTRIBQUOTE );  
  4437.         MCD_BLDRELEASE( strInsert );  
  4438.         nInsertAt = token.m_nNext;  
  4439.     }  
  4440.   
  4441.     int nAdjust = MCD_STRLENGTH(strInsert) - nReplace;  
  4442.     if ( m_nDocFlags & MDF_WRITEFILE )  
  4443.     {  
  4444.         int nNewDocLength = MCD_STRLENGTH(m_strDoc) + nAdjust;  
  4445.         MCD_STRCLEAR( m_strResult );  
  4446.         if ( nNodeStart && nNewDocLength > m_pFilePos->m_nBlockSizeBasis )  
  4447.         {  
  4448.             int nDocCapacity = MCD_STRCAPACITY(m_strDoc);  
  4449.             if ( nNewDocLength > nDocCapacity )  
  4450.             {  
  4451.                 m_pFilePos->FileFlush( *m_pFilePos->m_pstrBuffer, nNodeStart );  
  4452.                 m_strResult = m_pFilePos->m_strIOResult;  
  4453.                 nInsertAt -= nNodeStart;  
  4454.                 m_nNodeOffset = 0;  
  4455.                 if ( m_nNodeType == MNT_ELEMENT )  
  4456.                     ELEM(iPos).nStart = 0;  
  4457.             }  
  4458.         }  
  4459.     }  
  4460.     x_DocChange( nInsertAt, nReplace, strInsert );  
  4461.     if ( m_nNodeType == MNT_PROCESSING_INSTRUCTION )  
  4462.     {  
  4463.         x_AdjustForNode( m_iPosParent, m_iPos, nAdjust );  
  4464.         m_nNodeLength += nAdjust;  
  4465.     }  
  4466.     else  
  4467.     {  
  4468.         ELEM(iPos).AdjustStartTagLen( nAdjust );  
  4469.         ELEM(iPos).nLength += nAdjust;  
  4470.         x_Adjust( iPos, nAdjust );  
  4471.     }  
  4472.     MARKUP_SETDEBUGSTATE;  
  4473.     return true;  
  4474. }  
  4475.   
  4476.   
  4477. bool CMarkup::x_CreateNode( MCD_STR& strNode, int nNodeType, MCD_PCSZ pText )  
  4478. {  
  4479.     // Set strNode based on nNodeType and szData  
  4480.     // Return false if szData would jeopardize well-formed document  
  4481.     //  
  4482.     switch ( nNodeType )  
  4483.     {  
  4484.     case MNT_PROCESSING_INSTRUCTION:  
  4485.         strNode = MCD_T("
  4486.         strNode += pText;  
  4487.         strNode += MCD_T("?>");  
  4488.         break;  
  4489.     case MNT_COMMENT:  
  4490.         strNode = MCD_T("");  
  4491.         break;  
  4492.     case MNT_ELEMENT:  
  4493.         strNode = MCD_T("<");  
  4494.         strNode += pText;  
  4495.         strNode += MCD_T("/>");  
  4496.         break;  
  4497.     case MNT_TEXT:  
  4498.     case MNT_WHITESPACE:  
  4499.         strNode = EscapeText( pText );  
  4500.         break;  
  4501.     case MNT_DOCUMENT_TYPE:  
  4502.         strNode = pText;  
  4503.         break;  
  4504.     case MNT_LONE_END_TAG:  
  4505.         strNode = MCD_T("
  4506.         strNode += pText;  
  4507.         strNode += MCD_T(">");  
  4508.         break;  
  4509.     case MNT_CDATA_SECTION:  
  4510.         if ( MCD_PSZSTR(pText,MCD_T("]]>")) != NULL )  
  4511.             return false;  
  4512.         strNode = MCD_T("
  4513.         strNode += pText;  
  4514.         strNode += MCD_T("]]>");  
  4515.         break;  
  4516.     }  
  4517.     return true;  
  4518. }  
  4519.   
  4520. MCD_STR CMarkup::x_EncodeCDATASection( MCD_PCSZ szData )  
  4521. {  
  4522.     // Split CDATA Sections if there are any end delimiters  
  4523.     MCD_STR strData = MCD_T("
  4524.     MCD_PCSZ pszNextStart = szData;  
  4525.     MCD_PCSZ pszEnd = MCD_PSZSTR( szData, MCD_T("]]>") );  
  4526.     while ( pszEnd )  
  4527.     {  
  4528.         strData += MCD_STR( pszNextStart, (int)(pszEnd - pszNextStart) );  
  4529.         strData += MCD_T("]]]]>");  
  4530.         pszNextStart = pszEnd + 3;  
  4531.         pszEnd = MCD_PSZSTR( pszNextStart, MCD_T("]]>") );  
  4532.     }  
  4533.     strData += pszNextStart;  
  4534.     strData += MCD_T("]]-->");  
  4535.     return strData;  
  4536. }  
  4537.   
  4538. bool CMarkup::x_SetData( int iPos, int nValue )  
  4539. {  
  4540.     // Convert integer to string  
  4541.     MCD_CHAR szVal[25];  
  4542.     MCD_SPRINTF( MCD_SSZ(szVal), MCD_T("%d"), nValue );  
  4543.     return x_SetData( iPos, szVal, 0 );  
  4544. }  
  4545.   
  4546. bool CMarkup::x_SetData( int iPos, MCD_PCSZ szData, int nFlags )  
  4547. {  
  4548.     if ( m_nDocFlags & MDF_READFILE )  
  4549.         return false;  
  4550.     MCD_STR strInsert;  
  4551.     if ( m_nDocFlags & MDF_WRITEFILE )  
  4552.     {  
  4553.         if ( ! iPos || m_nNodeType != 1 || ! ELEM(iPos).IsEmptyElement() )  
  4554.             return false; // only set data on current empty element (no other kinds of nodes)  
  4555.     }  
  4556.     if ( iPos == m_iPos && m_nNodeLength )  
  4557.     {  
  4558.         // Not an element  
  4559.         if ( ! x_CreateNode(strInsert, m_nNodeType, szData) )  
  4560.             return false;  
  4561.         x_DocChange( m_nNodeOffset, m_nNodeLength, strInsert );  
  4562.         x_AdjustForNode( m_iPosParent, iPos, MCD_STRLENGTH(strInsert) - m_nNodeLength );  
  4563.         m_nNodeLength = MCD_STRLENGTH(strInsert);  
  4564.         MARKUP_SETDEBUGSTATE;  
  4565.         return true;  
  4566.     }  
  4567.   
  4568.     // Set data in iPos element  
  4569.     if ( ! iPos || ELEM(iPos).iElemChild )  
  4570.         return false;  
  4571.   
  4572.     // Build strInsert from szData based on nFlags  
  4573.     if ( nFlags & MNF_WITHCDATA )  
  4574.         strInsert = x_EncodeCDATASection( szData );  
  4575.     else  
  4576.         strInsert = EscapeText( szData, nFlags );  
  4577.   
  4578.     // Insert  
  4579.     NodePos node( MNF_WITHNOLINES|MNF_REPLACE );  
  4580.     node.strMeta = strInsert;  
  4581.     int iPosBefore = 0;  
  4582.     int nReplace = x_InsertNew( iPos, iPosBefore, node );  
  4583.     int nAdjust = MCD_STRLENGTH(node.strMeta) - nReplace;  
  4584.     x_Adjust( iPos, nAdjust );  
  4585.     ELEM(iPos).nLength += nAdjust;  
  4586.     if ( ELEM(iPos).nFlags & MNF_ILLDATA )  
  4587.         ELEM(iPos).nFlags &= ~MNF_ILLDATA;  
  4588.     MARKUP_SETDEBUGSTATE;  
  4589.     return true;  
  4590. }  
  4591.   
  4592. MCD_STR CMarkup::x_GetData( int iPos )  
  4593. {  
  4594.     if ( iPos == m_iPos && m_nNodeLength )  
  4595.     {  
  4596.         if ( m_nNodeType == MNT_COMMENT )  
  4597.             return MCD_STRMID( m_strDoc, m_nNodeOffset+4, m_nNodeLength-7 );  
  4598.         else if ( m_nNodeType == MNT_PROCESSING_INSTRUCTION )  
  4599.             return MCD_STRMID( m_strDoc, m_nNodeOffset+2, m_nNodeLength-4 );  
  4600.         else if ( m_nNodeType == MNT_CDATA_SECTION )  
  4601.             return MCD_STRMID( m_strDoc, m_nNodeOffset+9, m_nNodeLength-12 );  
  4602.         else if ( m_nNodeType == MNT_TEXT )  
  4603.             return UnescapeText( &(MCD_2PCSZ(m_strDoc))[m_nNodeOffset], m_nNodeLength );  
  4604.         else if ( m_nNodeType == MNT_LONE_END_TAG )  
  4605.             return MCD_STRMID( m_strDoc, m_nNodeOffset+2, m_nNodeLength-3 );  
  4606.         return MCD_STRMID( m_strDoc, m_nNodeOffset, m_nNodeLength );  
  4607.     }  
  4608.   
  4609.     // Return a string representing data between start and end tag  
  4610.     // Return empty string if there are any children elements  
  4611.     MCD_STR strData;  
  4612.     if ( iPos && ! ELEM(iPos).IsEmptyElement() )  
  4613.     {  
  4614.         ElemPos* pElem = &ELEM(iPos);  
  4615.         int nStartContent = pElem->StartContent();  
  4616.         if ( pElem->IsUnparsed() )  
  4617.         {  
  4618.             TokenPos token( m_strDoc, m_nDocFlags, m_pFilePos );  
  4619.             token.m_nNext = nStartContent;  
  4620.             NodePos node;  
  4621.             m_pFilePos->m_nReadBufferStart = pElem->nStart;  
  4622.             while ( 1 )  
  4623.             {  
  4624.                 m_pFilePos->m_nReadBufferRemoved = 0; // will be non-zero after ParseNode if read buffer shifted  
  4625.                 token.ParseNode( node );  
  4626.                 if ( m_pFilePos->m_nReadBufferRemoved )  
  4627.                 {  
  4628.                     pElem->nStart = 0;  
  4629.                     MARKUP_SETDEBUGSTATE;  
  4630.                 }  
  4631.                 if ( node.nNodeType == MNT_TEXT )  
  4632.                     strData += UnescapeText( &token.m_pDocText[node.nStart], node.nLength );  
  4633.                 else if ( node.nNodeType == MNT_CDATA_SECTION )  
  4634.                     strData += MCD_STRMID( m_strDoc, node.nStart+9, node.nLength-12 );  
  4635.                 else if ( node.nNodeType == MNT_ELEMENT )  
  4636.                 {  
  4637.                     MCD_STRCLEAR(strData);  
  4638.                     break;  
  4639.                 }  
  4640.                 else if ( node.nNodeType == 0 )  
  4641.                 {  
  4642.                     if ( token.Match(m_pFilePos->m_elemstack.Current().strTagName) )  
  4643.                     {  
  4644.                         pElem->SetEndTagLen( node.nLength );  
  4645.                         pElem->nLength = node.nStart + node.nLength - pElem->nStart;  
  4646.                         m_pFilePos->m_elemstack.OutOfLevel();  
  4647.                     }  
  4648.                     else  
  4649.                     {  
  4650.                         MCD_STRCLEAR(strData);  
  4651.                     }  
  4652.                     break;  
  4653.                 }  
  4654.             }  
  4655.         }  
  4656.         else if ( ! pElem->iElemChild )  
  4657.         {  
  4658.             // Quick scan for any tags inside content  
  4659.             int nContentLen = pElem->ContentLen();  
  4660.             MCD_PCSZ pszContent = &(MCD_2PCSZ(m_strDoc))[nStartContent];  
  4661.             MCD_PCSZ pszTag = MCD_PSZCHR( pszContent, '<' );  
  4662.             if ( pszTag && ((int)(pszTag-pszContent) < nContentLen) )  
  4663.             {  
  4664.                 // Concatenate all CDATA Sections and text nodes, ignore other nodes  
  4665.                 TokenPos token( m_strDoc, m_nDocFlags );  
  4666.                 token.m_nNext = nStartContent;  
  4667.                 NodePos node;  
  4668.                 while ( token.m_nNext < nStartContent + nContentLen )  
  4669.                 {  
  4670.                     token.ParseNode( node );  
  4671.                     if ( node.nNodeType == MNT_TEXT )  
  4672.                         strData += UnescapeText( &token.m_pDocText[node.nStart], node.nLength );  
  4673.                     else if ( node.nNodeType == MNT_CDATA_SECTION )  
  4674.                         strData += MCD_STRMID( m_strDoc, node.nStart+9, node.nLength-12 );  
  4675.                 }  
  4676.             }  
  4677.             else // no tags  
  4678.                 strData = UnescapeText( &(MCD_2PCSZ(m_strDoc))[nStartContent], nContentLen );  
  4679.         }  
  4680.     }  
  4681.     return strData;  
  4682. }  
  4683.   
  4684. MCD_STR CMarkup::x_GetElemContent( int iPos ) const  
  4685. {  
  4686.     if ( ! (m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE)) )  
  4687.     {  
  4688.         ElemPos* pElem = &ELEM(iPos);  
  4689.         if ( iPos && pElem->ContentLen() )  
  4690.             return MCD_STRMID( m_strDoc, pElem->StartContent(), pElem->ContentLen() );  
  4691.     }  
  4692.     return MCD_T("");  
  4693. }  
  4694.   
  4695. bool CMarkup::x_SetElemContent( MCD_PCSZ szContent )  
  4696. {  
  4697.     MCD_STRCLEAR(m_strResult);  
  4698.     if ( m_nDocFlags & (MDF_READFILE|MDF_WRITEFILE) )  
  4699.         return false;  
  4700.   
  4701.     // Set data in iPos element only  
  4702.     if ( ! m_iPos )  
  4703.         return false;  
  4704.   
  4705.     if ( m_nNodeLength )  
  4706.         return false; // not an element  
  4707.   
  4708.     // Unlink all children  
  4709.     int iPos = m_iPos;  
  4710.     int iPosChild = ELEM(iPos).iElemChild;  
  4711.     bool bHadChild = (iPosChild != 0);  
  4712.     while ( iPosChild )  
  4713.         iPosChild = x_ReleaseSubDoc( iPosChild );  
  4714.     if ( bHadChild )  
  4715.         x_CheckSavedPos();  
  4716.   
  4717.     // Parse content  
  4718.     bool bWellFormed = true;  
  4719.     TokenPos token( szContent, m_nDocFlags );  
  4720.     int iPosVirtual = x_GetFreePos();  
  4721.     ELEM(iPosVirtual).ClearVirtualParent();  
  4722.     ELEM(iPosVirtual).SetLevel( ELEM(iPos).Level() + 1 );  
  4723.     iPosChild = x_ParseElem( iPosVirtual, token );  
  4724.     if ( ELEM(iPosVirtual).nFlags & MNF_ILLFORMED )  
  4725.         bWellFormed = false;  
  4726.     ELEM(iPos).nFlags = (ELEM(iPos).nFlags & ~MNF_ILLDATA) | (ELEM(iPosVirtual).nFlags & MNF_ILLDATA);  
  4727.   
  4728.     // Prepare insert and adjust offsets  
  4729.     NodePos node( MNF_WITHNOLINES|MNF_REPLACE );  
  4730.     node.strMeta = szContent;  
  4731.     int iPosBefore = 0;  
  4732.     int nReplace = x_InsertNew( iPos, iPosBefore, node );  
  4733.       
  4734.     // Adjust and link in the inserted elements  
  4735.     x_Adjust( iPosChild, node.nStart );  
  4736.     ELEM(iPosChild).nStart += node.nStart;  
  4737.     ELEM(iPos).iElemChild = iPosChild;  
  4738.     while ( iPosChild )  
  4739.     {  
  4740.         ELEM(iPosChild).iElemParent = iPos;  
  4741.         iPosChild = ELEM(iPosChild).iElemNext;  
  4742.     }  
  4743.     x_ReleasePos( iPosVirtual );  
  4744.   
  4745.     int nAdjust = MCD_STRLENGTH(node.strMeta) - nReplace;  
  4746.     x_Adjust( iPos, nAdjust, true );  
  4747.     ELEM(iPos).nLength += nAdjust;  
  4748.   
  4749.     x_SetPos( m_iPosParent, m_iPos, 0 );  
  4750.     return bWellFormed;  
  4751. }  
  4752.   
  4753. void CMarkup::x_DocChange( int nLeft, int nReplace, const MCD_STR& strInsert )  
  4754. {  
  4755.     x_StrInsertReplace( m_strDoc, nLeft, nReplace, strInsert );  
  4756. }  
  4757.   
  4758. void CMarkup::x_Adjust( int iPos, int nShift, bool bAfterPos /*=false*/ )  
  4759. {  
  4760.     // Loop through affected elements and adjust indexes  
  4761.     // Algorithm:  
  4762.     // 1. update children unless bAfterPos  
  4763.     //    (if no children or bAfterPos is true, length of iPos not affected)  
  4764.     // 2. update starts of next siblings and their children  
  4765.     // 3. go up until there is a next sibling of a parent and update starts  
  4766.     // 4. step 2  
  4767.     int iPosTop = ELEM(iPos).iElemParent;  
  4768.     bool bPosFirst = bAfterPos; // mark as first to skip its children  
  4769.   
  4770.     // Stop when we've reached the virtual parent (which has no tags)  
  4771.     while ( ELEM(iPos).StartTagLen() )  
  4772.     {  
  4773.         // Were we at containing parent of affected position?  
  4774.         bool bPosTop = false;  
  4775.         if ( iPos == iPosTop )  
  4776.         {  
  4777.             // Move iPosTop up one towards root  
  4778.             iPosTop = ELEM(iPos).iElemParent;  
  4779.             bPosTop = true;  
  4780.         }  
  4781.   
  4782.         // Traverse to the next update position  
  4783.         if ( ! bPosTop && ! bPosFirst && ELEM(iPos).iElemChild )  
  4784.         {  
  4785.             // Depth first  
  4786.             iPos = ELEM(iPos).iElemChild;  
  4787.         }  
  4788.         else if ( ELEM(iPos).iElemNext )  
  4789.         {  
  4790.             iPos = ELEM(iPos).iElemNext;  
  4791.         }  
  4792.         else  
  4793.         {  
  4794.             // Look for next sibling of a parent of iPos  
  4795.             // When going back up, parents have already been done except iPosTop  
  4796.             while ( 1 )  
  4797.             {  
  4798.                 iPos = ELEM(iPos).iElemParent;  
  4799.                 if ( iPos == iPosTop )  
  4800.                     break;  
  4801.                 if ( ELEM(iPos).iElemNext )  
  4802.                 {  
  4803.                     iPos = ELEM(iPos).iElemNext;  
  4804.                     break;  
  4805.                 }  
  4806.             }  
  4807.         }  
  4808.         bPosFirst = false;  
  4809.   
  4810.         // Shift indexes at iPos  
  4811.         if ( iPos != iPosTop )  
  4812.             ELEM(iPos).nStart += nShift;  
  4813.         else  
  4814.             ELEM(iPos).nLength += nShift;  
  4815.     }  
  4816. }  
  4817.   
  4818. int CMarkup::x_InsertNew( int iPosParent, int& iPosRel, NodePos& node )  
  4819. {  
  4820.     // Parent empty tag or tags with no content?  
  4821.     bool bEmptyParentTag = iPosParent && ELEM(iPosParent).IsEmptyElement();  
  4822.     bool bNoContentParentTags = iPosParent && ! ELEM(iPosParent).ContentLen();  
  4823.     if ( iPosRel && ! node.nLength ) // current position element?  
  4824.     {  
  4825.         node.nStart = ELEM(iPosRel).nStart;  
  4826.         if ( ! (node.nNodeFlags & MNF_INSERT) ) // follow iPosRel  
  4827.             node.nStart += ELEM(iPosRel).nLength;  
  4828.     }  
  4829.     else if ( bEmptyParentTag ) // parent has no separate end tag?  
  4830.     {  
  4831.         // Split empty parent element  
  4832.         if ( ELEM(iPosParent).nFlags & MNF_NONENDED )  
  4833.             node.nStart = ELEM(iPosParent).StartContent();  
  4834.         else  
  4835.             node.nStart = ELEM(iPosParent).StartContent() - 1;  
  4836.     }  
  4837.     else if ( node.nLength || (m_nDocFlags&MDF_WRITEFILE) ) // non-element node or a file mode zero length position?  
  4838.     {  
  4839.         if ( ! (node.nNodeFlags & MNF_INSERT) )  
  4840.             node.nStart += node.nLength; // after node or file mode position  
  4841.     }  
  4842.     else // no current node  
  4843.     {  
  4844.         // Insert relative to parent's content  
  4845.         if ( node.nNodeFlags & (MNF_INSERT|MNF_REPLACE) )  
  4846.             node.nStart = ELEM(iPosParent).StartContent(); // beginning of parent's content  
  4847.         else // in front of parent's end tag  
  4848.             node.nStart = ELEM(iPosParent).StartAfter() - ELEM(iPosParent).EndTagLen();  
  4849.     }  
  4850.   
  4851.     // Go up to start of next node, unless its splitting an empty element  
  4852.     if ( ! (node.nNodeFlags&(MNF_WITHNOLINES|MNF_REPLACE)) && ! bEmptyParentTag )  
  4853.     {  
  4854.         TokenPos token( m_strDoc, m_nDocFlags );  
  4855.         node.nStart = token.WhitespaceToTag( node.nStart );  
  4856.     }  
  4857.   
  4858.     // Is insert relative to element position? (i.e. not other kind of node)  
  4859.     if ( ! node.nLength )  
  4860.     {  
  4861.         // Modify iPosRel to reflect position before  
  4862.         if ( iPosRel )  
  4863.         {  
  4864.             if ( node.nNodeFlags & MNF_INSERT )  
  4865.             {  
  4866.                 if ( ! (ELEM(iPosRel).nFlags & MNF_FIRST) )  
  4867.                     iPosRel = ELEM(iPosRel).iElemPrev;  
  4868.                 else  
  4869.                     iPosRel = 0;  
  4870.             }  
  4871.         }  
  4872.         else if ( ! (node.nNodeFlags & MNF_INSERT) )  
  4873.         {  
  4874.             // If parent has a child, add after last child  
  4875.             if ( ELEM(iPosParent).iElemChild )  
  4876.                 iPosRel = ELEM(ELEM(iPosParent).iElemChild).iElemPrev;  
  4877.         }  
  4878.     }  
  4879.   
  4880.     // Get node length (needed for x_AddNode and x_AddSubDoc in file write mode)  
  4881.     node.nLength = MCD_STRLENGTH(node.strMeta);  
  4882.   
  4883.     // Prepare end of lines  
  4884.     if ( (! (node.nNodeFlags & MNF_WITHNOLINES)) && (bEmptyParentTag || bNoContentParentTags) )  
  4885.         node.nStart += x_EOLLEN;  
  4886.     if ( ! (node.nNodeFlags & MNF_WITHNOLINES) )  
  4887.         node.strMeta += x_EOL;  
  4888.   
  4889.     // Calculate insert offset and replace length  
  4890.     int nReplace = 0;  
  4891.     int nInsertAt = node.nStart;  
  4892.     if ( bEmptyParentTag )  
  4893.     {  
  4894.         MCD_STR strTagName = x_GetTagName( iPosParent );  
  4895.         MCD_STR strFormat;  
  4896.         if ( node.nNodeFlags & MNF_WITHNOLINES )  
  4897.             strFormat = MCD_T(">");  
  4898.         else  
  4899.             strFormat = MCD_T(">") x_EOL;  
  4900.         strFormat += node.strMeta;  
  4901.         strFormat += MCD_T("
  4902.         strFormat += strTagName;  
  4903.         node.strMeta = strFormat;  
  4904.         if ( ELEM(iPosParent).nFlags & MNF_NONENDED )  
  4905.         {  
  4906.             nInsertAt = ELEM(iPosParent).StartAfter() - 1;  
  4907.             nReplace = 0;  
  4908.             ELEM(iPosParent).nFlags ^= MNF_NONENDED;  
  4909.         }  
  4910.         else  
  4911.         {  
  4912.             nInsertAt = ELEM(iPosParent).StartAfter() - 2;  
  4913.             nReplace = 1;  
  4914.             ELEM(iPosParent).AdjustStartTagLen( -1 );  
  4915.         }  
  4916.         ELEM(iPosParent).SetEndTagLen( 3 + MCD_STRLENGTH(strTagName) );  
  4917.     }  
  4918.     else  
  4919.     {  
  4920.         if ( node.nNodeFlags & MNF_REPLACE )  
  4921.         {  
  4922.             nInsertAt = ELEM(iPosParent).StartContent();  
  4923.             nReplace = ELEM(iPosParent).ContentLen();  
  4924.         }  
  4925.         else if ( bNoContentParentTags )  
  4926.         {  
  4927.             node.strMeta = x_EOL + node.strMeta;  
  4928.             nInsertAt = ELEM(iPosParent).StartContent();  
  4929.         }  
  4930.     }  
  4931.     if ( m_nDocFlags & MDF_WRITEFILE )  
  4932.     {  
  4933.         // Check if buffer is full  
  4934.         int nNewDocLength = MCD_STRLENGTH(m_strDoc) + MCD_STRLENGTH(node.strMeta) - nReplace;  
  4935.         int nFlushTo = node.nStart;  
  4936.         MCD_STRCLEAR( m_strResult );  
  4937.         if ( bEmptyParentTag )  
  4938.             nFlushTo = ELEM(iPosParent).nStart;  
  4939.         if ( nFlushTo && nNewDocLength > m_pFilePos->m_nBlockSizeBasis )  
  4940.         {  
  4941.             int nDocCapacity = MCD_STRCAPACITY(m_strDoc);  
  4942.             if ( nNewDocLength > nDocCapacity )  
  4943.             {  
  4944.                 if ( bEmptyParentTag )  
  4945.                     ELEM(iPosParent).nStart = 0;  
  4946.                 node.nStart -= nFlushTo;  
  4947.                 nInsertAt -= nFlushTo;  
  4948.                 m_pFilePos->FileFlush( m_strDoc, nFlushTo );  
  4949.                 m_strResult = m_pFilePos->m_strIOResult;  
  4950.             }  
  4951.         }  
  4952.     }  
  4953.     x_DocChange( nInsertAt, nReplace, node.strMeta );  
  4954.     return nReplace;  
  4955. }  
  4956.   
  4957. bool CMarkup::x_AddElem( MCD_PCSZ pName, int nValue, int nFlags )  
  4958. {  
  4959.     // Convert integer to string  
  4960.     MCD_CHAR szVal[25];  
  4961.     MCD_SPRINTF( MCD_SSZ(szVal), MCD_T("%d"), nValue );  
  4962.     return x_AddElem( pName, szVal, nFlags );  
  4963. }  
  4964.   
  4965. bool CMarkup::x_AddElem( MCD_PCSZ pName, MCD_PCSZ pValue, int nFlags )  
  4966. {  
  4967.     if ( m_nDocFlags & MDF_READFILE )  
  4968.         return false;  
  4969.     if ( nFlags & MNF_CHILD )  
  4970.     {  
  4971.         // Adding a child element under main position  
  4972.         if ( ! m_iPos || (m_nDocFlags & MDF_WRITEFILE) )  
  4973.             return false;  
  4974.     }  
  4975.   
  4976.     // Cannot have data in non-ended element  
  4977.     if ( (nFlags&MNF_WITHNOEND) && pValue && pValue[0] )  
  4978.         return false;  
  4979.   
  4980.     // Node and element structures  
  4981.     NodePos node( nFlags );  
  4982.     int iPosParent = 0, iPosBefore = 0;  
  4983.     int iPos = x_GetFreePos();  
  4984.     ElemPos* pElem = &ELEM(iPos);  
  4985.   
  4986.     // Locate where to add element relative to current node  
  4987.     if ( nFlags & MNF_CHILD )  
  4988.     {  
  4989.         iPosParent = m_iPos;  
  4990.         iPosBefore = m_iPosChild;  
  4991.     }  
  4992.     else  
  4993.     {  
  4994.         iPosParent = m_iPosParent;  
  4995.         iPosBefore = m_iPos;  
  4996.         node.nStart = m_nNodeOffset;  
  4997.         node.nLength = m_nNodeLength;  
  4998.     }  
  4999.   
  5000.     // Create string for insert  
  5001.     // If no pValue is specified, an empty element is created  
  5002.     // i.e. either value or   
  5003.     //  
  5004.     int nLenName = MCD_PSZLEN(pName);  
  5005.     if ( ! pValue || ! pValue[0] )  
  5006.     {  
  5007.         //  empty element  
  5008.         MCD_BLDRESERVE( node.strMeta, nLenName + 4 );  
  5009.         MCD_BLDAPPEND1( node.strMeta, '<' );  
  5010.         MCD_BLDAPPENDN( node.strMeta, pName, nLenName );  
  5011.         if ( nFlags & MNF_WITHNOEND )  
  5012.         {  
  5013.             MCD_BLDAPPEND1( node.strMeta, '>' );  
  5014.         }  
  5015.         else  
  5016.         {  
  5017.             if ( nFlags & MNF_WITHXHTMLSPACE )  
  5018.             {  
  5019.                 MCD_BLDAPPENDN( node.strMeta, MCD_T(" />"), 3 );  
  5020.             }  
  5021.             else  
  5022.             {  
  5023.                 MCD_BLDAPPENDN( node.strMeta, MCD_T("/>"), 2 );  
  5024.             }  
  5025.         }  
  5026.         MCD_BLDRELEASE( node.strMeta );  
  5027.         pElem->nLength = MCD_STRLENGTH( node.strMeta );  
  5028.         pElem->SetStartTagLen( pElem->nLength );  
  5029.         pElem->SetEndTagLen( 0 );  
  5030.     }  
  5031.     else  
  5032.     {  
  5033.         // value  
  5034.         MCD_STR strValue;  
  5035.         if ( nFlags & MNF_WITHCDATA )  
  5036.             strValue = x_EncodeCDATASection( pValue );  
  5037.         else  
  5038.             strValue = EscapeText( pValue, nFlags );  
  5039.         int nLenValue = MCD_STRLENGTH(strValue);  
  5040.         pElem->nLength = nLenName * 2 + nLenValue + 5;  
  5041.         MCD_BLDRESERVE( node.strMeta, pElem->nLength );  
  5042.         MCD_BLDAPPEND1( node.strMeta, '<' );  
  5043.         MCD_BLDAPPENDN( node.strMeta, pName, nLenName );  
  5044.         MCD_BLDAPPEND1( node.strMeta, '>' );  
  5045.         MCD_BLDAPPENDN( node.strMeta, MCD_2PCSZ(strValue), nLenValue );  
  5046.         MCD_BLDAPPENDN( node.strMeta, MCD_T("
  5047.         MCD_BLDAPPENDN( node.strMeta, pName, nLenName );  
  5048.         MCD_BLDAPPEND1( node.strMeta, '>' );  
  5049.         MCD_BLDRELEASE( node.strMeta );  
  5050.         pElem->SetEndTagLen( nLenName + 3 );  
  5051.         pElem->SetStartTagLen( nLenName + 2 );  
  5052.     }  
  5053.   
  5054.     // Insert  
  5055.     int nReplace = x_InsertNew( iPosParent, iPosBefore, node );  
  5056.     pElem->nStart = node.nStart;  
  5057.     pElem->iElemChild = 0;  
  5058.     if ( nFlags & MNF_WITHNOEND )  
  5059.         pElem->nFlags = MNF_NONENDED;  
  5060.     else  
  5061.         pElem->nFlags = 0;  
  5062.     if ( m_nDocFlags & MDF_WRITEFILE )  
  5063.     {  
  5064.         iPosParent = x_UnlinkPrevElem( iPosParent, iPosBefore, iPos );  
  5065.         TokenPos token( m_strDoc, m_nDocFlags );  
  5066.         token.m_nL = pElem->nStart + 1;  
  5067.         token.m_nR = pElem->nStart + nLenName;  
  5068.         m_pFilePos->m_elemstack.PushTagAndCount( token );  
  5069.     }  
  5070.     else  
  5071.     {  
  5072.         x_LinkElem( iPosParent, iPosBefore, iPos );  
  5073.         x_Adjust( iPos, MCD_STRLENGTH(node.strMeta) - nReplace );  
  5074.     }  
  5075.     if ( nFlags & MNF_CHILD )  
  5076.         x_SetPos( m_iPosParent, iPosParent, iPos );  
  5077.     else  
  5078.         x_SetPos( iPosParent, iPos, 0 );  
  5079.     return true;  
  5080. }  
  5081.   
  5082. MCD_STR CMarkup::x_GetSubDoc( int iPos )  
  5083. {  
  5084.     if ( iPos && ! (m_nDocFlags&MDF_WRITEFILE) )  
  5085.     {  
  5086.         if ( ! (m_nDocFlags&MDF_READFILE) )  
  5087.         {  
  5088.             TokenPos token( m_strDoc, m_nDocFlags );  
  5089.             token.WhitespaceToTag( ELEM(iPos).StartAfter() );  
  5090.             token.m_nL = ELEM(iPos).nStart;  
  5091.             return token.GetTokenText();  
  5092.         }  
  5093.     }  
  5094.     return MCD_T("");  
  5095. }  
  5096.   
  5097. bool CMarkup::x_AddSubDoc( MCD_PCSZ pSubDoc, int nFlags )  
  5098. {  
  5099.     if ( m_nDocFlags & MDF_READFILE || ((nFlags & MNF_CHILD) && (m_nDocFlags & MDF_WRITEFILE)) )  
  5100.         return false;  
  5101.   
  5102.     MCD_STRCLEAR(m_strResult);  
  5103.     NodePos node( nFlags );  
  5104.     int iPosParent, iPosBefore;  
  5105.     if ( nFlags & MNF_CHILD )  
  5106.     {  
  5107.         // Add a subdocument under main position, before or after child  
  5108.         if ( ! m_iPos )  
  5109.             return false;  
  5110.         iPosParent = m_iPos;  
  5111.         iPosBefore = m_iPosChild;  
  5112.     }  
  5113.     else  
  5114.     {  
  5115.         // Add a subdocument under parent position, before or after main  
  5116.         iPosParent = m_iPosParent;  
  5117.         iPosBefore = m_iPos;  
  5118.         node.nStart = m_nNodeOffset;  
  5119.         node.nLength = m_nNodeLength;  
  5120.     }  
  5121.   
  5122.     // Parse subdocument, generating indexes based on the subdocument string to be offset later  
  5123.     bool bWellFormed = true;  
  5124.     TokenPos token( pSubDoc, m_nDocFlags );  
  5125.     int iPosVirtual = x_GetFreePos();  
  5126.     ELEM(iPosVirtual).ClearVirtualParent();  
  5127.     ELEM(iPosVirtual).SetLevel( ELEM(iPosParent).Level() + 1 );  
  5128.     int iPos = x_ParseElem( iPosVirtual, token );  
  5129.     if ( (!iPos) || ELEM(iPosVirtual).nFlags & MNF_ILLFORMED )  
  5130.         bWellFormed = false;  
  5131.     if ( ELEM(iPosVirtual).nFlags & MNF_ILLDATA )  
  5132.         ELEM(iPosParent).nFlags |= MNF_ILLDATA;  
  5133.   
  5134.     // File write mode handling  
  5135.     bool bBypassSubDoc = false;  
  5136.     if ( m_nDocFlags & MDF_WRITEFILE )  
  5137.     {  
  5138.         // Current position will bypass subdoc unless well-formed single element  
  5139.         if ( (! bWellFormed) || ELEM(iPos).iElemChild || ELEM(iPos).iElemNext )  
  5140.             bBypassSubDoc = true;  
  5141.   
  5142.         // Count tag names of top level elements (usually one) in given markup  
  5143.         int iPosTop = iPos;  
  5144.         while ( iPosTop )  
  5145.         {  
  5146.             token.m_nNext = ELEM(iPosTop).nStart + 1;  
  5147.             token.FindName();  
  5148.             m_pFilePos->m_elemstack.PushTagAndCount( token );  
  5149.             iPosTop = ELEM(iPosTop).iElemNext;  
  5150.         }  
  5151.     }  
  5152.   
  5153.     // Extract subdocument without leading/trailing nodes  
  5154.     int nExtractStart = 0;  
  5155.     int iPosLast = ELEM(iPos).iElemPrev;  
  5156.     if ( bWellFormed )  
  5157.     {  
  5158.         nExtractStart = ELEM(iPos).nStart;  
  5159.         int nExtractLength = ELEM(iPos).nLength;  
  5160.         if ( iPos != iPosLast )  
  5161.         {  
  5162.             nExtractLength = ELEM(iPosLast).nStart - nExtractStart + ELEM(iPosLast).nLength;  
  5163.             bWellFormed = false; // treat as subdoc here, but return not well-formed  
  5164.         }  
  5165.         MCD_STRASSIGN(node.strMeta,&pSubDoc[nExtractStart],nExtractLength);  
  5166.     }  
  5167.     else  
  5168.     {  
  5169.         node.strMeta = pSubDoc;  
  5170.         node.nNodeFlags |= MNF_WITHNOLINES;  
  5171.     }  
  5172.   
  5173.     // Insert  
  5174.     int nReplace = x_InsertNew( iPosParent, iPosBefore, node );  
  5175.   
  5176.     // Clean up indexes  
  5177.     if ( m_nDocFlags & MDF_WRITEFILE )  
  5178.     {  
  5179.         if ( bBypassSubDoc )  
  5180.         {  
  5181.             // Release indexes used in parsing the subdocument  
  5182.             m_iPosParent = x_UnlinkPrevElem( iPosParent, iPosBefore, 0 );  
  5183.             m_iPosFree = 1;  
  5184.             m_iPosDeleted = 0;  
  5185.             m_iPos = 0;  
  5186.             m_nNodeOffset = node.nStart + node.nLength;  
  5187.             m_nNodeLength = 0;  
  5188.             m_nNodeType = 0;  
  5189.             MARKUP_SETDEBUGSTATE;  
  5190.             return bWellFormed;  
  5191.         }  
  5192.         else // single element added  
  5193.         {  
  5194.             m_iPos = iPos;  
  5195.             ElemPos* pElem = &ELEM(iPos);  
  5196.             pElem->nStart = node.nStart;  
  5197.             m_iPosParent = x_UnlinkPrevElem( iPosParent, iPosBefore, iPos );  
  5198.             x_ReleasePos( iPosVirtual );  
  5199.         }  
  5200.     }  
  5201.     else  
  5202.     {  
  5203.         // Adjust and link in the inserted elements  
  5204.         // iPosVirtual will stop it from affecting rest of document  
  5205.         int nAdjust = node.nStart - nExtractStart;  
  5206.         if ( iPos && nAdjust )  
  5207.         {  
  5208.             x_Adjust( iPos, nAdjust );  
  5209.             ELEM(iPos).nStart += nAdjust;  
  5210.         }  
  5211.         int iPosChild = iPos;  
  5212.         while ( iPosChild )  
  5213.         {  
  5214.             int iPosNext = ELEM(iPosChild).iElemNext;  
  5215.             x_LinkElem( iPosParent, iPosBefore, iPosChild );  
  5216.             iPosBefore = iPosChild;  
  5217.             iPosChild = iPosNext;  
  5218.         }  
  5219.         x_ReleasePos( iPosVirtual );  
  5220.   
  5221.         // Now adjust remainder of document  
  5222.         x_Adjust( iPosLast, MCD_STRLENGTH(node.strMeta) - nReplace, true );  
  5223.     }  
  5224.   
  5225.     // Set position to top element of subdocument  
  5226.     if ( nFlags & MNF_CHILD )  
  5227.         x_SetPos( m_iPosParent, iPosParent, iPos );  
  5228.     else // Main  
  5229.         x_SetPos( m_iPosParent, iPos, 0 );  
  5230.     return bWellFormed;  
  5231. }  
  5232.   
  5233. int CMarkup::x_RemoveElem( int iPos )  
  5234. {  
  5235.     // Determine whether any whitespace up to next tag  
  5236.     TokenPos token( m_strDoc, m_nDocFlags );  
  5237.     int nAfterEnd = token.WhitespaceToTag( ELEM(iPos).StartAfter() );  
  5238.   
  5239.     // Remove from document, adjust affected indexes, and unlink  
  5240.     int nLen = nAfterEnd - ELEM(iPos).nStart;  
  5241.     x_DocChange( ELEM(iPos).nStart, nLen, MCD_STR() );  
  5242.     x_Adjust( iPos, - nLen, true );  
  5243.     int iPosPrev = x_UnlinkElem( iPos );  
  5244.     x_CheckSavedPos();  
  5245.     return iPosPrev; // new position  
  5246. }  
  5247.   
  5248. void CMarkup::x_LinkElem( int iPosParent, int iPosBefore, int iPos )  
  5249. {  
  5250.     // Update links between elements and initialize nFlags  
  5251.     ElemPos* pElem = &ELEM(iPos);  
  5252.     if ( m_nDocFlags & MDF_WRITEFILE )  
  5253.     {  
  5254.         // In file write mode, only keep virtual parent 0 plus one element   
  5255.         if ( iPosParent )  
  5256.             x_ReleasePos( iPosParent );  
  5257.         else if ( iPosBefore )  
  5258.             x_ReleasePos( iPosBefore );  
  5259.         iPosParent = 0;  
  5260.         ELEM(iPosParent).iElemChild = iPos;  
  5261.         pElem->iElemParent = iPosParent;  
  5262.         pElem->iElemPrev = iPos;  
  5263.         pElem->iElemNext = 0;  
  5264.         pElem->nFlags |= MNF_FIRST;  
  5265.     }  
  5266.     else  
  5267.     {  
  5268.         pElem->iElemParent = iPosParent;  
  5269.         if ( iPosBefore )  
  5270.         {  
  5271.             // Link in after iPosBefore  
  5272.             pElem->nFlags &= ~MNF_FIRST;  
  5273.             pElem->iElemNext = ELEM(iPosBefore).iElemNext;  
  5274.             if ( pElem->iElemNext )  
  5275.                 ELEM(pElem->iElemNext).iElemPrev = iPos;  
  5276.             else  
  5277.                 ELEM(ELEM(iPosParent).iElemChild).iElemPrev = iPos;  
  5278.             ELEM(iPosBefore).iElemNext = iPos;  
  5279.             pElem->iElemPrev = iPosBefore;  
  5280.         }  
  5281.         else  
  5282.         {  
  5283.             // Link in as first child  
  5284.             pElem->nFlags |= MNF_FIRST;  
  5285.             if ( ELEM(iPosParent).iElemChild )  
  5286.             {  
  5287.                 pElem->iElemNext = ELEM(iPosParent).iElemChild;  
  5288.                 pElem->iElemPrev = ELEM(pElem->iElemNext).iElemPrev;  
  5289.                 ELEM(pElem->iElemNext).iElemPrev = iPos;  
  5290.                 ELEM(pElem->iElemNext).nFlags ^= MNF_FIRST;  
  5291.             }  
  5292.             else  
  5293.             {  
  5294.                 pElem->iElemNext = 0;  
  5295.                 pElem->iElemPrev = iPos;  
  5296.             }  
  5297.             ELEM(iPosParent).iElemChild = iPos;  
  5298.         }  
  5299.         if ( iPosParent )  
  5300.             pElem->SetLevel( ELEM(iPosParent).Level() + 1 );  
  5301.     }  
  5302. }  
  5303.   
  5304. int CMarkup::x_UnlinkElem( int iPos )  
  5305. {  
  5306.     // Fix links to remove element and mark as deleted  
  5307.     // return previous position or zero if none  
  5308.     ElemPos* pElem = &ELEM(iPos);  
  5309.   
  5310.     // Find previous sibling and bypass removed element  
  5311.     int iPosPrev = 0;  
  5312.     if ( pElem->nFlags & MNF_FIRST )  
  5313.     {  
  5314.         if ( pElem->iElemNext ) // set next as first child  
  5315.         {  
  5316.             ELEM(pElem->iElemParent).iElemChild = pElem->iElemNext;  
  5317.             ELEM(pElem->iElemNext).iElemPrev = pElem->iElemPrev;  
  5318.             ELEM(pElem->iElemNext).nFlags |= MNF_FIRST;  
  5319.         }  
  5320.         else // no children remaining  
  5321.             ELEM(pElem->iElemParent).iElemChild = 0;  
  5322.     }  
  5323.     else  
  5324.     {  
  5325.         iPosPrev = pElem->iElemPrev;  
  5326.         ELEM(iPosPrev).iElemNext = pElem->iElemNext;  
  5327.         if ( pElem->iElemNext )  
  5328.             ELEM(pElem->iElemNext).iElemPrev = iPosPrev;  
  5329.         else  
  5330.             ELEM(ELEM(pElem->iElemParent).iElemChild).iElemPrev = iPosPrev;  
  5331.     }  
  5332.     x_ReleaseSubDoc( iPos );  
  5333.     return iPosPrev;  
  5334. }  
  5335.   
  5336. int CMarkup::x_UnlinkPrevElem( int iPosParent, int iPosBefore, int iPos )  
  5337. {  
  5338.     // In file write mode, only keep virtual parent 0 plus one element if currently at element  
  5339.     if ( iPosParent )  
  5340.     {  
  5341.         x_ReleasePos( iPosParent );  
  5342.         iPosParent = 0;  
  5343.     }  
  5344.     else if ( iPosBefore )  
  5345.         x_ReleasePos( iPosBefore );  
  5346.     ELEM(iPosParent).iElemChild = iPos;  
  5347.     ELEM(iPosParent).nLength = MCD_STRLENGTH(m_strDoc);  
  5348.     if ( iPos )  
  5349.     {  
  5350.         ElemPos* pElem = &ELEM(iPos);  
  5351.         pElem->iElemParent = iPosParent;  
  5352.         pElem->iElemPrev = iPos;  
  5353.         pElem->iElemNext = 0;  
  5354.         pElem->nFlags |= MNF_FIRST;  
  5355.     }  
  5356.     return iPosParent;  
  5357. }  
  5358.   
  5359. int CMarkup::x_ReleasePos( int iPos )  
  5360. {  
  5361.     int iPosNext = ELEM(iPos).iElemNext;  
  5362.     ELEM(iPos).iElemNext = m_iPosDeleted;  
  5363.     ELEM(iPos).nFlags = MNF_DELETED;  
  5364.     m_iPosDeleted = iPos;  
  5365.     return iPosNext;  
  5366. }  
  5367.   
  5368. int CMarkup::x_ReleaseSubDoc( int iPos )  
  5369. {  
  5370.     // Mark position structures as deleted by depth first traversal  
  5371.     // Tricky because iElemNext used in traversal is overwritten for linked list of deleted  
  5372.     // Return value is what iElemNext was before being overwritten  
  5373.     //  
  5374.     int iPosNext = 0, iPosTop = iPos;  
  5375.     while ( 1 )  
  5376.     {  
  5377.         if ( ELEM(iPos).iElemChild )  
  5378.             iPos = ELEM(iPos).iElemChild;  
  5379.         else  
  5380.         {  
  5381.             while ( 1 )  
  5382.             {  
  5383.                 iPosNext = x_ReleasePos( iPos );  
  5384.                 if ( iPosNext || iPos == iPosTop )  
  5385.                     break;  
  5386.                 iPos = ELEM(iPos).iElemParent;  
  5387.             }  
  5388.             if ( iPos == iPosTop )  
  5389.                 break;  
  5390.             iPos = iPosNext;  
  5391.         }  
  5392.     }  
  5393.     return iPosNext;  
  5394. }  
  5395.   
  5396. void CMarkup::x_CheckSavedPos()  
  5397. {  
  5398.     // Remove any saved positions now pointing to deleted elements  
  5399.     // Must be done as part of element removal before position reassigned  
  5400.     if ( m_pSavedPosMaps->m_pMaps )  
  5401.     {  
  5402.         int nMap = 0;  
  5403.         while ( m_pSavedPosMaps->m_pMaps[nMap] )  
  5404.         {  
  5405.             SavedPosMap* pMap = m_pSavedPosMaps->m_pMaps[nMap];  
  5406.             for ( int nSlot = 0; nSlot < pMap->nMapSize; ++nSlot )  
  5407.             {  
  5408.                 SavedPos* pSavedPos = pMap->pTable[nSlot];  
  5409.                 if ( pSavedPos )  
  5410.                 {  
  5411.                     int nOffset = 0;  
  5412.                     int nSavedPosCount = 0;  
  5413.                     while ( 1 )  
  5414.                     {  
  5415.                         if ( pSavedPos[nOffset].nSavedPosFlags & SavedPos::SPM_USED )  
  5416.                         {  
  5417.                             int iPos = pSavedPos[nOffset].iPos;  
  5418.                             if ( ! (ELEM(iPos).nFlags & MNF_DELETED) )  
  5419.                             {  
  5420.                                 if ( nSavedPosCount < nOffset )  
  5421.                                 {  
  5422.                                     pSavedPos[nSavedPosCount] = pSavedPos[nOffset];  
  5423.                                     pSavedPos[nSavedPosCount].nSavedPosFlags &= ~SavedPos::SPM_LAST;  
  5424.                                 }  
  5425.                                 ++nSavedPosCount;  
  5426.                             }  
  5427.                         }  
  5428.                         if ( pSavedPos[nOffset].nSavedPosFlags & SavedPos::SPM_LAST )  
  5429.                         {  
  5430.                             while ( nSavedPosCount <= nOffset )  
  5431.                                 pSavedPos[nSavedPosCount++].nSavedPosFlags &= ~SavedPos::SPM_USED;  
  5432.                             break;  
  5433.                         }  
  5434.                         ++nOffset;  
  5435.                     }  
  5436.                 }  
  5437.             }  
  5438.             ++nMap;  
  5439.         }  
  5440.     }  
  5441. }  
  5442.   
  5443. void CMarkup::x_AdjustForNode( int iPosParent, int iPos, int nShift )  
  5444. {  
  5445.     // Adjust affected indexes  
  5446.     bool bAfterPos = true;  
  5447.     if ( ! iPos )  
  5448.     {  
  5449.         // Change happened before or at first element under iPosParent  
  5450.         // If there are any children of iPosParent, adjust from there  
  5451.         // otherwise start at parent and adjust from there  
  5452.         iPos = ELEM(iPosParent).iElemChild;  
  5453.         if ( iPos )  
  5454.         {  
  5455.             ELEM(iPos).nStart += nShift;  
  5456.             bAfterPos = false;  
  5457.         }  
  5458.         else  
  5459.         {  
  5460.             iPos = iPosParent;  
  5461.             ELEM(iPos).nLength += nShift;  
  5462.         }  
  5463.     }  
  5464.     x_Adjust( iPos, nShift, bAfterPos );  
  5465. }  
  5466.   
  5467. bool CMarkup::x_AddNode( int nNodeType, MCD_PCSZ pText, int nNodeFlags )  
  5468. {  
  5469.     if ( m_nDocFlags & MDF_READFILE )  
  5470.         return false;  
  5471.   
  5472.     // Comments, DTDs, and processing instructions are followed by CRLF  
  5473.     // Other nodes are usually concerned with mixed content, so no CRLF  
  5474.     if ( ! (nNodeType & (MNT_PROCESSING_INSTRUCTION|MNT_COMMENT|MNT_DOCUMENT_TYPE)) )  
  5475.         nNodeFlags |= MNF_WITHNOLINES;  
  5476.   
  5477.     // Add node of nNodeType after current node position  
  5478.     NodePos node( nNodeFlags );  
  5479.     if ( ! x_CreateNode(node.strMeta, nNodeType, pText) )  
  5480.         return false;  
  5481.   
  5482.     // Insert the new node relative to current node  
  5483.     node.nStart = m_nNodeOffset;  
  5484.     node.nLength = m_nNodeLength;  
  5485.     node.nNodeType = nNodeType;  
  5486.     int iPosBefore = m_iPos;  
  5487.     int nReplace = x_InsertNew( m_iPosParent, iPosBefore, node );  
  5488.   
  5489.     // If its a new element, create an ElemPos  
  5490.     int iPos = iPosBefore;  
  5491.     ElemPos* pElem = NULL;  
  5492.     if ( nNodeType == MNT_ELEMENT )  
  5493.     {  
  5494.         // Set indexes  
  5495.         iPos = x_GetFreePos();  
  5496.         pElem = &ELEM(iPos);  
  5497.         pElem->nStart = node.nStart;  
  5498.         pElem->SetStartTagLen( node.nLength );  
  5499.         pElem->SetEndTagLen( 0 );  
  5500.         pElem->nLength = node.nLength;  
  5501.         node.nStart = 0;  
  5502.         node.nLength = 0;  
  5503.         pElem->iElemChild = 0;  
  5504.         pElem->nFlags = 0;  
  5505.         x_LinkElem( m_iPosParent, iPosBefore, iPos );  
  5506.     }  
  5507.     if ( m_nDocFlags & MDF_WRITEFILE )  
  5508.     {  
  5509.         m_iPosParent = x_UnlinkPrevElem( m_iPosParent, iPosBefore, iPos );  
  5510.         if ( nNodeType == MNT_ELEMENT )  
  5511.         {  
  5512.             TokenPos token( m_strDoc, m_nDocFlags );  
  5513.             token.m_nL = pElem->nStart + 1;  
  5514.             token.m_nR = pElem->nStart + pElem->nLength - 3;  
  5515.             m_pFilePos->m_elemstack.PushTagAndCount( token );  
  5516.         }  
  5517.     }  
  5518.     else // need to adjust element positions after iPos  
  5519.         x_AdjustForNode( m_iPosParent, iPos, MCD_STRLENGTH(node.strMeta) - nReplace );  
  5520.   
  5521.     // Store current position  
  5522.     m_iPos = iPos;  
  5523.     m_iPosChild = 0;  
  5524.     m_nNodeOffset = node.nStart;  
  5525.     m_nNodeLength = node.nLength;  
  5526.     m_nNodeType = nNodeType;  
  5527.     MARKUP_SETDEBUGSTATE;  
  5528.     return true;  
  5529. }  
  5530.   
  5531. void CMarkup::x_RemoveNode( int iPosParent, int& iPos, int& nNodeType, int& nNodeOffset, int& nNodeLength )  
  5532. {  
  5533.     int iPosPrev = iPos;  
  5534.   
  5535.     // Removing an element?  
  5536.     if ( nNodeType == MNT_ELEMENT )  
  5537.     {  
  5538.         nNodeOffset = ELEM(iPos).nStart;  
  5539.         nNodeLength = ELEM(iPos).nLength;  
  5540.         iPosPrev = x_UnlinkElem( iPos );  
  5541.         x_CheckSavedPos();  
  5542.     }  
  5543.   
  5544.     // Find previous node type, offset and length  
  5545.     int nPrevOffset = 0;  
  5546.     if ( iPosPrev )  
  5547.         nPrevOffset = ELEM(iPosPrev).StartAfter();  
  5548.     else if ( iPosParent )  
  5549.         nPrevOffset = ELEM(iPosParent).StartContent();  
  5550.     TokenPos token( m_strDoc, m_nDocFlags );  
  5551.     NodePos node;  
  5552.     token.m_nNext = nPrevOffset;  
  5553.     int nPrevType = 0;  
  5554.     while ( token.m_nNext < nNodeOffset )  
  5555.     {  
  5556.         nPrevOffset = token.m_nNext;  
  5557.         nPrevType = token.ParseNode( node );  
  5558.     }  
  5559.     int nPrevLength = nNodeOffset - nPrevOffset;  
  5560.     if ( ! nPrevLength )  
  5561.     {  
  5562.         // Previous node is iPosPrev element  
  5563.         nPrevOffset = 0;  
  5564.         if ( iPosPrev )  
  5565.             nPrevType = MNT_ELEMENT;  
  5566.     }  
  5567.   
  5568.     // Remove node from document  
  5569.     x_DocChange( nNodeOffset, nNodeLength, MCD_STR() );  
  5570.     x_AdjustForNode( iPosParent, iPosPrev, - nNodeLength );  
  5571.   
  5572.     // Was removed node a lone end tag?  
  5573.     if ( nNodeType == MNT_LONE_END_TAG )  
  5574.     {  
  5575.         // See if we can unset parent MNF_ILLDATA flag  
  5576.         token.m_nNext = ELEM(iPosParent).StartContent();  
  5577.         int nEndOfContent = token.m_nNext + ELEM(iPosParent).ContentLen();  
  5578.         int iPosChild = ELEM(iPosParent).iElemChild;  
  5579.         while ( token.m_nNext < nEndOfContent )  
  5580.         {  
  5581.             if ( token.ParseNode(node) <= 0 )  
  5582.                 break;  
  5583.             if ( node.nNodeType == MNT_ELEMENT )  
  5584.             {  
  5585.                 token.m_nNext = ELEM(iPosChild).StartAfter();  
  5586.                 iPosChild = ELEM(iPosChild).iElemNext;  
  5587.             }  
  5588.         }  
  5589.         if ( token.m_nNext == nEndOfContent )  
  5590.             ELEM(iPosParent).nFlags &= ~MNF_ILLDATA;  
  5591.     }  
  5592.   
  5593.     nNodeType = nPrevType;  
  5594.     nNodeOffset = nPrevOffset;  
  5595.     nNodeLength = nPrevLength;  
  5596.     iPos = iPosPrev;  
  5597. }