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.41
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 * The following license terms for the "XMLParser library from Business-Insight" apply to projects
46 * that are in some way related to
47 * the "mcpat project", including applications
48 * using "mcpat project" and tools developed
49 * for enhancing "mcpat project". All other projects
50 * (not related to "mcpat project") have to use the "XMLParser library from Business-Insight"
51 * code under the Aladdin Free Public License (AFPL)
52 * See the file "AFPL-license.txt" for more informations about the AFPL license.
53 * (see http://www.artifex.com/downloads/doc/Public.htm for detailed AFPL terms)
54 *
55 * Redistribution and use of the "XMLParser library from Business-Insight" in source and binary forms, with or without
56 * modification, are permitted provided that the following conditions are met:
57 *     * Redistributions of source code must retain the above copyright
58 *       notice, this list of conditions and the following disclaimer.
59 *     * Redistributions in binary form must reproduce the above copyright
60 *       notice, this list of conditions and the following disclaimer in the
61 *       documentation and/or other materials provided with the distribution.
62 *     * Neither the name of Frank Vanden Berghen nor the
63 *       names of its contributors may be used to endorse or promote products
64 *       derived from this software without specific prior written permission.
65 *
66 * THIS SOFTWARE IS PROVIDED BY Business-Insight ``AS IS'' AND ANY
67 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
68 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
69 * DISCLAIMED. IN NO EVENT SHALL Business-Insight BE LIABLE FOR ANY
70 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
71 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
72 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
73 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
74 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
75 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
76 *
77 * Copyright (c) 2002, Business-Insight
78 * Copyright (c) 2010-2013 Advanced Micro Devices, Inc.
79 * <a href="http://www.Business-Insight.com">Business-Insight</a>
80 * All rights reserved.
81 *
82 ****************************************************************************
83 */
84#ifndef _CRT_SECURE_NO_DEPRECATE
85#define _CRT_SECURE_NO_DEPRECATE
86#endif
87#include "xmlParser.h"
88#ifdef _XMLWINDOWS
89//#ifdef _DEBUG
90//#define _CRTDBG_MAP_ALLOC
91//#include <crtdbg.h>
92//#endif
93#define WIN32_LEAN_AND_MEAN
94#include <Windows.h> // to have IsTextUnicode, MultiByteToWideChar, WideCharToMultiByte to handle unicode files
95// to have "MessageBoxA" to display error messages for openFilHelper
96#endif
97
98#include <memory.h>
99
100#include <cassert>
101#include <cstdio>
102#include <cstdlib>
103#include <cstring>
104
105XMLCSTR XMLNode::getVersion() {
106    return _CXML("v2.39");
107}
108void freeXMLString(XMLSTR t) {
109    if (t)free(t);
110}
111
112static XMLNode::XMLCharEncoding characterEncoding=XMLNode::char_encoding_UTF8;
113static char guessWideCharChars=1, dropWhiteSpace=1, removeCommentsInMiddleOfText=1;
114
115inline int mmin( const int t1, const int t2 ) {
116    return t1 < t2 ? t1 : t2;
117}
118
119// You can modify the initialization of the variable "XMLClearTags" below
120// to change the clearTags that are currently recognized by the library.
121// The number on the second columns is the length of the string inside the
122// first column. The "<!DOCTYPE" declaration must be the second in the list.
123// The "<!--" declaration must be the third in the list.
124typedef struct {
125    XMLCSTR lpszOpen;
126    int openTagLen;
127    XMLCSTR lpszClose;
128} ALLXMLClearTag;
129static ALLXMLClearTag XMLClearTags[] = {
130    {    _CXML("<![CDATA["), 9,  _CXML("]]>")      },
131    {    _CXML("<!DOCTYPE"), 9,  _CXML(">")        },
132    {    _CXML("<!--")     , 4,  _CXML("-->")      },
133    {    _CXML("<PRE>")    , 5,  _CXML("</PRE>")   },
134//  {    _CXML("<Script>") ,8,  _CXML("</Script>")},
135    {    NULL              , 0,  NULL           }
136};
137
138// You can modify the initialization of the variable "XMLEntities" below
139// to change the character entities that are currently recognized by the library.
140// The number on the second columns is the length of the string inside the
141// first column. Additionally, the syntaxes "&#xA0;" and "&#160;" are recognized.
142typedef struct {
143    XMLCSTR s;
144    int l;
145    XMLCHAR c;
146} XMLCharacterEntity;
147static XMLCharacterEntity XMLEntities[] = {
148    { _CXML("&amp;" ), 5, _CXML('&' )},
149    { _CXML("&lt;"  ), 4, _CXML('<' )},
150    { _CXML("&gt;"  ), 4, _CXML('>' )},
151    { _CXML("&quot;"), 6, _CXML('\"')},
152    { _CXML("&apos;"), 6, _CXML('\'')},
153    { NULL           , 0, '\0'    }
154};
155
156// When rendering the XMLNode to a string (using the "createXMLString" function),
157// you can ask for a beautiful formatting. This formatting is using the
158// following indentation character:
159#define INDENTCHAR _CXML('\t')
160
161// The following function parses the XML errors into a user friendly string.
162// You can edit this to change the output language of the library to something else.
163XMLCSTR XMLNode::getError(XMLError xerror) {
164    switch (xerror) {
165    case eXMLErrorNone:
166        return _CXML("No error");
167    case eXMLErrorMissingEndTag:
168        return _CXML("Warning: Unmatched end tag");
169    case eXMLErrorNoXMLTagFound:
170        return _CXML("Warning: No XML tag found");
171    case eXMLErrorEmpty:
172        return _CXML("Error: No XML data");
173    case eXMLErrorMissingTagName:
174        return _CXML("Error: Missing start tag name");
175    case eXMLErrorMissingEndTagName:
176        return _CXML("Error: Missing end tag name");
177    case eXMLErrorUnmatchedEndTag:
178        return _CXML("Error: Unmatched end tag");
179    case eXMLErrorUnmatchedEndClearTag:
180        return _CXML("Error: Unmatched clear tag end");
181    case eXMLErrorUnexpectedToken:
182        return _CXML("Error: Unexpected token found");
183    case eXMLErrorNoElements:
184        return _CXML("Error: No elements found");
185    case eXMLErrorFileNotFound:
186        return _CXML("Error: File not found");
187    case eXMLErrorFirstTagNotFound:
188        return _CXML("Error: First Tag not found");
189    case eXMLErrorUnknownCharacterEntity:
190        return _CXML("Error: Unknown character entity");
191    case eXMLErrorCharacterCodeAbove255:
192        return _CXML("Error: Character code above 255 is forbidden in MultiByte char mode.");
193    case eXMLErrorCharConversionError:
194        return _CXML("Error: unable to convert between WideChar and MultiByte chars");
195    case eXMLErrorCannotOpenWriteFile:
196        return _CXML("Error: unable to open file for writing");
197    case eXMLErrorCannotWriteFile:
198        return _CXML("Error: cannot write into file");
199
200    case eXMLErrorBase64DataSizeIsNotMultipleOf4:
201        return _CXML("Warning: Base64-string length is not a multiple of 4");
202    case eXMLErrorBase64DecodeTruncatedData:
203        return _CXML("Warning: Base64-string is truncated");
204    case eXMLErrorBase64DecodeIllegalCharacter:
205        return _CXML("Error: Base64-string contains an illegal character");
206    case eXMLErrorBase64DecodeBufferTooSmall:
207        return _CXML("Error: Base64 decode output buffer is too small");
208    };
209    return _CXML("Unknown");
210}
211
212/////////////////////////////////////////////////////////////////////////
213//      Here start the abstraction layer to be OS-independent          //
214/////////////////////////////////////////////////////////////////////////
215
216// Here is an abstraction layer to access some common string manipulation functions.
217// The abstraction layer is currently working for gcc, Microsoft Visual Studio 6.0,
218// Microsoft Visual Studio .NET, CC (sun compiler) and Borland C++.
219// If you plan to "port" the library to a new system/compiler, all you have to do is
220// to edit the following lines.
221#ifdef XML_NO_WIDE_CHAR
222char myIsTextWideChar(const void *b, int len) {
223    return FALSE;
224}
225#else
226#if defined (UNDER_CE) || !defined(_XMLWINDOWS)
227// inspired by the Wine API: RtlIsTextUnicode
228char myIsTextWideChar(const void *b, int len) {
229#ifdef sun
230    // for SPARC processors: wchar_t* buffers must always be alligned, otherwise it's a char* buffer.
231    if ((((unsigned long)b)%sizeof(wchar_t))!=0) return FALSE;
232#endif
233    const wchar_t *s = (const wchar_t*)b;
234
235    // buffer too small:
236    if (len < (int)sizeof(wchar_t)) return FALSE;
237
238    // odd length test
239    if (len&1) return FALSE;
240
241    /* only checks the first 256 characters */
242    len = mmin(256, len / sizeof(wchar_t));
243
244    // Check for the special byte order:
245    if (*((unsigned short*)s) == 0xFFFE) return TRUE;     // IS_TEXT_UNICODE_REVERSE_SIGNATURE;
246    if (*((unsigned short*)s) == 0xFEFF) return TRUE;      // IS_TEXT_UNICODE_SIGNATURE
247
248    // checks for ASCII characters in the UNICODE stream
249    int i, stats=0;
250    for (i=0; i<len; i++) if (s[i]<=(unsigned short)255) stats++;
251    if (stats>len/2) return TRUE;
252
253    // Check for UNICODE NULL chars
254    for (i=0; i<len; i++) if (!s[i]) return TRUE;
255
256    return FALSE;
257}
258#else
259char myIsTextWideChar(const void *b, int l) {
260    return (char)IsTextUnicode((CONST LPVOID)b, l, NULL);
261};
262#endif
263#endif
264
265#ifdef _XMLWINDOWS
266// for Microsoft Visual Studio 6.0 and Microsoft Visual Studio .NET and Borland C++ Builder 6.0
267#ifdef _XMLWIDECHAR
268wchar_t *myMultiByteToWideChar(const char *s, XMLNode::XMLCharEncoding ce) {
269    int i;
270    if (ce == XMLNode::char_encoding_UTF8) {
271        i = (int)MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);
272    } else {
273        i = (int)MultiByteToWideChar(CP_ACP , MB_PRECOMPOSED, s, -1, NULL, 0);
274    }
275    if (i < 0) {
276        return NULL;
277    }
278    wchar_t *d = (wchar_t *)malloc((i + 1) * sizeof(XMLCHAR));
279    if (ce == XMLNode::char_encoding_UTF8) {
280        i = (int)MultiByteToWideChar(CP_UTF8, 0, s, -1, d, i);
281    } else {
282        i = (int)MultiByteToWideChar(CP_ACP , MB_PRECOMPOSED, s, -1, d, i);
283    }
284    d[i] = 0;
285    return d;
286}
287static inline FILE *xfopen(XMLCSTR filename, XMLCSTR mode) {
288    return _wfopen(filename, mode);
289}
290static inline int xstrlen(XMLCSTR c)   {
291    return (int)wcslen(c);
292}
293static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) {
294    return _wcsnicmp(c1, c2, l);
295}
296static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) {
297    return wcsncmp(c1, c2, l);
298}
299static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) {
300    return _wcsicmp(c1, c2);
301}
302static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) {
303    return (XMLSTR)wcsstr(c1, c2);
304}
305static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) {
306    return (XMLSTR)wcscpy(c1, c2);
307}
308#else
309char *myWideCharToMultiByte(const wchar_t *s) {
310    UINT codePage = CP_ACP;
311    if (characterEncoding == XMLNode::char_encoding_UTF8) codePage = CP_UTF8;
312    int i = (int)WideCharToMultiByte(codePage,  // code page
313                                     0,                       // performance and mapping flags
314                                     s,                       // wide-character string
315                                     -1,                       // number of chars in string
316                                     NULL,                       // buffer for new string
317                                     0,                       // size of buffer
318                                     NULL,                    // default for unmappable chars
319                                     NULL                     // set when default char used
320                                    );
321    if (i<0) return NULL;
322    char *d=(char*)malloc(i+1);
323    WideCharToMultiByte(codePage,  // code page
324                        0,                       // performance and mapping flags
325                        s,                       // wide-character string
326                        -1,                       // number of chars in string
327                        d,                       // buffer for new string
328                        i,                       // size of buffer
329                        NULL,                    // default for unmappable chars
330                        NULL                     // set when default char used
331                       );
332    d[i] = 0;
333    return d;
334}
335static inline FILE *xfopen(XMLCSTR filename, XMLCSTR mode) {
336    return fopen(filename, mode);
337}
338static inline int xstrlen(XMLCSTR c)   {
339    return (int)strlen(c);
340}
341#ifdef __BORLANDC__
342static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) {
343    return strnicmp(c1, c2, l);
344}
345static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) {
346    return stricmp(c1, c2);
347}
348#else
349static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) {
350    return _strnicmp(c1, c2, l);
351}
352static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) {
353    return _stricmp(c1, c2);
354}
355#endif
356static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) {
357    return strncmp(c1, c2, l);
358}
359static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) {
360    return (XMLSTR)strstr(c1, c2);
361}
362static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) {
363    return (XMLSTR)strcpy(c1, c2);
364}
365#endif
366#else
367// for gcc and CC
368#ifdef XML_NO_WIDE_CHAR
369char *myWideCharToMultiByte(const wchar_t *s) {
370    return NULL;
371}
372#else
373char *myWideCharToMultiByte(const wchar_t *s) {
374    const wchar_t *ss = s;
375    int i = (int)wcsrtombs(NULL, &ss, 0, NULL);
376    if (i < 0) return NULL;
377    char *d = (char *)malloc(i + 1);
378    wcsrtombs(d, &s, i, NULL);
379    d[i] = 0;
380    return d;
381}
382#endif
383#ifdef _XMLWIDECHAR
384wchar_t *myMultiByteToWideChar(const char *s, XMLNode::XMLCharEncoding ce) {
385    const char *ss = s;
386    int i = (int)mbsrtowcs(NULL, &ss, 0, NULL);
387    if (i < 0) return NULL;
388    wchar_t *d = (wchar_t *)malloc((i + 1) * sizeof(wchar_t));
389    mbsrtowcs(d, &s, i, NULL);
390    d[i] = 0;
391    return d;
392}
393int xstrlen(XMLCSTR c)   {
394    return wcslen(c);
395}
396#ifdef sun
397// for CC
398#include <widec.h>
399static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) {
400    return wsncasecmp(c1, c2, l);
401}
402static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) {
403    return wsncmp(c1, c2, l);
404}
405static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) {
406    return wscasecmp(c1, c2);
407}
408#else
409// for gcc
410static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) {
411    return wcsncasecmp(c1, c2, l);
412}
413static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) {
414    return wcsncmp(c1, c2, l);
415}
416static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) {
417    return wcscasecmp(c1, c2);
418}
419#endif
420static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) {
421    return (XMLSTR)wcsstr(c1, c2);
422}
423static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) {
424    return (XMLSTR)wcscpy(c1, c2);
425}
426static inline FILE *xfopen(XMLCSTR filename, XMLCSTR mode) {
427    char *filenameAscii = myWideCharToMultiByte(filename);
428    FILE *f;
429    if (mode[0] == _CXML('r')) f = fopen(filenameAscii, "rb");
430    else                     f = fopen(filenameAscii, "wb");
431    free(filenameAscii);
432    return f;
433}
434#else
435static inline FILE *xfopen(XMLCSTR filename, XMLCSTR mode) {
436    return fopen(filename, mode);
437}
438static inline int xstrlen(XMLCSTR c)   {
439    return strlen(c);
440}
441static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) {
442    return strncasecmp(c1, c2, l);
443}
444static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) {
445    return strncmp(c1, c2, l);
446}
447static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) {
448    return strcasecmp(c1, c2);
449}
450static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) {
451    return (XMLSTR)strstr(c1, c2);
452}
453static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) {
454    return (XMLSTR)strcpy(c1, c2);
455}
456#endif
457static inline int _strnicmp(const char *c1, const char *c2, int l) {
458    return strncasecmp(c1, c2, l);
459}
460#endif
461
462
463///////////////////////////////////////////////////////////////////////////////
464//            the "xmltoc,xmltob,xmltoi,xmltol,xmltof,xmltoa" functions      //
465///////////////////////////////////////////////////////////////////////////////
466// These 6 functions are not used inside the XMLparser.
467// There are only here as "convenience" functions for the user.
468// If you don't need them, you can delete them without any trouble.
469#ifdef _XMLWIDECHAR
470#ifdef _XMLWINDOWS
471// for Microsoft Visual Studio 6.0 and Microsoft Visual Studio .NET and Borland C++ Builder 6.0
472char    xmltob(XMLCSTR t, int     v) {
473    if (t && (*t)) return (char)_wtoi(t);
474    return v;
475}
476int     xmltoi(XMLCSTR t, int     v) {
477    if (t && (*t)) return _wtoi(t);
478    return v;
479}
480long    xmltol(XMLCSTR t, long    v) {
481    if (t && (*t)) return _wtol(t);
482    return v;
483}
484double  xmltof(XMLCSTR t, double  v) {
485    if (t && (*t)) wscanf(t, "%f", &v); /*v=_wtof(t);*/
486    return v;
487}
488#else
489#ifdef sun
490// for CC
491#include <widec.h>
492char    xmltob(XMLCSTR t, int     v) {
493    if (t) return (char)wstol(t, NULL, 10);
494    return v;
495}
496int     xmltoi(XMLCSTR t, int     v) {
497    if (t) return (int)wstol(t, NULL, 10);
498    return v;
499}
500long    xmltol(XMLCSTR t, long    v) {
501    if (t) return wstol(t, NULL, 10);
502    return v;
503}
504#else
505// for gcc
506char    xmltob(XMLCSTR t, int     v) {
507    if (t) return (char)wcstol(t, NULL, 10);
508    return v;
509}
510int     xmltoi(XMLCSTR t, int     v) {
511    if (t) return (int)wcstol(t, NULL, 10);
512    return v;
513}
514long    xmltol(XMLCSTR t, long    v) {
515    if (t) return wcstol(t, NULL, 10);
516    return v;
517}
518#endif
519double  xmltof(XMLCSTR t, double  v) {
520    if (t && (*t)) wscanf(t, "%f", &v); /*v=_wtof(t);*/
521    return v;
522}
523#endif
524#else
525char    xmltob(XMLCSTR t, char    v) {
526    if (t && (*t)) return (char)atoi(t);
527    return v;
528}
529int     xmltoi(XMLCSTR t, int     v) {
530    if (t && (*t)) return atoi(t);
531    return v;
532}
533long    xmltol(XMLCSTR t, long    v) {
534    if (t && (*t)) return atol(t);
535    return v;
536}
537double  xmltof(XMLCSTR t, double  v) {
538    if (t && (*t)) return atof(t);
539    return v;
540}
541#endif
542XMLCSTR xmltoa(XMLCSTR t, XMLCSTR v) {
543    if (t)       return  t;
544    return v;
545}
546XMLCHAR xmltoc(XMLCSTR t, XMLCHAR v) {
547    if (t && (*t)) return *t;
548    return v;
549}
550
551/////////////////////////////////////////////////////////////////////////
552//                    the "openFileHelper" function                    //
553/////////////////////////////////////////////////////////////////////////
554
555// Since each application has its own way to report and deal with errors, you should modify & rewrite
556// the following "openFileHelper" function to get an "error reporting mechanism" tailored to your needs.
557XMLNode XMLNode::openFileHelper(XMLCSTR filename, XMLCSTR tag) {
558    // guess the value of the global parameter "characterEncoding"
559    // (the guess is based on the first 200 bytes of the file).
560    FILE *f = xfopen(filename, _CXML("rb"));
561    if (f) {
562        char bb[205];
563        int l = (int)fread(bb, 1, 200, f);
564        setGlobalOptions(guessCharEncoding(bb, l), guessWideCharChars,
565                         dropWhiteSpace, removeCommentsInMiddleOfText);
566        fclose(f);
567    }
568
569    // parse the file
570    XMLResults pResults;
571    XMLNode xnode = XMLNode::parseFile(filename, tag, &pResults);
572
573    // display error message (if any)
574    if (pResults.error != eXMLErrorNone) {
575        // create message
576        char message[2000], *s1 = (char*)"", *s3 = (char*)"";
577        XMLCSTR s2 = _CXML("");
578        if (pResults.error == eXMLErrorFirstTagNotFound) {
579            s1 = (char*)"First Tag should be '";
580            s2 = tag;
581            s3 = (char*)"'.\n";
582        }
583        sprintf(message,
584#ifdef _XMLWIDECHAR
585                "XML Parsing error inside file '%S'.\n%S\nAt line %i, column %i.\n%s%S%s"
586#else
587                "XML Parsing error inside file '%s'.\n%s\nAt line %i, column %i.\n%s%s%s"
588#endif
589                , filename, XMLNode::getError(pResults.error), pResults.nLine,
590                pResults.nColumn, s1, s2, s3);
591
592        // display message
593#if defined(_XMLWINDOWS) && !defined(UNDER_CE) && !defined(_XMLPARSER_NO_MESSAGEBOX_)
594        MessageBoxA(NULL, message, "XML Parsing error", MB_OK | MB_ICONERROR |
595                    MB_TOPMOST);
596#else
597        printf("%s", message);
598#endif
599        exit(255);
600    }
601    return xnode;
602}
603
604/////////////////////////////////////////////////////////////////////////
605//      Here start the core implementation of the XMLParser library    //
606/////////////////////////////////////////////////////////////////////////
607
608// You should normally not change anything below this point.
609
610#ifndef _XMLWIDECHAR
611// If "characterEncoding=ascii" then we assume that all characters have the same length of 1 byte.
612// If "characterEncoding=UTF8" then the characters have different lengths (from 1 byte to 4 bytes).
613// If "characterEncoding=ShiftJIS" then the characters have different lengths (from 1 byte to 2 bytes).
614// This table is used as lookup-table to know the length of a character (in byte) based on the
615// content of the first byte of the character.
616// (note: if you modify this, you must always have XML_utf8ByteTable[0]=0 ).
617static const char XML_utf8ByteTable[256] = {
618    //  0 1 2 3 4 5 6 7 8 9 a b c d e f
619    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x00
620    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x10
621    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x20
622    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x30
623    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x40
624    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x50
625    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x60
626    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x70 End of ASCII range
627    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x80 0x80 to 0xc1 invalid
628    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x90
629    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0xa0
630    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0xb0
631    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0xc0 0xc2 to 0xdf 2 byte
632    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0xd0
633    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,// 0xe0 0xe0 to 0xef 3 byte
634    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
635};
636static const char XML_legacyByteTable[256] = {
637    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,
638    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,
639    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,
640    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,
641    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,
642    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
643};
644static const char XML_sjisByteTable[256] = {
645    //  0 1 2 3 4 5 6 7 8 9 a b c d e f
646    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x00
647    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x10
648    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x20
649    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x30
650    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x40
651    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x50
652    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x60
653    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x70
654    1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0x80 0x81 to 0x9F 2 bytes
655    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0x90
656    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0xa0
657    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0xb0
658    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0xc0
659    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0xd0
660    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0xe0 0xe0 to 0xef 2 bytes
661    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0
662};
663static const char XML_gb2312ByteTable[256] = {
664//  0 1 2 3 4 5 6 7 8 9 a b c d e f
665    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x00
666    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x10
667    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x20
668    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x30
669    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x40
670    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x50
671    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x60
672    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x70
673    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x80
674    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x90
675    1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0xa0 0xa1 to 0xf7 2 bytes
676    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0xb0
677    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0xc0
678    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0xd0
679    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0xe0
680    2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0
681};
682static const char XML_gbk_big5_ByteTable[256] = {
683    //  0 1 2 3 4 5 6 7 8 9 a b c d e f
684    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x00
685    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x10
686    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x20
687    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x30
688    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x40
689    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x50
690    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x60
691    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// 0x70
692    1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0x80 0x81 to 0xfe 2 bytes
693    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0x90
694    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0xa0
695    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0xb0
696    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0xc0
697    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0xd0
698    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,// 0xe0
699    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1 // 0xf0
700};
701// the default is "characterEncoding=XMLNode::encoding_UTF8"
702static const char *XML_ByteTable = (const char *)XML_utf8ByteTable;
703#endif
704
705
706XMLNode XMLNode::emptyXMLNode;
707XMLClear XMLNode::emptyXMLClear = { NULL, NULL, NULL};
708XMLAttribute XMLNode::emptyXMLAttribute = { NULL, NULL};
709
710// Enumeration used to decipher what type a token is
711typedef enum XMLTokenTypeTag {
712    eTokenText = 0,
713    eTokenQuotedText,
714    eTokenTagStart,         /* "<"            */
715    eTokenTagEnd,           /* "</"           */
716    eTokenCloseTag,         /* ">"            */
717    eTokenEquals,           /* "="            */
718    eTokenDeclaration,      /* "<?"           */
719    eTokenShortHandClose,   /* "/>"           */
720    eTokenClear,
721    eTokenError
722} XMLTokenType;
723
724// Main structure used for parsing XML
725typedef struct XML {
726    XMLCSTR                lpXML;
727    XMLCSTR                lpszText;
728    int                    nIndex,nIndexMissigEndTag;
729    enum XMLError          error;
730    XMLCSTR                lpEndTag;
731    int                    cbEndTag;
732    XMLCSTR                lpNewElement;
733    int                    cbNewElement;
734    int                    nFirst;
735} XML;
736
737typedef struct {
738    ALLXMLClearTag *pClr;
739    XMLCSTR     pStr;
740} NextToken;
741
742// Enumeration used when parsing attributes
743typedef enum Attrib {
744    eAttribName = 0,
745    eAttribEquals,
746    eAttribValue
747} Attrib;
748
749// Enumeration used when parsing elements to dictate whether we are currently
750// inside a tag
751typedef enum Status {
752    eInsideTag = 0,
753    eOutsideTag
754} Status;
755
756XMLError XMLNode::writeToFile(XMLCSTR filename, const char *encoding, char nFormat) const {
757    if (!d) return eXMLErrorNone;
758    FILE *f = xfopen(filename, _CXML("wb"));
759    if (!f) return eXMLErrorCannotOpenWriteFile;
760#ifdef _XMLWIDECHAR
761    unsigned char h[2] = { 0xFF, 0xFE };
762    if (!fwrite(h, 2, 1, f)) return eXMLErrorCannotWriteFile;
763    if ((!isDeclaration()) && ((d->lpszName) ||
764                               (!getChildNode().isDeclaration()))) {
765        if (!fwrite(L"<?xml version=\"1.0\" encoding=\"utf-16\"?>\n",
766                    sizeof(wchar_t)*40, 1, f))
767            return eXMLErrorCannotWriteFile;
768    }
769#else
770    if ((!isDeclaration()) && ((d->lpszName) ||
771                               (!getChildNode().isDeclaration()))) {
772        if (characterEncoding == char_encoding_UTF8) {
773            // header so that windows recognize the file as UTF-8:
774            unsigned char h[3] = {0xEF, 0xBB, 0xBF};
775            if (!fwrite(h, 3, 1, f)) return eXMLErrorCannotWriteFile;
776            encoding = "utf-8";
777        } else if (characterEncoding == char_encoding_ShiftJIS)
778            encoding = "SHIFT-JIS";
779
780        if (!encoding) encoding = "ISO-8859-1";
781        if (fprintf(f, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", encoding)
782            < 0)
783            return eXMLErrorCannotWriteFile;
784    } else {
785        if (characterEncoding == char_encoding_UTF8) {
786            unsigned char h[3] = {0xEF, 0xBB, 0xBF};
787            if (!fwrite(h, 3, 1, f)) return eXMLErrorCannotWriteFile;
788        }
789    }
790#endif
791    int i;
792    XMLSTR t = createXMLString(nFormat, &i);
793    if (!fwrite(t, sizeof(XMLCHAR)*i, 1, f)) return eXMLErrorCannotWriteFile;
794    if (fclose(f) != 0) return eXMLErrorCannotWriteFile;
795    free(t);
796    return eXMLErrorNone;
797}
798
799// Duplicate a given string.
800XMLSTR stringDup(XMLCSTR lpszData, int cbData) {
801    if (lpszData == NULL) return NULL;
802
803    XMLSTR lpszNew;
804    if (cbData == -1) cbData = (int)xstrlen(lpszData);
805    lpszNew = (XMLSTR)malloc((cbData + 1) * sizeof(XMLCHAR));
806    if (lpszNew) {
807        memcpy(lpszNew, lpszData, (cbData) * sizeof(XMLCHAR));
808        lpszNew[cbData] = (XMLCHAR)NULL;
809    }
810    return lpszNew;
811}
812
813XMLSTR ToXMLStringTool::toXMLUnSafe(XMLSTR dest, XMLCSTR source) {
814    XMLSTR dd = dest;
815    XMLCHAR ch;
816    XMLCharacterEntity *entity;
817    while ((ch = *source)) {
818        entity = XMLEntities;
819        do {
820            if (ch == entity->c) {
821                xstrcpy(dest, entity->s);
822                dest += entity->l;
823                source++;
824                goto out_of_loop1;
825            }
826            entity++;
827        } while (entity->s);
828#ifdef _XMLWIDECHAR
829        *(dest++) = *(source++);
830#else
831        switch (XML_ByteTable[(unsigned char)ch]) {
832        case 4:
833            *(dest++) = *(source++);
834        case 3:
835            *(dest++) = *(source++);
836        case 2:
837            *(dest++) = *(source++);
838        case 1:
839            *(dest++) = *(source++);
840        }
841#endif
842out_of_loop1:
843        ;
844    }
845    *dest = 0;
846    return dd;
847}
848
849// private (used while rendering):
850int ToXMLStringTool::lengthXMLString(XMLCSTR source) {
851    int r = 0;
852    XMLCharacterEntity *entity;
853    XMLCHAR ch;
854    while ((ch = *source)) {
855        entity = XMLEntities;
856        do {
857            if (ch == entity->c) {
858                r += entity->l;
859                source++;
860                goto out_of_loop1;
861            }
862            entity++;
863        } while (entity->s);
864#ifdef _XMLWIDECHAR
865        r++;
866        source++;
867#else
868        ch = XML_ByteTable[(unsigned char)ch];
869        r += ch;
870        source += ch;
871#endif
872out_of_loop1:
873        ;
874    }
875    return r;
876}
877
878ToXMLStringTool::~ToXMLStringTool() {
879    freeBuffer();
880}
881void ToXMLStringTool::freeBuffer() {
882    if (buf) free(buf);
883    buf = NULL;
884    buflen = 0;
885}
886XMLSTR ToXMLStringTool::toXML(XMLCSTR source) {
887    int l = lengthXMLString(source) + 1;
888    if (l > buflen) {
889        buflen = l;
890        buf = (XMLSTR)realloc(buf, l * sizeof(XMLCHAR));
891    }
892    return toXMLUnSafe(buf, source);
893}
894
895// private:
896XMLSTR fromXMLString(XMLCSTR s, int lo, XML *pXML) {
897    // This function is the opposite of the function "toXMLString". It decodes the escape
898    // sequences &amp;, &quot;, &apos;, &lt;, &gt; and replace them by the characters
899    // &,",',<,>. This function is used internally by the XML Parser. All the calls to
900    // the XML library will always gives you back "decoded" strings.
901    //
902    // in: string (s) and length (lo) of string
903    // out:  new allocated string converted from xml
904    if (!s) return NULL;
905
906    int ll = 0, j;
907    XMLSTR d;
908    XMLCSTR ss = s;
909    XMLCharacterEntity *entity;
910    while ((lo > 0) && (*s)) {
911        if (*s == _CXML('&')) {
912            if ((lo > 2) && (s[1] == _CXML('#'))) {
913                s += 2;
914                lo -= 2;
915                if ((*s == _CXML('X')) || (*s == _CXML('x'))) {
916                    s++;
917                    lo--;
918                }
919                while ((*s) && (*s != _CXML(';')) && ((lo--) > 0)) {
920                    s++;
921                }
922                if (*s != _CXML(';')) {
923                    pXML->error = eXMLErrorUnknownCharacterEntity;
924                    return NULL;
925                }
926                s++;
927                lo--;
928            } else {
929                entity = XMLEntities;
930                do {
931                    if ((lo >= entity->l) &&
932                        (xstrnicmp(s, entity->s, entity->l) == 0)) {
933                        s += entity->l;
934                        lo -= entity->l;
935                        break;
936                    }
937                    entity++;
938                } while (entity->s);
939                if (!entity->s) {
940                    pXML->error = eXMLErrorUnknownCharacterEntity;
941                    return NULL;
942                }
943            }
944        } else {
945#ifdef _XMLWIDECHAR
946            s++;
947            lo--;
948#else
949            j = XML_ByteTable[(unsigned char)*s];
950            s += j;
951            lo -= j;
952            ll += j - 1;
953#endif
954        }
955        ll++;
956    }
957
958    d = (XMLSTR)malloc((ll + 1) * sizeof(XMLCHAR));
959    s = d;
960    while (ll-- > 0) {
961        if (*ss == _CXML('&')) {
962            if (ss[1] == _CXML('#')) {
963                ss += 2;
964                j = 0;
965                if ((*ss == _CXML('X')) || (*ss == _CXML('x'))) {
966                    ss++;
967                    while (*ss != _CXML(';')) {
968                        if ((*ss >= _CXML('0')) && (*ss <= _CXML('9'))) {
969                            j = (j << 4) + *ss - _CXML('0');
970                        } else if ((*ss >= _CXML('A')) && (*ss <= _CXML('F'))) {
971                            j = (j << 4) + *ss - _CXML('A') + 10;
972                        } else if ((*ss >= _CXML('a')) && (*ss <= _CXML('f'))) {
973                            j = (j << 4) + *ss - _CXML('a') + 10;
974                        } else {
975                            free((void*)s);
976                            pXML->error = eXMLErrorUnknownCharacterEntity;
977                            return NULL;
978                        }
979                        ss++;
980                    }
981                } else {
982                    while (*ss != _CXML(';')) {
983                        if ((*ss >= _CXML('0')) && (*ss <= _CXML('9'))) {
984                            j = (j * 10) + *ss - _CXML('0');
985                        } else {
986                            free((void*)s);
987                            pXML->error = eXMLErrorUnknownCharacterEntity;
988                            return NULL;
989                        }
990                        ss++;
991                    }
992                }
993#ifndef _XMLWIDECHAR
994                if (j > 255) {
995                    free((void*)s);
996                    pXML->error = eXMLErrorCharacterCodeAbove255;
997                    return NULL;
998                }
999#endif
1000                (*d++) = (XMLCHAR)j;
1001                ss++;
1002            } else {
1003                entity = XMLEntities;
1004                do {
1005                    if (xstrnicmp(ss, entity->s, entity->l) == 0) {
1006                        *(d++) = entity->c;
1007                        ss += entity->l;
1008                        break;
1009                    }
1010                    entity++;
1011                } while (entity->s);
1012            }
1013        } else {
1014#ifdef _XMLWIDECHAR
1015            *(d++) = *(ss++);
1016#else
1017            switch (XML_ByteTable[(unsigned char)*ss]) {
1018            case 4:
1019                *(d++) = *(ss++);
1020                ll--;
1021            case 3:
1022                *(d++) = *(ss++);
1023                ll--;
1024            case 2:
1025                *(d++) = *(ss++);
1026                ll--;
1027            case 1:
1028                *(d++) = *(ss++);
1029            }
1030#endif
1031        }
1032    }
1033    *d = 0;
1034    return (XMLSTR)s;
1035}
1036
1037#define XML_isSPACECHAR(ch) ((ch==_CXML('\n'))||(ch==_CXML(' '))||(ch== _CXML('\t'))||(ch==_CXML('\r')))
1038
1039// private:
1040char myTagCompare(XMLCSTR cclose, XMLCSTR copen)
1041// !!!! WARNING strange convention&:
1042// return 0 if equals
1043// return 1 if different
1044{
1045    if (!cclose) return 1;
1046    int l = (int)xstrlen(cclose);
1047    if (xstrnicmp(cclose, copen, l) != 0) return 1;
1048    const XMLCHAR c = copen[l];
1049    if (XML_isSPACECHAR(c) ||
1050            (c == _CXML('/' )) ||
1051            (c == _CXML('<' )) ||
1052            (c == _CXML('>' )) ||
1053            (c == _CXML('=' ))) return 0;
1054    return 1;
1055}
1056
1057// Obtain the next character from the string.
1058static inline XMLCHAR getNextChar(XML *pXML) {
1059    XMLCHAR ch = pXML->lpXML[pXML->nIndex];
1060#ifdef _XMLWIDECHAR
1061    if (ch != 0) pXML->nIndex++;
1062#else
1063    pXML->nIndex += XML_ByteTable[(unsigned char)ch];
1064#endif
1065    return ch;
1066}
1067
1068// Find the next token in a string.
1069// pcbToken contains the number of characters that have been read.
1070static NextToken GetNextToken(XML *pXML, int *pcbToken,
1071                              enum XMLTokenTypeTag *pType) {
1072    NextToken        result;
1073    XMLCHAR            ch;
1074    XMLCHAR            chTemp;
1075    int              indexStart, nFoundMatch, nIsText = FALSE;
1076    result.pClr = NULL; // prevent warning
1077
1078    // Find next non-white space character
1079    do {
1080        indexStart = pXML->nIndex;
1081        ch = getNextChar(pXML);
1082    } while XML_isSPACECHAR(ch);
1083
1084    if (ch) {
1085        // Cache the current string pointer
1086        result.pStr = &pXML->lpXML[indexStart];
1087
1088        // First check whether the token is in the clear tag list (meaning it
1089        // does not need formatting).
1090        ALLXMLClearTag *ctag = XMLClearTags;
1091        do {
1092            if (xstrncmp(ctag->lpszOpen, result.pStr, ctag->openTagLen) == 0) {
1093                result.pClr = ctag;
1094                pXML->nIndex += ctag->openTagLen - 1;
1095                *pType = eTokenClear;
1096                return result;
1097            }
1098            ctag++;
1099        } while (ctag->lpszOpen);
1100
1101        // If we didn't find a clear tag then check for standard tokens
1102        switch (ch) {
1103            // Check for quotes
1104        case _CXML('\''):
1105        case _CXML('\"'):
1106            // Type of token
1107            *pType = eTokenQuotedText;
1108            chTemp = ch;
1109
1110            // Set the size
1111            nFoundMatch = FALSE;
1112
1113            // Search through the string to find a matching quote
1114            while ((ch = getNextChar(pXML))) {
1115                if (ch == chTemp) {
1116                    nFoundMatch = TRUE;
1117                    break;
1118                }
1119                if (ch == _CXML('<')) {
1120                    break;
1121                }
1122            }
1123
1124            // If we failed to find a matching quote
1125            if (nFoundMatch == FALSE) {
1126                pXML->nIndex = indexStart + 1;
1127                nIsText = TRUE;
1128                break;
1129            }
1130
1131//  4.02.2002
1132//            if (FindNonWhiteSpace(pXML)) pXML->nIndex--;
1133
1134            break;
1135
1136            // Equals (used with attribute values)
1137        case _CXML('='):
1138            *pType = eTokenEquals;
1139            break;
1140
1141            // Close tag
1142        case _CXML('>'):
1143            *pType = eTokenCloseTag;
1144            break;
1145
1146            // Check for tag start and tag end
1147        case _CXML('<'):
1148
1149            // Peek at the next character to see if we have an end tag '</',
1150            // or an xml declaration '<?'
1151            chTemp = pXML->lpXML[pXML->nIndex];
1152
1153            // If we have a tag end...
1154            if (chTemp == _CXML('/')) {
1155                // Set the type and ensure we point at the next character
1156                getNextChar(pXML);
1157                *pType = eTokenTagEnd;
1158            }
1159
1160            // If we have an XML declaration tag
1161            else if (chTemp == _CXML('?')) {
1162
1163                // Set the type and ensure we point at the next character
1164                getNextChar(pXML);
1165                *pType = eTokenDeclaration;
1166            }
1167
1168            // Otherwise we must have a start tag
1169            else {
1170                *pType = eTokenTagStart;
1171            }
1172            break;
1173
1174            // Check to see if we have a short hand type end tag ('/>').
1175        case _CXML('/'):
1176
1177            // Peek at the next character to see if we have a short end tag '/>'
1178            chTemp = pXML->lpXML[pXML->nIndex];
1179
1180            // If we have a short hand end tag...
1181            if (chTemp == _CXML('>')) {
1182                // Set the type and ensure we point at the next character
1183                getNextChar(pXML);
1184                *pType = eTokenShortHandClose;
1185                break;
1186            }
1187
1188            // If we haven't found a short hand closing tag then drop into the
1189            // text process
1190
1191            // Other characters
1192        default:
1193            nIsText = TRUE;
1194        }
1195
1196        // If this is a TEXT node
1197        if (nIsText) {
1198            // Indicate we are dealing with text
1199            *pType = eTokenText;
1200            while ((ch = getNextChar(pXML))) {
1201                if XML_isSPACECHAR(ch) {
1202                    indexStart++;
1203                    break;
1204
1205                } else if (ch == _CXML('/')) {
1206                    // If we find a slash then this maybe text or a short hand end tag
1207                    // Peek at the next character to see it we have short hand end tag
1208                    ch = pXML->lpXML[pXML->nIndex];
1209                    // If we found a short hand end tag then we need to exit the loop
1210                    if (ch == _CXML('>')) {
1211                        pXML->nIndex--;
1212                        break;
1213                    }
1214
1215                } else if ((ch == _CXML('<')) || (ch == _CXML('>')) ||
1216                           (ch == _CXML('='))) {
1217                    pXML->nIndex--;
1218                    break;
1219                }
1220            }
1221        }
1222        *pcbToken = pXML->nIndex - indexStart;
1223    } else {
1224        // If we failed to obtain a valid character
1225        *pcbToken = 0;
1226        *pType = eTokenError;
1227        result.pStr = NULL;
1228    }
1229
1230    return result;
1231}
1232
1233XMLCSTR XMLNode::updateName_WOSD(XMLSTR lpszName) {
1234    if (!d) {
1235        free(lpszName);
1236        return NULL;
1237    }
1238    if (d->lpszName && (lpszName != d->lpszName)) free((void*)d->lpszName);
1239    d->lpszName = lpszName;
1240    return lpszName;
1241}
1242
1243// private:
1244XMLNode::XMLNode(struct XMLNodeDataTag *p) {
1245    d = p;
1246    (p->ref_count)++;
1247}
1248XMLNode::XMLNode(XMLNodeData *pParent, XMLSTR lpszName, char isDeclaration) {
1249    d = (XMLNodeData*)malloc(sizeof(XMLNodeData));
1250    d->ref_count = 1;
1251
1252    d->lpszName = NULL;
1253    d->nChild = 0;
1254    d->nText = 0;
1255    d->nClear = 0;
1256    d->nAttribute = 0;
1257
1258    d->isDeclaration = isDeclaration;
1259
1260    d->pParent = pParent;
1261    d->pChild = NULL;
1262    d->pText = NULL;
1263    d->pClear = NULL;
1264    d->pAttribute = NULL;
1265    d->pOrder = NULL;
1266
1267    updateName_WOSD(lpszName);
1268}
1269
1270XMLNode XMLNode::createXMLTopNode_WOSD(XMLSTR lpszName, char isDeclaration) {
1271    return XMLNode(NULL, lpszName, isDeclaration);
1272}
1273XMLNode XMLNode::createXMLTopNode(XMLCSTR lpszName, char isDeclaration) {
1274    return XMLNode(NULL, stringDup(lpszName), isDeclaration);
1275}
1276
1277#define MEMORYINCREASE 50
1278
1279static inline void myFree(void *p) {
1280    if (p) free(p);
1281}
1282static inline void *myRealloc(void *p, int newsize, int memInc, int sizeofElem) {
1283    if (p == NULL) {
1284        if (memInc) return malloc(memInc*sizeofElem);
1285        return malloc(sizeofElem);
1286    }
1287    if ((memInc == 0) || ((newsize % memInc) == 0)) {
1288        p = realloc(p, (newsize + memInc) * sizeofElem);
1289    }
1290//    if (!p)
1291//    {
1292//        printf("XMLParser Error: Not enough memory! Aborting...\n"); exit(220);
1293//    }
1294    return p;
1295}
1296
1297// private:
1298XMLElementPosition XMLNode::findPosition(XMLNodeData *d, int index,
1299                                         XMLElementType xxtype) {
1300    if (index < 0) return -1;
1301    int i = 0, j = (int)((index << 2) + xxtype), *o = d->pOrder;
1302    while (o[i] != j) i++;
1303    return i;
1304}
1305
1306// private:
1307// update "order" information when deleting a content of a XMLNode
1308int XMLNode::removeOrderElement(XMLNodeData *d, XMLElementType t, int index) {
1309    int n = d->nChild + d->nText + d->nClear;
1310    int *o = d->pOrder;
1311    int i = findPosition(d, index, t);
1312    memmove(o + i, o + i + 1, (n - i)*sizeof(int));
1313    for (; i < n; i++)
1314        if ((o[i]&3) == (int)t) o[i] -= 4;
1315    // We should normally do:
1316    // d->pOrder=(int)realloc(d->pOrder,n*sizeof(int));
1317    // but we skip reallocation because it's too time consuming.
1318    // Anyway, at the end, it will be free'd completely at once.
1319    return i;
1320}
1321
1322void *XMLNode::addToOrder(int memoryIncrease, int *_pos, int nc, void *p,
1323                          int size, XMLElementType xtype) {
1324    //  in: *_pos is the position inside d->pOrder ("-1" means "EndOf")
1325    // out: *_pos is the index inside p
1326    p = myRealloc(p, (nc + 1), memoryIncrease, size);
1327    int n = d->nChild + d->nText + d->nClear;
1328    d->pOrder = (int*)myRealloc(d->pOrder, n + 1, memoryIncrease * 3,
1329                                sizeof(int));
1330    int pos = *_pos, *o = d->pOrder;
1331
1332    if ((pos < 0) || (pos >= n)) {
1333        *_pos = nc;
1334        o[n] = (int)((nc << 2) + xtype);
1335        return p;
1336    }
1337
1338    int i = pos;
1339    memmove(o + i + 1, o + i, (n - i)*sizeof(int));
1340
1341    while ((pos < n) && ((o[pos]&3) != (int)xtype)) pos++;
1342    if (pos == n) {
1343        *_pos = nc;
1344        o[n] = (int)((nc << 2) + xtype);
1345        return p;
1346    }
1347
1348    o[i] = o[pos];
1349    for (i = pos + 1; i <= n; i++) if ((o[i]&3) == (int)xtype) o[i] += 4;
1350
1351    *_pos = pos = o[pos] >> 2;
1352    memmove(((char*)p) + (pos + 1)*size, ((char*)p) + pos*size, (nc - pos)*size);
1353
1354    return p;
1355}
1356
1357// Add a child node to the given element.
1358XMLNode XMLNode::addChild_priv(int memoryIncrease, XMLSTR lpszName,
1359                               char isDeclaration, int pos) {
1360    if (!lpszName) return emptyXMLNode;
1361    d->pChild = (XMLNode*)addToOrder(memoryIncrease, &pos, d->nChild,
1362                                     d->pChild, sizeof(XMLNode), eNodeChild);
1363    d->pChild[pos].d = NULL;
1364    d->pChild[pos] = XMLNode(d, lpszName, isDeclaration);
1365    d->nChild++;
1366    return d->pChild[pos];
1367}
1368
1369// Add an attribute to an element.
1370XMLAttribute *XMLNode::addAttribute_priv(int memoryIncrease, XMLSTR lpszName,
1371                                         XMLSTR lpszValuev) {
1372    if (!lpszName) return &emptyXMLAttribute;
1373    if (!d) {
1374        myFree(lpszName);
1375        myFree(lpszValuev);
1376        return &emptyXMLAttribute;
1377    }
1378    int nc = d->nAttribute;
1379    d->pAttribute = (XMLAttribute*)myRealloc(d->pAttribute, (nc + 1),
1380                                             memoryIncrease,
1381                                             sizeof(XMLAttribute));
1382    XMLAttribute *pAttr = d->pAttribute + nc;
1383    pAttr->lpszName = lpszName;
1384    pAttr->lpszValue = lpszValuev;
1385    d->nAttribute++;
1386    return pAttr;
1387}
1388
1389// Add text to the element.
1390XMLCSTR XMLNode::addText_priv(int memoryIncrease, XMLSTR lpszValue, int pos) {
1391    if (!lpszValue) return NULL;
1392    if (!d) {
1393        myFree(lpszValue);
1394        return NULL;
1395    }
1396    d->pText = (XMLCSTR*)addToOrder(memoryIncrease, &pos, d->nText, d->pText,
1397                                    sizeof(XMLSTR), eNodeText);
1398    d->pText[pos] = lpszValue;
1399    d->nText++;
1400    return lpszValue;
1401}
1402
1403// Add clear (unformatted) text to the element.
1404XMLClear *XMLNode::addClear_priv(int memoryIncrease, XMLSTR lpszValue,
1405                                 XMLCSTR lpszOpen, XMLCSTR lpszClose,
1406                                 int pos) {
1407    if (!lpszValue) return &emptyXMLClear;
1408    if (!d) {
1409        myFree(lpszValue);
1410        return &emptyXMLClear;
1411    }
1412    d->pClear = (XMLClear *)addToOrder(memoryIncrease, &pos, d->nClear,
1413                                       d->pClear, sizeof(XMLClear),
1414                                       eNodeClear);
1415    XMLClear *pNewClear = d->pClear + pos;
1416    pNewClear->lpszValue = lpszValue;
1417    if (!lpszOpen) lpszOpen = XMLClearTags->lpszOpen;
1418    if (!lpszClose) lpszClose = XMLClearTags->lpszClose;
1419    pNewClear->lpszOpenTag = lpszOpen;
1420    pNewClear->lpszCloseTag = lpszClose;
1421    d->nClear++;
1422    return pNewClear;
1423}
1424
1425// private:
1426// Parse a clear (unformatted) type node.
1427char XMLNode::parseClearTag(void *px, void *_pClear) {
1428    XML *pXML = (XML *)px;
1429    ALLXMLClearTag pClear = *((ALLXMLClearTag*)_pClear);
1430    int cbTemp = 0;
1431    XMLCSTR lpszTemp = NULL;
1432    XMLCSTR lpXML = &pXML->lpXML[pXML->nIndex];
1433    static XMLCSTR docTypeEnd = _CXML("]>");
1434
1435    // Find the closing tag
1436    // Seems the <!DOCTYPE need a better treatment so lets handle it
1437    if (pClear.lpszOpen == XMLClearTags[1].lpszOpen) {
1438        XMLCSTR pCh = lpXML;
1439        while (*pCh) {
1440            if (*pCh == _CXML('<')) {
1441                pClear.lpszClose = docTypeEnd;
1442                lpszTemp = xstrstr(lpXML, docTypeEnd);
1443                break;
1444            } else if (*pCh == _CXML('>')) {
1445                lpszTemp = pCh;
1446                break;
1447            }
1448#ifdef _XMLWIDECHAR
1449            pCh++;
1450#else
1451            pCh += XML_ByteTable[(unsigned char)(*pCh)];
1452#endif
1453        }
1454    } else lpszTemp = xstrstr(lpXML, pClear.lpszClose);
1455
1456    if (lpszTemp) {
1457        // Cache the size and increment the index
1458        cbTemp = (int)(lpszTemp - lpXML);
1459
1460        pXML->nIndex += cbTemp + (int)xstrlen(pClear.lpszClose);
1461
1462        // Add the clear node to the current element
1463        addClear_priv(MEMORYINCREASE, stringDup(lpXML, cbTemp),
1464                      pClear.lpszOpen, pClear.lpszClose, -1);
1465        return 0;
1466    }
1467
1468    // If we failed to find the end tag
1469    pXML->error = eXMLErrorUnmatchedEndClearTag;
1470    return 1;
1471}
1472
1473void XMLNode::exactMemory(XMLNodeData *d) {
1474    if (d->pOrder) {
1475        d->pOrder = (int*)realloc(d->pOrder, (d->nChild + d->nText + d->nClear)
1476                                  * sizeof(int));
1477    }
1478    if (d->pChild) {
1479        d->pChild = (XMLNode*)realloc(d->pChild, d->nChild * sizeof(XMLNode));
1480    }
1481    if (d->pAttribute) {
1482        d->pAttribute = (XMLAttribute*)realloc(d->pAttribute, d->nAttribute *
1483                                               sizeof(XMLAttribute));
1484    }
1485    if (d->pText) {
1486        d->pText = (XMLCSTR*)realloc(d->pText, d->nText * sizeof(XMLSTR));
1487    }
1488    if (d->pClear) {
1489        d->pClear = (XMLClear *)realloc(d->pClear, d->nClear * sizeof(XMLClear));
1490    }
1491}
1492
1493char XMLNode::maybeAddTxT(void *pa, XMLCSTR tokenPStr) {
1494    XML *pXML = (XML *)pa;
1495    XMLCSTR lpszText = pXML->lpszText;
1496    if (!lpszText) return 0;
1497    if (dropWhiteSpace) while (XML_isSPACECHAR(*lpszText) &&
1498                               (lpszText != tokenPStr)) lpszText++;
1499    int cbText = (int)(tokenPStr - lpszText);
1500    if (!cbText) {
1501        pXML->lpszText = NULL;
1502        return 0;
1503    }
1504    if (dropWhiteSpace) {
1505        cbText--;
1506        while ((cbText) && XML_isSPACECHAR(lpszText[cbText])) cbText--;
1507        cbText++;
1508    }
1509    if (!cbText) {
1510        pXML->lpszText = NULL;
1511        return 0;
1512    }
1513    XMLSTR lpt = fromXMLString(lpszText, cbText, pXML);
1514    if (!lpt) return 1;
1515    pXML->lpszText = NULL;
1516    if (removeCommentsInMiddleOfText && d->nText && d->nClear) {
1517        // if the previous insertion was a comment (<!-- -->) AND
1518        // if the previous previous insertion was a text then, delete the comment and append the text
1519        int n = d->nChild + d->nText + d->nClear - 1, *o = d->pOrder;
1520        if (((o[n]&3) == eNodeClear) && ((o[n-1]&3) == eNodeText)) {
1521            int i = o[n] >> 2;
1522            if (d->pClear[i].lpszOpenTag == XMLClearTags[2].lpszOpen) {
1523                deleteClear(i);
1524                i = o[n-1] >> 2;
1525                n = xstrlen(d->pText[i]);
1526                int n2 = xstrlen(lpt) + 1;
1527                d->pText[i] = (XMLSTR)realloc((void*)d->pText[i], (n + n2) *
1528                                              sizeof(XMLCHAR));
1529                if (!d->pText[i]) return 1;
1530                memcpy((void*)(d->pText[i] + n), lpt, n2*sizeof(XMLCHAR));
1531                free(lpt);
1532                return 0;
1533            }
1534        }
1535    }
1536    addText_priv(MEMORYINCREASE, lpt, -1);
1537    return 0;
1538}
1539// private:
1540// Recursively parse an XML element.
1541int XMLNode::ParseXMLElement(void *pa) {
1542    XML *pXML = (XML *)pa;
1543    int cbToken;
1544    enum XMLTokenTypeTag xtype;
1545    NextToken token;
1546    XMLCSTR lpszTemp = NULL;
1547    int cbTemp = 0;
1548    char nDeclaration;
1549    XMLNode pNew;
1550    enum Status status; // inside or outside a tag
1551    enum Attrib attrib = eAttribName;
1552
1553    assert(pXML);
1554
1555    // If this is the first call to the function
1556    if (pXML->nFirst) {
1557        // Assume we are outside of a tag definition
1558        pXML->nFirst = FALSE;
1559        status = eOutsideTag;
1560    } else {
1561        // If this is not the first call then we should only be called when inside a tag.
1562        status = eInsideTag;
1563    }
1564
1565    // Iterate through the tokens in the document
1566    for (;;) {
1567        // Obtain the next token
1568        token = GetNextToken(pXML, &cbToken, &xtype);
1569
1570        if (xtype != eTokenError) {
1571            // Check the current status
1572            switch (status) {
1573
1574                // If we are outside of a tag definition
1575            case eOutsideTag:
1576
1577                // Check what type of token we obtained
1578                switch (xtype) {
1579                    // If we have found text or quoted text
1580                case eTokenText:
1581                case eTokenCloseTag:          /* '>'         */
1582                case eTokenShortHandClose:    /* '/>'        */
1583                case eTokenQuotedText:
1584                case eTokenEquals:
1585                    break;
1586
1587                    // If we found a start tag '<' and declarations '<?'
1588                case eTokenTagStart:
1589                case eTokenDeclaration:
1590
1591                    // Cache whether this new element is a declaration or not
1592                    nDeclaration = (xtype == eTokenDeclaration);
1593
1594                    // If we have node text then add this to the element
1595                    if (maybeAddTxT(pXML, token.pStr)) return FALSE;
1596
1597                    // Find the name of the tag
1598                    token = GetNextToken(pXML, &cbToken, &xtype);
1599
1600                    // Return an error if we couldn't obtain the next token or
1601                    // it wasnt text
1602                    if (xtype != eTokenText) {
1603                        pXML->error = eXMLErrorMissingTagName;
1604                        return FALSE;
1605                    }
1606
1607                    // If we found a new element which is the same as this
1608                    // element then we need to pass this back to the caller..
1609
1610#ifdef APPROXIMATE_PARSING
1611                    if (d->lpszName &&
1612                            myTagCompare(d->lpszName, token.pStr) == 0) {
1613                        // Indicate to the caller that it needs to create a
1614                        // new element.
1615                        pXML->lpNewElement = token.pStr;
1616                        pXML->cbNewElement = cbToken;
1617                        return TRUE;
1618                    } else
1619#endif
1620                    {
1621                        // If the name of the new element differs from the name of
1622                        // the current element we need to add the new element to
1623                        // the current one and recurse
1624                        pNew = addChild_priv(MEMORYINCREASE,
1625                                             stringDup(token.pStr, cbToken),
1626                                             nDeclaration, -1);
1627
1628                        while (!pNew.isEmpty()) {
1629                            // Callself to process the new node.  If we return
1630                            // FALSE this means we dont have any more
1631                            // processing to do...
1632
1633                            if (!pNew.ParseXMLElement(pXML)) return FALSE;
1634                            else {
1635                                // If the call to recurse this function
1636                                // evented in a end tag specified in XML then
1637                                // we need to unwind the calls to this
1638                                // function until we find the appropriate node
1639                                // (the element name and end tag name must
1640                                // match)
1641                                if (pXML->cbEndTag) {
1642                                    // If we are back at the root node then we
1643                                    // have an unmatched end tag
1644                                    if (!d->lpszName) {
1645                                        pXML->error = eXMLErrorUnmatchedEndTag;
1646                                        return FALSE;
1647                                    }
1648
1649                                    // If the end tag matches the name of this
1650                                    // element then we only need to unwind
1651                                    // once more...
1652
1653                                    if (myTagCompare(d->lpszName,
1654                                                     pXML->lpEndTag) == 0) {
1655                                        pXML->cbEndTag = 0;
1656                                    }
1657
1658                                    return TRUE;
1659                                } else if (pXML->cbNewElement) {
1660                                    // If the call indicated a new element is to
1661                                    // be created on THIS element.
1662
1663                                    // If the name of this element matches the
1664                                    // name of the element we need to create
1665                                    // then we need to return to the caller
1666                                    // and let it process the element.
1667
1668                                    if (myTagCompare(d->lpszName,
1669                                                     pXML->lpNewElement) == 0) {
1670                                        return TRUE;
1671                                    }
1672
1673                                    // Add the new element and recurse
1674                                    pNew =
1675                                        addChild_priv(MEMORYINCREASE,
1676                                                      stringDup(pXML->
1677                                                                lpNewElement,
1678                                                                pXML->
1679                                                                cbNewElement),
1680                                                      0, -1);
1681                                    pXML->cbNewElement = 0;
1682                                } else {
1683                                    // If we didn't have a new element to create
1684                                    pNew = emptyXMLNode;
1685
1686                                }
1687                            }
1688                        }
1689                    }
1690                    break;
1691
1692                    // If we found an end tag
1693                case eTokenTagEnd:
1694
1695                    // If we have node text then add this to the element
1696                    if (maybeAddTxT(pXML, token.pStr)) return FALSE;
1697
1698                    // Find the name of the end tag
1699                    token = GetNextToken(pXML, &cbTemp, &xtype);
1700
1701                    // The end tag should be text
1702                    if (xtype != eTokenText) {
1703                        pXML->error = eXMLErrorMissingEndTagName;
1704                        return FALSE;
1705                    }
1706                    lpszTemp = token.pStr;
1707
1708                    // After the end tag we should find a closing tag
1709                    token = GetNextToken(pXML, &cbToken, &xtype);
1710                    if (xtype != eTokenCloseTag) {
1711                        pXML->error = eXMLErrorMissingEndTagName;
1712                        return FALSE;
1713                    }
1714                    pXML->lpszText = pXML->lpXML + pXML->nIndex;
1715
1716                    // We need to return to the previous caller.  If the name
1717                    // of the tag cannot be found we need to keep returning to
1718                    // caller until we find a match
1719                    if (myTagCompare(d->lpszName, lpszTemp) != 0)
1720#ifdef STRICT_PARSING
1721                    {
1722                        pXML->error = eXMLErrorUnmatchedEndTag;
1723                        pXML->nIndexMissigEndTag = pXML->nIndex;
1724                        return FALSE;
1725                    }
1726#else
1727                    {
1728                        pXML->error = eXMLErrorMissingEndTag;
1729                        pXML->nIndexMissigEndTag = pXML->nIndex;
1730                        pXML->lpEndTag = lpszTemp;
1731                        pXML->cbEndTag = cbTemp;
1732                    }
1733#endif
1734
1735                    // Return to the caller
1736                    exactMemory(d);
1737                    return TRUE;
1738
1739                    // If we found a clear (unformatted) token
1740                case eTokenClear:
1741                    // If we have node text then add this to the element
1742                    if (maybeAddTxT(pXML, token.pStr)) return FALSE;
1743                    if (parseClearTag(pXML, token.pClr)) return FALSE;
1744                    pXML->lpszText = pXML->lpXML + pXML->nIndex;
1745                    break;
1746
1747                default:
1748                    break;
1749                }
1750                break;
1751
1752                // If we are inside a tag definition we need to search for attributes
1753            case eInsideTag:
1754
1755                // Check what part of the attribute (name, equals, value) we
1756                // are looking for.
1757                switch (attrib) {
1758                    // If we are looking for a new attribute
1759                case eAttribName:
1760
1761                    // Check what the current token type is
1762                    switch (xtype) {
1763                        // If the current type is text...
1764                        // Eg.  'attribute'
1765                    case eTokenText:
1766                        // Cache the token then indicate that we are next to
1767                        // look for the equals
1768                        lpszTemp = token.pStr;
1769                        cbTemp = cbToken;
1770                        attrib = eAttribEquals;
1771                        break;
1772
1773                        // If we found a closing tag...
1774                        // Eg.  '>'
1775                    case eTokenCloseTag:
1776                        // We are now outside the tag
1777                        status = eOutsideTag;
1778                        pXML->lpszText = pXML->lpXML + pXML->nIndex;
1779                        break;
1780
1781                        // If we found a short hand '/>' closing tag then we can
1782                        // return to the caller
1783                    case eTokenShortHandClose:
1784                        exactMemory(d);
1785                        pXML->lpszText = pXML->lpXML + pXML->nIndex;
1786                        return TRUE;
1787
1788                        // Errors...
1789                    case eTokenQuotedText:    /* '"SomeText"'   */
1790                    case eTokenTagStart:      /* '<'            */
1791                    case eTokenTagEnd:        /* '</'           */
1792                    case eTokenEquals:        /* '='            */
1793                    case eTokenDeclaration:   /* '<?'           */
1794                    case eTokenClear:
1795                        pXML->error = eXMLErrorUnexpectedToken;
1796                        return FALSE;
1797                    default:
1798                        break;
1799                    }
1800                    break;
1801
1802                    // If we are looking for an equals
1803                case eAttribEquals:
1804                    // Check what the current token type is
1805                    switch (xtype) {
1806                        // If the current type is text...
1807                        // Eg.  'Attribute AnotherAttribute'
1808                    case eTokenText:
1809                        // Add the unvalued attribute to the list
1810                        addAttribute_priv(MEMORYINCREASE,
1811                                          stringDup(lpszTemp, cbTemp), NULL);
1812                        // Cache the token then indicate.  We are next to
1813                        // look for the equals attribute
1814                        lpszTemp = token.pStr;
1815                        cbTemp = cbToken;
1816                        break;
1817
1818                        // If we found a closing tag 'Attribute >' or a short hand
1819                        // closing tag 'Attribute />'
1820                    case eTokenShortHandClose:
1821                    case eTokenCloseTag:
1822                        // If we are a declaration element '<?' then we need
1823                        // to remove extra closing '?' if it exists
1824                        pXML->lpszText = pXML->lpXML + pXML->nIndex;
1825
1826                        if (d->isDeclaration &&
1827                                (lpszTemp[cbTemp-1]) == _CXML('?')) {
1828                            cbTemp--;
1829                            if (d->pParent && d->pParent->pParent) {
1830                                xtype = eTokenShortHandClose;
1831                            }
1832                        }
1833
1834                        if (cbTemp) {
1835                            // Add the unvalued attribute to the list
1836                            addAttribute_priv(MEMORYINCREASE,
1837                                              stringDup(lpszTemp, cbTemp), NULL);
1838                        }
1839
1840                        // If this is the end of the tag then return to the caller
1841                        if (xtype == eTokenShortHandClose) {
1842                            exactMemory(d);
1843                            return TRUE;
1844                        }
1845
1846                        // We are now outside the tag
1847                        status = eOutsideTag;
1848                        break;
1849
1850                        // If we found the equals token...
1851                        // Eg.  'Attribute ='
1852                    case eTokenEquals:
1853                        // Indicate that we next need to search for the value
1854                        // for the attribute
1855                        attrib = eAttribValue;
1856                        break;
1857
1858                        // Errors...
1859                    case eTokenQuotedText:    /* 'Attribute "InvalidAttr"'*/
1860                    case eTokenTagStart:      /* 'Attribute <'            */
1861                    case eTokenTagEnd:        /* 'Attribute </'           */
1862                    case eTokenDeclaration:   /* 'Attribute <?'           */
1863                    case eTokenClear:
1864                        pXML->error = eXMLErrorUnexpectedToken;
1865                        return FALSE;
1866                    default:
1867                        break;
1868                    }
1869                    break;
1870
1871                    // If we are looking for an attribute value
1872                case eAttribValue:
1873                    // Check what the current token type is
1874                    switch (xtype) {
1875                        // If the current type is text or quoted text...
1876                        // Eg.  'Attribute = "Value"' or 'Attribute = Value' or
1877                        // 'Attribute = 'Value''.
1878                    case eTokenText:
1879                    case eTokenQuotedText:
1880                        // If we are a declaration element '<?' then we need
1881                        // to remove extra closing '?' if it exists
1882                        if (d->isDeclaration &&
1883                                (token.pStr[cbToken-1]) == _CXML('?')) {
1884                            cbToken--;
1885                        }
1886
1887                        if (cbTemp) {
1888                            // Add the valued attribute to the list
1889                            if (xtype == eTokenQuotedText) {
1890                                token.pStr++;
1891                                cbToken -= 2;
1892                            }
1893                            XMLSTR attrVal = (XMLSTR)token.pStr;
1894                            if (attrVal) {
1895                                attrVal = fromXMLString(attrVal, cbToken, pXML);
1896                                if (!attrVal) return FALSE;
1897                            }
1898                            addAttribute_priv(MEMORYINCREASE,
1899                                              stringDup(lpszTemp, cbTemp),
1900                                              attrVal);
1901                        }
1902
1903                        // Indicate we are searching for a new attribute
1904                        attrib = eAttribName;
1905                        break;
1906
1907                        // Errors...
1908                    case eTokenTagStart:        /* 'Attr = <'          */
1909                    case eTokenTagEnd:          /* 'Attr = </'         */
1910                    case eTokenCloseTag:        /* 'Attr = >'          */
1911                    case eTokenShortHandClose:  /* "Attr = />"         */
1912                    case eTokenEquals:          /* 'Attr = ='          */
1913                    case eTokenDeclaration:     /* 'Attr = <?'         */
1914                    case eTokenClear:
1915                        pXML->error = eXMLErrorUnexpectedToken;
1916                        return FALSE;
1917                        break;
1918                    default:
1919                        break;
1920                    }
1921                }
1922            }
1923        }
1924        // If we failed to obtain the next token
1925        else {
1926            if ((!d->isDeclaration) && (d->pParent)) {
1927#ifdef STRICT_PARSING
1928                pXML->error = eXMLErrorUnmatchedEndTag;
1929#else
1930                pXML->error = eXMLErrorMissingEndTag;
1931#endif
1932                pXML->nIndexMissigEndTag = pXML->nIndex;
1933            }
1934            maybeAddTxT(pXML, pXML->lpXML + pXML->nIndex);
1935            return FALSE;
1936        }
1937    }
1938}
1939
1940// Count the number of lines and columns in an XML string.
1941static void CountLinesAndColumns(XMLCSTR lpXML, int nUpto,
1942                                 XMLResults *pResults) {
1943    XMLCHAR ch;
1944    assert(lpXML);
1945    assert(pResults);
1946
1947    struct XML xml = { lpXML, lpXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0,
1948                       TRUE };
1949
1950    pResults->nLine = 1;
1951    pResults->nColumn = 1;
1952    while (xml.nIndex < nUpto) {
1953        ch = getNextChar(&xml);
1954        if (ch != _CXML('\n')) pResults->nColumn++;
1955        else {
1956            pResults->nLine++;
1957            pResults->nColumn = 1;
1958        }
1959    }
1960}
1961
1962// Parse XML and return the root element.
1963XMLNode XMLNode::parseString(XMLCSTR lpszXML, XMLCSTR tag,
1964                             XMLResults *pResults) {
1965    if (!lpszXML) {
1966        if (pResults) {
1967            pResults->error = eXMLErrorNoElements;
1968            pResults->nLine = 0;
1969            pResults->nColumn = 0;
1970        }
1971        return emptyXMLNode;
1972    }
1973
1974    XMLNode xnode(NULL, NULL, FALSE);
1975    struct XML xml = { lpszXML, lpszXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0,
1976                       TRUE };
1977
1978    // Create header element
1979    xnode.ParseXMLElement(&xml);
1980    enum XMLError error = xml.error;
1981    if (!xnode.nChildNode()) error = eXMLErrorNoXMLTagFound;
1982    if ((xnode.nChildNode() == 1) && (xnode.nElement() == 1)) {
1983        xnode = xnode.getChildNode(); // skip the empty node
1984    }
1985
1986    // If no error occurred
1987    if ((error == eXMLErrorNone) || (error == eXMLErrorMissingEndTag) ||
1988        (error == eXMLErrorNoXMLTagFound)) {
1989        XMLCSTR name = xnode.getName();
1990        if (tag && (*tag) && ((!name) || (xstricmp(name, tag)))) {
1991            xnode = xnode.getChildNode(tag);
1992            if (xnode.isEmpty()) {
1993                if (pResults) {
1994                    pResults->error = eXMLErrorFirstTagNotFound;
1995                    pResults->nLine = 0;
1996                    pResults->nColumn = 0;
1997                }
1998                return emptyXMLNode;
1999            }
2000        }
2001    } else {
2002        // Cleanup: this will destroy all the nodes
2003        xnode = emptyXMLNode;
2004    }
2005
2006
2007    // If we have been given somewhere to place results
2008    if (pResults) {
2009        pResults->error = error;
2010
2011        // If we have an error
2012        if (error != eXMLErrorNone) {
2013            if (error == eXMLErrorMissingEndTag) {
2014                xml.nIndex = xml.nIndexMissigEndTag;
2015            }
2016            // Find which line and column it starts on.
2017            CountLinesAndColumns(xml.lpXML, xml.nIndex, pResults);
2018        }
2019    }
2020    return xnode;
2021}
2022
2023XMLNode XMLNode::parseFile(XMLCSTR filename, XMLCSTR tag, XMLResults *pResults) {
2024    if (pResults) {
2025        pResults->nLine = 0;
2026        pResults->nColumn = 0;
2027    }
2028    FILE *f = xfopen(filename, _CXML("rb"));
2029    if (f == NULL) {
2030        if (pResults) pResults->error = eXMLErrorFileNotFound;
2031        return emptyXMLNode;
2032    }
2033    fseek(f, 0, SEEK_END);
2034    int l = ftell(f), headerSz = 0;
2035    if (!l) {
2036        if (pResults) pResults->error = eXMLErrorEmpty;
2037        fclose(f);
2038        return emptyXMLNode;
2039    }
2040    fseek(f, 0, SEEK_SET);
2041    unsigned char *buf = (unsigned char*)malloc(l + 4);
2042    l = fread(buf, 1, l, f);
2043    fclose(f);
2044    buf[l] = 0;
2045    buf[l+1] = 0;
2046    buf[l+2] = 0;
2047    buf[l+3] = 0;
2048#ifdef _XMLWIDECHAR
2049    if (guessWideCharChars) {
2050        if (!myIsTextWideChar(buf, l)) {
2051            XMLNode::XMLCharEncoding ce = XMLNode::char_encoding_legacy;
2052            if ((buf[0] == 0xef) && (buf[1] == 0xbb) && (buf[2] == 0xbf)) {
2053                headerSz = 3;
2054                ce = XMLNode::char_encoding_UTF8;
2055            }
2056            XMLSTR b2 = myMultiByteToWideChar((const char*)(buf + headerSz), ce);
2057            free(buf);
2058            buf = (unsigned char*)b2;
2059            headerSz = 0;
2060        } else {
2061            if ((buf[0] == 0xef) && (buf[1] == 0xff)) headerSz = 2;
2062            if ((buf[0] == 0xff) && (buf[1] == 0xfe)) headerSz = 2;
2063        }
2064    }
2065#else
2066    if (guessWideCharChars) {
2067        if (myIsTextWideChar(buf, l)) {
2068            if ((buf[0] == 0xef) && (buf[1] == 0xff)) headerSz = 2;
2069            if ((buf[0] == 0xff) && (buf[1] == 0xfe)) headerSz = 2;
2070            char *b2 = myWideCharToMultiByte((const wchar_t*)(buf + headerSz));
2071            free(buf);
2072            buf = (unsigned char*)b2;
2073            headerSz = 0;
2074        } else {
2075            if ((buf[0] == 0xef) && (buf[1] == 0xbb) && (buf[2] == 0xbf)) {
2076                headerSz = 3;
2077            }
2078        }
2079    }
2080#endif
2081
2082    if (!buf) {
2083        if (pResults) pResults->error = eXMLErrorCharConversionError;
2084        return emptyXMLNode;
2085    }
2086    XMLNode x = parseString((XMLSTR)(buf + headerSz), tag, pResults);
2087    free(buf);
2088    return x;
2089}
2090
2091static inline void charmemset(XMLSTR dest, XMLCHAR c, int l) {
2092    while (l--) *(dest++) = c;
2093}
2094// private:
2095// Creates an user friendly XML string from a given element with
2096// appropriate white space and carriage returns.
2097//
2098// This recurses through all subnodes then adds contents of the nodes to the
2099// string.
2100int XMLNode::CreateXMLStringR(XMLNodeData *pEntry, XMLSTR lpszMarker,
2101                              int nFormat) {
2102    int nResult = 0;
2103    int cb = nFormat < 0 ? 0 : nFormat;
2104    int cbElement;
2105    int nChildFormat = -1;
2106    int nElementI = pEntry->nChild + pEntry->nText + pEntry->nClear;
2107    int i, j;
2108    if ((nFormat >= 0) && (nElementI == 1) && (pEntry->nText == 1) &&
2109        (!pEntry->isDeclaration)) {
2110        nFormat = -2;
2111    }
2112
2113    assert(pEntry);
2114
2115#define LENSTR(lpsz) (lpsz ? xstrlen(lpsz) : 0)
2116
2117    // If the element has no name then assume this is the head node.
2118    cbElement = (int)LENSTR(pEntry->lpszName);
2119
2120    if (cbElement) {
2121        // "<elementname "
2122        if (lpszMarker) {
2123            if (cb) charmemset(lpszMarker, INDENTCHAR, cb);
2124            nResult = cb;
2125            lpszMarker[nResult++] = _CXML('<');
2126            if (pEntry->isDeclaration) lpszMarker[nResult++] = _CXML('?');
2127            xstrcpy(&lpszMarker[nResult], pEntry->lpszName);
2128            nResult += cbElement;
2129            lpszMarker[nResult++] = _CXML(' ');
2130
2131        } else {
2132            nResult += cbElement + 2 + cb;
2133            if (pEntry->isDeclaration) nResult++;
2134        }
2135
2136        // Enumerate attributes and add them to the string
2137        XMLAttribute *pAttr = pEntry->pAttribute;
2138        for (i = 0; i < pEntry->nAttribute; i++) {
2139            // "Attrib
2140            cb = (int)LENSTR(pAttr->lpszName);
2141            if (cb) {
2142                if (lpszMarker) xstrcpy(&lpszMarker[nResult], pAttr->lpszName);
2143                nResult += cb;
2144                // "Attrib=Value "
2145                if (pAttr->lpszValue) {
2146                    cb = (int)ToXMLStringTool::lengthXMLString(pAttr->lpszValue);
2147                    if (lpszMarker) {
2148                        lpszMarker[nResult] = _CXML('=');
2149                        lpszMarker[nResult+1] = _CXML('"');
2150                        if (cb) {
2151                            ToXMLStringTool::toXMLUnSafe(&lpszMarker[nResult+2],
2152                                                         pAttr->lpszValue);
2153                        }
2154                        lpszMarker[nResult+cb+2] = _CXML('"');
2155                    }
2156                    nResult += cb + 3;
2157                }
2158                if (lpszMarker) lpszMarker[nResult] = _CXML(' ');
2159                nResult++;
2160            }
2161            pAttr++;
2162        }
2163
2164        if (pEntry->isDeclaration) {
2165            if (lpszMarker) {
2166                lpszMarker[nResult-1] = _CXML('?');
2167                lpszMarker[nResult] = _CXML('>');
2168            }
2169            nResult++;
2170            if (nFormat != -1) {
2171                if (lpszMarker) lpszMarker[nResult] = _CXML('\n');
2172                nResult++;
2173            }
2174        } else
2175            // If there are child nodes we need to terminate the start tag
2176            if (nElementI) {
2177                if (lpszMarker) lpszMarker[nResult-1] = _CXML('>');
2178                if (nFormat >= 0) {
2179                    if (lpszMarker) lpszMarker[nResult] = _CXML('\n');
2180                    nResult++;
2181                }
2182            } else nResult--;
2183    }
2184
2185    // Calculate the child format for when we recurse.  This is used to
2186    // determine the number of spaces used for prefixes.
2187    if (nFormat != -1) {
2188        if (cbElement && (!pEntry->isDeclaration)) nChildFormat = nFormat + 1;
2189        else nChildFormat = nFormat;
2190    }
2191
2192    // Enumerate through remaining children
2193    for (i = 0; i < nElementI; i++) {
2194        j = pEntry->pOrder[i];
2195        switch ((XMLElementType)(j&3)) {
2196            // Text nodes
2197        case eNodeText: {
2198            // "Text"
2199            XMLCSTR pChild = pEntry->pText[j>>2];
2200            cb = (int)ToXMLStringTool::lengthXMLString(pChild);
2201            if (cb) {
2202                if (nFormat >= 0) {
2203                    if (lpszMarker) {
2204                        charmemset(&lpszMarker[nResult], INDENTCHAR,
2205                                   nFormat + 1);
2206                        ToXMLStringTool::toXMLUnSafe(
2207                            &lpszMarker[nResult+nFormat+1], pChild);
2208                        lpszMarker[nResult+nFormat+1+cb] = _CXML('\n');
2209                    }
2210                    nResult += cb + nFormat + 2;
2211                } else {
2212                    if (lpszMarker) {
2213                        ToXMLStringTool::toXMLUnSafe(&lpszMarker[nResult],
2214                                                     pChild);
2215                    }
2216                    nResult += cb;
2217                }
2218            }
2219            break;
2220        }
2221
2222        // Clear type nodes
2223        case eNodeClear: {
2224            XMLClear *pChild = pEntry->pClear + (j >> 2);
2225            // "OpenTag"
2226            cb = (int)LENSTR(pChild->lpszOpenTag);
2227            if (cb) {
2228                if (nFormat != -1) {
2229                    if (lpszMarker) {
2230                        charmemset(&lpszMarker[nResult], INDENTCHAR,
2231                                   nFormat + 1);
2232                        xstrcpy(&lpszMarker[nResult+nFormat+1],
2233                                pChild->lpszOpenTag);
2234                    }
2235                    nResult += cb + nFormat + 1;
2236                } else {
2237                    if (lpszMarker) {
2238                        xstrcpy(&lpszMarker[nResult], pChild->lpszOpenTag);
2239                    }
2240                    nResult += cb;
2241                }
2242            }
2243
2244            // "OpenTag Value"
2245            cb = (int)LENSTR(pChild->lpszValue);
2246            if (cb) {
2247                if (lpszMarker) {
2248                    xstrcpy(&lpszMarker[nResult], pChild->lpszValue);
2249                }
2250                nResult += cb;
2251            }
2252
2253            // "OpenTag Value CloseTag"
2254            cb = (int)LENSTR(pChild->lpszCloseTag);
2255            if (cb) {
2256                if (lpszMarker) {
2257                    xstrcpy(&lpszMarker[nResult], pChild->lpszCloseTag);
2258                }
2259                nResult += cb;
2260            }
2261
2262            if (nFormat != -1) {
2263                if (lpszMarker) lpszMarker[nResult] = _CXML('\n');
2264                nResult++;
2265            }
2266            break;
2267        }
2268
2269        // Element nodes
2270        case eNodeChild: {
2271            // Recursively add child nodes
2272            nResult += CreateXMLStringR(pEntry->pChild[j>>2].d,
2273                                        lpszMarker ? lpszMarker + nResult : 0,
2274                                        nChildFormat);
2275            break;
2276        }
2277        default:
2278            break;
2279        }
2280    }
2281
2282    if ((cbElement) && (!pEntry->isDeclaration)) {
2283        // If we have child entries we need to use long XML notation for
2284        // closing the element - "<elementname>blah blah blah</elementname>"
2285        if (nElementI) {
2286            // "</elementname>\0"
2287            if (lpszMarker) {
2288                if (nFormat >= 0) {
2289                    charmemset(&lpszMarker[nResult], INDENTCHAR, nFormat);
2290                    nResult += nFormat;
2291                }
2292
2293                lpszMarker[nResult] = _CXML('<');
2294                lpszMarker[nResult+1] = _CXML('/');
2295                nResult += 2;
2296                xstrcpy(&lpszMarker[nResult], pEntry->lpszName);
2297                nResult += cbElement;
2298
2299                lpszMarker[nResult] = _CXML('>');
2300                if (nFormat == -1) nResult++;
2301                else {
2302                    lpszMarker[nResult+1] = _CXML('\n');
2303                    nResult += 2;
2304                }
2305            } else {
2306                if (nFormat >= 0) nResult += cbElement + 4 + nFormat;
2307                else if (nFormat == -1) nResult += cbElement + 3;
2308                else nResult += cbElement + 4;
2309            }
2310        } else {
2311            // If there are no children we can use shorthand XML notation -
2312            // "<elementname/>"
2313            // "/>\0"
2314            if (lpszMarker) {
2315                lpszMarker[nResult] = _CXML('/');
2316                lpszMarker[nResult+1] = _CXML('>');
2317                if (nFormat != -1) lpszMarker[nResult+2] = _CXML('\n');
2318            }
2319            nResult += nFormat == -1 ? 2 : 3;
2320        }
2321    }
2322
2323    return nResult;
2324}
2325
2326#undef LENSTR
2327
2328// Create an XML string
2329// @param       int nFormat             - 0 if no formatting is required
2330//                                        otherwise nonzero for formatted text
2331//                                        with carriage returns and indentation.
2332// @param       int *pnSize             - [out] pointer to the size of the
2333//                                        returned string not including the
2334//                                        NULL terminator.
2335// @return      XMLSTR                  - Allocated XML string, you must free
2336//                                        this with free().
2337XMLSTR XMLNode::createXMLString(int nFormat, int *pnSize) const {
2338    if (!d) {
2339        if (pnSize) *pnSize = 0;
2340        return NULL;
2341    }
2342
2343    XMLSTR lpszResult = NULL;
2344    int cbStr;
2345
2346    // Recursively Calculate the size of the XML string
2347    if (!dropWhiteSpace) nFormat = 0;
2348    nFormat = nFormat ? 0 : -1;
2349    cbStr = CreateXMLStringR(d, 0, nFormat);
2350    // Alllocate memory for the XML string + the NULL terminator and
2351    // create the recursively XML string.
2352    lpszResult = (XMLSTR)malloc((cbStr + 1) * sizeof(XMLCHAR));
2353    CreateXMLStringR(d, lpszResult, nFormat);
2354    lpszResult[cbStr] = _CXML('\0');
2355    if (pnSize) *pnSize = cbStr;
2356    return lpszResult;
2357}
2358
2359int XMLNode::detachFromParent(XMLNodeData *d) {
2360    XMLNode *pa = d->pParent->pChild;
2361    int i = 0;
2362    while (((void*)(pa[i].d)) != ((void*)d)) i++;
2363    d->pParent->nChild--;
2364    if (d->pParent->nChild) {
2365        memmove(pa + i, pa + i + 1, (d->pParent->nChild - i)*sizeof(XMLNode));
2366    } else {
2367        free(pa);
2368        d->pParent->pChild = NULL;
2369    }
2370    return removeOrderElement(d->pParent, eNodeChild, i);
2371}
2372
2373XMLNode::~XMLNode() {
2374    if (!d) return;
2375    d->ref_count--;
2376    emptyTheNode(0);
2377}
2378void XMLNode::deleteNodeContent() {
2379    if (!d) return;
2380    if (d->pParent) {
2381        detachFromParent(d);
2382        d->pParent = NULL;
2383        d->ref_count--;
2384    }
2385    emptyTheNode(1);
2386}
2387void XMLNode::emptyTheNode(char force) {
2388    XMLNodeData *dd = d; // warning: must stay this way!
2389    if ((dd->ref_count == 0) || force) {
2390        if (d->pParent) detachFromParent(d);
2391        int i;
2392        XMLNode *pc;
2393        for (i = 0; i < dd->nChild; i++) {
2394            pc = dd->pChild + i;
2395            pc->d->pParent = NULL;
2396            pc->d->ref_count--;
2397            pc->emptyTheNode(force);
2398        }
2399        myFree(dd->pChild);
2400        for (i = 0; i < dd->nText; i++) free((void*)dd->pText[i]);
2401        myFree(dd->pText);
2402        for (i = 0; i < dd->nClear; i++) free((void*)dd->pClear[i].lpszValue);
2403        myFree(dd->pClear);
2404        for (i = 0; i < dd->nAttribute; i++) {
2405            free((void*)dd->pAttribute[i].lpszName);
2406            if (dd->pAttribute[i].lpszValue) {
2407                free((void*)dd->pAttribute[i].lpszValue);
2408            }
2409        }
2410        myFree(dd->pAttribute);
2411        myFree(dd->pOrder);
2412        myFree((void*)dd->lpszName);
2413        dd->nChild = 0;
2414        dd->nText = 0;
2415        dd->nClear = 0;
2416        dd->nAttribute = 0;
2417        dd->pChild = NULL;
2418        dd->pText = NULL;
2419        dd->pClear = NULL;
2420        dd->pAttribute = NULL;
2421        dd->pOrder = NULL;
2422        dd->lpszName = NULL;
2423        dd->pParent = NULL;
2424    }
2425    if (dd->ref_count == 0) {
2426        free(dd);
2427        d = NULL;
2428    }
2429}
2430
2431XMLNode& XMLNode::operator=( const XMLNode & A ) {
2432    // shallow copy
2433    if (this != &A) {
2434        if (d) {
2435            d->ref_count--;
2436            emptyTheNode(0);
2437        }
2438        d = A.d;
2439        if (d) (d->ref_count) ++ ;
2440    }
2441    return *this;
2442}
2443
2444XMLNode::XMLNode(const XMLNode &A) {
2445    // shallow copy
2446    d = A.d;
2447    if (d) (d->ref_count)++ ;
2448}
2449
2450XMLNode XMLNode::deepCopy() const {
2451    if (!d) return XMLNode::emptyXMLNode;
2452    XMLNode x(NULL, stringDup(d->lpszName), d->isDeclaration);
2453    XMLNodeData *p = x.d;
2454    int n = d->nAttribute;
2455    if (n) {
2456        p->nAttribute = n;
2457        p->pAttribute = (XMLAttribute*)malloc(n * sizeof(XMLAttribute));
2458        while (n--) {
2459            p->pAttribute[n].lpszName = stringDup(d->pAttribute[n].lpszName);
2460            p->pAttribute[n].lpszValue = stringDup(d->pAttribute[n].lpszValue);
2461        }
2462    }
2463    if (d->pOrder) {
2464        n = (d->nChild + d->nText + d->nClear) * sizeof(int);
2465        p->pOrder = (int*)malloc(n);
2466        memcpy(p->pOrder, d->pOrder, n);
2467    }
2468    n = d->nText;
2469    if (n) {
2470        p->nText = n;
2471        p->pText = (XMLCSTR*)malloc(n * sizeof(XMLCSTR));
2472        while (n--) p->pText[n] = stringDup(d->pText[n]);
2473    }
2474    n = d->nClear;
2475    if (n) {
2476        p->nClear = n;
2477        p->pClear = (XMLClear*)malloc(n * sizeof(XMLClear));
2478        while (n--) {
2479            p->pClear[n].lpszCloseTag = d->pClear[n].lpszCloseTag;
2480            p->pClear[n].lpszOpenTag = d->pClear[n].lpszOpenTag;
2481            p->pClear[n].lpszValue = stringDup(d->pClear[n].lpszValue);
2482        }
2483    }
2484    n = d->nChild;
2485    if (n) {
2486        p->nChild = n;
2487        p->pChild = (XMLNode*)malloc(n * sizeof(XMLNode));
2488        while (n--) {
2489            p->pChild[n].d = NULL;
2490            p->pChild[n] = d->pChild[n].deepCopy();
2491            p->pChild[n].d->pParent = p;
2492        }
2493    }
2494    return x;
2495}
2496
2497XMLNode XMLNode::addChild(XMLNode childNode, int pos) {
2498    XMLNodeData *dc = childNode.d;
2499    if ((!dc) || (!d)) return childNode;
2500    if (!dc->lpszName) {
2501        // this is a root node: todo: correct fix
2502        int j = pos;
2503        while (dc->nChild) {
2504            addChild(dc->pChild[0], j);
2505            if (pos >= 0) j++;
2506        }
2507        return childNode;
2508    }
2509    if (dc->pParent) {
2510        if ((detachFromParent(dc) <= pos) && (dc->pParent == d)) pos--;
2511    } else dc->ref_count++;
2512    dc->pParent = d;
2513//     int nc=d->nChild;
2514//     d->pChild=(XMLNode*)myRealloc(d->pChild,(nc+1),memoryIncrease,sizeof(XMLNode));
2515    d->pChild = (XMLNode*)addToOrder(0, &pos, d->nChild, d->pChild,
2516                                     sizeof(XMLNode), eNodeChild);
2517    d->pChild[pos].d = dc;
2518    d->nChild++;
2519    return childNode;
2520}
2521
2522void XMLNode::deleteAttribute(int i) {
2523    if ((!d) || (i < 0) || (i >= d->nAttribute)) return;
2524    d->nAttribute--;
2525    XMLAttribute *p = d->pAttribute + i;
2526    free((void*)p->lpszName);
2527    if (p->lpszValue) free((void*)p->lpszValue);
2528    if (d->nAttribute) {
2529        memmove(p, p + 1, (d->nAttribute - i)*sizeof(XMLAttribute));
2530    }
2531    else {
2532        free(p);
2533        d->pAttribute = NULL;
2534    }
2535}
2536
2537void XMLNode::deleteAttribute(XMLAttribute *a) {
2538    if (a) deleteAttribute(a->lpszName);
2539}
2540void XMLNode::deleteAttribute(XMLCSTR lpszName) {
2541    int j = 0;
2542    getAttribute(lpszName, &j);
2543    if (j) deleteAttribute(j - 1);
2544}
2545
2546XMLAttribute *XMLNode::updateAttribute_WOSD(XMLSTR lpszNewValue,
2547                                            XMLSTR lpszNewName, int i) {
2548    if (!d) {
2549        if (lpszNewValue) free(lpszNewValue);
2550        if (lpszNewName) free(lpszNewName);
2551        return NULL;
2552    }
2553    if (i >= d->nAttribute) {
2554        if (lpszNewName) return addAttribute_WOSD(lpszNewName, lpszNewValue);
2555        return NULL;
2556    }
2557    XMLAttribute *p = d->pAttribute + i;
2558    if (p->lpszValue && p->lpszValue != lpszNewValue) {
2559        free((void*)p->lpszValue);
2560    }
2561    p->lpszValue = lpszNewValue;
2562    if (lpszNewName && p->lpszName != lpszNewName) {
2563        free((void*)p->lpszName);
2564        p->lpszName = lpszNewName;
2565    };
2566    return p;
2567}
2568
2569XMLAttribute *XMLNode::updateAttribute_WOSD(XMLAttribute *newAttribute,
2570                                            XMLAttribute *oldAttribute) {
2571    if (oldAttribute) {
2572        return updateAttribute_WOSD((XMLSTR)newAttribute->lpszValue,
2573                                    (XMLSTR)newAttribute->lpszName,
2574                                    oldAttribute->lpszName);
2575    }
2576    return addAttribute_WOSD((XMLSTR)newAttribute->lpszName,
2577                             (XMLSTR)newAttribute->lpszValue);
2578}
2579
2580XMLAttribute *XMLNode::updateAttribute_WOSD(XMLSTR lpszNewValue,
2581                                            XMLSTR lpszNewName,
2582                                            XMLCSTR lpszOldName) {
2583    int j = 0;
2584    getAttribute(lpszOldName, &j);
2585    if (j) return updateAttribute_WOSD(lpszNewValue, lpszNewName, j - 1);
2586    else {
2587        if (lpszNewName) {
2588            return addAttribute_WOSD(lpszNewName, lpszNewValue);
2589        } else {
2590            return addAttribute_WOSD(stringDup(lpszOldName), lpszNewValue);
2591        }
2592    }
2593}
2594
2595int XMLNode::indexText(XMLCSTR lpszValue) const {
2596    if (!d) return -1;
2597    int i, l = d->nText;
2598    if (!lpszValue) {
2599        if (l) return 0;
2600        return -1;
2601    }
2602    XMLCSTR *p = d->pText;
2603    for (i = 0; i < l; i++) if (lpszValue == p[i]) return i;
2604    return -1;
2605}
2606
2607void XMLNode::deleteText(int i) {
2608    if ((!d) || (i < 0) || (i >= d->nText)) return;
2609    d->nText--;
2610    XMLCSTR *p = d->pText + i;
2611    free((void*)*p);
2612    if (d->nText) memmove(p, p + 1, (d->nText - i)*sizeof(XMLCSTR));
2613    else {
2614        free(p);
2615        d->pText = NULL;
2616    }
2617    removeOrderElement(d, eNodeText, i);
2618}
2619
2620void XMLNode::deleteText(XMLCSTR lpszValue) {
2621    deleteText(indexText(lpszValue));
2622}
2623
2624XMLCSTR XMLNode::updateText_WOSD(XMLSTR lpszNewValue, int i) {
2625    if (!d) {
2626        if (lpszNewValue) free(lpszNewValue);
2627        return NULL;
2628    }
2629    if (i >= d->nText) return addText_WOSD(lpszNewValue);
2630    XMLCSTR *p = d->pText + i;
2631    if (*p != lpszNewValue) {
2632        free((void*)*p);
2633        *p = lpszNewValue;
2634    }
2635    return lpszNewValue;
2636}
2637
2638XMLCSTR XMLNode::updateText_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue) {
2639    if (!d) {
2640        if (lpszNewValue) free(lpszNewValue);
2641        return NULL;
2642    }
2643    int i = indexText(lpszOldValue);
2644    if (i >= 0) return updateText_WOSD(lpszNewValue, i);
2645    return addText_WOSD(lpszNewValue);
2646}
2647
2648void XMLNode::deleteClear(int i) {
2649    if ((!d) || (i < 0) || (i >= d->nClear)) return;
2650    d->nClear--;
2651    XMLClear *p = d->pClear + i;
2652    free((void*)p->lpszValue);
2653    if (d->nClear) memmove(p, p + 1, (d->nClear - i)*sizeof(XMLClear));
2654    else {
2655        free(p);
2656        d->pClear = NULL;
2657    }
2658    removeOrderElement(d, eNodeClear, i);
2659}
2660
2661int XMLNode::indexClear(XMLCSTR lpszValue) const {
2662    if (!d) return -1;
2663    int i, l = d->nClear;
2664    if (!lpszValue) {
2665        if (l) return 0;
2666        return -1;
2667    }
2668    XMLClear *p = d->pClear;
2669    for (i = 0; i < l; i++) if (lpszValue == p[i].lpszValue) return i;
2670    return -1;
2671}
2672
2673void XMLNode::deleteClear(XMLCSTR lpszValue) {
2674    deleteClear(indexClear(lpszValue));
2675}
2676void XMLNode::deleteClear(XMLClear *a) {
2677    if (a) deleteClear(a->lpszValue);
2678}
2679
2680XMLClear *XMLNode::updateClear_WOSD(XMLSTR lpszNewContent, int i) {
2681    if (!d) {
2682        if (lpszNewContent) free(lpszNewContent);
2683        return NULL;
2684    }
2685    if (i >= d->nClear) return addClear_WOSD(lpszNewContent);
2686    XMLClear *p = d->pClear + i;
2687    if (lpszNewContent != p->lpszValue) {
2688        free((void*)p->lpszValue);
2689        p->lpszValue = lpszNewContent;
2690    }
2691    return p;
2692}
2693
2694XMLClear *XMLNode::updateClear_WOSD(XMLSTR lpszNewContent,
2695                                    XMLCSTR lpszOldValue) {
2696    if (!d) {
2697        if (lpszNewContent) free(lpszNewContent);
2698        return NULL;
2699    }
2700    int i = indexClear(lpszOldValue);
2701    if (i >= 0) return updateClear_WOSD(lpszNewContent, i);
2702    return addClear_WOSD(lpszNewContent);
2703}
2704
2705XMLClear *XMLNode::updateClear_WOSD(XMLClear *newP, XMLClear *oldP) {
2706    if (oldP) {
2707        return updateClear_WOSD((XMLSTR)newP->lpszValue,
2708                                (XMLSTR)oldP->lpszValue);
2709    }
2710    return NULL;
2711}
2712
2713int XMLNode::nChildNode(XMLCSTR name) const {
2714    if (!d) return 0;
2715    int i, j = 0, n = d->nChild;
2716    XMLNode *pc = d->pChild;
2717    for (i = 0; i < n; i++) {
2718        if (xstricmp(pc->d->lpszName, name) == 0) j++;
2719        pc++;
2720    }
2721    return j;
2722}
2723
2724XMLNode XMLNode::getChildNode(XMLCSTR name, int *j) const {
2725    if (!d) return emptyXMLNode;
2726    int i = 0, n = d->nChild;
2727    if (j) i = *j;
2728    XMLNode *pc = d->pChild + i;
2729    for (; i < n; i++) {
2730        if (!xstricmp(pc->d->lpszName, name)) {
2731            if (j) *j = i + 1;
2732            return *pc;
2733        }
2734        pc++;
2735    }
2736    return emptyXMLNode;
2737}
2738
2739XMLNode XMLNode::getChildNode(XMLCSTR name, int j) const {
2740    if (!d) return emptyXMLNode;
2741    if (j >= 0) {
2742        int i = 0;
2743        while (j-- > 0) getChildNode(name, &i);
2744        return getChildNode(name, &i);
2745    }
2746    int i = d->nChild;
2747    while (i--) if (!xstricmp(name, d->pChild[i].d->lpszName)) break;
2748    if (i < 0) return emptyXMLNode;
2749    return getChildNode(i);
2750}
2751
2752XMLNode* XMLNode::getChildNodePtr(XMLCSTR name, int *j) const {
2753    if (!d) return &emptyXMLNode;
2754    int i = 0, n = d->nChild;
2755    int foundIndex = 0;
2756    XMLNode *pc = d->pChild + i;
2757    for (; i < n; i++) {
2758        if (!xstricmp(pc->d->lpszName, name)) {
2759            if (*j == foundIndex) return pc;
2760            foundIndex++;
2761        }
2762        pc++;
2763    }
2764    return &emptyXMLNode;
2765}
2766
2767XMLNode XMLNode::getChildNodeByPath(XMLCSTR _path, char createMissing,
2768                                    XMLCHAR sep) {
2769    XMLSTR path = stringDup(_path);
2770    XMLNode x = getChildNodeByPathNonConst(path, createMissing, sep);
2771    if (path) free(path);
2772    return x;
2773}
2774
2775XMLNode XMLNode::getChildNodeByPathNonConst(XMLSTR path,
2776                                            char createIfMissing, XMLCHAR sep) {
2777    if ((!path) || (!(*path))) return *this;
2778    XMLNode xn, xbase = *this;
2779    XMLCHAR *tend1, sepString[2];
2780    sepString[0] = sep;
2781    sepString[1] = 0;
2782    tend1 = xstrstr(path, sepString);
2783    while (tend1) {
2784        *tend1 = 0;
2785        xn = xbase.getChildNode(path);
2786        if (xn.isEmpty()) {
2787            if (createIfMissing) xn = xbase.addChild(path);
2788            else {
2789                *tend1 = sep;
2790                return XMLNode::emptyXMLNode;
2791            }
2792        }
2793        *tend1 = sep;
2794        xbase = xn;
2795        path = tend1 + 1;
2796        tend1 = xstrstr(path, sepString);
2797    }
2798    xn = xbase.getChildNode(path);
2799    if (xn.isEmpty() && createIfMissing) xn = xbase.addChild(path);
2800    return xn;
2801}
2802
2803XMLElementPosition XMLNode::positionOfText     (int i) const {
2804    if (i >= d->nText ) i = d->nText - 1;
2805    return findPosition(d, i, eNodeText );
2806}
2807XMLElementPosition XMLNode::positionOfClear    (int i) const {
2808    if (i >= d->nClear) i = d->nClear - 1;
2809    return findPosition(d, i, eNodeClear);
2810}
2811XMLElementPosition XMLNode::positionOfChildNode(int i) const {
2812    if (i >= d->nChild) i = d->nChild - 1;
2813    return findPosition(d, i, eNodeChild);
2814}
2815XMLElementPosition XMLNode::positionOfText (XMLCSTR lpszValue) const {
2816    return positionOfText (indexText (lpszValue));
2817}
2818XMLElementPosition XMLNode::positionOfClear(XMLCSTR lpszValue) const {
2819    return positionOfClear(indexClear(lpszValue));
2820}
2821XMLElementPosition XMLNode::positionOfClear(XMLClear *a) const {
2822    if (a) return positionOfClear(a->lpszValue);
2823    return positionOfClear();
2824}
2825XMLElementPosition XMLNode::positionOfChildNode(XMLNode x)  const {
2826    if ((!d) || (!x.d)) return -1;
2827    XMLNodeData *dd = x.d;
2828    XMLNode *pc = d->pChild;
2829    int i = d->nChild;
2830    while (i--) if (pc[i].d == dd) return findPosition(d, i, eNodeChild);
2831    return -1;
2832}
2833XMLElementPosition XMLNode::positionOfChildNode(XMLCSTR name, int count) const {
2834    if (!name) return positionOfChildNode(count);
2835    int j = 0;
2836    do {
2837        getChildNode(name, &j);
2838        if (j < 0) return -1;
2839    } while (count--);
2840    return findPosition(d, j - 1, eNodeChild);
2841}
2842
2843XMLNode XMLNode::getChildNodeWithAttribute(XMLCSTR name, XMLCSTR attributeName,
2844                                           XMLCSTR attributeValue,
2845                                           int *k) const {
2846    int i = 0, j;
2847    if (k) i = *k;
2848    XMLNode x;
2849    XMLCSTR t;
2850    do {
2851        x = getChildNode(name, &i);
2852        if (!x.isEmpty()) {
2853            if (attributeValue) {
2854                j = 0;
2855                do {
2856                    t = x.getAttribute(attributeName, &j);
2857                    if (t && (xstricmp(attributeValue, t) == 0)) {
2858                        if (k) *k = i;
2859                        return x;
2860                    }
2861                } while (t);
2862            } else {
2863                if (x.isAttributeSet(attributeName)) {
2864                    if (k) *k = i;
2865                    return x;
2866                }
2867            }
2868        }
2869    } while (!x.isEmpty());
2870    return emptyXMLNode;
2871}
2872
2873// Find an attribute on an node.
2874XMLCSTR XMLNode::getAttribute(XMLCSTR lpszAttrib, int *j) const {
2875    if (!d) return NULL;
2876    int i = 0, n = d->nAttribute;
2877    if (j) i = *j;
2878    XMLAttribute *pAttr = d->pAttribute + i;
2879    for (; i < n; i++) {
2880        if (xstricmp(pAttr->lpszName, lpszAttrib) == 0) {
2881            if (j) *j = i + 1;
2882            return pAttr->lpszValue;
2883        }
2884        pAttr++;
2885    }
2886    return NULL;
2887}
2888
2889char XMLNode::isAttributeSet(XMLCSTR lpszAttrib) const {
2890    if (!d) return FALSE;
2891    int i, n = d->nAttribute;
2892    XMLAttribute *pAttr = d->pAttribute;
2893    for (i = 0; i < n; i++) {
2894        if (xstricmp(pAttr->lpszName, lpszAttrib) == 0) {
2895            return TRUE;
2896        }
2897        pAttr++;
2898    }
2899    return FALSE;
2900}
2901
2902XMLCSTR XMLNode::getAttribute(XMLCSTR name, int j) const {
2903    if (!d) return NULL;
2904    int i = 0;
2905    while (j-- > 0) getAttribute(name, &i);
2906    return getAttribute(name, &i);
2907}
2908
2909XMLNodeContents XMLNode::enumContents(int i) const {
2910    XMLNodeContents c;
2911    if (!d) {
2912        c.etype = eNodeNULL;
2913        return c;
2914    }
2915    if (i < d->nAttribute) {
2916        c.etype = eNodeAttribute;
2917        c.attrib = d->pAttribute[i];
2918        return c;
2919    }
2920    i -= d->nAttribute;
2921    c.etype = (XMLElementType)(d->pOrder[i] & 3);
2922    i = (d->pOrder[i]) >> 2;
2923    switch (c.etype) {
2924    case eNodeChild:
2925        c.child = d->pChild[i];
2926        break;
2927    case eNodeText:
2928        c.text  = d->pText[i];
2929        break;
2930    case eNodeClear:
2931        c.clear = d->pClear[i];
2932        break;
2933    default:
2934        break;
2935    }
2936    return c;
2937}
2938
2939XMLCSTR XMLNode::getName() const {
2940    if (!d) return NULL;
2941    return d->lpszName;
2942}
2943int XMLNode::nText()       const {
2944    if (!d) return 0;
2945    return d->nText;
2946}
2947int XMLNode::nChildNode()  const {
2948    if (!d) return 0;
2949    return d->nChild;
2950}
2951int XMLNode::nAttribute()  const {
2952    if (!d) return 0;
2953    return d->nAttribute;
2954}
2955int XMLNode::nClear()      const {
2956    if (!d) return 0;
2957    return d->nClear;
2958}
2959int XMLNode::nElement()    const {
2960    if (!d) return 0;
2961    return d->nAttribute + d->nChild + d->nText + d->nClear;
2962}
2963XMLClear     XMLNode::getClear         (int i) const {
2964    if ((!d) || (i >= d->nClear    )) return emptyXMLClear;
2965    return d->pClear[i];
2966}
2967XMLAttribute XMLNode::getAttribute     (int i) const {
2968    if ((!d) || (i >= d->nAttribute)) return emptyXMLAttribute;
2969    return d->pAttribute[i];
2970}
2971XMLCSTR      XMLNode::getAttributeName (int i) const {
2972    if ((!d) || (i >= d->nAttribute)) return NULL;
2973    return d->pAttribute[i].lpszName;
2974}
2975XMLCSTR      XMLNode::getAttributeValue(int i) const {
2976    if ((!d) || (i >= d->nAttribute)) return NULL;
2977    return d->pAttribute[i].lpszValue;
2978}
2979XMLCSTR      XMLNode::getText          (int i) const {
2980    if ((!d) || (i >= d->nText     )) return NULL;
2981    return d->pText[i];
2982}
2983XMLNode      XMLNode::getChildNode     (int i) const {
2984    if ((!d) || (i >= d->nChild    )) return emptyXMLNode;
2985    return d->pChild[i];
2986}
2987XMLNode      XMLNode::getParentNode    (     ) const {
2988    if ((!d) || (!d->pParent     )) return emptyXMLNode;
2989    return XMLNode(d->pParent);
2990}
2991char         XMLNode::isDeclaration    (     ) const {
2992    if (!d) return 0;
2993    return d->isDeclaration;
2994}
2995char         XMLNode::isEmpty          (     ) const {
2996    return (d == NULL);
2997}
2998XMLNode       XMLNode::emptyNode       (     )       {
2999    return XMLNode::emptyXMLNode;
3000}
3001
3002XMLNode XMLNode::addChild(XMLCSTR lpszName, char isDeclaration,
3003                          XMLElementPosition pos) {
3004    return addChild_priv(0, stringDup(lpszName), isDeclaration, pos);
3005}
3006XMLNode XMLNode::addChild_WOSD(XMLSTR lpszName, char isDeclaration,
3007                               XMLElementPosition pos) {
3008    return addChild_priv(0, lpszName, isDeclaration, pos);
3009}
3010XMLAttribute *XMLNode::addAttribute(XMLCSTR lpszName, XMLCSTR lpszValue) {
3011    return addAttribute_priv(0, stringDup(lpszName), stringDup(lpszValue));
3012}
3013XMLAttribute *XMLNode::addAttribute_WOSD(XMLSTR lpszName, XMLSTR lpszValuev) {
3014    return addAttribute_priv(0, lpszName, lpszValuev);
3015}
3016XMLCSTR XMLNode::addText(XMLCSTR lpszValue, XMLElementPosition pos) {
3017    return addText_priv(0, stringDup(lpszValue), pos);
3018}
3019XMLCSTR XMLNode::addText_WOSD(XMLSTR lpszValue, XMLElementPosition pos) {
3020    return addText_priv(0, lpszValue, pos);
3021}
3022XMLClear *XMLNode::addClear(XMLCSTR lpszValue, XMLCSTR lpszOpen,
3023                            XMLCSTR lpszClose, XMLElementPosition pos) {
3024    return addClear_priv(0, stringDup(lpszValue), lpszOpen, lpszClose, pos);
3025}
3026XMLClear *XMLNode::addClear_WOSD(XMLSTR lpszValue, XMLCSTR lpszOpen,
3027                                 XMLCSTR lpszClose, XMLElementPosition pos) {
3028    return addClear_priv(0, lpszValue, lpszOpen, lpszClose, pos);
3029}
3030XMLCSTR XMLNode::updateName(XMLCSTR lpszName) {
3031    return updateName_WOSD(stringDup(lpszName));
3032}
3033XMLAttribute *XMLNode::updateAttribute(XMLAttribute *newAttribute,
3034                                       XMLAttribute *oldAttribute) {
3035    return updateAttribute_WOSD(stringDup(newAttribute->lpszValue),
3036                                stringDup(newAttribute->lpszName),
3037                                oldAttribute->lpszName);
3038}
3039XMLAttribute *XMLNode::updateAttribute(XMLCSTR lpszNewValue,
3040                                       XMLCSTR lpszNewName, int i) {
3041    return updateAttribute_WOSD(stringDup(lpszNewValue),
3042                                stringDup(lpszNewName), i);
3043}
3044XMLAttribute *XMLNode::updateAttribute(XMLCSTR lpszNewValue,
3045                                       XMLCSTR lpszNewName,
3046                                       XMLCSTR lpszOldName) {
3047    return updateAttribute_WOSD(stringDup(lpszNewValue),
3048                                stringDup(lpszNewName), lpszOldName);
3049}
3050XMLCSTR XMLNode::updateText(XMLCSTR lpszNewValue, int i) {
3051    return updateText_WOSD(stringDup(lpszNewValue), i);
3052}
3053XMLCSTR XMLNode::updateText(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue) {
3054    return updateText_WOSD(stringDup(lpszNewValue), lpszOldValue);
3055}
3056XMLClear *XMLNode::updateClear(XMLCSTR lpszNewContent, int i) {
3057    return updateClear_WOSD(stringDup(lpszNewContent), i);
3058}
3059XMLClear *XMLNode::updateClear(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue) {
3060    return updateClear_WOSD(stringDup(lpszNewValue), lpszOldValue);
3061}
3062XMLClear     *XMLNode::updateClear(XMLClear *newP, XMLClear *oldP) {
3063    return updateClear_WOSD(stringDup(newP->lpszValue), oldP->lpszValue);
3064}
3065
3066char XMLNode::setGlobalOptions(XMLCharEncoding _characterEncoding,
3067                               char _guessWideCharChars,
3068                               char _dropWhiteSpace,
3069                               char _removeCommentsInMiddleOfText) {
3070    guessWideCharChars = _guessWideCharChars;
3071    dropWhiteSpace = _dropWhiteSpace;
3072    removeCommentsInMiddleOfText = _removeCommentsInMiddleOfText;
3073#ifdef _XMLWIDECHAR
3074    if (_characterEncoding) characterEncoding = _characterEncoding;
3075#else
3076    switch (_characterEncoding) {
3077    case char_encoding_UTF8:
3078        characterEncoding = _characterEncoding;
3079        XML_ByteTable = XML_utf8ByteTable;
3080        break;
3081    case char_encoding_legacy:
3082        characterEncoding = _characterEncoding;
3083        XML_ByteTable = XML_legacyByteTable;
3084        break;
3085    case char_encoding_ShiftJIS:
3086        characterEncoding = _characterEncoding;
3087        XML_ByteTable = XML_sjisByteTable;
3088        break;
3089    case char_encoding_GB2312:
3090        characterEncoding = _characterEncoding;
3091        XML_ByteTable = XML_gb2312ByteTable;
3092        break;
3093    case char_encoding_Big5:
3094    case char_encoding_GBK:
3095        characterEncoding = _characterEncoding;
3096        XML_ByteTable = XML_gbk_big5_ByteTable;
3097        break;
3098    default:
3099        return 1;
3100    }
3101#endif
3102    return 0;
3103}
3104
3105XMLNode::XMLCharEncoding XMLNode::guessCharEncoding(void *buf, int l,
3106                                                    char useXMLEncodingAttribute) {
3107#ifdef _XMLWIDECHAR
3108    return (XMLCharEncoding)0;
3109#else
3110    if (l < 25) return (XMLCharEncoding)0;
3111    if (guessWideCharChars && (myIsTextWideChar(buf, l))) {
3112        return (XMLCharEncoding)0;
3113    }
3114    unsigned char *b = (unsigned char*)buf;
3115    if ((b[0] == 0xef) && (b[1] == 0xbb) && (b[2] == 0xbf)) {
3116        return char_encoding_UTF8;
3117    }
3118
3119    // Match utf-8 model ?
3120    XMLCharEncoding bestGuess = char_encoding_UTF8;
3121    int i = 0;
3122    while (i < l)
3123        switch (XML_utf8ByteTable[b[i]]) {
3124        case 4:
3125            i++;
3126            if ((i < l) && (b[i]& 0xC0) != 0x80) {
3127                bestGuess = char_encoding_legacy;    // 10bbbbbb ?
3128                i = l;
3129            }
3130        case 3:
3131            i++;
3132            if ((i < l) && (b[i]& 0xC0) != 0x80) {
3133                bestGuess = char_encoding_legacy;    // 10bbbbbb ?
3134                i = l;
3135            }
3136        case 2:
3137            i++;
3138            if ((i < l) && (b[i]& 0xC0) != 0x80) {
3139                bestGuess = char_encoding_legacy;    // 10bbbbbb ?
3140                i = l;
3141            }
3142        case 1:
3143            i++;
3144            break;
3145        case 0:
3146            i = l;
3147        }
3148    if (!useXMLEncodingAttribute) return bestGuess;
3149    // if encoding is specified and different from utf-8 than it's non-utf8
3150    // otherwise it's utf-8
3151    char bb[201];
3152    l = mmin(l, 200);
3153    memcpy(bb, buf, l); // copy buf into bb to be able to do "bb[l]=0"
3154    bb[l] = 0;
3155    b = (unsigned char*)strstr(bb, "encoding");
3156    if (!b) return bestGuess;
3157    b += 8;
3158    while XML_isSPACECHAR(*b) b++;
3159    if (*b != '=') return bestGuess;
3160    b++;
3161    while XML_isSPACECHAR(*b) b++;
3162    if ((*b != '\'') && (*b != '"')) return bestGuess;
3163    b++;
3164    while XML_isSPACECHAR(*b) b++;
3165
3166    if ((xstrnicmp((char*)b, "utf-8", 5) == 0) ||
3167            (xstrnicmp((char*)b, "utf8", 4) == 0)) {
3168        if (bestGuess == char_encoding_legacy) return char_encoding_error;
3169        return char_encoding_UTF8;
3170    }
3171
3172    if ((xstrnicmp((char*)b, "shiftjis", 8) == 0) ||
3173            (xstrnicmp((char*)b, "shift-jis", 9) == 0) ||
3174            (xstrnicmp((char*)b, "sjis", 4) == 0)) return char_encoding_ShiftJIS;
3175
3176    if (xstrnicmp((char*)b, "GB2312", 6) == 0) return char_encoding_GB2312;
3177    if (xstrnicmp((char*)b, "Big5", 4) == 0) return char_encoding_Big5;
3178    if (xstrnicmp((char*)b, "GBK", 3) == 0) return char_encoding_GBK;
3179
3180    return char_encoding_legacy;
3181#endif
3182}
3183#undef XML_isSPACECHAR
3184
3185//////////////////////////////////////////////////////////
3186//      Here starts the base64 conversion functions.    //
3187//////////////////////////////////////////////////////////
3188
3189// used to mark partial words at the end
3190static const char base64Fillchar = _CXML('=');
3191
3192// this lookup table defines the base64 encoding
3193XMLCSTR base64EncodeTable = _CXML("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
3194
3195// Decode Table gives the index of any valid base64 character in the Base64 table]
3196// 96: '='  -   97: space char   -   98: illegal char   -   99: end of string
3197const unsigned char base64DecodeTable[] = {
3198    99, 98, 98, 98, 98, 98, 98, 98, 98, 97,  97, 98, 98, 97, 98, 98, 98, 98, 98, 98,  98, 98, 98, 98, 98, 98, 98, 98, 98, 98,  //00 -29
3199    98, 98, 97, 98, 98, 98, 98, 98, 98, 98,  98, 98, 98, 62, 98, 98, 98, 63, 52, 53,  54, 55, 56, 57, 58, 59, 60, 61, 98, 98,  //30 -59
3200    98, 96, 98, 98, 98, 0, 1, 2, 3, 4,   5, 6, 7, 8, 9, 10, 11, 12, 13, 14,  15, 16, 17, 18, 19, 20, 21, 22, 23, 24,  //60 -89
3201    25, 98, 98, 98, 98, 98, 98, 26, 27, 28,  29, 30, 31, 32, 33, 34, 35, 36, 37, 38,  39, 40, 41, 42, 43, 44, 45, 46, 47, 48,  //90 -119
3202    49, 50, 51, 98, 98, 98, 98, 98, 98, 98,  98, 98, 98, 98, 98, 98, 98, 98, 98, 98,  98, 98, 98, 98, 98, 98, 98, 98, 98, 98,  //120 -149
3203    98, 98, 98, 98, 98, 98, 98, 98, 98, 98,  98, 98, 98, 98, 98, 98, 98, 98, 98, 98,  98, 98, 98, 98, 98, 98, 98, 98, 98, 98,  //150 -179
3204    98, 98, 98, 98, 98, 98, 98, 98, 98, 98,  98, 98, 98, 98, 98, 98, 98, 98, 98, 98,  98, 98, 98, 98, 98, 98, 98, 98, 98, 98,  //180 -209
3205    98, 98, 98, 98, 98, 98, 98, 98, 98, 98,  98, 98, 98, 98, 98, 98, 98, 98, 98, 98,  98, 98, 98, 98, 98, 98, 98, 98, 98, 98,  //210 -239
3206    98, 98, 98, 98, 98, 98, 98, 98, 98, 98,  98, 98, 98, 98, 98, 98                                 //240 -255
3207};
3208
3209XMLParserBase64Tool::~XMLParserBase64Tool() {
3210    freeBuffer();
3211}
3212
3213void XMLParserBase64Tool::freeBuffer() {
3214    if (buf) free(buf);
3215    buf = NULL;
3216    buflen = 0;
3217}
3218
3219int XMLParserBase64Tool::encodeLength(int inlen, char formatted) {
3220    unsigned int i = ((inlen - 1) / 3 * 4 + 4 + 1);
3221    if (formatted) i += inlen / 54;
3222    return i;
3223}
3224
3225XMLSTR XMLParserBase64Tool::encode(unsigned char *inbuf, unsigned int inlen,
3226                                   char formatted) {
3227    int i = encodeLength(inlen, formatted), k = 17, eLen = inlen / 3, j;
3228    alloc(i*sizeof(XMLCHAR));
3229    XMLSTR curr = (XMLSTR)buf;
3230    for (i = 0; i < eLen; i++) {
3231        // Copy next three bytes into lower 24 bits of int, paying attention to sign.
3232        j = (inbuf[0] << 16) | (inbuf[1] << 8) | inbuf[2];
3233        inbuf += 3;
3234        // Encode the int into four chars
3235        *(curr++) = base64EncodeTable[ j>>18      ];
3236        *(curr++) = base64EncodeTable[(j>>12)&0x3f];
3237        *(curr++) = base64EncodeTable[(j>> 6)&0x3f];
3238        *(curr++) = base64EncodeTable[(j    )&0x3f];
3239        if (formatted) {
3240            if (!k) {
3241                *(curr++) = _CXML('\n');
3242                k = 18;
3243            }
3244            k--;
3245        }
3246    }
3247    eLen = inlen - eLen * 3; // 0 - 2.
3248    if (eLen == 1) {
3249        *(curr++) = base64EncodeTable[ inbuf[0] >> 2      ];
3250        *(curr++) = base64EncodeTable[(inbuf[0] << 4) & 0x3F];
3251        *(curr++) = base64Fillchar;
3252        *(curr++) = base64Fillchar;
3253    } else if (eLen == 2) {
3254        j = (inbuf[0] << 8) | inbuf[1];
3255        *(curr++) = base64EncodeTable[ j>>10      ];
3256        *(curr++) = base64EncodeTable[(j>> 4)&0x3f];
3257        *(curr++) = base64EncodeTable[(j<< 2)&0x3f];
3258        *(curr++) = base64Fillchar;
3259    }
3260    *(curr++) = 0;
3261    return (XMLSTR)buf;
3262}
3263
3264unsigned int XMLParserBase64Tool::decodeSize(XMLCSTR data, XMLError *xe) {
3265    if (xe) *xe = eXMLErrorNone;
3266    int size = 0;
3267    unsigned char c;
3268    //skip any extra characters (e.g. newlines or spaces)
3269    while (*data) {
3270#ifdef _XMLWIDECHAR
3271        if (*data > 255) {
3272            if (xe) *xe = eXMLErrorBase64DecodeIllegalCharacter;
3273            return 0;
3274        }
3275#endif
3276        c = base64DecodeTable[(unsigned char)(*data)];
3277        if (c < 97) size++;
3278        else if (c == 98) {
3279            if (xe) *xe = eXMLErrorBase64DecodeIllegalCharacter;
3280            return 0;
3281        }
3282        data++;
3283    }
3284    if (xe && (size % 4 != 0)) *xe = eXMLErrorBase64DataSizeIsNotMultipleOf4;
3285    if (size == 0) return 0;
3286    do {
3287        data--;
3288        size--;
3289    } while (*data == base64Fillchar);
3290    size++;
3291    return (unsigned int)((size*3) / 4);
3292}
3293
3294unsigned char XMLParserBase64Tool::decode(XMLCSTR data, unsigned char *buf,
3295                                          int len, XMLError *xe) {
3296    if (xe) *xe = eXMLErrorNone;
3297    int i = 0, p = 0;
3298    unsigned char d, c;
3299    for (;;) {
3300
3301#ifdef _XMLWIDECHAR
3302#define BASE64DECODE_READ_NEXT_CHAR(c)                                              \
3303        do {                                                                        \
3304            if (data[i]>255){ c=98; break; }                                        \
3305            c=base64DecodeTable[(unsigned char)data[i++]];                       \
3306        }while (c==97);                                                             \
3307        if(c==98){ if(xe)*xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; }
3308#else
3309#define BASE64DECODE_READ_NEXT_CHAR(c)                                           \
3310        do { c=base64DecodeTable[(unsigned char)data[i++]]; }while (c==97);   \
3311        if(c==98){ if(xe)*xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; }
3312#endif
3313
3314        BASE64DECODE_READ_NEXT_CHAR(c)
3315        if (c == 99) {
3316            return 2;
3317        }
3318        if (c == 96) {
3319            if (p == (int)len) return 2;
3320            if (xe) *xe = eXMLErrorBase64DecodeTruncatedData;
3321            return 1;
3322        }
3323
3324        BASE64DECODE_READ_NEXT_CHAR(d)
3325        if ((d == 99) || (d == 96)) {
3326            if (xe) *xe = eXMLErrorBase64DecodeTruncatedData;
3327            return 1;
3328        }
3329        if (p == (int)len) {
3330            if (xe) *xe = eXMLErrorBase64DecodeBufferTooSmall;
3331            return 0;
3332        }
3333        buf[p++] = (unsigned char)((c << 2) | ((d >> 4) & 0x3));
3334
3335        BASE64DECODE_READ_NEXT_CHAR(c)
3336        if (c == 99) {
3337            if (xe) *xe = eXMLErrorBase64DecodeTruncatedData;
3338            return 1;
3339        }
3340        if (p == (int)len) {
3341            if (c == 96) return 2;
3342            if (xe) *xe = eXMLErrorBase64DecodeBufferTooSmall;
3343            return 0;
3344        }
3345        if (c == 96) {
3346            if (xe) *xe = eXMLErrorBase64DecodeTruncatedData;
3347            return 1;
3348        }
3349        buf[p++] = (unsigned char)(((d << 4) & 0xf0) | ((c >> 2) & 0xf));
3350
3351        BASE64DECODE_READ_NEXT_CHAR(d)
3352        if (d == 99 ) {
3353            if (xe) *xe = eXMLErrorBase64DecodeTruncatedData;
3354            return 1;
3355        }
3356        if (p == (int)len) {
3357            if (d == 96) return 2;
3358            if (xe) *xe = eXMLErrorBase64DecodeBufferTooSmall;
3359            return 0;
3360        }
3361        if (d == 96) {
3362            if (xe) *xe = eXMLErrorBase64DecodeTruncatedData;
3363            return 1;
3364        }
3365        buf[p++] = (unsigned char)(((c << 6) & 0xc0) | d);
3366    }
3367}
3368#undef BASE64DECODE_READ_NEXT_CHAR
3369
3370void XMLParserBase64Tool::alloc(int newsize) {
3371    if ((!buf) && (newsize)) {
3372        buf = malloc(newsize);
3373        buflen = newsize;
3374        return;
3375    }
3376    if (newsize > buflen) {
3377        buf = realloc(buf, newsize);
3378        buflen = newsize;
3379    }
3380}
3381
3382unsigned char *XMLParserBase64Tool::decode(XMLCSTR data, int *outlen, XMLError *xe) {
3383    if (xe) *xe = eXMLErrorNone;
3384    unsigned int len = decodeSize(data, xe);
3385    if (outlen) *outlen = len;
3386    if (!len) return NULL;
3387    alloc(len + 1);
3388    if (!decode(data, (unsigned char*)buf, len, xe)) {
3389        return NULL;
3390    }
3391    return (unsigned char*)buf;
3392}
3393
3394