PageRenderTime 106ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/Cpp/source/xml/xmlParser.cpp

http://ops.googlecode.com/
C++ | 1712 lines | 1229 code | 169 blank | 314 comment | 230 complexity | f5cb0c7fd186a9868aefcb2af3a31cac MD5 | raw file
  1. /**
  2. ****************************************************************************
  3. * <P> XML.c - implementation file for basic XML parser written in ANSI C++
  4. * for portability. It works by using recursion and a node tree for breaking
  5. * down the elements of an XML document. </P>
  6. *
  7. * @version V2.38
  8. * @author Frank Vanden Berghen
  9. *
  10. * NOTE:
  11. *
  12. * If you add "#define STRICT_PARSING", on the first line of this file
  13. * the parser will see the following XML-stream:
  14. * <a><b>some text</b><b>other text </a>
  15. * as an error. Otherwise, this tring will be equivalent to:
  16. * <a><b>some text</b><b>other text</b></a>
  17. *
  18. * NOTE:
  19. *
  20. * If you add "#define APPROXIMATE_PARSING" on the first line of this file
  21. * the parser will see the following XML-stream:
  22. * <data name="n1">
  23. * <data name="n2">
  24. * <data name="n3" />
  25. * as equivalent to the following XML-stream:
  26. * <data name="n1" />
  27. * <data name="n2" />
  28. * <data name="n3" />
  29. * This can be useful for badly-formed XML-streams but prevent the use
  30. * of the following XML-stream (problem is: tags at contiguous levels
  31. * have the same names):
  32. * <data name="n1">
  33. * <data name="n2">
  34. * <data name="n3" />
  35. * </data>
  36. * </data>
  37. *
  38. * NOTE:
  39. *
  40. * If you add "#define _XMLPARSER_NO_MESSAGEBOX_" on the first line of this file
  41. * the "openFileHelper" function will always display error messages inside the
  42. * console instead of inside a message-box-window. Message-box-windows are
  43. * available on windows 9x/NT/2000/XP/Vista only.
  44. *
  45. * Copyright (c) 2002, Frank Vanden Berghen
  46. * All rights reserved.
  47. * See the file "AFPL-license.txt" about the licensing terms
  48. *
  49. ****************************************************************************
  50. */
  51. #define _XMLPARSER_NO_MESSAGEBOX_
  52. #ifndef _CRT_SECURE_NO_DEPRECATE
  53. #define _CRT_SECURE_NO_DEPRECATE
  54. #endif
  55. #include "xml/xmlParser.h"
  56. #ifdef _XMLWINDOWS
  57. //#ifdef _DEBUG
  58. //#define _CRTDBG_MAP_ALLOC
  59. //#include <crtdbg.h>
  60. //#endif
  61. #define WIN32_LEAN_AND_MEAN
  62. #include <Windows.h> // to have IsTextUnicode, MultiByteToWideChar, WideCharToMultiByte to handle unicode files
  63. // to have "MessageBoxA" to display error messages for openFilHelper
  64. #endif
  65. #include <memory.h>
  66. #include <assert.h>
  67. #include <stdio.h>
  68. #include <string.h>
  69. #include <stdlib.h>
  70. namespace opsXML{
  71. XMLCSTR XMLNode::getVersion() { return _CXML("v2.38"); }
  72. void freeXMLString(XMLSTR t){if(t)free(t);}
  73. static XMLNode::XMLCharEncoding characterEncoding=XMLNode::encoding_UTF8;
  74. static char guessWideCharChars=1, dropWhiteSpace=1, removeCommentsInMiddleOfText=1;
  75. inline int mmin( const int t1, const int t2 ) { return t1 < t2 ? t1 : t2; }
  76. // You can modify the initialization of the variable "XMLClearTags" below
  77. // to change the clearTags that are currently recognized by the library.
  78. // The number on the second columns is the length of the string inside the
  79. // first column. The "<!DOCTYPE" declaration must be the second in the list.
  80. // The "<!--" declaration must be the third in the list.
  81. typedef struct { XMLCSTR lpszOpen; int openTagLen; XMLCSTR lpszClose;} ALLXMLClearTag;
  82. static ALLXMLClearTag XMLClearTags[] =
  83. {
  84. { _CXML("<![CDATA["),9, _CXML("]]>") },
  85. { _CXML("<!DOCTYPE"),9, _CXML(">") },
  86. { _CXML("<!--") ,4, _CXML("-->") },
  87. { _CXML("<PRE>") ,5, _CXML("</PRE>") },
  88. { _CXML("<Script>") ,8, _CXML("</Script>")},
  89. { NULL ,0, NULL }
  90. };
  91. // You can modify the initialization of the variable "XMLEntities" below
  92. // to change the character entities that are currently recognized by the library.
  93. // The number on the second columns is the length of the string inside the
  94. // first column. Additionally, the syntaxes "&#xA0;" and "&#160;" are recognized.
  95. typedef struct { XMLCSTR s; int l; XMLCHAR c;} XMLCharacterEntity;
  96. static XMLCharacterEntity XMLEntities[] =
  97. {
  98. { _CXML("&amp;" ), 5, _CXML('&' )},
  99. { _CXML("&lt;" ), 4, _CXML('<' )},
  100. { _CXML("&gt;" ), 4, _CXML('>' )},
  101. { _CXML("&quot;"), 6, _CXML('\"')},
  102. { _CXML("&apos;"), 6, _CXML('\'')},
  103. { NULL , 0, '\0' }
  104. };
  105. // When rendering the XMLNode to a string (using the "createXMLString" function),
  106. // you can ask for a beautiful formatting. This formatting is using the
  107. // following indentation character:
  108. #define INDENTCHAR _CXML('\t')
  109. // The following function parses the XML errors into a user friendly string.
  110. // You can edit this to change the output language of the library to something else.
  111. XMLCSTR XMLNode::getError(XMLError xerror)
  112. {
  113. switch (xerror)
  114. {
  115. case eXMLErrorNone: return _CXML("No error");
  116. case eXMLErrorMissingEndTag: return _CXML("Warning: Unmatched end tag");
  117. case eXMLErrorNoXMLTagFound: return _CXML("Warning: No XML tag found");
  118. case eXMLErrorEmpty: return _CXML("Error: No XML data");
  119. case eXMLErrorMissingTagName: return _CXML("Error: Missing start tag name");
  120. case eXMLErrorMissingEndTagName: return _CXML("Error: Missing end tag name");
  121. case eXMLErrorUnmatchedEndTag: return _CXML("Error: Unmatched end tag");
  122. case eXMLErrorUnmatchedEndClearTag: return _CXML("Error: Unmatched clear tag end");
  123. case eXMLErrorUnexpectedToken: return _CXML("Error: Unexpected token found");
  124. case eXMLErrorNoElements: return _CXML("Error: No elements found");
  125. case eXMLErrorFileNotFound: return _CXML("Error: File not found");
  126. case eXMLErrorFirstTagNotFound: return _CXML("Error: First Tag not found");
  127. case eXMLErrorUnknownCharacterEntity:return _CXML("Error: Unknown character entity");
  128. case eXMLErrorCharacterCodeAbove255: return _CXML("Error: Character code above 255 is forbidden in MultiByte char mode.");
  129. case eXMLErrorCharConversionError: return _CXML("Error: unable to convert between WideChar and MultiByte chars");
  130. case eXMLErrorCannotOpenWriteFile: return _CXML("Error: unable to open file for writing");
  131. case eXMLErrorCannotWriteFile: return _CXML("Error: cannot write into file");
  132. case eXMLErrorBase64DataSizeIsNotMultipleOf4: return _CXML("Warning: Base64-string length is not a multiple of 4");
  133. case eXMLErrorBase64DecodeTruncatedData: return _CXML("Warning: Base64-string is truncated");
  134. case eXMLErrorBase64DecodeIllegalCharacter: return _CXML("Error: Base64-string contains an illegal character");
  135. case eXMLErrorBase64DecodeBufferTooSmall: return _CXML("Error: Base64 decode output buffer is too small");
  136. };
  137. return _CXML("Unknown");
  138. }
  139. /////////////////////////////////////////////////////////////////////////
  140. // Here start the abstraction layer to be OS-independent //
  141. /////////////////////////////////////////////////////////////////////////
  142. // Here is an abstraction layer to access some common string manipulation functions.
  143. // The abstraction layer is currently working for gcc, Microsoft Visual Studio 6.0,
  144. // Microsoft Visual Studio .NET, CC (sun compiler) and Borland C++.
  145. // If you plan to "port" the library to a new system/compiler, all you have to do is
  146. // to edit the following lines.
  147. #ifdef XML_NO_WIDE_CHAR
  148. char myIsTextWideChar(const void *b, int len) { return FALSE; }
  149. #else
  150. #if defined (UNDER_CE) || !defined(_XMLWINDOWS)
  151. char myIsTextWideChar(const void *b, int len) // inspired by the Wine API: RtlIsTextUnicode
  152. {
  153. #ifdef sun
  154. // for SPARC processors: wchar_t* buffers must always be alligned, otherwise it's a char* buffer.
  155. if ((((unsigned long)b)%sizeof(wchar_t))!=0) return FALSE;
  156. #endif
  157. const wchar_t *s=(const wchar_t*)b;
  158. // buffer too small:
  159. if (len<(int)sizeof(wchar_t)) return FALSE;
  160. // odd length test
  161. if (len&1) return FALSE;
  162. /* only checks the first 256 characters */
  163. len=mmin(256,len/sizeof(wchar_t));
  164. // Check for the special byte order:
  165. if (*((unsigned short*)s) == 0xFFFE) return TRUE; // IS_TEXT_UNICODE_REVERSE_SIGNATURE;
  166. if (*((unsigned short*)s) == 0xFEFF) return TRUE; // IS_TEXT_UNICODE_SIGNATURE
  167. // checks for ASCII characters in the UNICODE stream
  168. int i,stats=0;
  169. for (i=0; i<len; i++) if (s[i]<=(unsigned short)255) stats++;
  170. if (stats>len/2) return TRUE;
  171. // Check for UNICODE NULL chars
  172. for (i=0; i<len; i++) if (!s[i]) return TRUE;
  173. return FALSE;
  174. }
  175. #else
  176. char myIsTextWideChar(const void *b,int l) { return (char)IsTextUnicode((CONST LPVOID)b,l,NULL); };
  177. #endif
  178. #endif
  179. #ifdef _XMLWINDOWS
  180. // for Microsoft Visual Studio 6.0 and Microsoft Visual Studio .NET and Borland C++ Builder 6.0
  181. #ifdef _XMLWIDECHAR
  182. wchar_t *myMultiByteToWideChar(const char *s, XMLNode::XMLCharEncoding ce)
  183. {
  184. int i;
  185. if (ce==XMLNode::encoding_UTF8) i=(int)MultiByteToWideChar(CP_UTF8,0 ,s,-1,NULL,0);
  186. else i=(int)MultiByteToWideChar(CP_ACP ,MB_PRECOMPOSED,s,-1,NULL,0);
  187. if (i<0) return NULL;
  188. wchar_t *d=(wchar_t *)malloc((i+1)*sizeof(XMLCHAR));
  189. if (ce==XMLNode::encoding_UTF8) i=(int)MultiByteToWideChar(CP_UTF8,0 ,s,-1,d,i);
  190. else i=(int)MultiByteToWideChar(CP_ACP ,MB_PRECOMPOSED,s,-1,d,i);
  191. d[i]=0;
  192. return d;
  193. }
  194. static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) { return _wfopen(filename,mode); }
  195. static inline int xstrlen(XMLCSTR c) { return (int)wcslen(c); }
  196. static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return _wcsnicmp(c1,c2,l);}
  197. static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncmp(c1,c2,l);}
  198. static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return _wcsicmp(c1,c2); }
  199. static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)wcsstr(c1,c2); }
  200. static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)wcscpy(c1,c2); }
  201. #else
  202. char *myWideCharToMultiByte(const wchar_t *s)
  203. {
  204. UINT codePage=CP_ACP; if (characterEncoding==XMLNode::encoding_UTF8) codePage=CP_UTF8;
  205. int i=(int)WideCharToMultiByte(codePage, // code page
  206. 0, // performance and mapping flags
  207. s, // wide-character string
  208. -1, // number of chars in string
  209. NULL, // buffer for new string
  210. 0, // size of buffer
  211. NULL, // default for unmappable chars
  212. NULL // set when default char used
  213. );
  214. if (i<0) return NULL;
  215. char *d=(char*)malloc(i+1);
  216. WideCharToMultiByte(codePage, // code page
  217. 0, // performance and mapping flags
  218. s, // wide-character string
  219. -1, // number of chars in string
  220. d, // buffer for new string
  221. i, // size of buffer
  222. NULL, // default for unmappable chars
  223. NULL // set when default char used
  224. );
  225. d[i]=0;
  226. return d;
  227. }
  228. static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) { return fopen(filename,mode); }
  229. static inline int xstrlen(XMLCSTR c) { return (int)strlen(c); }
  230. #ifdef __BORLANDC__
  231. static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return strnicmp(c1,c2,l);}
  232. static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return stricmp(c1,c2); }
  233. #else
  234. static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return _strnicmp(c1,c2,l);}
  235. static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return _stricmp(c1,c2); }
  236. #endif
  237. static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncmp(c1,c2,l);}
  238. static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)strstr(c1,c2); }
  239. static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)strcpy(c1,c2); }
  240. #endif
  241. #else
  242. // for gcc and CC
  243. #ifdef XML_NO_WIDE_CHAR
  244. char *myWideCharToMultiByte(const wchar_t *s) { return NULL; }
  245. #else
  246. char *myWideCharToMultiByte(const wchar_t *s)
  247. {
  248. const wchar_t *ss=s;
  249. int i=(int)wcsrtombs(NULL,&ss,0,NULL);
  250. if (i<0) return NULL;
  251. char *d=(char *)malloc(i+1);
  252. wcsrtombs(d,&s,i,NULL);
  253. d[i]=0;
  254. return d;
  255. }
  256. #endif
  257. #ifdef _XMLWIDECHAR
  258. wchar_t *myMultiByteToWideChar(const char *s, XMLNode::XMLCharEncoding ce)
  259. {
  260. const char *ss=s;
  261. int i=(int)mbsrtowcs(NULL,&ss,0,NULL);
  262. if (i<0) return NULL;
  263. wchar_t *d=(wchar_t *)malloc((i+1)*sizeof(wchar_t));
  264. mbsrtowcs(d,&s,i,NULL);
  265. d[i]=0;
  266. return d;
  267. }
  268. int xstrlen(XMLCSTR c) { return wcslen(c); }
  269. #ifdef sun
  270. // for CC
  271. #include <widec.h>
  272. static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wsncasecmp(c1,c2,l);}
  273. static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wsncmp(c1,c2,l);}
  274. static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return wscasecmp(c1,c2); }
  275. #else
  276. // for gcc
  277. static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncasecmp(c1,c2,l);}
  278. static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncmp(c1,c2,l);}
  279. static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return wcscasecmp(c1,c2); }
  280. #endif
  281. static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)wcsstr(c1,c2); }
  282. static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)wcscpy(c1,c2); }
  283. static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode)
  284. {
  285. char *filenameAscii=myWideCharToMultiByte(filename);
  286. FILE *f;
  287. if (mode[0]==_CXML('r')) f=fopen(filenameAscii,"rb");
  288. else f=fopen(filenameAscii,"wb");
  289. free(filenameAscii);
  290. return f;
  291. }
  292. #else
  293. static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) { return fopen(filename,mode); }
  294. static inline int xstrlen(XMLCSTR c) { return strlen(c); }
  295. static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncasecmp(c1,c2,l);}
  296. static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncmp(c1,c2,l);}
  297. static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return strcasecmp(c1,c2); }
  298. static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)strstr(c1,c2); }
  299. static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)strcpy(c1,c2); }
  300. #endif
  301. static inline int _strnicmp(const char *c1,const char *c2, int l) { return strncasecmp(c1,c2,l);}
  302. #endif
  303. ///////////////////////////////////////////////////////////////////////////////
  304. // the "xmltoc,xmltoi,xmltol,xmltof,xmltoa" functions //
  305. ///////////////////////////////////////////////////////////////////////////////
  306. // These 5 functions are not used inside the XMLparser.
  307. // There are only here as "convenience" functions for the user.
  308. // If you don't need them, you can delete them without any trouble.
  309. #ifdef _XMLWIDECHAR
  310. #ifdef _XMLWINDOWS
  311. // for Microsoft Visual Studio 6.0 and Microsoft Visual Studio .NET and Borland C++ Builder 6.0
  312. char xmltoc(XMLCSTR t,int v){ if (t&&(*t)) return (char)_wtoi(t); return v; }
  313. int xmltoi(XMLCSTR t,int v){ if (t&&(*t)) return _wtoi(t); return v; }
  314. long xmltol(XMLCSTR t,long v){ if (t&&(*t)) return _wtol(t); return v; }
  315. double xmltof(XMLCSTR t,double v){ if (t&&(*t)) wscanf(t, "%f", &v); /*v=_wtof(t);*/ return v; }
  316. #else
  317. #ifdef sun
  318. // for CC
  319. #include <widec.h>
  320. char xmltoc(XMLCSTR t,int v){ if (t) return (char)wstol(t,NULL,10); return v; }
  321. int xmltoi(XMLCSTR t,int v){ if (t) return (int)wstol(t,NULL,10); return v; }
  322. long xmltol(XMLCSTR t,long v){ if (t) return wstol(t,NULL,10); return v; }
  323. #else
  324. // for gcc
  325. char xmltoc(XMLCSTR t,int v){ if (t) return (char)wcstol(t,NULL,10); return v; }
  326. int xmltoi(XMLCSTR t,int v){ if (t) return (int)wcstol(t,NULL,10); return v; }
  327. long xmltol(XMLCSTR t,long v){ if (t) return wcstol(t,NULL,10); return v; }
  328. #endif
  329. double xmltof(XMLCSTR t,double v){ if (t&&(*t)) wscanf(t, "%f", &v); /*v=_wtof(t);*/ return v; }
  330. #endif
  331. #else
  332. char xmltoc(XMLCSTR t,char v){ if (t&&(*t)) return (char)atoi(t); return v; }
  333. int xmltoi(XMLCSTR t,int v){ if (t&&(*t)) return atoi(t); return v; }
  334. long xmltol(XMLCSTR t,long v){ if (t&&(*t)) return atol(t); return v; }
  335. double xmltof(XMLCSTR t,double v){ if (t&&(*t)) return atof(t); return v; }
  336. #endif
  337. XMLCSTR xmltoa(XMLCSTR t,XMLCSTR v){ if (t) return t; return v; }
  338. /////////////////////////////////////////////////////////////////////////
  339. // the "openFileHelper" function //
  340. /////////////////////////////////////////////////////////////////////////
  341. // Since each application has its own way to report and deal with errors, you should modify & rewrite
  342. // the following "openFileHelper" function to get an "error reporting mechanism" tailored to your needs.
  343. XMLNode XMLNode::openFileHelper(XMLCSTR filename, XMLCSTR tag)
  344. {
  345. // guess the value of the global parameter "characterEncoding"
  346. // (the guess is based on the first 200 bytes of the file).
  347. FILE *f=xfopen(filename,_CXML("rb"));
  348. if (f)
  349. {
  350. char bb[205];
  351. int l=(int)fread(bb,1,200,f);
  352. setGlobalOptions(guessCharEncoding(bb,l),guessWideCharChars,dropWhiteSpace,removeCommentsInMiddleOfText);
  353. fclose(f);
  354. }
  355. // parse the file
  356. XMLResults pResults;
  357. XMLNode xnode=XMLNode::parseFile(filename,tag,&pResults);
  358. // display error message (if any)
  359. if (pResults.error != eXMLErrorNone)
  360. {
  361. // create message
  362. char message[2000],*s1=(char*)"",*s3=(char*)""; XMLCSTR s2=_CXML("");
  363. if (pResults.error==eXMLErrorFirstTagNotFound) { s1=(char*)"First Tag should be '"; s2=tag; s3=(char*)"'.\n"; }
  364. sprintf(message,
  365. #ifdef _XMLWIDECHAR
  366. "XML Parsing error inside file '%S'.\n%S\nAt line %i, column %i.\n%s%S%s"
  367. #else
  368. "XML Parsing error inside file '%s'.\n%s\nAt line %i, column %i.\n%s%s%s"
  369. #endif
  370. ,filename,XMLNode::getError(pResults.error),pResults.nLine,pResults.nColumn,s1,s2,s3);
  371. // display message
  372. #if defined(_XMLWINDOWS) && !defined(UNDER_CE) && !defined(_XMLPARSER_NO_MESSAGEBOX_)
  373. MessageBoxA(NULL,message,"XML Parsing error",MB_OK|MB_ICONERROR|MB_TOPMOST);
  374. #else
  375. printf("%s",message);
  376. #endif
  377. exit(255);
  378. }
  379. return xnode;
  380. }
  381. /////////////////////////////////////////////////////////////////////////
  382. // Here start the core implementation of the XMLParser library //
  383. /////////////////////////////////////////////////////////////////////////
  384. // You should normally not change anything below this point.
  385. #ifndef _XMLWIDECHAR
  386. // If "characterEncoding=ascii" then we assume that all characters have the same length of 1 byte.
  387. // If "characterEncoding=UTF8" then the characters have different lengths (from 1 byte to 4 bytes).
  388. // If "characterEncoding=ShiftJIS" then the characters have different lengths (from 1 byte to 2 bytes).
  389. // This table is used as lookup-table to know the length of a character (in byte) based on the
  390. // content of the first byte of the character.
  391. // (note: if you modify this, you must always have XML_utf8ByteTable[0]=0 ).
  392. static const char XML_utf8ByteTable[256] =
  393. {
  394. // 0 1 2 3 4 5 6 7 8 9 a b c d e f
  395. 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00
  396. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10
  397. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20
  398. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30
  399. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40
  400. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50
  401. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60
  402. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70End of ASCII range
  403. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x80 0x80 to 0xc1 invalid
  404. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x90
  405. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xa0
  406. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xb0
  407. 1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0 0xc2 to 0xdf 2 byte
  408. 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0
  409. 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,// 0xe0 0xe0 to 0xef 3 byte
  410. 4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid
  411. };
  412. static const char XML_asciiByteTable[256] =
  413. {
  414. 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  415. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  416. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  417. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  418. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  419. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
  420. };
  421. static const char XML_sjisByteTable[256] =
  422. {
  423. // 0 1 2 3 4 5 6 7 8 9 a b c d e f
  424. 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00
  425. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10
  426. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20
  427. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30
  428. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40
  429. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50
  430. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60
  431. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 End of ASCII range
  432. 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x80 0x81 to 0x9F 2 bytes
  433. 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x90
  434. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xa0
  435. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xb0
  436. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xc0
  437. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xd0
  438. 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xe0 0xe0 to 0xef 2 bytes
  439. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 // 0xf0
  440. };
  441. static const char *XML_ByteTable=(const char *)XML_utf8ByteTable; // the default is "characterEncoding=XMLNode::encoding_UTF8"
  442. #endif
  443. XMLNode XMLNode::emptyXMLNode;
  444. XMLClear XMLNode::emptyXMLClear={ NULL, NULL, NULL};
  445. XMLAttribute XMLNode::emptyXMLAttribute={ NULL, NULL};
  446. // Enumeration used to decipher what type a token is
  447. typedef enum XMLTokenTypeTag
  448. {
  449. eTokenText = 0,
  450. eTokenQuotedText,
  451. eTokenTagStart, /* "<" */
  452. eTokenTagEnd, /* "</" */
  453. eTokenCloseTag, /* ">" */
  454. eTokenEquals, /* "=" */
  455. eTokenDeclaration, /* "<?" */
  456. eTokenShortHandClose, /* "/>" */
  457. eTokenClear,
  458. eTokenError
  459. } XMLTokenType;
  460. // Main structure used for parsing XML
  461. typedef struct XML
  462. {
  463. XMLCSTR lpXML;
  464. XMLCSTR lpszText;
  465. int nIndex,nIndexMissigEndTag;
  466. enum XMLError error;
  467. XMLCSTR lpEndTag;
  468. int cbEndTag;
  469. XMLCSTR lpNewElement;
  470. int cbNewElement;
  471. int nFirst;
  472. } XML;
  473. typedef struct
  474. {
  475. ALLXMLClearTag *pClr;
  476. XMLCSTR pStr;
  477. } NextToken;
  478. // Enumeration used when parsing attributes
  479. typedef enum Attrib
  480. {
  481. eAttribName = 0,
  482. eAttribEquals,
  483. eAttribValue
  484. } Attrib;
  485. // Enumeration used when parsing elements to dictate whether we are currently
  486. // inside a tag
  487. typedef enum Status
  488. {
  489. eInsideTag = 0,
  490. eOutsideTag
  491. } Status;
  492. XMLError XMLNode::writeToFile(XMLCSTR filename, const char *encoding, char nFormat) const
  493. {
  494. if (!d) return eXMLErrorNone;
  495. FILE *f=xfopen(filename,_CXML("wb"));
  496. if (!f) return eXMLErrorCannotOpenWriteFile;
  497. #ifdef _XMLWIDECHAR
  498. unsigned char h[2]={ 0xFF, 0xFE };
  499. if (!fwrite(h,2,1,f)) return eXMLErrorCannotWriteFile;
  500. if ((!isDeclaration())&&((d->lpszName)||(!getChildNode().isDeclaration())))
  501. {
  502. if (!fwrite(L"<?xml version=\"1.0\" encoding=\"utf-16\"?>\n",sizeof(wchar_t)*40,1,f))
  503. return eXMLErrorCannotWriteFile;
  504. }
  505. #else
  506. if ((!isDeclaration())&&((d->lpszName)||(!getChildNode().isDeclaration())))
  507. {
  508. if (characterEncoding==encoding_UTF8)
  509. {
  510. // header so that windows recognize the file as UTF-8:
  511. unsigned char h[3]={0xEF,0xBB,0xBF}; if (!fwrite(h,3,1,f)) return eXMLErrorCannotWriteFile;
  512. encoding="utf-8";
  513. } else if (characterEncoding==encoding_ShiftJIS) encoding="SHIFT-JIS";
  514. if (!encoding) encoding="ISO-8859-1";
  515. if (fprintf(f,"<?xml version=\"1.0\" encoding=\"%s\"?>\n",encoding)<0) return eXMLErrorCannotWriteFile;
  516. } else
  517. {
  518. if (characterEncoding==encoding_UTF8)
  519. {
  520. unsigned char h[3]={0xEF,0xBB,0xBF}; if (!fwrite(h,3,1,f)) return eXMLErrorCannotWriteFile;
  521. }
  522. }
  523. #endif
  524. int i;
  525. XMLSTR t=createXMLString(nFormat,&i);
  526. if (!fwrite(t,sizeof(XMLCHAR)*i,1,f)) return eXMLErrorCannotWriteFile;
  527. if (fclose(f)!=0) return eXMLErrorCannotWriteFile;
  528. free(t);
  529. return eXMLErrorNone;
  530. }
  531. // Duplicate a given string.
  532. XMLSTR stringDup(XMLCSTR lpszData, int cbData)
  533. {
  534. if (lpszData==NULL) return NULL;
  535. XMLSTR lpszNew;
  536. if (cbData==-1) cbData=(int)xstrlen(lpszData);
  537. lpszNew = (XMLSTR)malloc((cbData+1) * sizeof(XMLCHAR));
  538. if (lpszNew)
  539. {
  540. memcpy(lpszNew, lpszData, (cbData) * sizeof(XMLCHAR));
  541. lpszNew[cbData] = (XMLCHAR)NULL;
  542. }
  543. return lpszNew;
  544. }
  545. XMLSTR ToXMLStringTool::toXMLUnSafe(XMLSTR dest,XMLCSTR source)
  546. {
  547. XMLSTR dd=dest;
  548. XMLCHAR ch;
  549. XMLCharacterEntity *entity;
  550. while ((ch=*source))
  551. {
  552. entity=XMLEntities;
  553. do
  554. {
  555. if (ch==entity->c) {xstrcpy(dest,entity->s); dest+=entity->l; source++; goto out_of_loop1; }
  556. entity++;
  557. } while(entity->s);
  558. #ifdef _XMLWIDECHAR
  559. *(dest++)=*(source++);
  560. #else
  561. switch(XML_ByteTable[(unsigned char)ch])
  562. {
  563. case 4: *(dest++)=*(source++);
  564. case 3: *(dest++)=*(source++);
  565. case 2: *(dest++)=*(source++);
  566. case 1: *(dest++)=*(source++);
  567. }
  568. #endif
  569. out_of_loop1:
  570. ;
  571. }
  572. *dest=0;
  573. return dd;
  574. }
  575. // private (used while rendering):
  576. int ToXMLStringTool::lengthXMLString(XMLCSTR source)
  577. {
  578. int r=0;
  579. XMLCharacterEntity *entity;
  580. XMLCHAR ch;
  581. while ((ch=*source))
  582. {
  583. entity=XMLEntities;
  584. do
  585. {
  586. if (ch==entity->c) { r+=entity->l; source++; goto out_of_loop1; }
  587. entity++;
  588. } while(entity->s);
  589. #ifdef _XMLWIDECHAR
  590. r++; source++;
  591. #else
  592. ch=XML_ByteTable[(unsigned char)ch]; r+=ch; source+=ch;
  593. #endif
  594. out_of_loop1:
  595. ;
  596. }
  597. return r;
  598. }
  599. ToXMLStringTool::~ToXMLStringTool(){ freeBuffer(); }
  600. void ToXMLStringTool::freeBuffer(){ if (buf) free(buf); buf=NULL; buflen=0; }
  601. XMLSTR ToXMLStringTool::toXML(XMLCSTR source)
  602. {
  603. int l=lengthXMLString(source)+1;
  604. if (l>buflen) { buflen=l; buf=(XMLSTR)realloc(buf,l*sizeof(XMLCHAR)); }
  605. return toXMLUnSafe(buf,source);
  606. }
  607. // private:
  608. XMLSTR fromXMLString(XMLCSTR s, int lo, XML *pXML)
  609. {
  610. // This function is the opposite of the function "toXMLString". It decodes the escape
  611. // sequences &amp;, &quot;, &apos;, &lt;, &gt; and replace them by the characters
  612. // &,",',<,>. This function is used internally by the XML Parser. All the calls to
  613. // the XML library will always gives you back "decoded" strings.
  614. //
  615. // in: string (s) and length (lo) of string
  616. // out: new allocated string converted from xml
  617. if (!s) return NULL;
  618. int ll=0,j;
  619. XMLSTR d;
  620. XMLCSTR ss=s;
  621. XMLCharacterEntity *entity;
  622. while ((lo>0)&&(*s))
  623. {
  624. if (*s==_CXML('&'))
  625. {
  626. if ((lo>2)&&(s[1]==_CXML('#')))
  627. {
  628. s+=2; lo-=2;
  629. if ((*s==_CXML('X'))||(*s==_CXML('x'))) { s++; lo--; }
  630. while ((*s)&&(*s!=_CXML(';'))&&((lo--)>0)) s++;
  631. if (*s!=_CXML(';'))
  632. {
  633. pXML->error=eXMLErrorUnknownCharacterEntity;
  634. return NULL;
  635. }
  636. s++; lo--;
  637. } else
  638. {
  639. entity=XMLEntities;
  640. do
  641. {
  642. if ((lo>=entity->l)&&(xstrnicmp(s,entity->s,entity->l)==0)) { s+=entity->l; lo-=entity->l; break; }
  643. entity++;
  644. } while(entity->s);
  645. if (!entity->s)
  646. {
  647. pXML->error=eXMLErrorUnknownCharacterEntity;
  648. return NULL;
  649. }
  650. }
  651. } else
  652. {
  653. #ifdef _XMLWIDECHAR
  654. s++; lo--;
  655. #else
  656. j=XML_ByteTable[(unsigned char)*s]; s+=j; lo-=j; ll+=j-1;
  657. #endif
  658. }
  659. ll++;
  660. }
  661. d=(XMLSTR)malloc((ll+1)*sizeof(XMLCHAR));
  662. s=d;
  663. while (ll-->0)
  664. {
  665. if (*ss==_CXML('&'))
  666. {
  667. if (ss[1]==_CXML('#'))
  668. {
  669. ss+=2; j=0;
  670. if ((*ss==_CXML('X'))||(*ss==_CXML('x')))
  671. {
  672. ss++;
  673. while (*ss!=_CXML(';'))
  674. {
  675. if ((*ss>=_CXML('0'))&&(*ss<=_CXML('9'))) j=(j<<4)+*ss-_CXML('0');
  676. else if ((*ss>=_CXML('A'))&&(*ss<=_CXML('F'))) j=(j<<4)+*ss-_CXML('A')+10;
  677. else if ((*ss>=_CXML('a'))&&(*ss<=_CXML('f'))) j=(j<<4)+*ss-_CXML('a')+10;
  678. else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;}
  679. ss++;
  680. }
  681. } else
  682. {
  683. while (*ss!=_CXML(';'))
  684. {
  685. if ((*ss>=_CXML('0'))&&(*ss<=_CXML('9'))) j=(j*10)+*ss-_CXML('0');
  686. else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;}
  687. ss++;
  688. }
  689. }
  690. #ifndef _XMLWIDECHAR
  691. if (j>255) { free((void*)s); pXML->error=eXMLErrorCharacterCodeAbove255;return NULL;}
  692. #endif
  693. (*d++)=(XMLCHAR)j; ss++;
  694. } else
  695. {
  696. entity=XMLEntities;
  697. do
  698. {
  699. if (xstrnicmp(ss,entity->s,entity->l)==0) { *(d++)=entity->c; ss+=entity->l; break; }
  700. entity++;
  701. } while(entity->s);
  702. }
  703. } else
  704. {
  705. #ifdef _XMLWIDECHAR
  706. *(d++)=*(ss++);
  707. #else
  708. switch(XML_ByteTable[(unsigned char)*ss])
  709. {
  710. case 4: *(d++)=*(ss++); ll--;
  711. case 3: *(d++)=*(ss++); ll--;
  712. case 2: *(d++)=*(ss++); ll--;
  713. case 1: *(d++)=*(ss++);
  714. }
  715. #endif
  716. }
  717. }
  718. *d=0;
  719. return (XMLSTR)s;
  720. }
  721. #define XML_isSPACECHAR(ch) ((ch==_CXML('\n'))||(ch==_CXML(' '))||(ch== _CXML('\t'))||(ch==_CXML('\r')))
  722. // private:
  723. char myTagCompare(XMLCSTR cclose, XMLCSTR copen)
  724. // !!!! WARNING strange convention&:
  725. // return 0 if equals
  726. // return 1 if different
  727. {
  728. if (!cclose) return 1;
  729. int l=(int)xstrlen(cclose);
  730. if (xstrnicmp(cclose, copen, l)!=0) return 1;
  731. const XMLCHAR c=copen[l];
  732. if (XML_isSPACECHAR(c)||
  733. (c==_CXML('/' ))||
  734. (c==_CXML('<' ))||
  735. (c==_CXML('>' ))||
  736. (c==_CXML('=' ))) return 0;
  737. return 1;
  738. }
  739. // Obtain the next character from the string.
  740. static inline XMLCHAR getNextChar(XML *pXML)
  741. {
  742. XMLCHAR ch = pXML->lpXML[pXML->nIndex];
  743. #ifdef _XMLWIDECHAR
  744. if (ch!=0) pXML->nIndex++;
  745. #else
  746. pXML->nIndex+=XML_ByteTable[(unsigned char)ch];
  747. #endif
  748. return ch;
  749. }
  750. // Find the next token in a string.
  751. // pcbToken contains the number of characters that have been read.
  752. static NextToken GetNextToken(XML *pXML, int *pcbToken, enum XMLTokenTypeTag *pType)
  753. {
  754. NextToken result;
  755. XMLCHAR ch;
  756. XMLCHAR chTemp;
  757. int indexStart,nFoundMatch,nIsText=FALSE;
  758. result.pClr=NULL; // prevent warning
  759. // Find next non-white space character
  760. do { indexStart=pXML->nIndex; ch=getNextChar(pXML); } while XML_isSPACECHAR(ch);
  761. if (ch)
  762. {
  763. // Cache the current string pointer
  764. result.pStr = &pXML->lpXML[indexStart];
  765. // First check whether the token is in the clear tag list (meaning it
  766. // does not need formatting).
  767. ALLXMLClearTag *ctag=XMLClearTags;
  768. do
  769. {
  770. if (xstrncmp(ctag->lpszOpen, result.pStr, ctag->openTagLen)==0)
  771. {
  772. result.pClr=ctag;
  773. pXML->nIndex+=ctag->openTagLen-1;
  774. *pType=eTokenClear;
  775. return result;
  776. }
  777. ctag++;
  778. } while(ctag->lpszOpen);
  779. // If we didn't find a clear tag then check for standard tokens
  780. switch(ch)
  781. {
  782. // Check for quotes
  783. case _CXML('\''):
  784. case _CXML('\"'):
  785. // Type of token
  786. *pType = eTokenQuotedText;
  787. chTemp = ch;
  788. // Set the size
  789. nFoundMatch = FALSE;
  790. // Search through the string to find a matching quote
  791. while((ch = getNextChar(pXML)))
  792. {
  793. if (ch==chTemp) { nFoundMatch = TRUE; break; }
  794. if (ch==_CXML('<')) break;
  795. }
  796. // If we failed to find a matching quote
  797. if (nFoundMatch == FALSE)
  798. {
  799. pXML->nIndex=indexStart+1;
  800. nIsText=TRUE;
  801. break;
  802. }
  803. // 4.02.2002
  804. // if (FindNonWhiteSpace(pXML)) pXML->nIndex--;
  805. break;
  806. // Equals (used with attribute values)
  807. case _CXML('='):
  808. *pType = eTokenEquals;
  809. break;
  810. // Close tag
  811. case _CXML('>'):
  812. *pType = eTokenCloseTag;
  813. break;
  814. // Check for tag start and tag end
  815. case _CXML('<'):
  816. // Peek at the next character to see if we have an end tag '</',
  817. // or an xml declaration '<?'
  818. chTemp = pXML->lpXML[pXML->nIndex];
  819. // If we have a tag end...
  820. if (chTemp == _CXML('/'))
  821. {
  822. // Set the type and ensure we point at the next character
  823. getNextChar(pXML);
  824. *pType = eTokenTagEnd;
  825. }
  826. // If we have an XML declaration tag
  827. else if (chTemp == _CXML('?'))
  828. {
  829. // Set the type and ensure we point at the next character
  830. getNextChar(pXML);
  831. *pType = eTokenDeclaration;
  832. }
  833. // Otherwise we must have a start tag
  834. else
  835. {
  836. *pType = eTokenTagStart;
  837. }
  838. break;
  839. // Check to see if we have a short hand type end tag ('/>').
  840. case _CXML('/'):
  841. // Peek at the next character to see if we have a short end tag '/>'
  842. chTemp = pXML->lpXML[pXML->nIndex];
  843. // If we have a short hand end tag...
  844. if (chTemp == _CXML('>'))
  845. {
  846. // Set the type and ensure we point at the next character
  847. getNextChar(pXML);
  848. *pType = eTokenShortHandClose;
  849. break;
  850. }
  851. // If we haven't found a short hand closing tag then drop into the
  852. // text process
  853. // Other characters
  854. default:
  855. nIsText = TRUE;
  856. }
  857. // If this is a TEXT node
  858. if (nIsText)
  859. {
  860. // Indicate we are dealing with text
  861. *pType = eTokenText;
  862. while((ch = getNextChar(pXML)))
  863. {
  864. if XML_isSPACECHAR(ch)
  865. {
  866. indexStart++; break;
  867. } else if (ch==_CXML('/'))
  868. {
  869. // If we find a slash then this maybe text or a short hand end tag
  870. // Peek at the next character to see it we have short hand end tag
  871. ch=pXML->lpXML[pXML->nIndex];
  872. // If we found a short hand end tag then we need to exit the loop
  873. if (ch==_CXML('>')) { pXML->nIndex--; break; }
  874. } else if ((ch==_CXML('<'))||(ch==_CXML('>'))||(ch==_CXML('=')))
  875. {
  876. pXML->nIndex--; break;
  877. }
  878. }
  879. }
  880. *pcbToken = pXML->nIndex-indexStart;
  881. } else
  882. {
  883. // If we failed to obtain a valid character
  884. *pcbToken = 0;
  885. *pType = eTokenError;
  886. result.pStr=NULL;
  887. }
  888. return result;
  889. }
  890. XMLCSTR XMLNode::updateName_WOSD(XMLSTR lpszName)
  891. {
  892. if (!d) { free(lpszName); return NULL; }
  893. if (d->lpszName&&(lpszName!=d->lpszName)) free((void*)d->lpszName);
  894. d->lpszName=lpszName;
  895. return lpszName;
  896. }
  897. // private:
  898. XMLNode::XMLNode(struct XMLNodeDataTag *p){ d=p; (p->ref_count)++; }
  899. XMLNode::XMLNode(XMLNodeData *pParent, XMLSTR lpszName, char isDeclaration)
  900. {
  901. d=(XMLNodeData*)malloc(sizeof(XMLNodeData));
  902. d->ref_count=1;
  903. d->lpszName=NULL;
  904. d->nChild= 0;
  905. d->nText = 0;
  906. d->nClear = 0;
  907. d->nAttribute = 0;
  908. d->isDeclaration = isDeclaration;
  909. d->pParent = pParent;
  910. d->pChild= NULL;
  911. d->pText= NULL;
  912. d->pClear= NULL;
  913. d->pAttribute= NULL;
  914. d->pOrder= NULL;
  915. updateName_WOSD(lpszName);
  916. }
  917. XMLNode XMLNode::createXMLTopNode_WOSD(XMLSTR lpszName, char isDeclaration) { return XMLNode(NULL,lpszName,isDeclaration); }
  918. XMLNode XMLNode::createXMLTopNode(XMLCSTR lpszName, char isDeclaration) { return XMLNode(NULL,stringDup(lpszName),isDeclaration); }
  919. #define MEMORYINCREASE 50
  920. static inline void myFree(void *p) { if (p) free(p); };
  921. static inline void *myRealloc(void *p, int newsize, int memInc, int sizeofElem)
  922. {
  923. if (p==NULL) { if (memInc) return malloc(memInc*sizeofElem); return malloc(sizeofElem); }
  924. if ((memInc==0)||((newsize%memInc)==0)) p=realloc(p,(newsize+memInc)*sizeofElem);
  925. // if (!p)
  926. // {
  927. // printf("XMLParser Error: Not enough memory! Aborting...\n"); exit(220);
  928. // }
  929. return p;
  930. }
  931. // private:
  932. XMLElementPosition XMLNode::findPosition(XMLNodeData *d, int index, XMLElementType xxtype)
  933. {
  934. if (index<0) return -1;
  935. int i=0,j=(int)((index<<2)+xxtype),*o=d->pOrder; while (o[i]!=j) i++; return i;
  936. }
  937. // private:
  938. // update "order" information when deleting a content of a XMLNode
  939. int XMLNode::removeOrderElement(XMLNodeData *d, XMLElementType t, int index)
  940. {
  941. int n=d->nChild+d->nText+d->nClear, *o=d->pOrder,i=findPosition(d,index,t);
  942. memmove(o+i, o+i+1, (n-i)*sizeof(int));
  943. for (;i<n;i++)
  944. if ((o[i]&3)==(int)t) o[i]-=4;
  945. // We should normally do:
  946. // d->pOrder=(int)realloc(d->pOrder,n*sizeof(int));
  947. // but we skip reallocation because it's too time consuming.
  948. // Anyway, at the end, it will be free'd completely at once.
  949. return i;
  950. }
  951. void *XMLNode::addToOrder(int memoryIncrease,int *_pos, int nc, void *p, int size, XMLElementType xtype)
  952. {
  953. // in: *_pos is the position inside d->pOrder ("-1" means "EndOf")
  954. // out: *_pos is the index inside p
  955. p=myRealloc(p,(nc+1),memoryIncrease,size);
  956. int n=d->nChild+d->nText+d->nClear;
  957. d->pOrder=(int*)myRealloc(d->pOrder,n+1,memoryIncrease*3,sizeof(int));
  958. int pos=*_pos,*o=d->pOrder;
  959. if ((pos<0)||(pos>=n)) { *_pos=nc; o[n]=(int)((nc<<2)+xtype); return p; }
  960. int i=pos;
  961. memmove(o+i+1, o+i, (n-i)*sizeof(int));
  962. while ((pos<n)&&((o[pos]&3)!=(int)xtype)) pos++;
  963. if (pos==n) { *_pos=nc; o[n]=(int)((nc<<2)+xtype); return p; }
  964. o[i]=o[pos];
  965. for (i=pos+1;i<=n;i++) if ((o[i]&3)==(int)xtype) o[i]+=4;
  966. *_pos=pos=o[pos]>>2;
  967. memmove(((char*)p)+(pos+1)*size,((char*)p)+pos*size,(nc-pos)*size);
  968. return p;
  969. }
  970. // Add a child node to the given element.
  971. XMLNode XMLNode::addChild_priv(int memoryIncrease, XMLSTR lpszName, char isDeclaration, int pos)
  972. {
  973. if (!lpszName) return emptyXMLNode;
  974. d->pChild=(XMLNode*)addToOrder(memoryIncrease,&pos,d->nChild,d->pChild,sizeof(XMLNode),eNodeChild);
  975. d->pChild[pos].d=NULL;
  976. d->pChild[pos]=XMLNode(d,lpszName,isDeclaration);
  977. d->nChild++;
  978. return d->pChild[pos];
  979. }
  980. // Add an attribute to an element.
  981. XMLAttribute *XMLNode::addAttribute_priv(int memoryIncrease,XMLSTR lpszName, XMLSTR lpszValuev)
  982. {
  983. if (!lpszName) return &emptyXMLAttribute;
  984. if (!d) { myFree(lpszName); myFree(lpszValuev); return &emptyXMLAttribute; }
  985. int nc=d->nAttribute;
  986. d->pAttribute=(XMLAttribute*)myRealloc(d->pAttribute,(nc+1),memoryIncrease,sizeof(XMLAttribute));
  987. XMLAttribute *pAttr=d->pAttribute+nc;
  988. pAttr->lpszName = lpszName;
  989. pAttr->lpszValue = lpszValuev;
  990. d->nAttribute++;
  991. return pAttr;
  992. }
  993. // Add text to the element.
  994. XMLCSTR XMLNode::addText_priv(int memoryIncrease, XMLSTR lpszValue, int pos)
  995. {
  996. if (!lpszValue) return NULL;
  997. if (!d) { myFree(lpszValue); return NULL; }
  998. d->pText=(XMLCSTR*)addToOrder(memoryIncrease,&pos,d->nText,d->pText,sizeof(XMLSTR),eNodeText);
  999. d->pText[pos]=lpszValue;
  1000. d->nText++;
  1001. return lpszValue;
  1002. }
  1003. // Add clear (unformatted) text to the element.
  1004. XMLClear *XMLNode::addClear_priv(int memoryIncrease, XMLSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, int pos)
  1005. {
  1006. if (!lpszValue) return &emptyXMLClear;
  1007. if (!d) { myFree(lpszValue); return &emptyXMLClear; }
  1008. d->pClear=(XMLClear *)addToOrder(memoryIncrease,&pos,d->nClear,d->pClear,sizeof(XMLClear),eNodeClear);
  1009. XMLClear *pNewClear=d->pClear+pos;
  1010. pNewClear->lpszValue = lpszValue;
  1011. if (!lpszOpen) lpszOpen=XMLClearTags->lpszOpen;
  1012. if (!lpszClose) lpszClose=XMLClearTags->lpszClose;
  1013. pNewClear->lpszOpenTag = lpszOpen;
  1014. pNewClear->lpszCloseTag = lpszClose;
  1015. d->nClear++;
  1016. return pNewClear;
  1017. }
  1018. // private:
  1019. // Parse a clear (unformatted) type node.
  1020. char XMLNode::parseClearTag(void *px, void *_pClear)
  1021. {
  1022. XML *pXML=(XML *)px;
  1023. ALLXMLClearTag pClear=*((ALLXMLClearTag*)_pClear);
  1024. int cbTemp=0;
  1025. XMLCSTR lpszTemp=NULL;
  1026. XMLCSTR lpXML=&pXML->lpXML[pXML->nIndex];
  1027. static XMLCSTR docTypeEnd=_CXML("]>");
  1028. // Find the closing tag
  1029. // Seems the <!DOCTYPE need a better treatment so lets handle it
  1030. if (pClear.lpszOpen==XMLClearTags[1].lpszOpen)
  1031. {
  1032. XMLCSTR pCh=lpXML;
  1033. while (*pCh)
  1034. {
  1035. if (*pCh==_CXML('<')) { pClear.lpszClose=docTypeEnd; lpszTemp=xstrstr(lpXML,docTypeEnd); break; }
  1036. else if (*pCh==_CXML('>')) { lpszTemp=pCh; break; }
  1037. #ifdef _XMLWIDECHAR
  1038. pCh++;
  1039. #else
  1040. pCh+=XML_ByteTable[(unsigned char)(*pCh)];
  1041. #endif
  1042. }
  1043. } else lpszTemp=xstrstr(lpXML, pClear.lpszClose);
  1044. if (lpszTemp)
  1045. {
  1046. // Cache the size and increment the index
  1047. cbTemp = (int)(lpszTemp - lpXML);
  1048. pXML->nIndex += cbTemp+(int)xstrlen(pClear.lpszClose);
  1049. // Add the clear node to the current element
  1050. addClear_priv(MEMORYINCREASE,stringDup(lpXML,cbTemp), pClear.lpszOpen, pClear.lpszClose,-1);
  1051. return 0;
  1052. }
  1053. // If we failed to find the end tag
  1054. pXML->error = eXMLErrorUnmatchedEndClearTag;
  1055. return 1;
  1056. }
  1057. void XMLNode::exactMemory(XMLNodeData *d)
  1058. {
  1059. if (d->pOrder) d->pOrder=(int*)realloc(d->pOrder,(d->nChild+d->nText+d->nClear)*sizeof(int));
  1060. if (d->pChild) d->pChild=(XMLNode*)realloc(d->pChild,d->nChild*sizeof(XMLNode));
  1061. if (d->pAttribute) d->pAttribute=(XMLAttribute*)realloc(d->pAttribute,d->nAttribute*sizeof(XMLAttribute));
  1062. if (d->pText) d->pText=(XMLCSTR*)realloc(d->pText,d->nText*sizeof(XMLSTR));
  1063. if (d->pClear) d->pClear=(XMLClear *)realloc(d->pClear,d->nClear*sizeof(XMLClear));
  1064. }
  1065. char XMLNode::maybeAddTxT(void *pa, XMLCSTR tokenPStr)
  1066. {
  1067. XML *pXML=(XML *)pa;
  1068. XMLCSTR lpszText=pXML->lpszText;
  1069. if (!lpszText) return 0;
  1070. if (dropWhiteSpace) while (XML_isSPACECHAR(*lpszText)&&(lpszText!=tokenPStr)) lpszText++;
  1071. int cbText = (int)(tokenPStr - lpszText);
  1072. if (!cbText) { pXML->lpszText=NULL; return 0; }
  1073. if (dropWhiteSpace) { cbText--; while ((cbText)&&XML_isSPACECHAR(lpszText[cbText])) cbText--; cbText++; }
  1074. if (!cbText) { pXML->lpszText=NULL; return 0; }
  1075. XMLSTR lpt=fromXMLString(lpszText,cbText,pXML);
  1076. if (!lpt) return 1;
  1077. pXML->lpszText=NULL;
  1078. if (removeCommentsInMiddleOfText && d->nText && d->nClear)
  1079. {
  1080. // if the previous insertion was a comment (<!-- -->) AND
  1081. // if the previous previous insertion was a text then, delete the comment and append the text
  1082. int n=d->nChild+d->nText+d->nClear-1,*o=d->pOrder;
  1083. if (((o[n]&3)==eNodeClear)&&((o[n-1]&3)==eNodeText))
  1084. {
  1085. int i=o[n]>>2;
  1086. if (d->pClear[i].lpszOpenTag==XMLClearTags[2].lpszOpen)
  1087. {
  1088. deleteClear(i);
  1089. i=o[n-1]>>2;
  1090. n=xstrlen(d->pText[i]);
  1091. int n2=xstrlen(lpt)+1;
  1092. d->pText[i]=(XMLSTR)realloc((void*)d->pText[i],(n+n2)*sizeof(XMLCHAR));
  1093. if (!d->pText[i]) return 1;
  1094. memcpy((void*)(d->pText[i]+n),lpt,n2*sizeof(XMLCHAR));
  1095. free(lpt);
  1096. return 0;
  1097. }
  1098. }
  1099. }
  1100. addText_priv(MEMORYINCREASE,lpt,-1);
  1101. return 0;
  1102. }
  1103. // private:
  1104. // Recursively parse an XML element.
  1105. int XMLNode::ParseXMLElement(void *pa)
  1106. {
  1107. XML *pXML=(XML *)pa;
  1108. int cbToken;
  1109. enum XMLTokenTypeTag xtype;
  1110. NextToken token;
  1111. XMLCSTR lpszTemp=NULL;
  1112. int cbTemp=0;
  1113. char nDeclaration;
  1114. XMLNode pNew;
  1115. enum Status status; // inside or outside a tag
  1116. enum Attrib attrib = eAttribName;
  1117. assert(pXML);
  1118. // If this is the first call to the function
  1119. if (pXML->nFirst)
  1120. {
  1121. // Assume we are outside of a tag definition
  1122. pXML->nFirst = FALSE;
  1123. status = eOutsideTag;
  1124. } else
  1125. {
  1126. // If this is not the first call then we should only be called when inside a tag.
  1127. status = eInsideTag;
  1128. }
  1129. // Iterate through the tokens in the document
  1130. for(;;)
  1131. {
  1132. // Obtain the next token
  1133. token = GetNextToken(pXML, &cbToken, &xtype);
  1134. if (xtype != eTokenError)
  1135. {
  1136. // Check the current status
  1137. switch(status)
  1138. {
  1139. // If we are outside of a tag definition
  1140. case eOutsideTag:
  1141. // Check what type of token we obtained
  1142. switch(xtype)
  1143. {
  1144. // If we have found text or quoted text
  1145. case eTokenText:
  1146. case eTokenCloseTag: /* '>' */
  1147. case eTokenShortHandClose: /* '/>' */
  1148. case eTokenQuotedText:
  1149. case eTokenEquals:
  1150. break;
  1151. // If we found a start tag '<' and declarations '<?'
  1152. case eTokenTagStart:
  1153. case eTokenDeclaration:
  1154. // Cache whether this new element is a declaration or not
  1155. nDeclaration = (xtype == eTokenDeclaration);
  1156. // If we have node text then add this to the element
  1157. if (maybeAddTxT(pXML,token.pStr)) return FALSE;
  1158. // Find the name of the tag
  1159. token = GetNextToken(pXML, &cbToken, &xtype);
  1160. // Return an error if we couldn't obtain the next token or
  1161. // it wasnt text
  1162. if (xtype != eTokenText)
  1163. {
  1164. pXML->error = eXMLErrorMissingTagName;
  1165. return FALSE;
  1166. }
  1167. // If we found a new element which is the same as this
  1168. // element then we need to pass this back to the caller..
  1169. #ifdef APPROXIMATE_PARSING
  1170. if (d->lpszName &&
  1171. myTagCompare(d->lpszName, token.pStr) == 0)
  1172. {
  1173. // Indicate to the caller that it needs to create a
  1174. // new element.
  1175. pXML->lpNewElement = token.pStr;
  1176. pXML->cbNewElement = cbToken;
  1177. return TRUE;
  1178. } else
  1179. #endif
  1180. {
  1181. // If the name of the new element differs from the name of
  1182. // the current element we need to add the new element to
  1183. // the current one and recurse
  1184. pNew = addChild_priv(MEMORYINCREASE,stringDup(token.pStr,cbToken), nDeclaration,-1);
  1185. while (!pNew.isEmpty())
  1186. {
  1187. // Callself to process the new node. If we return
  1188. // FALSE this means we dont have any more
  1189. // processing to do...
  1190. if (!pNew.ParseXMLElement(pXML)) return FALSE;
  1191. else
  1192. {
  1193. // If the call to recurse this function
  1194. // evented in a end tag specified in XML then
  1195. // we need to unwind the calls to this
  1196. // function until we find the appropriate node
  1197. // (the element name and end tag name must
  1198. // match)
  1199. if (pXML->cbEndTag)
  1200. {
  1201. // If we are back at the root node then we
  1202. // have an unmatched end tag
  1203. if (!d->lpszName)
  1204. {
  1205. pXML->error=eXMLErrorUnmatchedEndTag;
  1206. return FALSE;
  1207. }
  1208. // If the end tag matches the name of this
  1209. // element then we only need to unwind
  1210. // once more...
  1211. if (myTagCompare(d->lpszName, pXML->lpEndTag)==0)
  1212. {
  1213. pXML->cbEndTag = 0;
  1214. }
  1215. return TRUE;
  1216. } else
  1217. if (pXML->cbNewElement)
  1218. {
  1219. // If the call indicated a new element is to
  1220. // be created on THIS element.
  1221. // If the name of this element matches the
  1222. // name of the element we need to create
  1223. // then we need to return to the caller
  1224. // and let it process the element.
  1225. if (myTagCompare(d->lpszName, pXML->lpNewElement)==0)
  1226. {
  1227. return TRUE;
  1228. }
  1229. // Add the new element and recurse
  1230. pNew = addChild_priv(MEMORYINCREASE,stringDup(pXML->lpNewElement,pXML->cbNewElement),0,-1);
  1231. pXML->cbNewElement = 0;
  1232. }
  1233. else
  1234. {
  1235. // If we didn't have a new element to create
  1236. pNew = emptyXMLNode;
  1237. }
  1238. }
  1239. }
  1240. }
  1241. break;
  1242. // If we found an end tag
  1243. case eTokenTagEnd:
  1244. // If we have node text then add this to the element
  1245. if (maybeAddTxT(pXML,token.pStr)) return FALSE;
  1246. // Find the name of the end tag
  1247. token = GetNextToken(pXML, &cbTemp, &xtype);
  1248. // The end tag should be text
  1249. if (xtype != eTokenText)
  1250. {
  1251. pXML->error = eXMLErrorMissingEndTagName;
  1252. return FALSE;
  1253. }
  1254. lpszTemp = token.pStr;
  1255. // After the end tag we should find a closing tag
  1256. token = GetNextToken(pXML, &cbToken, &xtype);
  1257. if (xtype != eTokenCloseTag)
  1258. {
  1259. pXML->error = eXMLErrorMissingEndTagName;
  1260. return FALSE;
  1261. }
  1262. pXML->lpszText=pXML->lpXML+pXML->nIndex;
  1263. // We need to return to the previous caller. If the name
  1264. // of the tag cannot be found we need to keep returning to
  1265. // caller until we find a match
  1266. if (myTagCompare(d->lpszName, lpszTemp) != 0)
  1267. #ifdef STRICT_PARSING
  1268. {
  1269. pXML->error=eXMLErrorUnmatchedEndTag;
  1270. pXML->nIndexMissigEndTag=pXML->nIndex;
  1271. return FALSE;
  1272. }
  1273. #else
  1274. {
  1275. pXML->error=eXMLErrorMissingEndTag;
  1276. pXML->nIndexMissigEndTag=pXML->nIndex;
  1277. pXML->lpEndTag = lpszTemp;
  1278. pXML->cbEndTag = cbTemp;
  1279. }
  1280. #endif
  1281. // Return to the caller
  1282. exactMemory(d);
  1283. return TRUE;
  1284. // If we found a clear (unformatted) token
  1285. case eTokenClear:
  1286. // If we have node text then add this to the element
  1287. if (maybeAddTxT(pXML,token.pStr)) return FALSE;
  1288. if (parseClearTag(pXML, token.pClr)) return FALSE;
  1289. pXML->lpszText=pXML->lpXML+pXML->nIndex;
  1290. break;
  1291. default:
  1292. break;
  1293. }
  1294. break;
  1295. // If we are inside a tag definition we need to search for attributes
  1296. case eInsideTag:
  1297. // Check what part of the attribute (name, equals, value) we
  1298. // are looking for.
  1299. switch(attrib)
  1300. {
  1301. // If we are looking for a new attribute
  1302. case eAttribName:
  1303. // Check what the current token type is
  1304. switch(xtype)
  1305. {
  1306. // If the current type is text...
  1307. // Eg. 'attribute'
  1308. case eTokenText:
  1309. // Cache the token then indicate that we are next to
  1310. // look for the equals
  1311. lpszTemp = token.pStr;
  1312. cbTemp = cbToken;
  1313. attrib = eAttribEquals;
  1314. break;
  1315. // If we found a closing tag...
  1316. // Eg. '>'
  1317. case eTokenCloseTag:
  1318. // We are now outside the tag
  1319. status = eOutsideTag;
  1320. pXML->lpszText=pXML->lpXML+pXML->nIndex;
  1321. break;
  1322. // If we found a short hand '/>' closing tag then we can
  1323. // return to the caller
  1324. case eTokenShortHandClose:
  1325. exactMemory(d);
  1326. pXML->lpszText=pXML->lpXML+pXML->nIndex;
  1327. return TRUE;
  1328. // Errors...
  1329. case eTokenQuotedText: /* '"SomeText"' */
  1330. case eTokenTagStart: /* '<' */
  1331. case eTokenTagEnd: /* '</' */
  1332. case eTokenEquals: /* '=' */
  1333. case eTokenDeclaration: /* '<?' */
  1334. case eTokenClear:
  1335. pXML->error = eXMLErrorUnexpectedToken;
  1336. return FALSE;
  1337. default: break;
  1338. }
  1339. break;
  1340. // If we are looking for an equals
  1341. case eAttribEquals:
  1342. // Check what the current token type is
  1343. switch(xtype)
  1344. {
  1345. // If the current type is text...
  1346. // Eg. 'Attribute AnotherAttribute'
  1347. case eTokenText:
  1348. // Add the unvalued attribute to the list
  1349. addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp), NULL);
  1350. // Cache the token then indicate. We are next to
  1351. // look for the equals attribute
  1352. lpszTemp = token.pStr;
  1353. cbTemp = cbToken;
  1354. break;
  1355. // If we found a closing tag 'Attribute >' or a short hand
  1356. // closing tag 'Attribute />'
  1357. case eTokenShortHandClose:
  1358. case eTokenCloseTag:
  1359. // If we are a declaration element '<?' then we need
  1360. // to remove extra closing '?' if it exists
  1361. pXML->lpszText=pXML->lpXML+pXML->nIndex;
  1362. if (d->isDeclaration &&
  1363. (lpszTemp[cbTemp-1]) == _CXML('?'))
  1364. {
  1365. cbTemp--;
  1366. if (d->pParent && d->pParent->pParent) xtype = eTokenShortHandClose;
  1367. }
  1368. if (cbTemp)
  1369. {
  1370. // Add the unvalued attribute to the list
  1371. addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp), NULL);
  1372. }
  1373. // If this is the end of the tag then return to the caller
  1374. if (xtype == eTokenShortHandClose)
  1375. {
  1376. exactMemory(d);
  1377. return TRUE;
  1378. }
  1379. // We are now outside the tag
  1380. status = eOutsideTag;
  1381. break;
  1382. // If we found the equals token...
  1383. // Eg. 'Attribute ='
  1384. case eTokenEquals:
  1385. // Indicate that we next need to search for the value
  1386. // for the attribute
  1387. attrib = eAttribValue;
  1388. break;
  1389. // Errors...
  1390. case eTokenQuotedText: /* 'Attribute "InvalidAttr"'*/
  1391. case eTokenTagStart: /* 'Attribute <' */
  1392. case eTokenTagEnd: /* 'Attribute </' */
  1393. case eTokenDeclaration: /* 'Attribute <?' */
  1394. case eTokenClear:
  1395. pXML->error = eXMLErrorUnexpectedToken;
  1396. return FALSE;
  1397. default: break;
  1398. }
  1399. break;
  1400. // If we are looking for an attribute value
  1401. case eAttribValue:
  1402. // Check what the current token type is
  1403. switch(xtype)
  1404. {
  1405. // If the current type is text or quoted text...
  1406. // Eg. 'Attribute = "Value"' or 'Attribute = Value' or
  1407. // 'Attribute = 'Value''.
  1408. case eTokenText:
  1409. case eTokenQuotedText:
  1410. // If we are a declaration element '<?' then we need
  1411. // to remove extra closing '?' if it exists
  1412. if (d->isDeclaration &&
  1413. (token.pStr[cbToken-1]) == _CXML('?'))
  1414. {
  1415. cbToken--;
  1416. }
  1417. if (cbTemp)
  1418. {
  1419. // Add the valued attribute to the list
  1420. if (xtype==eTokenQuotedText) { token.pStr++; cbToken-=2; }
  1421. XMLSTR attrVal=(XMLSTR)token.pStr;
  1422. if (attrVal)
  1423. {
  1424. attrVal=fromXMLString(attrVal,cbToken,pXML);
  1425. if (!attrVal) return FALSE;
  1426. }
  1427. addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp),attrVal);
  1428. }
  1429. // Indicate we are searching for a new attribute
  1430. attrib = eAttribName;
  1431. break;
  1432. // Errors...
  1433. case eTokenTagStart: /* 'Attr = <' */
  1434. case eTokenTagEnd: /* 'Attr = </' */
  1435. case eTokenCloseTag: /* 'Attr = >' */
  1436. case eTokenShortHandClose: /* "Attr = />" */
  1437. case eTokenEquals: /* 'Attr = =' */
  1438. case eTokenDeclaration: /* 'Attr = <?' */
  1439. case eTokenClear:
  1440. pXML->error = eXMLErrorUnexpectedToken;
  1441. return FALSE;
  1442. break;
  1443. default: break;
  1444. }
  1445. }
  1446. }
  1447. }
  1448. // If we failed to obtain the next token
  1449. else
  1450. {
  1451. if ((!d->isDeclaration)&&(d->pParent))
  1452. {
  1453. #ifdef STRICT_PARSING
  1454. pXML->error=eXMLErrorUnmatchedEndTag;
  1455. #else
  1456. pXML->error=eXMLErrorMissingEndTag;
  1457. #endif
  1458. pXML->nIndexMissigEndTag=pXML->nIndex;
  1459. }
  1460. maybeAddTxT(pXML,pXML->lpXML+pXML->nIndex);
  1461. return FALSE;
  1462. }
  1463. }
  1464. }
  1465. // Count the number of lines and columns in an XML string.
  1466. static void CountLinesAndColumns(XMLCSTR lpXML, int nUpto, XMLResults *pResults)
  1467. {
  1468. XMLCHAR ch;
  1469. assert(lpXML);
  1470. assert(pResults);
  1471. struct XML xml={ lpXML,lpXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0, TRUE };
  1472. pResults->nLine = 1;
  1473. pResults->nColumn = 1;
  1474. while (xml.nIndex<nUpto)
  1475. {
  1476. ch = getNextChar(&xml);
  1477. if (ch != _CXML('\n')) pResults->nColumn++;
  1478. else
  1479. {
  1480. pResults->nLine++;
  1481. pResults->nColumn=1;
  1482. }
  1483. }
  1484. }
  1485. // Parse XML and return the root element.
  1486. XMLNode XMLNode::parseString(XMLCSTR lpszXML, XMLCSTR tag, XMLResults *pResults)
  1487. {
  1488. if (!lpszXML)
  1489. {
  1490. if (pResults)
  1491. {
  1492. pResults->error=eXMLErrorNoElements;
  1493. pResults->nLine=0;
  1494. pResults->nColumn=0;
  1495. }
  1496. return emptyXMLNode;
  1497. }
  1498. XMLNode xnode(NULL,NULL,FALSE);
  1499. struct XML xml={ lpszXML, lpszXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0, TRUE };
  1500. // Create header element
  1501. xnode.ParseXMLElement(&xml);
  1502. enum XMLError error = xml.error;
  1503. if (!xnode.nChildNode()) error=eXMLErrorNoXMLTagFound;
  1504. if ((xnode.nChildNode()==1)&&(xnode.nElement()==1)) xnode=xnode.getChildNode(); // skip the empty node
  1505. // If no error occurred
  1506. if ((error==eXMLErrorNone)||(error==eXMLErrorMissingEndTag)||(error==eXMLErrorNoXMLTagFound))
  1507. {
  1508. XMLCSTR name=xnode.getName();
  1509. if (tag&&xstrlen(tag)&&((!name)||(xstricmp(xnode.getName(),tag))))
  1510. {
  1511. XMLNode nodeTmp;
  1512. int i=0;
  1513. while (i<xnode.nChildNode())
  1514. {
  1515. nodeTmp=xnode.getChildNode(i);
  1516. if (xstricmp(nodeTmp.getName(),tag)==0) break;
  1517. if (nodeTmp.isDeclaration()) { xnode=nodeTmp; i=0; } else i++;
  1518. }
  1519. if (i>=xnode.nChildNode())
  1520. {
  1521. if (pResults)
  1522. {
  1523. pResults->error=eXMLErrorFirstTagNotFound;
  1524. pResults->nLine=0;
  1525. pResults->nColumn=0;
  1526. }
  1527. return emptyXMLNode;
  1528. }
  1529. xnode=nodeTmp;
  1530. }
  1531. } else
  1532. {
  1533. // Cleanup: this will destroy all the nodes
  1534. xnode = emptyXMLNode;
  1535. }
  1536. // If we have been given somewhere to place results
  1537. if (pResults)
  1538. {
  1539. pResults->error = error;
  1540. // If we have an error
  1541. if (error!=eXMLErrorNone)
  1542. {
  1543. if (error==eXMLErrorM