升龙物业 老版本 ocx IPO, 加密狗 转值班电话

MarkupSTL.cpp 66KB

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