source: GTP/trunk/App/Demos/Illum/pathmap/xmlParser.cpp @ 2197

Revision 2197, 91.2 KB checked in by szirmay, 17 years ago (diff)
Line 
1#include "dxstdafx.h"
2/**
3 ****************************************************************************
4 * <P> XML.c - implementation file for basic XML parser written in ANSI C++
5 * for portability. It works by using recursion and a node tree for breaking
6 * down the elements of an XML document.  </P>
7 *
8 * @version     V2.20
9 * @author      Frank Vanden Bergh²en
10 *
11 * NOTE:
12 *
13 *   If you add "#define STRICT_PARSING", on the first line of this file
14 *   the parser will see the following XML-stream:
15 *      <a><b>some text</b><b>other text    </a>
16 *   as an error. Otherwise, this tring will be equivalent to:
17 *      <a><b>some text</b><b>other text</b></a>
18 *
19 * NOTE:
20 *
21 *   If you add "#define APPROXIMATE_PARSING" on the first line of this file
22 *   the parser will see the following XML-stream:
23 *     <data name="n1">
24 *     <data name="n2">
25 *     <data name="n3" />
26 *   as equivalent to the following XML-stream:
27 *     <data name="n1" />
28 *     <data name="n2" />
29 *     <data name="n3" />
30 *   This can be useful for badly-formed XML-streams but prevent the use
31 *   of the following XML-stream (problem is: tags at contiguous levels
32 *   have the same names):
33 *     <data name="n1">
34 *        <data name="n2">
35 *            <data name="n3" />
36 *        </data>
37 *     </data>
38 *
39 * NOTE:
40 *
41 *   If you add "#define _XMLPARSER_NO_MESSAGEBOX_" on the first line of this file
42 *   the "openFileHelper" function will always display error messages inside the
43 *   console instead of inside a message-box-window. Message-box-windows are
44 *   available on windows 9x/NT/2000/XP/Vista only.
45 *
46 * BSD license:
47 * Copyright (c) 2002, Frank Vanden Berghen
48 * All rights reserved.
49 * Redistribution and use in source and binary forms, with or without
50 * modification, are permitted provided that the following conditions are met:
51 *
52 *     * Redistributions of source code must retain the above copyright
53 *       notice, this list of conditions and the following disclaimer.
54 *     * Redistributions in binary form must reproduce the above copyright
55 *       notice, this list of conditions and the following disclaimer in the
56 *       documentation and/or other materials provided with the distribution.
57 *     * Neither the name of the Frank Vanden Berghen nor the
58 *       names of its contributors may be used to endorse or promote products
59 *       derived from this software without specific prior written permission.
60 *
61 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
62 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
63 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
64 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
65 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
66 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
67 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
68 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
69 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
70 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
71 *
72 ****************************************************************************
73 */
74#ifndef _CRT_SECURE_NO_DEPRECATE
75#define _CRT_SECURE_NO_DEPRECATE
76#endif
77#include "xmlParser.h"
78#ifdef _XMLWINDOWS
79//#ifdef _DEBUG
80//#define _CRTDBG_MAP_ALLOC
81//#include <crtdbg.h>
82//#endif
83#define WIN32_LEAN_AND_MEAN
84#include <Windows.h> // to have IsTextUnicode, MultiByteToWideChar, WideCharToMultiByte to handle unicode files
85                     // to have "MessageBoxA" to display error messages for openFilHelper
86#endif
87
88#include <memory.h>
89#include <assert.h>
90#include <stdio.h>
91#include <string.h>
92#include <stdlib.h>
93
94LPCTSTR XMLNode::getVersion() { return _T("v2.20"); }
95void free_XMLDLL(void *t){free(t);}
96
97static char strictUTF8Parsing=1, guessUnicodeChars=1, dropWhiteSpace=1;
98
99inline int mmin( const int t1, const int t2 ) { return t1 < t2 ? t1 : t2; }
100
101// You can modify the initialization of the variable "XMLClearTags" below
102// to change the clearTags that are currently recognized by the library.
103// The number on the second columns is the length of the string inside the
104// first column. The "<!DOCTYPE" declaration must be the second in the list.
105static ALLXMLClearTag XMLClearTags[] =
106{
107    {    _T("<![CDATA["),9,  _T("]]>")      },
108    {    _T("<!DOCTYPE"),9,  _T(">")        },
109    {    _T("<PRE>")    ,5,  _T("</PRE>")   },
110    {    _T("<Script>") ,8,  _T("</Script>")},
111    {    _T("<!--")     ,4,  _T("-->")      },
112    {    NULL           ,0,  NULL           }
113};
114ALLXMLClearTag* XMLNode::getClearTagTable() { return XMLClearTags; }
115
116// You can modify the initialization of the variable "XMLEntities" below
117// to change the character entities that are currently recognized by the library.
118// The number on the second columns is the length of the string inside the
119// first column. Additionally, the syntaxes "&#xA0;" and "&#160;" are recognized.
120typedef struct { XMLCSTR s; int l; XMLCHAR c;} XMLCharacterEntity;
121static XMLCharacterEntity XMLEntities[] =
122{
123    { _T("&amp;" ), 5, _T('&' )},
124    { _T("&lt;"  ), 4, _T('<' )},
125    { _T("&gt;"  ), 4, _T('>' )},
126    { _T("&quot;"), 6, _T('\"')},
127    { _T("&apos;"), 6, _T('\'')},
128    { NULL        , 0, '\0'    }
129};
130
131// When rendering the XMLNode to a string (using the "createXMLString" function),
132// you can ask for a beautiful formatting. This formatting is using the
133// following indentation character:
134#define INDENTCHAR _T('\t')
135
136// The following function parses the XML errors into a user friendly string.
137// You can edit this to change the output language of the library to something else.
138XMLCSTR XMLNode::getError(XMLError xerror)
139{
140    switch (xerror)
141    {
142    case eXMLErrorNone:                  return _T("No error");
143    case eXMLErrorMissingEndTag:         return _T("Warning: Unmatched end tag");
144    case eXMLErrorEmpty:                 return _T("Error: No XML data");
145    case eXMLErrorFirstNotStartTag:      return _T("Error: First token not start tag");
146    case eXMLErrorMissingTagName:        return _T("Error: Missing start tag name");
147    case eXMLErrorMissingEndTagName:     return _T("Error: Missing end tag name");
148    case eXMLErrorNoMatchingQuote:       return _T("Error: Unmatched quote");
149    case eXMLErrorUnmatchedEndTag:       return _T("Error: Unmatched end tag");
150    case eXMLErrorUnmatchedEndClearTag:  return _T("Error: Unmatched clear tag end");
151    case eXMLErrorUnexpectedToken:       return _T("Error: Unexpected token found");
152    case eXMLErrorInvalidTag:            return _T("Error: Invalid tag found");
153    case eXMLErrorNoElements:            return _T("Error: No elements found");
154    case eXMLErrorFileNotFound:          return _T("Error: File not found");
155    case eXMLErrorFirstTagNotFound:      return _T("Error: First Tag not found");
156    case eXMLErrorUnknownCharacterEntity:return _T("Error: Unknown character entity");
157    case eXMLErrorCharConversionError:   return _T("Error: unable to convert between UNICODE and MultiByte chars");
158    case eXMLErrorCannotOpenWriteFile:   return _T("Error: unable to open file for writing");
159    case eXMLErrorCannotWriteFile:       return _T("Error: cannot write into file");
160
161    case eXMLErrorBase64DataSizeIsNotMultipleOf4: return _T("Warning: Base64-string length is not a multiple of 4");
162    case eXMLErrorBase64DecodeTruncatedData:      return _T("Warning: Base64-string is truncated");
163    case eXMLErrorBase64DecodeIllegalCharacter:   return _T("Error: Base64-string contains an illegal character");
164    case eXMLErrorBase64DecodeBufferTooSmall:     return _T("Error: Base64 decode output buffer is too small");
165    };
166    return _T("Unknown");
167}
168
169// Here is an abstraction layer to access some common string manipulation functions.
170// The abstraction layer is currently working for gcc, Microsoft Visual Studio 6.0,
171// Microsoft Visual Studio .NET, CC (sun compiler) and Borland C++.
172// If you plan to "port" the library to a new system/compiler, all you have to do is
173// to edit the following lines.
174#ifdef XML_NO_WIDE_CHAR
175char myIsTextUnicode(const void *b, int len) { return FALSE; }
176#else
177    #if defined (UNDER_CE) || !defined(WIN32)
178    char myIsTextUnicode(const void *b, int len) // inspired by the Wine API: RtlIsTextUnicode
179    {
180        const wchar_t *s=(const wchar_t*)b;
181
182        // buffer too small:
183        if (len<(int)sizeof(wchar_t)) return FALSE;
184
185        // odd length test
186        if (len&1) return FALSE;
187
188        /* only checks the first 256 characters */
189        len=mmin(256,len/sizeof(wchar_t));
190
191        // Check for the special byte order:
192        if (*s == 0xFFFE) return FALSE;     // IS_TEXT_UNICODE_REVERSE_SIGNATURE;
193        if (*s == 0xFEFF) return TRUE;      // IS_TEXT_UNICODE_SIGNATURE
194
195        // checks for ASCII characters in the UNICODE stream
196        int i,stats=0;
197        for (i=0; i<len; i++) if (s[i]<=(unsigned short)255) stats++;
198        if (stats>len/2) return TRUE;
199
200        // Check for UNICODE NULL chars
201        for (i=0; i<len; i++) if (!s[i]) return TRUE;
202
203        return FALSE;
204    }
205    #else
206    char myIsTextUnicode(const void *b,int l) { return (char)IsTextUnicode((CONST LPVOID)b,l,NULL); };
207    #endif
208#endif
209
210#ifdef _XMLWINDOWS
211// for Microsoft Visual Studio 6.0 and Microsoft Visual Studio .NET,
212    #ifdef _XMLUNICODE
213        wchar_t *myMultiByteToWideChar(const char *s,int l)
214        {
215            int i;
216            if (strictUTF8Parsing)  i=(int)MultiByteToWideChar(CP_UTF8,0             ,s,l,NULL,0);
217            else                    i=(int)MultiByteToWideChar(CP_ACP ,MB_PRECOMPOSED,s,l,NULL,0);
218            if (i<0) return NULL;
219            wchar_t *d=(wchar_t *)malloc((i+1)*sizeof(XMLCHAR));
220            if (strictUTF8Parsing)  i=(int)MultiByteToWideChar(CP_UTF8,0             ,s,l,d,i);
221            else                    i=(int)MultiByteToWideChar(CP_ACP ,MB_PRECOMPOSED,s,l,d,i);
222            d[i]=0;
223            return d;
224        }
225    #else
226        char *myWideCharToMultiByte(const wchar_t *s,int l)
227        {
228            UINT codePage=CP_ACP; if (strictUTF8Parsing) codePage=CP_UTF8;
229            int i=(int)WideCharToMultiByte(codePage,  // code page
230                0,                       // performance and mapping flags
231                s,                       // wide-character string
232                l,                       // number of chars in string
233                NULL,                       // buffer for new string
234                0,                       // size of buffer
235                NULL,                    // default for unmappable chars
236                NULL                     // set when default char used
237                );
238            if (i<0) return NULL;
239            char *d=(char*)malloc(i+1);
240            WideCharToMultiByte(codePage,  // code page
241                0,                       // performance and mapping flags
242                s,                       // wide-character string
243                l,                       // number of chars in string
244                d,                       // buffer for new string
245                i,                       // size of buffer
246                NULL,                    // default for unmappable chars
247                NULL                     // set when default char used
248                );
249            d[i]=0;
250            return d;
251        }
252    #endif
253    #ifdef __BORLANDC__
254    int _strnicmp(char *c1, char *c2, int l){ return strnicmp(c1,c2,l);}
255    #endif
256#else
257// for gcc and CC
258    #ifdef XML_NO_WIDE_CHAR
259        char *myWideCharToMultiByte(const wchar_t *s, int l) { return NULL; }
260    #else
261        char *myWideCharToMultiByte(const wchar_t *s, int l)
262        {
263            const wchar_t *ss=s;
264            int i=(int)wcsrtombs(NULL,&ss,0,NULL);
265            if (i<0) return NULL;
266            char *d=(char *)malloc(i+1);
267            wcsrtombs(d,&s,i,NULL);
268            d[i]=0;
269            return d;
270        }
271    #endif
272    #ifdef _XMLUNICODE
273        wchar_t *myMultiByteToWideChar(const char *s, int l)
274        {
275            const char *ss=s;
276            int i=(int)mbsrtowcs(NULL,&ss,0,NULL);
277            if (i<0) return NULL;
278            wchar_t *d=(wchar_t *)malloc((i+1)*sizeof(wchar_t));
279            mbsrtowcs(d,&s,l,NULL);
280            d[i]=0;
281            return d;
282        }
283        int _tcslen(XMLCSTR c)   { return wcslen(c); }
284        #ifdef sun
285        // for CC
286           #include <widec.h>
287           int _tcsnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wsncasecmp(c1,c2,l);}
288           int _tcsicmp(XMLCSTR c1, XMLCSTR c2) { return wscasecmp(c1,c2); }
289        #else
290        // for gcc
291           int _tcsnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncasecmp(c1,c2,l);}
292           int _tcsicmp(XMLCSTR c1, XMLCSTR c2) { return wcscasecmp(c1,c2); }
293        #endif
294        XMLSTR _tcsstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)wcsstr(c1,c2); }
295        XMLSTR _tcscpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)wcscpy(c1,c2); }
296        FILE *_tfopen(XMLCSTR filename,XMLCSTR mode)
297        {
298            char *filenameAscii=myWideCharToMultiByte(filename,0);
299            FILE *f;
300            if (mode[0]==_T('r')) f=fopen(filenameAscii,"rb");
301            else                  f=fopen(filenameAscii,"wb");
302            free(filenameAscii);
303            return f;
304        }
305    #else
306        FILE *_tfopen(XMLCSTR filename,XMLCSTR mode) { return fopen(filename,mode); }
307        int _tcslen(XMLCSTR c)   { return strlen(c); }
308        int _tcsnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncasecmp(c1,c2,l);}
309        int _tcsicmp(XMLCSTR c1, XMLCSTR c2) { return strcasecmp(c1,c2); }
310        XMLSTR _tcsstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)strstr(c1,c2); }
311        XMLSTR _tcscpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)strcpy(c1,c2); }
312    #endif
313    int _strnicmp(const char *c1,const char *c2, int l) { return strncasecmp(c1,c2,l);}
314#endif
315
316/////////////////////////////////////////////////////////////////////////
317//      Here start the core implementation of the XMLParser library    //
318/////////////////////////////////////////////////////////////////////////
319
320// You should normally not change anything below this point.
321// For your own information, I suggest that you read the openFileHelper below:
322XMLNode XMLNode::openFileHelper(XMLCSTR filename, XMLCSTR tag)
323{
324    // guess the value of the global parameter "strictUTF8Parsing"
325    // (the guess is based on the first 200 bytes of the file).
326    FILE *f=_tfopen(filename,_T("rb"));
327    if (f)
328    {
329        char bb[205];
330        int l=(int)fread(bb,1,200,f);
331        setGlobalOptions(guessUnicodeChars,guessUTF8ParsingParameterValue(bb,l),dropWhiteSpace);
332        fclose(f);
333    }
334
335    // parse the file
336    XMLResults pResults;
337    XMLNode xnode=XMLNode::parseFile(filename,tag,&pResults);
338
339    // display error message (if any)
340    if (pResults.error != eXMLErrorNone)
341    {
342        // create message
343        char message[2000],*s1=(char*)"",*s3=(char*)""; XMLCSTR s2=_T("");
344        if (pResults.error==eXMLErrorFirstTagNotFound) { s1=(char*)"First Tag should be '"; s2=tag; s3=(char*)"'.\n"; }
345        sprintf(message,
346#ifdef _XMLUNICODE
347            "XML Parsing error inside file '%S'.\n%S\nAt line %i, column %i.\n%s%S%s"
348#else
349            "XML Parsing error inside file '%s'.\n%s\nAt line %i, column %i.\n%s%s%s"
350#endif
351            ,filename,XMLNode::getError(pResults.error),pResults.nLine,pResults.nColumn,s1,s2,s3);
352
353        // display message
354#if defined(WIN32) && !defined(UNDER_CE) && !defined(_XMLPARSER_NO_MESSAGEBOX_)
355        MessageBoxA(NULL,message,"XML Parsing error",MB_OK|MB_ICONERROR|MB_TOPMOST);
356#else
357        printf("%s",message);
358#endif
359        exit(255);
360    }
361    return xnode;
362}
363
364#ifndef _XMLUNICODE
365// If "strictUTF8Parsing=0" then we assume that all characters have the same length of 1 byte.
366// If "strictUTF8Parsing=1" then the characters have different lengths (from 1 byte to 4 bytes).
367// This table is used as lookup-table to know the length of a character (in byte) based on the
368// content of the first byte of the character.
369// (note: if you modify this, you must always have XML_utf8ByteTable[0]=0 ).
370static const char XML_utf8ByteTable[256] =
371{
372    //  0 1 2 3 4 5 6 7 8 9 a b c d e f
373    0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00
374    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10
375    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20
376    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30
377    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40
378    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50
379    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60
380    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70End of ASCII range
381    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x80 0x80 to 0xc1 invalid
382    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x90
383    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xa0
384    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xb0
385    1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0 0xc2 to 0xdf 2 byte
386    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0
387    3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,// 0xe0 0xe0 to 0xef 3 byte
388    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
389};
390static const char XML_asciiByteTable[256] =
391{
392    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,
393    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,
394    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,
395    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,
396    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,
397    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
398};
399static const char *XML_ByteTable=(const char *)XML_utf8ByteTable; // the default is "strictUTF8Parsing=1"
400#endif
401
402XMLError XMLNode::writeToFile(XMLCSTR filename, const char *encoding, char nFormat) const
403{
404    int i;
405    XMLSTR t=createXMLString(nFormat,&i);
406    FILE *f=_tfopen(filename,_T("wb"));
407    if (!f) return eXMLErrorCannotOpenWriteFile;
408#ifdef _XMLUNICODE
409    unsigned char h[2]={ 0xFF, 0xFE };
410    if (!fwrite(h,2,1,f)) return eXMLErrorCannotWriteFile;
411    if (!isDeclaration())
412    {
413        if (!fwrite(_T("<?xml version=\"1.0\" encoding=\"utf-16\"?>\n"),sizeof(wchar_t)*40,1,f))
414            return eXMLErrorCannotWriteFile;
415    }
416#else
417    if (!isDeclaration())
418    {
419        if ((!encoding)||(XML_ByteTable==XML_utf8ByteTable))
420        {
421            // header so that windows recognize the file as UTF-8:
422            unsigned char h[3]={0xEF,0xBB,0xBF};
423            if (!fwrite(h,3,1,f)) return eXMLErrorCannotWriteFile;
424            if (!fwrite("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",39,1,f)) return eXMLErrorCannotWriteFile;
425        }
426        else
427            if (fprintf(f,"<?xml version=\"1.0\" encoding=\"%s\"?>\n",encoding)<0) return eXMLErrorCannotWriteFile;
428    } else
429    {
430        if (XML_ByteTable==XML_utf8ByteTable) // test if strictUTF8Parsing==1"
431        {
432            unsigned char h[3]={0xEF,0xBB,0xBF}; if (!fwrite(h,3,1,f)) return eXMLErrorCannotWriteFile;
433        }
434    }
435#endif
436    if (!fwrite(t,sizeof(XMLCHAR)*i,1,f)) return eXMLErrorCannotWriteFile;
437    if (fclose(f)!=0) return eXMLErrorCannotWriteFile;
438    free(t);
439    return eXMLErrorNone;
440}
441
442// Duplicate a given string.
443XMLSTR stringDup(XMLCSTR lpszData, int cbData)
444{
445    if (lpszData==NULL) return NULL;
446
447    XMLSTR lpszNew;
448    if (cbData==0) cbData=(int)_tcslen(lpszData);
449    lpszNew = (XMLSTR)malloc((cbData+1) * sizeof(XMLCHAR));
450    if (lpszNew)
451    {
452        memcpy(lpszNew, lpszData, (cbData) * sizeof(XMLCHAR));
453        lpszNew[cbData] = (XMLCHAR)NULL;
454    }
455    return lpszNew;
456}
457
458XMLNode XMLNode::emptyXMLNode;
459XMLClear XMLNode::emptyXMLClear={ NULL, NULL, NULL};
460XMLAttribute XMLNode::emptyXMLAttribute={ NULL, NULL};
461
462// Enumeration used to decipher what type a token is
463typedef enum XMLTokenTypeTag
464{
465    eTokenText = 0,
466    eTokenQuotedText,
467    eTokenTagStart,         /* "<"            */
468    eTokenTagEnd,           /* "</"           */
469    eTokenCloseTag,         /* ">"            */
470    eTokenEquals,           /* "="            */
471    eTokenDeclaration,      /* "<?"           */
472    eTokenShortHandClose,   /* "/>"           */
473    eTokenClear,
474    eTokenError
475} XMLTokenType;
476
477// Main structure used for parsing XML
478typedef struct XML
479{
480    XMLCSTR                lpXML;
481    XMLCSTR                lpszText;
482    int                    nIndex,nIndexMissigEndTag;
483    enum XMLError          error;
484    XMLCSTR                lpEndTag;
485    int                    cbEndTag;
486    XMLCSTR                lpNewElement;
487    int                    cbNewElement;
488    int                    nFirst;
489} XML;
490
491typedef struct
492{
493    ALLXMLClearTag *pClr;
494    XMLCSTR     pStr;
495} NextToken;
496
497// Enumeration used when parsing attributes
498typedef enum Attrib
499{
500    eAttribName = 0,
501    eAttribEquals,
502    eAttribValue
503} Attrib;
504
505// Enumeration used when parsing elements to dictate whether we are currently
506// inside a tag
507typedef enum Status
508{
509    eInsideTag = 0,
510    eOutsideTag
511} Status;
512
513// private (used while rendering):
514XMLSTR toXMLString(XMLSTR dest,XMLCSTR source)
515{
516    XMLSTR dd=dest;
517    XMLCHAR ch;
518    XMLCharacterEntity *entity;
519    while ((ch=*source))
520    {
521        entity=XMLEntities;
522        do
523        {
524            if (ch==entity->c) {_tcscpy(dest,entity->s); dest+=entity->l; source++; goto out_of_loop1; }
525            entity++;
526        } while(entity->s);
527#ifdef _XMLUNICODE
528        *(dest++)=*(source++);
529#else
530        switch(XML_ByteTable[(unsigned char)ch])
531        {
532        case 4: *(dest++)=*(source++);
533        case 3: *(dest++)=*(source++);
534        case 2: *(dest++)=*(source++);
535        case 1: *(dest++)=*(source++);
536        }
537#endif
538out_of_loop1:
539        ;
540    }
541    *dest=0;
542    return dd;
543}
544
545// private (used while rendering):
546int lengthXMLString(XMLCSTR source)
547{
548    int r=0;
549    XMLCharacterEntity *entity;
550    XMLCHAR ch;
551    while ((ch=*source))
552    {
553        entity=XMLEntities;
554        do
555        {
556            if (ch==entity->c) { r+=entity->l; source++; goto out_of_loop1; }
557            entity++;
558        } while(entity->s);
559#ifdef _XMLUNICODE
560        r++; source++;
561#else
562        ch=XML_ByteTable[(unsigned char)ch]; r+=ch; source+=ch;
563#endif
564out_of_loop1:
565        ;
566    }
567    return r;
568}
569
570XMLSTR toXMLString(XMLCSTR source)
571{
572    XMLSTR dest=(XMLSTR)malloc((lengthXMLString(source)+1)*sizeof(XMLCHAR));
573    return toXMLString(dest,source);
574}
575
576XMLSTR toXMLStringFast(XMLSTR *dest,int *destSz, XMLCSTR source)
577{
578    int l=lengthXMLString(source)+1;
579    if (l>*destSz) { *destSz=l; *dest=(XMLSTR)realloc(*dest,l*sizeof(XMLCHAR)); }
580    return toXMLString(*dest,source);
581}
582
583// private:
584XMLSTR fromXMLString(XMLCSTR s, int lo, XML *pXML)
585{
586    // This function is the opposite of the function "toXMLString". It decodes the escape
587    // sequences &amp;, &quot;, &apos;, &lt;, &gt; and replace them by the characters
588    // &,",',<,>. This function is used internally by the XML Parser. All the calls to
589    // the XML library will always gives you back "decoded" strings.
590    //
591    // in: string (s) and length (lo) of string
592    // out:  new allocated string converted from xml
593    if (!s) return NULL;
594
595    int ll=0,j;
596    XMLSTR d;
597    XMLCSTR ss=s;
598    XMLCharacterEntity *entity;
599    while ((lo>0)&&(*s))
600    {
601        if (*s==_T('&'))
602        {
603            if ((lo>2)&&(s[1]==_T('#')))
604            {
605                s+=2; lo-=2;
606                if ((*s==_T('X'))||(*s==_T('x'))) { s++; lo--; }
607                while ((*s)&&(*s!=_T(';'))&&((lo--)>0)) s++;
608                if (*s!=_T(';'))
609                {
610                    pXML->error=eXMLErrorUnknownCharacterEntity;
611                    return NULL;
612                }
613                s++; lo--;
614            } else
615            {
616                entity=XMLEntities;
617                do
618                {
619                    if ((lo>=entity->l)&&(_tcsnicmp(s,entity->s,entity->l)==0)) { s+=entity->l; lo-=entity->l; break; }
620                    entity++;
621                } while(entity->s);
622                if (!entity->s)
623                {
624                    pXML->error=eXMLErrorUnknownCharacterEntity;
625                    return NULL;
626                }
627            }
628        } else
629        {
630#ifdef _XMLUNICODE
631            s++; lo--;
632#else
633            j=XML_ByteTable[(unsigned char)*s]; s+=j; lo-=j; ll+=j-1;
634#endif
635        }
636        ll++;
637    }
638
639    d=(XMLSTR)malloc((ll+1)*sizeof(XMLCHAR));
640    s=d;
641    while (ll-->0)
642    {
643        if (*ss==_T('&'))
644        {
645            if (ss[1]==_T('#'))
646            {
647                ss+=2; j=0;
648                if ((*ss==_T('X'))||(*ss==_T('x')))
649                {
650                    ss++;
651                    while (*ss!=_T(';'))
652                    {
653                        if ((*ss>=_T('0'))&&(*ss<=_T('9'))) j=(j<<4)+*ss-_T('0');
654                        else if ((*ss>=_T('A'))&&(*ss<=_T('F'))) j=(j<<4)+*ss-_T('A')+10;
655                        else if ((*ss>=_T('a'))&&(*ss<=_T('f'))) j=(j<<4)+*ss-_T('a')+10;
656                        else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;}
657                        ss++;
658                    }
659                } else
660                {
661                    while (*ss!=_T(';'))
662                    {
663                        if ((*ss>=_T('0'))&&(*ss<=_T('9'))) j=(j*10)+*ss-_T('0');
664                        else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;}
665                        ss++;
666                    }
667                }
668                (*d++)=(XMLCHAR)j; ss++;
669            } else
670            {
671                entity=XMLEntities;
672                do
673                {
674                    if (_tcsnicmp(ss,entity->s,entity->l)==0) { *(d++)=entity->c; ss+=entity->l; break; }
675                    entity++;
676                } while(entity->s);
677            }
678        } else
679        {
680#ifdef _XMLUNICODE
681            *(d++)=*(ss++);
682#else
683            switch(XML_ByteTable[(unsigned char)*ss])
684            {
685            case 4: *(d++)=*(ss++); ll--;
686            case 3: *(d++)=*(ss++); ll--;
687            case 2: *(d++)=*(ss++); ll--;
688            case 1: *(d++)=*(ss++);
689            }
690#endif
691        }
692    }
693    *d=0;
694    return (XMLSTR)s;
695}
696
697#define XML_isSPACECHAR(ch) ((ch==_T('\n'))||(ch==_T(' '))||(ch== _T('\t'))||(ch==_T('\r')))
698
699// private:
700char myTagCompare(XMLCSTR cclose, XMLCSTR copen)
701// !!!! WARNING strange convention&:
702// return 0 if equals
703// return 1 if different
704{
705    if (!cclose) return 1;
706    int l=(int)_tcslen(cclose);
707    if (_tcsnicmp(cclose, copen, l)!=0) return 1;
708    const XMLCHAR c=copen[l];
709    if (XML_isSPACECHAR(c)||
710        (c==_T('/' ))||
711        (c==_T('<' ))||
712        (c==_T('>' ))||
713        (c==_T('=' ))) return 0;
714    return 1;
715}
716
717// Obtain the next character from the string.
718static inline XMLCHAR getNextChar(XML *pXML)
719{
720    XMLCHAR ch = pXML->lpXML[pXML->nIndex];
721#ifdef _XMLUNICODE
722    if (ch!=0) pXML->nIndex++;
723#else
724    pXML->nIndex+=XML_ByteTable[(unsigned char)ch];
725#endif
726    return ch;
727}
728
729// Find the next token in a string.
730// pcbToken contains the number of characters that have been read.
731static NextToken GetNextToken(XML *pXML, int *pcbToken, enum XMLTokenTypeTag *pType)
732{
733    NextToken        result;
734    XMLCHAR            ch;
735    XMLCHAR            chTemp;
736    int              indexStart,nFoundMatch,nIsText=FALSE;
737    result.pClr=NULL; // prevent warning
738
739    // Find next non-white space character
740    do { indexStart=pXML->nIndex; ch=getNextChar(pXML); } while XML_isSPACECHAR(ch);
741
742    if (ch)
743    {
744        // Cache the current string pointer
745        result.pStr = &pXML->lpXML[indexStart];
746
747        // First check whether the token is in the clear tag list (meaning it
748        // does not need formatting).
749        ALLXMLClearTag *ctag=XMLClearTags;
750        do
751        {
752            if (_tcsnicmp(ctag->lpszOpen, result.pStr, ctag->openTagLen)==0)
753            {
754                result.pClr=ctag;
755                pXML->nIndex+=ctag->openTagLen-1;
756                *pType=eTokenClear;
757                return result;
758            }
759            ctag++;
760        } while(ctag->lpszOpen);
761
762        // If we didn't find a clear tag then check for standard tokens
763        switch(ch)
764        {
765        // Check for quotes
766        case _T('\''):
767        case _T('\"'):
768            // Type of token
769            *pType = eTokenQuotedText;
770            chTemp = ch;
771
772            // Set the size
773            nFoundMatch = FALSE;
774
775            // Search through the string to find a matching quote
776            while((ch = getNextChar(pXML)))
777            {
778                if (ch==chTemp) { nFoundMatch = TRUE; break; }
779                if (ch==_T('<')) break;
780            }
781
782            // If we failed to find a matching quote
783            if (nFoundMatch == FALSE)
784            {
785                pXML->nIndex=indexStart+1;
786                nIsText=TRUE;
787                break;
788            }
789
790//  4.02.2002
791//            if (FindNonWhiteSpace(pXML)) pXML->nIndex--;
792
793            break;
794
795        // Equals (used with attribute values)
796        case _T('='):
797            *pType = eTokenEquals;
798            break;
799
800        // Close tag
801        case _T('>'):
802            *pType = eTokenCloseTag;
803            break;
804
805        // Check for tag start and tag end
806        case _T('<'):
807
808            // Peek at the next character to see if we have an end tag '</',
809            // or an xml declaration '<?'
810            chTemp = pXML->lpXML[pXML->nIndex];
811
812            // If we have a tag end...
813            if (chTemp == _T('/'))
814            {
815                // Set the type and ensure we point at the next character
816                getNextChar(pXML);
817                *pType = eTokenTagEnd;
818            }
819
820            // If we have an XML declaration tag
821            else if (chTemp == _T('?'))
822            {
823
824                // Set the type and ensure we point at the next character
825                getNextChar(pXML);
826                *pType = eTokenDeclaration;
827            }
828
829            // Otherwise we must have a start tag
830            else
831            {
832                *pType = eTokenTagStart;
833            }
834            break;
835
836        // Check to see if we have a short hand type end tag ('/>').
837        case _T('/'):
838
839            // Peek at the next character to see if we have a short end tag '/>'
840            chTemp = pXML->lpXML[pXML->nIndex];
841
842            // If we have a short hand end tag...
843            if (chTemp == _T('>'))
844            {
845                // Set the type and ensure we point at the next character
846                getNextChar(pXML);
847                *pType = eTokenShortHandClose;
848                break;
849            }
850
851            // If we haven't found a short hand closing tag then drop into the
852            // text process
853
854        // Other characters
855        default:
856            nIsText = TRUE;
857        }
858
859        // If this is a TEXT node
860        if (nIsText)
861        {
862            // Indicate we are dealing with text
863            *pType = eTokenText;
864            while((ch = getNextChar(pXML)))
865            {
866                if XML_isSPACECHAR(ch)
867                {
868                    indexStart++; break;
869
870                } else if (ch==_T('/'))
871                {
872                    // If we find a slash then this maybe text or a short hand end tag
873                    // Peek at the next character to see it we have short hand end tag
874                    ch=pXML->lpXML[pXML->nIndex];
875                    // If we found a short hand end tag then we need to exit the loop
876                    if (ch==_T('>')) { pXML->nIndex--; break; }
877
878                } else if ((ch==_T('<'))||(ch==_T('>'))||(ch==_T('=')))
879                {
880                    pXML->nIndex--; break;
881                }
882            }
883        }
884        *pcbToken = pXML->nIndex-indexStart;
885    } else
886    {
887        // If we failed to obtain a valid character
888        *pcbToken = 0;
889        *pType = eTokenError;
890        result.pStr=NULL;
891    }
892
893    return result;
894}
895
896XMLCSTR XMLNode::updateName_WOSD(XMLCSTR lpszName)
897{
898    if (d->lpszName&&(lpszName!=d->lpszName)) free((void*)d->lpszName);
899    d->lpszName=lpszName;
900    return lpszName;
901}
902
903// private:
904XMLNode::XMLNode(struct XMLNodeDataTag *p){ d=p; (p->ref_count)++; }
905XMLNode::XMLNode(XMLNodeData *pParent, XMLCSTR lpszName, char isDeclaration)
906{
907    d=(XMLNodeData*)malloc(sizeof(XMLNodeData));
908    d->ref_count=1;
909
910    d->lpszName=NULL;
911    d->nChild= 0;
912    d->nText = 0;
913    d->nClear = 0;
914    d->nAttribute = 0;
915
916    d->isDeclaration = isDeclaration;
917
918    d->pParent = pParent;
919    d->pChild= NULL;
920    d->pText= NULL;
921    d->pClear= NULL;
922    d->pAttribute= NULL;
923    d->pOrder= NULL;
924
925    updateName_WOSD(lpszName);
926}
927
928XMLNode XMLNode::createXMLTopNode_WOSD(XMLCSTR lpszName, char isDeclaration) { return XMLNode(NULL,lpszName,isDeclaration); }
929XMLNode XMLNode::createXMLTopNode(XMLCSTR lpszName, char isDeclaration) { return XMLNode(NULL,stringDup(lpszName),isDeclaration); }
930
931#define MEMORYINCREASE 50
932static int memoryIncrease=0;
933
934static inline void *myRealloc(void *p, int newsize, int memInc, int sizeofElem)
935{
936    if (p==NULL) { if (memInc) return malloc(memInc*sizeofElem); return malloc(sizeofElem); }
937    if ((memInc==0)||((newsize%memInc)==0)) p=realloc(p,(newsize+memInc)*sizeofElem);
938//    if (!p)
939//    {
940//        printf("XMLParser Error: Not enough memory! Aborting...\n"); exit(220);
941//    }
942    return p;
943}
944
945// private:
946int XMLNode::findPosition(XMLNodeData *d, int index, XMLElementType xtype)
947{
948    if (index<0) return -1;
949    int i=0,j=(int)((index<<2)+xtype),*o=d->pOrder; while (o[i]!=j) i++; return i;
950}
951
952// private:
953// update "order" information when deleting a content of a XMLNode
954int XMLNode::removeOrderElement(XMLNodeData *d, XMLElementType t, int index)
955{
956    int n=d->nChild+d->nText+d->nClear, *o=d->pOrder,i=findPosition(d,index,t);
957    memmove(o+i, o+i+1, (n-i)*sizeof(int));
958    for (;i<n;i++)
959        if ((o[i]&3)==(int)t) o[i]-=4;
960    // We should normally do:
961    // d->pOrder=(int)realloc(d->pOrder,n*sizeof(int));
962    // but we skip reallocation because it's too time consuming.
963    // Anyway, at the end, it will be free'd completely at once.
964    return i;
965}
966
967void *XMLNode::addToOrder(int *_pos, int nc, void *p, int size, XMLElementType xtype)
968{
969    //  in: *_pos is the position inside d->pOrder ("-1" means "EndOf")
970    // out: *_pos is the index inside p
971    p=myRealloc(p,(nc+1),memoryIncrease,size);
972    int n=d->nChild+d->nText+d->nClear;
973    d->pOrder=(int*)myRealloc(d->pOrder,n+1,memoryIncrease*3,sizeof(int));
974    int pos=*_pos,*o=d->pOrder;
975
976    if ((pos<0)||(pos>=n)) { *_pos=nc; o[n]=(int)((nc<<2)+xtype); return p; }
977
978    int i=pos;
979    memmove(o+i+1, o+i, (n-i)*sizeof(int));
980
981    while ((pos<n)&&((o[pos]&3)!=(int)xtype)) pos++;
982    if (pos==n) { *_pos=nc; o[n]=(int)((nc<<2)+xtype); return p; }
983
984    o[i]=o[pos];
985    for (i=pos+1;i<=n;i++) if ((o[i]&3)==(int)xtype) o[i]+=4;
986
987    *_pos=pos=o[pos]>>2;
988    memmove(((char*)p)+(pos+1)*size,((char*)p)+pos*size,(nc-pos)*size);
989
990    return p;
991}
992
993// Add a child node to the given element.
994XMLNode XMLNode::addChild_WOSD(XMLCSTR lpszName, char isDeclaration, int pos)
995{
996    if (!lpszName) return emptyXMLNode;
997    d->pChild=(XMLNode*)addToOrder(&pos,d->nChild,d->pChild,sizeof(XMLNode),eNodeChild);
998    d->pChild[pos].d=NULL;
999    d->pChild[pos]=XMLNode(d,lpszName,isDeclaration);
1000    d->nChild++;
1001    return d->pChild[pos];
1002}
1003
1004// Add an attribute to an element.
1005XMLAttribute *XMLNode::addAttribute_WOSD(XMLCSTR lpszName, XMLCSTR lpszValuev)
1006{
1007    if (!lpszName) return &emptyXMLAttribute;
1008    int nc=d->nAttribute;
1009    d->pAttribute=(XMLAttribute*)myRealloc(d->pAttribute,(nc+1),memoryIncrease,sizeof(XMLAttribute));
1010    XMLAttribute *pAttr=d->pAttribute+nc;
1011    pAttr->lpszName = lpszName;
1012    pAttr->lpszValue = lpszValuev;
1013    d->nAttribute++;
1014    return pAttr;
1015}
1016
1017// Add text to the element.
1018XMLCSTR XMLNode::addText_WOSD(XMLCSTR lpszValue, int pos)
1019{
1020    if (!lpszValue) return NULL;
1021    d->pText=(XMLCSTR*)addToOrder(&pos,d->nText,d->pText,sizeof(XMLSTR),eNodeText);
1022    d->pText[pos]=lpszValue;
1023    d->nText++;
1024    return lpszValue;
1025}
1026
1027// Add clear (unformatted) text to the element.
1028XMLClear *XMLNode::addClear_WOSD(XMLCSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, int pos)
1029{
1030    if (!lpszValue) return &emptyXMLClear;
1031    d->pClear=(XMLClear *)addToOrder(&pos,d->nClear,d->pClear,sizeof(XMLClear),eNodeClear);
1032    XMLClear *pNewClear=d->pClear+pos;
1033    pNewClear->lpszValue = lpszValue;
1034    if (!lpszOpen) lpszOpen=getClearTagTable()->lpszOpen;
1035    if (!lpszClose) lpszOpen=getClearTagTable()->lpszClose;
1036    pNewClear->lpszOpenTag = lpszOpen;
1037    pNewClear->lpszCloseTag = lpszClose;
1038    d->nClear++;
1039    return pNewClear;
1040}
1041
1042// private:
1043// Parse a clear (unformatted) type node.
1044char XMLNode::parseClearTag(void *px, ALLXMLClearTag *pClear)
1045{
1046    XML *pXML=(XML *)px;
1047    int cbTemp=0;
1048    XMLCSTR lpszTemp=NULL;
1049    XMLCSTR lpXML=&pXML->lpXML[pXML->nIndex];
1050
1051    // Find the closing tag
1052    // Seems the <!DOCTYPE need a better treatment so lets handle it
1053    if (pClear->lpszOpen==XMLClearTags[1].lpszOpen)
1054    {
1055        int openCount=1;
1056        XMLCSTR pCh=lpXML;
1057        while (*pCh)
1058        {
1059            if (*pCh==_T('<')) openCount++;
1060            else if (*pCh==_T('>')) { openCount--; if (!openCount) { lpszTemp=pCh; break; } }
1061#ifdef _XMLUNICODE
1062            pCh++;
1063#else
1064            pCh+=XML_ByteTable[(unsigned char)(*pCh)];
1065#endif
1066        }
1067    } else lpszTemp = _tcsstr(lpXML, pClear->lpszClose);
1068
1069    if (lpszTemp)
1070    {
1071        // Cache the size and increment the index
1072        cbTemp = (int)(lpszTemp - lpXML);
1073
1074        pXML->nIndex += cbTemp+(int)_tcslen(pClear->lpszClose);
1075
1076        // Add the clear node to the current element
1077        addClear_WOSD(stringDup(lpXML,cbTemp), pClear->lpszOpen, pClear->lpszClose);
1078        return 0;
1079    }
1080
1081    // If we failed to find the end tag
1082    pXML->error = eXMLErrorUnmatchedEndClearTag;
1083    return 1;
1084}
1085
1086void XMLNode::exactMemory(XMLNodeData *d)
1087{
1088    if (memoryIncrease<=1) return;
1089    if (d->pOrder)     d->pOrder=(int*)realloc(d->pOrder,(d->nChild+d->nAttribute+d->nText+d->nClear)*sizeof(int));
1090    if (d->pChild)     d->pChild=(XMLNode*)realloc(d->pChild,d->nChild*sizeof(XMLNode));
1091    if (d->pAttribute) d->pAttribute=(XMLAttribute*)realloc(d->pAttribute,d->nAttribute*sizeof(XMLAttribute));
1092    if (d->pText)      d->pText=(XMLCSTR*)realloc(d->pText,d->nText*sizeof(XMLSTR));
1093    if (d->pClear)     d->pClear=(XMLClear *)realloc(d->pClear,d->nClear*sizeof(XMLClear));
1094}
1095
1096char XMLNode::maybeAddTxT(void *pa, XMLCSTR tokenPStr)
1097{
1098    XML *pXML=(XML *)pa;
1099    XMLCSTR lpszText=pXML->lpszText;
1100    if (!lpszText) return 0;
1101    if (dropWhiteSpace) while (XML_isSPACECHAR(*lpszText)&&(lpszText!=tokenPStr)) lpszText++;
1102    int cbText = (int)(tokenPStr - lpszText);
1103    if (!cbText) { pXML->lpszText=NULL; return 0; }
1104    if (dropWhiteSpace) { cbText--; while ((cbText)&&XML_isSPACECHAR(lpszText[cbText])) cbText--; cbText++; }
1105    if (!cbText) { pXML->lpszText=NULL; return 0; }
1106    lpszText=fromXMLString(lpszText,cbText,pXML);
1107    if (!lpszText) return 1;
1108    addText_WOSD(lpszText);
1109    pXML->lpszText=NULL;
1110    return 0;
1111}
1112// private:
1113// Recursively parse an XML element.
1114int XMLNode::ParseXMLElement(void *pa)
1115{
1116    XML *pXML=(XML *)pa;
1117    int cbToken;
1118    enum XMLTokenTypeTag type;
1119    NextToken token;
1120    XMLCSTR lpszTemp=NULL;
1121    int cbTemp=0;
1122    char nDeclaration;
1123    XMLNode pNew;
1124    enum Status status; // inside or outside a tag
1125    enum Attrib attrib = eAttribName;
1126
1127    assert(pXML);
1128
1129    // If this is the first call to the function
1130    if (pXML->nFirst)
1131    {
1132        // Assume we are outside of a tag definition
1133        pXML->nFirst = FALSE;
1134        status = eOutsideTag;
1135    } else
1136    {
1137        // If this is not the first call then we should only be called when inside a tag.
1138        status = eInsideTag;
1139    }
1140
1141    // Iterate through the tokens in the document
1142    for(;;)
1143    {
1144        // Obtain the next token
1145        token = GetNextToken(pXML, &cbToken, &type);
1146
1147        if (type != eTokenError)
1148        {
1149            // Check the current status
1150            switch(status)
1151            {
1152
1153            // If we are outside of a tag definition
1154            case eOutsideTag:
1155
1156                // Check what type of token we obtained
1157                switch(type)
1158                {
1159                // If we have found text or quoted text
1160                case eTokenText:
1161                case eTokenCloseTag:          /* '>'         */
1162                case eTokenShortHandClose:    /* '/>'        */
1163                case eTokenQuotedText:
1164                case eTokenEquals:
1165                    break;
1166
1167                // If we found a start tag '<' and declarations '<?'
1168                case eTokenTagStart:
1169                case eTokenDeclaration:
1170
1171                    // Cache whether this new element is a declaration or not
1172                    nDeclaration = (type == eTokenDeclaration);
1173
1174                    // If we have node text then add this to the element
1175                    if (maybeAddTxT(pXML,token.pStr)) return FALSE;
1176
1177                    // Find the name of the tag
1178                    token = GetNextToken(pXML, &cbToken, &type);
1179
1180                    // Return an error if we couldn't obtain the next token or
1181                    // it wasnt text
1182                    if (type != eTokenText)
1183                    {
1184                        pXML->error = eXMLErrorMissingTagName;
1185                        return FALSE;
1186                    }
1187
1188                    // If we found a new element which is the same as this
1189                    // element then we need to pass this back to the caller..
1190
1191#ifdef APPROXIMATE_PARSING
1192                    if (d->lpszName &&
1193                        myTagCompare(d->lpszName, token.pStr) == 0)
1194                    {
1195                        // Indicate to the caller that it needs to create a
1196                        // new element.
1197                        pXML->lpNewElement = token.pStr;
1198                        pXML->cbNewElement = cbToken;
1199                        return TRUE;
1200                    } else
1201#endif
1202                    {
1203                        // If the name of the new element differs from the name of
1204                        // the current element we need to add the new element to
1205                        // the current one and recurse
1206                        pNew = addChild_WOSD(stringDup(token.pStr,cbToken), nDeclaration);
1207
1208                        while (!pNew.isEmpty())
1209                        {
1210                            // Callself to process the new node.  If we return
1211                            // FALSE this means we dont have any more
1212                            // processing to do...
1213
1214                            if (!pNew.ParseXMLElement(pXML)) return FALSE;
1215                            else
1216                            {
1217                                // If the call to recurse this function
1218                                // evented in a end tag specified in XML then
1219                                // we need to unwind the calls to this
1220                                // function until we find the appropriate node
1221                                // (the element name and end tag name must
1222                                // match)
1223                                if (pXML->cbEndTag)
1224                                {
1225                                    // If we are back at the root node then we
1226                                    // have an unmatched end tag
1227                                    if (!d->lpszName)
1228                                    {
1229                                        pXML->error=eXMLErrorUnmatchedEndTag;
1230                                        return FALSE;
1231                                    }
1232
1233                                    // If the end tag matches the name of this
1234                                    // element then we only need to unwind
1235                                    // once more...
1236
1237                                    if (myTagCompare(d->lpszName, pXML->lpEndTag)==0)
1238                                    {
1239                                        pXML->cbEndTag = 0;
1240                                    }
1241
1242                                    return TRUE;
1243                                } else
1244                                    if (pXML->cbNewElement)
1245                                    {
1246                                        // If the call indicated a new element is to
1247                                        // be created on THIS element.
1248
1249                                        // If the name of this element matches the
1250                                        // name of the element we need to create
1251                                        // then we need to return to the caller
1252                                        // and let it process the element.
1253
1254                                        if (myTagCompare(d->lpszName, pXML->lpNewElement)==0)
1255                                        {
1256                                            return TRUE;
1257                                        }
1258
1259                                        // Add the new element and recurse
1260                                        pNew = addChild_WOSD(stringDup(pXML->lpNewElement,pXML->cbNewElement));
1261                                        pXML->cbNewElement = 0;
1262                                    }
1263                                    else
1264                                    {
1265                                        // If we didn't have a new element to create
1266                                        pNew = emptyXMLNode;
1267
1268                                    }
1269                            }
1270                        }
1271                    }
1272                    break;
1273
1274                // If we found an end tag
1275                case eTokenTagEnd:
1276
1277                    // If we have node text then add this to the element
1278                    if (maybeAddTxT(pXML,token.pStr)) return FALSE;
1279
1280                    // Find the name of the end tag
1281                    token = GetNextToken(pXML, &cbTemp, &type);
1282
1283                    // The end tag should be text
1284                    if (type != eTokenText)
1285                    {
1286                        pXML->error = eXMLErrorMissingEndTagName;
1287                        return FALSE;
1288                    }
1289                    lpszTemp = token.pStr;
1290
1291                    // After the end tag we should find a closing tag
1292                    token = GetNextToken(pXML, &cbToken, &type);
1293                    if (type != eTokenCloseTag)
1294                    {
1295                        pXML->error = eXMLErrorMissingEndTagName;
1296                        return FALSE;
1297                    }
1298                    pXML->lpszText=pXML->lpXML+pXML->nIndex;
1299
1300                    // We need to return to the previous caller.  If the name
1301                    // of the tag cannot be found we need to keep returning to
1302                    // caller until we find a match
1303                    if (myTagCompare(d->lpszName, lpszTemp) != 0)
1304#ifdef STRICT_PARSING
1305                    {
1306                        pXML->error=eXMLErrorUnmatchedEndTag;
1307                        pXML->nIndexMissigEndTag=pXML->nIndex;
1308                        return FALSE;
1309                    }
1310#else
1311                    {
1312                        pXML->error=eXMLErrorMissingEndTag;
1313                        pXML->nIndexMissigEndTag=pXML->nIndex;
1314                        pXML->lpEndTag = lpszTemp;
1315                        pXML->cbEndTag = cbTemp;
1316                    }
1317#endif
1318
1319                    // Return to the caller
1320                    exactMemory(d);
1321                    return TRUE;
1322
1323                // If we found a clear (unformatted) token
1324                case eTokenClear:
1325                    // If we have node text then add this to the element
1326                    if (maybeAddTxT(pXML,token.pStr)) return FALSE;
1327                    if (parseClearTag(pXML, token.pClr)) return FALSE;
1328                    pXML->lpszText=pXML->lpXML+pXML->nIndex;
1329                    break;
1330
1331                default:
1332                    break;
1333                }
1334                break;
1335
1336            // If we are inside a tag definition we need to search for attributes
1337            case eInsideTag:
1338
1339                // Check what part of the attribute (name, equals, value) we
1340                // are looking for.
1341                switch(attrib)
1342                {
1343                // If we are looking for a new attribute
1344                case eAttribName:
1345
1346                    // Check what the current token type is
1347                    switch(type)
1348                    {
1349                    // If the current type is text...
1350                    // Eg.  'attribute'
1351                    case eTokenText:
1352                        // Cache the token then indicate that we are next to
1353                        // look for the equals
1354                        lpszTemp = token.pStr;
1355                        cbTemp = cbToken;
1356                        attrib = eAttribEquals;
1357                        break;
1358
1359                    // If we found a closing tag...
1360                    // Eg.  '>'
1361                    case eTokenCloseTag:
1362                        // We are now outside the tag
1363                        status = eOutsideTag;
1364                        pXML->lpszText=pXML->lpXML+pXML->nIndex;
1365                        break;
1366
1367                    // If we found a short hand '/>' closing tag then we can
1368                    // return to the caller
1369                    case eTokenShortHandClose:
1370                        exactMemory(d);
1371                        pXML->lpszText=pXML->lpXML+pXML->nIndex;
1372                        return TRUE;
1373
1374                    // Errors...
1375                    case eTokenQuotedText:    /* '"SomeText"'   */
1376                    case eTokenTagStart:      /* '<'            */
1377                    case eTokenTagEnd:        /* '</'           */
1378                    case eTokenEquals:        /* '='            */
1379                    case eTokenDeclaration:   /* '<?'           */
1380                    case eTokenClear:
1381                        pXML->error = eXMLErrorUnexpectedToken;
1382                        return FALSE;
1383                    default: break;
1384                    }
1385                    break;
1386
1387                // If we are looking for an equals
1388                case eAttribEquals:
1389                    // Check what the current token type is
1390                    switch(type)
1391                    {
1392                    // If the current type is text...
1393                    // Eg.  'Attribute AnotherAttribute'
1394                    case eTokenText:
1395                        // Add the unvalued attribute to the list
1396                        addAttribute_WOSD(stringDup(lpszTemp,cbTemp), NULL);
1397                        // Cache the token then indicate.  We are next to
1398                        // look for the equals attribute
1399                        lpszTemp = token.pStr;
1400                        cbTemp = cbToken;
1401                        break;
1402
1403                    // If we found a closing tag 'Attribute >' or a short hand
1404                    // closing tag 'Attribute />'
1405                    case eTokenShortHandClose:
1406                    case eTokenCloseTag:
1407                        // If we are a declaration element '<?' then we need
1408                        // to remove extra closing '?' if it exists
1409                        pXML->lpszText=pXML->lpXML+pXML->nIndex;
1410
1411                        if (d->isDeclaration &&
1412                            (lpszTemp[cbTemp-1]) == _T('?'))
1413                        {
1414                            cbTemp--;
1415                        }
1416
1417                        if (cbTemp)
1418                        {
1419                            // Add the unvalued attribute to the list
1420                            addAttribute_WOSD(stringDup(lpszTemp,cbTemp), NULL);
1421                        }
1422
1423                        // If this is the end of the tag then return to the caller
1424                        if (type == eTokenShortHandClose)
1425                        {
1426                            exactMemory(d);
1427                            return TRUE;
1428                        }
1429
1430                        // We are now outside the tag
1431                        status = eOutsideTag;
1432                        break;
1433
1434                    // If we found the equals token...
1435                    // Eg.  'Attribute ='
1436                    case eTokenEquals:
1437                        // Indicate that we next need to search for the value
1438                        // for the attribute
1439                        attrib = eAttribValue;
1440                        break;
1441
1442                    // Errors...
1443                    case eTokenQuotedText:    /* 'Attribute "InvalidAttr"'*/
1444                    case eTokenTagStart:      /* 'Attribute <'            */
1445                    case eTokenTagEnd:        /* 'Attribute </'           */
1446                    case eTokenDeclaration:   /* 'Attribute <?'           */
1447                    case eTokenClear:
1448                        pXML->error = eXMLErrorUnexpectedToken;
1449                        return FALSE;
1450                    default: break;
1451                    }
1452                    break;
1453
1454                // If we are looking for an attribute value
1455                case eAttribValue:
1456                    // Check what the current token type is
1457                    switch(type)
1458                    {
1459                    // If the current type is text or quoted text...
1460                    // Eg.  'Attribute = "Value"' or 'Attribute = Value' or
1461                    // 'Attribute = 'Value''.
1462                    case eTokenText:
1463                    case eTokenQuotedText:
1464                        // If we are a declaration element '<?' then we need
1465                        // to remove extra closing '?' if it exists
1466                        if (d->isDeclaration &&
1467                            (token.pStr[cbToken-1]) == _T('?'))
1468                        {
1469                            cbToken--;
1470                        }
1471
1472                        if (cbTemp)
1473                        {
1474                            // Add the valued attribute to the list
1475                            if (type==eTokenQuotedText) { token.pStr++; cbToken-=2; }
1476                            XMLCSTR attrVal=token.pStr;
1477                            if (attrVal)
1478                            {
1479                                attrVal=fromXMLString(attrVal,cbToken,pXML);
1480                                if (!attrVal) return FALSE;
1481                            }
1482                            addAttribute_WOSD(stringDup(lpszTemp,cbTemp),attrVal);
1483                        }
1484
1485                        // Indicate we are searching for a new attribute
1486                        attrib = eAttribName;
1487                        break;
1488
1489                    // Errors...
1490                    case eTokenTagStart:        /* 'Attr = <'          */
1491                    case eTokenTagEnd:          /* 'Attr = </'         */
1492                    case eTokenCloseTag:        /* 'Attr = >'          */
1493                    case eTokenShortHandClose:  /* "Attr = />"         */
1494                    case eTokenEquals:          /* 'Attr = ='          */
1495                    case eTokenDeclaration:     /* 'Attr = <?'         */
1496                    case eTokenClear:
1497                        pXML->error = eXMLErrorUnexpectedToken;
1498                        return FALSE;
1499                        break;
1500                    default: break;
1501                    }
1502                }
1503            }
1504        }
1505        // If we failed to obtain the next token
1506        else
1507        {
1508            if ((!d->isDeclaration)&&(d->pParent))
1509            {
1510#ifdef STRICT_PARSING
1511                pXML->error=eXMLErrorUnmatchedEndTag;
1512#else
1513                pXML->error=eXMLErrorMissingEndTag;
1514#endif
1515                pXML->nIndexMissigEndTag=pXML->nIndex;
1516            }
1517            return FALSE;
1518        }
1519    }
1520}
1521
1522// Count the number of lines and columns in an XML string.
1523static void CountLinesAndColumns(XMLCSTR lpXML, int nUpto, XMLResults *pResults)
1524{
1525    XMLCHAR ch;
1526    assert(lpXML);
1527    assert(pResults);
1528
1529    struct XML xml={ lpXML,lpXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0, TRUE };
1530
1531    pResults->nLine = 1;
1532    pResults->nColumn = 1;
1533    while (xml.nIndex<nUpto)
1534    {
1535        ch = getNextChar(&xml);
1536        if (ch != _T('\n')) pResults->nColumn++;
1537        else
1538        {
1539            pResults->nLine++;
1540            pResults->nColumn=1;
1541        }
1542    }
1543}
1544
1545// Parse XML and return the root element.
1546XMLNode XMLNode::parseString(XMLCSTR lpszXML, XMLCSTR tag, XMLResults *pResults)
1547{
1548    if (!lpszXML)
1549    {
1550        if (pResults)
1551        {
1552            pResults->error=eXMLErrorNoElements;
1553            pResults->nLine=0;
1554            pResults->nColumn=0;
1555        }
1556        return emptyXMLNode;
1557    }
1558
1559    XMLNode xnode(NULL,NULL,FALSE);
1560    struct XML xml={ lpszXML, lpszXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0, TRUE };
1561
1562    // Create header element
1563    memoryIncrease=MEMORYINCREASE; xnode.ParseXMLElement(&xml); memoryIncrease=0;
1564    enum XMLError error = xml.error;
1565    if ((xnode.nChildNode()==1)&&(xnode.nElement()==1)) xnode=xnode.getChildNode(); // skip the empty node
1566
1567    // If no error occurred
1568    if ((error==eXMLErrorNone)||(error==eXMLErrorMissingEndTag))
1569    {
1570        if (tag&&_tcslen(tag)&&_tcsicmp(xnode.getName(),tag))
1571        {
1572            XMLNode nodeTmp;
1573            int i=0;
1574            while (i<xnode.nChildNode())
1575            {
1576                nodeTmp=xnode.getChildNode(i);
1577                if (_tcsicmp(nodeTmp.getName(),tag)==0) break;
1578                if (nodeTmp.isDeclaration()) { xnode=nodeTmp; i=0; } else i++;
1579            }
1580            if (i>=xnode.nChildNode())
1581            {
1582                if (pResults)
1583                {
1584                    pResults->error=eXMLErrorFirstTagNotFound;
1585                    pResults->nLine=0;
1586                    pResults->nColumn=0;
1587                }
1588                return emptyXMLNode;
1589            }
1590            xnode=nodeTmp;
1591        }
1592    } else
1593    {
1594        // Cleanup: this will destroy all the nodes
1595        xnode = emptyXMLNode;
1596    }
1597
1598
1599    // If we have been given somewhere to place results
1600    if (pResults)
1601    {
1602        pResults->error = error;
1603
1604        // If we have an error
1605        if (error!=eXMLErrorNone)
1606        {
1607            if (error==eXMLErrorMissingEndTag) xml.nIndex=xml.nIndexMissigEndTag;
1608            // Find which line and column it starts on.
1609            CountLinesAndColumns(xml.lpXML, xml.nIndex, pResults);
1610        }
1611    }
1612    return xnode;
1613}
1614
1615XMLNode XMLNode::parseFile(XMLCSTR filename, XMLCSTR tag, XMLResults *pResults)
1616{
1617    if (pResults) { pResults->nLine=0; pResults->nColumn=0; }
1618    FILE *f=_tfopen(filename,_T("rb"));
1619    if (f==NULL) { if (pResults) pResults->error=eXMLErrorFileNotFound; return emptyXMLNode; }
1620    fseek(f,0,SEEK_END);
1621    int l=ftell(f),headerSz=0;
1622    if (!l) { if (pResults) pResults->error=eXMLErrorEmpty; return emptyXMLNode; }
1623    fseek(f,0,SEEK_SET);
1624    unsigned char *buf=(unsigned char*)malloc(l+1);
1625    fread(buf,l,1,f);
1626    fclose(f);
1627    buf[l]=0;
1628#ifdef _XMLUNICODE
1629    if (guessUnicodeChars)
1630    {
1631        if (!myIsTextUnicode(buf,l))
1632        {
1633            if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) headerSz=3;
1634            XMLSTR b2=myMultiByteToWideChar((const char*)(buf+headerSz),l-headerSz);
1635            free(buf); buf=(unsigned char*)b2; headerSz=0;
1636        } else
1637        {
1638            if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2;
1639            if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2;
1640        }
1641    }
1642#else
1643    if (guessUnicodeChars)
1644    {
1645        if (myIsTextUnicode(buf,l))
1646        {
1647            l/=sizeof(wchar_t);
1648            if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2;
1649            if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2;
1650            char *b2=myWideCharToMultiByte((const wchar_t*)(buf+headerSz),l-headerSz);
1651            free(buf); buf=(unsigned char*)b2; headerSz=0;
1652        } else
1653        {
1654            if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) headerSz=3;
1655        }
1656    }
1657#endif
1658
1659    if (!buf) { if (pResults) pResults->error=eXMLErrorCharConversionError; return emptyXMLNode; }
1660    XMLNode x=parseString((XMLSTR)(buf+headerSz),tag,pResults);
1661    free(buf);
1662    return x;
1663}
1664
1665static inline void charmemset(XMLSTR dest,XMLCHAR c,int l) { while (l--) *(dest++)=c; }
1666// private:
1667// Creates an user friendly XML string from a given element with
1668// appropriate white space and carriage returns.
1669//
1670// This recurses through all subnodes then adds contents of the nodes to the
1671// string.
1672int XMLNode::CreateXMLStringR(XMLNodeData *pEntry, XMLSTR lpszMarker, int nFormat)
1673{
1674    int nResult = 0;
1675    int cb;
1676    int cbElement;
1677    int nChildFormat=-1;
1678    int nElementI=pEntry->nChild+pEntry->nText+pEntry->nClear;
1679    int i,j;
1680
1681    assert(pEntry);
1682
1683#define LENSTR(lpsz) (lpsz ? _tcslen(lpsz) : 0)
1684
1685    // If the element has no name then assume this is the head node.
1686    cbElement = (int)LENSTR(pEntry->lpszName);
1687
1688    if (cbElement)
1689    {
1690        // "<elementname "
1691        cb = nFormat == -1 ? 0 : nFormat;
1692
1693        if (lpszMarker)
1694        {
1695            if (cb) charmemset(lpszMarker, INDENTCHAR, sizeof(XMLCHAR)*cb);
1696            nResult = cb;
1697            lpszMarker[nResult++]=_T('<');
1698            if (pEntry->isDeclaration) lpszMarker[nResult++]=_T('?');
1699            _tcscpy(&lpszMarker[nResult], pEntry->lpszName);
1700            nResult+=cbElement;
1701            lpszMarker[nResult++]=_T(' ');
1702
1703        } else
1704        {
1705            nResult+=cbElement+2+cb;
1706            if (pEntry->isDeclaration) nResult++;
1707        }
1708
1709        // Enumerate attributes and add them to the string
1710        XMLAttribute *pAttr=pEntry->pAttribute;
1711        for (i=0; i<pEntry->nAttribute; i++)
1712        {
1713            // "Attrib
1714            cb = (int)LENSTR(pAttr->lpszName);
1715            if (cb)
1716            {
1717                if (lpszMarker) _tcscpy(&lpszMarker[nResult], pAttr->lpszName);
1718                nResult += cb;
1719                // "Attrib=Value "
1720                if (pAttr->lpszValue)
1721                {
1722                    cb=(int)lengthXMLString(pAttr->lpszValue);
1723                    if (lpszMarker)
1724                    {
1725                        lpszMarker[nResult]=_T('=');
1726                        lpszMarker[nResult+1]=_T('"');
1727                        if (cb) toXMLString(&lpszMarker[nResult+2],pAttr->lpszValue);
1728                        lpszMarker[nResult+cb+2]=_T('"');
1729                    }
1730                    nResult+=cb+3;
1731                }
1732                if (lpszMarker) lpszMarker[nResult] = _T(' ');
1733                nResult++;
1734            }
1735            pAttr++;
1736        }
1737
1738        if (pEntry->isDeclaration)
1739        {
1740            if (lpszMarker)
1741            {
1742                lpszMarker[nResult-1]=_T('?');
1743                lpszMarker[nResult]=_T('>');
1744            }
1745            nResult++;
1746            if (nFormat!=-1)
1747            {
1748                if (lpszMarker) lpszMarker[nResult]=_T('\n');
1749                nResult++;
1750            }
1751        } else
1752            // If there are child nodes we need to terminate the start tag
1753            if (nElementI)
1754            {
1755                if (lpszMarker) lpszMarker[nResult-1]=_T('>');
1756                if (nFormat!=-1)
1757                {
1758                    if (lpszMarker) lpszMarker[nResult]=_T('\n');
1759                    nResult++;
1760                }
1761            } else nResult--;
1762    }
1763
1764    // Calculate the child format for when we recurse.  This is used to
1765    // determine the number of spaces used for prefixes.
1766    if (nFormat!=-1)
1767    {
1768        if (cbElement&&(!pEntry->isDeclaration)) nChildFormat=nFormat+1;
1769        else nChildFormat=nFormat;
1770    }
1771
1772    // Enumerate through remaining children
1773    for (i=0; i<nElementI; i++)
1774    {
1775        j=pEntry->pOrder[i];
1776        switch((XMLElementType)(j&3))
1777        {
1778        // Text nodes
1779        case eNodeText:
1780            {
1781                // "Text"
1782                XMLCSTR pChild=pEntry->pText[j>>2];
1783                cb = (int)lengthXMLString(pChild);
1784                if (cb)
1785                {
1786                    if (nFormat!=-1)
1787                    {
1788                        if (lpszMarker)
1789                        {
1790                            charmemset(&lpszMarker[nResult],INDENTCHAR,sizeof(XMLCHAR)*(nFormat + 1));
1791                            toXMLString(&lpszMarker[nResult+nFormat+1],pChild);
1792                            lpszMarker[nResult+nFormat+1+cb]=_T('\n');
1793                        }
1794                        nResult+=cb+nFormat+2;
1795                    } else
1796                    {
1797                        if (lpszMarker) toXMLString(&lpszMarker[nResult], pChild);
1798                        nResult += cb;
1799                    }
1800                }
1801                break;
1802            }
1803
1804        // Clear type nodes
1805        case eNodeClear:
1806            {
1807                XMLClear *pChild=pEntry->pClear+(j>>2);
1808                // "OpenTag"
1809                cb = (int)LENSTR(pChild->lpszOpenTag);
1810                if (cb)
1811                {
1812                    if (nFormat!=-1)
1813                    {
1814                        if (lpszMarker)
1815                        {
1816                            charmemset(&lpszMarker[nResult], INDENTCHAR, sizeof(XMLCHAR)*(nFormat + 1));
1817                            _tcscpy(&lpszMarker[nResult+nFormat+1], pChild->lpszOpenTag);
1818                        }
1819                        nResult+=cb+nFormat+1;
1820                    }
1821                    else
1822                    {
1823                        if (lpszMarker)_tcscpy(&lpszMarker[nResult], pChild->lpszOpenTag);
1824                        nResult += cb;
1825                    }
1826                }
1827
1828                // "OpenTag Value"
1829                cb = (int)LENSTR(pChild->lpszValue);
1830                if (cb)
1831                {
1832                    if (lpszMarker) _tcscpy(&lpszMarker[nResult], pChild->lpszValue);
1833                    nResult += cb;
1834                }
1835
1836                // "OpenTag Value CloseTag"
1837                cb = (int)LENSTR(pChild->lpszCloseTag);
1838                if (cb)
1839                {
1840                    if (lpszMarker) _tcscpy(&lpszMarker[nResult], pChild->lpszCloseTag);
1841                    nResult += cb;
1842                }
1843
1844                if (nFormat!=-1)
1845                {
1846                    if (lpszMarker) lpszMarker[nResult] = _T('\n');
1847                    nResult++;
1848                }
1849                break;
1850            }
1851
1852        // Element nodes
1853        case eNodeChild:
1854            {
1855                // Recursively add child nodes
1856                nResult += CreateXMLStringR(pEntry->pChild[j>>2].d, lpszMarker ? lpszMarker + nResult : 0, nChildFormat);
1857                break;
1858            }
1859        default: break;
1860        }
1861    }
1862
1863    if ((cbElement)&&(!pEntry->isDeclaration))
1864    {
1865        // If we have child entries we need to use long XML notation for
1866        // closing the element - "<elementname>blah blah blah</elementname>"
1867        if (nElementI)
1868        {
1869            // "</elementname>\0"
1870            if (lpszMarker)
1871            {
1872                if (nFormat != -1)
1873                {
1874                    if (nFormat)
1875                    {
1876                        charmemset(&lpszMarker[nResult], INDENTCHAR,sizeof(XMLCHAR)*nFormat);
1877                        nResult+=nFormat;
1878                    }
1879                }
1880
1881                _tcscpy(&lpszMarker[nResult], _T("</"));
1882                nResult += 2;
1883                _tcscpy(&lpszMarker[nResult], pEntry->lpszName);
1884                nResult += cbElement;
1885
1886                if (nFormat == -1)
1887                {
1888                    _tcscpy(&lpszMarker[nResult], _T(">"));
1889                    nResult++;
1890                } else
1891                {
1892                    _tcscpy(&lpszMarker[nResult], _T(">\n"));
1893                    nResult+=2;
1894                }
1895            } else
1896            {
1897                if (nFormat != -1) nResult+=cbElement+4+nFormat;
1898                else nResult+=cbElement+3;
1899            }
1900        } else
1901        {
1902            // If there are no children we can use shorthand XML notation -
1903            // "<elementname/>"
1904            // "/>\0"
1905            if (lpszMarker)
1906            {
1907                if (nFormat == -1)
1908                {
1909                    _tcscpy(&lpszMarker[nResult], _T("/>"));
1910                    nResult += 2;
1911                }
1912                else
1913                {
1914                    _tcscpy(&lpszMarker[nResult], _T("/>\n"));
1915                    nResult += 3;
1916                }
1917            }
1918            else
1919            {
1920                nResult += nFormat == -1 ? 2 : 3;
1921            }
1922        }
1923    }
1924
1925    return nResult;
1926}
1927
1928#undef LENSTR
1929
1930// Create an XML string
1931// @param       int nFormat             - 0 if no formatting is required
1932//                                        otherwise nonzero for formatted text
1933//                                        with carriage returns and indentation.
1934// @param       int *pnSize             - [out] pointer to the size of the
1935//                                        returned string not including the
1936//                                        NULL terminator.
1937// @return      XMLSTR                  - Allocated XML string, you must free
1938//                                        this with free().
1939XMLSTR XMLNode::createXMLString(int nFormat, int *pnSize) const
1940{
1941    if (!d) { if (pnSize) *pnSize=0; return NULL; }
1942
1943    XMLSTR lpszResult = NULL;
1944    int cbStr;
1945
1946    // Recursively Calculate the size of the XML string
1947    if (!dropWhiteSpace) nFormat=0;
1948    nFormat = nFormat ? 0 : -1;
1949    cbStr = CreateXMLStringR(d, 0, nFormat);
1950    assert(cbStr);
1951    // Alllocate memory for the XML string + the NULL terminator and
1952    // create the recursively XML string.
1953    lpszResult=(XMLSTR)malloc((cbStr+1)*sizeof(XMLCHAR));
1954    CreateXMLStringR(d, lpszResult, nFormat);
1955    if (pnSize) *pnSize = cbStr;
1956    return lpszResult;
1957}
1958
1959XMLNode::~XMLNode() { deleteNodeContent(); }
1960
1961int XMLNode::detachFromParent(XMLNodeData *d)
1962{
1963    XMLNode *pa=d->pParent->pChild;
1964    int i=0;
1965    while (((void*)(pa[i].d))!=((void*)d)) i++;
1966    d->pParent->nChild--;
1967    if (d->pParent->nChild) memmove(pa+i,pa+i+1,(d->pParent->nChild-i)*sizeof(XMLNode));
1968    else { free(pa); d->pParent->pChild=NULL; }
1969    return removeOrderElement(d->pParent,eNodeChild,i);
1970}
1971
1972void XMLNode::deleteNodeContent(char force)
1973{
1974    if (!d) return;
1975    (d->ref_count) --;
1976    if ((d->ref_count==0)||force)
1977    {
1978        int i;
1979        if (d->pParent) detachFromParent(d);
1980        for(i=0; i<d->nChild; i++) { d->pChild[i].d->pParent=NULL; d->pChild[i].deleteNodeContent(force); }
1981        free(d->pChild);
1982        for(i=0; i<d->nText; i++) free((void*)d->pText[i]);
1983        free(d->pText);
1984        for(i=0; i<d->nClear; i++) free((void*)d->pClear[i].lpszValue);
1985        free(d->pClear);
1986        for(i=0; i<d->nAttribute; i++)
1987        {
1988            free((void*)d->pAttribute[i].lpszName);
1989            if (d->pAttribute[i].lpszValue) free((void*)d->pAttribute[i].lpszValue);
1990        }
1991        free(d->pAttribute);
1992        free(d->pOrder);
1993        free((void*)d->lpszName);
1994        free(d);
1995        d=NULL;
1996    }
1997}
1998
1999XMLNode XMLNode::addChild(XMLNode childNode, int pos)
2000{
2001    XMLNodeData *dc=childNode.d;
2002    if ((!dc)||(!d)) return childNode;
2003    if (dc->pParent) { if ((detachFromParent(dc)<=pos)&&(dc->pParent==d)) pos--; } else dc->ref_count++;
2004    dc->pParent=d;
2005//     int nc=d->nChild;
2006//     d->pChild=(XMLNode*)myRealloc(d->pChild,(nc+1),memoryIncrease,sizeof(XMLNode));
2007    d->pChild=(XMLNode*)addToOrder(&pos,d->nChild,d->pChild,sizeof(XMLNode),eNodeChild);
2008    d->pChild[pos].d=dc;
2009    d->nChild++;
2010    return childNode;
2011}
2012
2013void XMLNode::deleteAttribute(int i)
2014{
2015    if ((!d)||(i<0)||(i>=d->nAttribute)) return;
2016    d->nAttribute--;
2017    XMLAttribute *p=d->pAttribute+i;
2018    free((void*)p->lpszName);
2019    if (p->lpszValue) free((void*)p->lpszValue);
2020    if (d->nAttribute) memmove(p,p+1,(d->nAttribute-i)*sizeof(XMLAttribute)); else { free(p); d->pAttribute=NULL; }
2021}
2022
2023void XMLNode::deleteAttribute(XMLAttribute *a){ if (a) deleteAttribute(a->lpszName); }
2024void XMLNode::deleteAttribute(XMLCSTR lpszName)
2025{
2026    int j=0;
2027    getAttribute(lpszName,&j);
2028    if (j) deleteAttribute(j-1);
2029}
2030
2031XMLAttribute *XMLNode::updateAttribute_WOSD(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,int i)
2032{
2033    if (!d) return NULL;
2034    if (i>=d->nAttribute)
2035    {
2036        if (lpszNewName) return addAttribute_WOSD(lpszNewName,lpszNewValue);
2037        return NULL;
2038    }
2039    XMLAttribute *p=d->pAttribute+i;
2040    if (p->lpszValue&&p->lpszValue!=lpszNewValue) free((void*)p->lpszValue);
2041    p->lpszValue=lpszNewValue;
2042    if (lpszNewName&&p->lpszName!=lpszNewName) { free((void*)p->lpszName); p->lpszName=lpszNewName; };
2043    return p;
2044}
2045
2046XMLAttribute *XMLNode::updateAttribute_WOSD(XMLAttribute *newAttribute, XMLAttribute *oldAttribute)
2047{
2048    if (oldAttribute) return updateAttribute_WOSD(newAttribute->lpszValue,newAttribute->lpszName,oldAttribute->lpszName);
2049    return addAttribute_WOSD(newAttribute->lpszName,newAttribute->lpszValue);
2050}
2051
2052XMLAttribute *XMLNode::updateAttribute_WOSD(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,XMLCSTR lpszOldName)
2053{
2054    int j=0;
2055    getAttribute(lpszOldName,&j);
2056    if (j) return updateAttribute_WOSD(lpszNewValue,lpszNewName,j-1);
2057    else
2058    {
2059        if (lpszNewName) return addAttribute_WOSD(lpszNewName,lpszNewValue);
2060        else             return addAttribute_WOSD(stringDup(lpszOldName),lpszNewValue);
2061    }
2062}
2063
2064int XMLNode::indexText(XMLCSTR lpszValue) const
2065{
2066    if (!d) return -1;
2067    int i,l=d->nText;
2068    if (!lpszValue) { if (l) return 0; return -1; }
2069    XMLCSTR *p=d->pText;
2070    for (i=0; i<l; i++) if (lpszValue==p[i]) return i;
2071    return -1;
2072}
2073
2074void XMLNode::deleteText(int i)
2075{
2076    if ((!d)||(i<0)||(i>=d->nText)) return;
2077    d->nText--;
2078    XMLCSTR *p=d->pText+i;
2079    free((void*)*p);
2080    if (d->nText) memmove(p,p+1,(d->nText-i)*sizeof(XMLCSTR)); else { free(p); d->pText=NULL; }
2081    removeOrderElement(d,eNodeText,i);
2082}
2083
2084void XMLNode::deleteText(XMLCSTR lpszValue) { deleteText(indexText(lpszValue)); }
2085
2086XMLCSTR XMLNode::updateText_WOSD(XMLCSTR lpszNewValue, int i)
2087{
2088    if (!d) return NULL;
2089    if (i>=d->nText) return addText_WOSD(lpszNewValue);
2090    XMLCSTR *p=d->pText+i;
2091    if (*p!=lpszNewValue) { free((void*)*p); *p=lpszNewValue; }
2092    return lpszNewValue;
2093}
2094
2095XMLCSTR XMLNode::updateText_WOSD(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue)
2096{
2097    if (!d) return NULL;
2098    int i=indexText(lpszOldValue);
2099    if (i>=0) return updateText_WOSD(lpszNewValue,i);
2100    return addText_WOSD(lpszNewValue);
2101}
2102
2103void XMLNode::deleteClear(int i)
2104{
2105    if ((!d)||(i<0)||(i>=d->nClear)) return;
2106    d->nClear--;
2107    XMLClear *p=d->pClear+i;
2108    free((void*)p->lpszValue);
2109    if (d->nClear) memmove(p,p+1,(d->nText-i)*sizeof(XMLClear)); else { free(p); d->pClear=NULL; }
2110    removeOrderElement(d,eNodeClear,i);
2111}
2112
2113int XMLNode::indexClear(XMLCSTR lpszValue) const
2114{
2115    if (!d) return -1;
2116    int i,l=d->nClear;
2117    if (!lpszValue) { if (l) return 0; return -1; }
2118    XMLClear *p=d->pClear;
2119    for (i=0; i<l; i++) if (lpszValue==p[i].lpszValue) return i;
2120    return -1;
2121}
2122
2123void XMLNode::deleteClear(XMLCSTR lpszValue) { deleteClear(indexClear(lpszValue)); }
2124void XMLNode::deleteClear(XMLClear *a) { if (a) deleteClear(a->lpszValue); }
2125
2126XMLClear *XMLNode::updateClear_WOSD(XMLCSTR lpszNewContent, int i)
2127{
2128    if (!d) return NULL;
2129    if (i>=d->nClear)
2130    {
2131        return addClear_WOSD(XMLClearTags[0].lpszOpen,lpszNewContent,XMLClearTags[0].lpszClose);
2132    }
2133    XMLClear *p=d->pClear+i;
2134    if (lpszNewContent!=p->lpszValue) { free((void*)p->lpszValue); p->lpszValue=lpszNewContent; }
2135    return p;
2136}
2137
2138XMLClear *XMLNode::updateClear_WOSD(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue)
2139{
2140    if (!d) return NULL;
2141    int i=indexClear(lpszOldValue);
2142    if (i>=0) return updateClear_WOSD(lpszNewValue,i);
2143    return addClear_WOSD(lpszNewValue,XMLClearTags[0].lpszOpen,XMLClearTags[0].lpszClose);
2144}
2145
2146XMLClear *XMLNode::updateClear_WOSD(XMLClear *newP,XMLClear *oldP)
2147{
2148    if (oldP) return updateClear_WOSD(newP->lpszValue,oldP->lpszValue);
2149    return NULL;
2150}
2151
2152XMLNode& XMLNode::operator=( const XMLNode& A )
2153{
2154    // shallow copy
2155    if (this != &A)
2156    {
2157        deleteNodeContent();
2158        d=A.d;
2159        if (d) (d->ref_count) ++ ;
2160    }
2161    return *this;
2162}
2163
2164XMLNode::XMLNode(const XMLNode &A)
2165{
2166    // shallow copy
2167    d=A.d;
2168    if (d) (d->ref_count)++ ;
2169}
2170
2171int XMLNode::nChildNode(XMLCSTR name) const
2172{
2173    if (!d) return 0;
2174    int i,j=0,n=d->nChild;
2175    XMLNode *pc=d->pChild;
2176    for (i=0; i<n; i++)
2177    {
2178        if (_tcsicmp(pc->d->lpszName, name)==0) j++;
2179        pc++;
2180    }
2181    return j;
2182}
2183
2184XMLNode XMLNode::getChildNode(XMLCSTR name, int *j) const
2185{
2186    if (!d) return emptyXMLNode;
2187    int i=0,n=d->nChild;
2188    if (j) i=*j;
2189    XMLNode *pc=d->pChild+i;
2190    for (; i<n; i++)
2191    {
2192        if (_tcsicmp(pc->d->lpszName, name)==0)
2193        {
2194            if (j) *j=i+1;
2195            return *pc;
2196        }
2197        pc++;
2198    }
2199    return emptyXMLNode;
2200}
2201
2202XMLNode XMLNode::getChildNode(XMLCSTR name, int j) const
2203{
2204    if (!d) return emptyXMLNode;
2205    int i=0;
2206    while (j-->0) getChildNode(name,&i);
2207    return getChildNode(name,&i);
2208}
2209
2210int XMLNode::positionOfText     (int i) const { if (i>=d->nText ) i=d->nText-1;  return findPosition(d,i,eNodeText ); }
2211int XMLNode::positionOfClear    (int i) const { if (i>=d->nClear) i=d->nClear-1; return findPosition(d,i,eNodeClear); }
2212int XMLNode::positionOfChildNode(int i) const { if (i>=d->nChild) i=d->nChild-1; return findPosition(d,i,eNodeChild); }
2213int XMLNode::positionOfText (XMLCSTR lpszValue) const { return positionOfText (indexText (lpszValue)); }
2214int XMLNode::positionOfClear(XMLCSTR lpszValue) const { return positionOfClear(indexClear(lpszValue)); }
2215int XMLNode::positionOfClear(XMLClear *a) const { if (a) return positionOfClear(a->lpszValue); return positionOfClear(); }
2216int XMLNode::positionOfChildNode(XMLNode x)  const
2217{
2218    if ((!d)||(!x.d)) return -1;
2219    XMLNodeData *dd=x.d;
2220    XMLNode *pc=d->pChild;
2221    int i=d->nChild;
2222    while (i--) if (pc[i].d==dd) return findPosition(d,i,eNodeChild);
2223    return -1;
2224}
2225int XMLNode::positionOfChildNode(XMLCSTR name, int count) const
2226{
2227    if (!name) return positionOfChildNode(count);
2228    int j=0;
2229    do { getChildNode(name,&j); if (j<0) return -1; } while (count--);
2230    return findPosition(d,j-1,eNodeChild);
2231}
2232
2233XMLNode XMLNode::getChildNodeWithAttribute(XMLCSTR name,XMLCSTR attributeName,XMLCSTR attributeValue, int *k) const
2234{
2235     int i=0,j;
2236     if (k) i=*k;
2237     XMLNode x;
2238     XMLCSTR t;
2239     do
2240     {
2241         x=getChildNode(name,&i);
2242         if (!x.isEmpty())
2243         {
2244             if (attributeValue)
2245             {
2246                 j=0;
2247                 do
2248                 {
2249                     t=x.getAttribute(attributeName,&j);
2250                     if (t&&(_tcsicmp(attributeValue,t)==0)) { if (k) *k=i+1; return x; }
2251                 } while (t);
2252             } else
2253             {
2254                 if (x.isAttributeSet(attributeName)) { if (k) *k=i+1; return x; }
2255             }
2256         }
2257     } while (!x.isEmpty());
2258     return emptyXMLNode;
2259}
2260
2261// Find an attribute on an node.
2262XMLCSTR XMLNode::getAttribute(XMLCSTR lpszAttrib, int *j) const
2263{
2264    if (!d) return NULL;
2265    int i=0,n=d->nAttribute;
2266    if (j) i=*j;
2267    XMLAttribute *pAttr=d->pAttribute+i;
2268    for (; i<n; i++)
2269    {
2270        if (_tcsicmp(pAttr->lpszName, lpszAttrib)==0)
2271        {
2272            if (j) *j=i+1;
2273            return pAttr->lpszValue;
2274        }
2275        pAttr++;
2276    }
2277    return NULL;
2278}
2279
2280char XMLNode::isAttributeSet(XMLCSTR lpszAttrib) const
2281{
2282    if (!d) return FALSE;
2283    int i,n=d->nAttribute;
2284    XMLAttribute *pAttr=d->pAttribute;
2285    for (i=0; i<n; i++)
2286    {
2287        if (_tcsicmp(pAttr->lpszName, lpszAttrib)==0)
2288        {
2289            return TRUE;
2290        }
2291        pAttr++;
2292    }
2293    return FALSE;
2294}
2295
2296XMLCSTR XMLNode::getAttribute(XMLCSTR name, int j) const
2297{
2298    if (!d) return NULL;
2299    int i=0;
2300    while (j-->0) getAttribute(name,&i);
2301    return getAttribute(name,&i);
2302}
2303
2304XMLNodeContents XMLNode::enumContents(int i) const
2305{
2306    XMLNodeContents c;
2307    if (!d) { c.type=eNodeNULL; return c; }
2308    if (i<d->nAttribute)
2309    {
2310        c.type=eNodeAttribute;
2311        c.attrib=d->pAttribute[i];
2312        return c;
2313    }
2314    i-=d->nAttribute;
2315    c.type=(XMLElementType)(d->pOrder[i]&3);
2316    i=(d->pOrder[i])>>2;
2317    switch (c.type)
2318    {
2319    case eNodeChild:     c.child = d->pChild[i];      break;
2320    case eNodeText:      c.text  = d->pText[i];       break;
2321    case eNodeClear:     c.clear = d->pClear[i];      break;
2322    default: break;
2323    }
2324    return c;
2325}
2326
2327XMLCSTR XMLNode::getName() const { if (!d) return NULL; return d->lpszName;   }
2328int XMLNode::nText()       const { if (!d) return 0;    return d->nText;      }
2329int XMLNode::nChildNode()  const { if (!d) return 0;    return d->nChild;     }
2330int XMLNode::nAttribute()  const { if (!d) return 0;    return d->nAttribute; }
2331int XMLNode::nClear()      const { if (!d) return 0;    return d->nClear;     }
2332int XMLNode::nElement()    const { if (!d) return 0;    return d->nAttribute+d->nChild+d->nText+d->nClear; }
2333XMLClear     XMLNode::getClear         (int i) const { if ((!d)||(i>=d->nClear    )) return emptyXMLClear;     return d->pClear[i];     }
2334XMLAttribute XMLNode::getAttribute     (int i) const { if ((!d)||(i>=d->nAttribute)) return emptyXMLAttribute; return d->pAttribute[i]; }
2335XMLCSTR      XMLNode::getAttributeName (int i) const { if ((!d)||(i>=d->nAttribute)) return NULL;              return d->pAttribute[i].lpszName;  }
2336XMLCSTR      XMLNode::getAttributeValue(int i) const { if ((!d)||(i>=d->nAttribute)) return NULL;              return d->pAttribute[i].lpszValue; }
2337XMLCSTR      XMLNode::getText          (int i) const { if ((!d)||(i>=d->nText     )) return NULL;              return d->pText[i];      }
2338XMLNode      XMLNode::getChildNode     (int i) const { if ((!d)||(i>=d->nChild    )) return emptyXMLNode;      return d->pChild[i];     }
2339XMLNode      XMLNode::getParentNode    (     ) const { if ((!d)||(!d->pParent     )) return emptyXMLNode;      return XMLNode(d->pParent); }
2340char         XMLNode::isDeclaration    (     ) const { if (!d) return 0;             return d->isDeclaration; }
2341char         XMLNode::isEmpty          (     ) const { return (d==NULL); }
2342
2343XMLNode       XMLNode::addChild(XMLCSTR lpszName, char isDeclaration, int pos)
2344              { return addChild_WOSD(stringDup(lpszName),isDeclaration,pos); }
2345XMLAttribute *XMLNode::addAttribute(XMLCSTR lpszName, XMLCSTR lpszValue)
2346              { return addAttribute_WOSD(stringDup(lpszName),stringDup(lpszValue)); }
2347XMLCSTR       XMLNode::addText(XMLCSTR lpszValue, int pos)
2348              { return addText_WOSD(stringDup(lpszValue),pos); }
2349XMLClear     *XMLNode::addClear(XMLCSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, int pos)
2350              { return addClear_WOSD(stringDup(lpszValue),lpszOpen,lpszClose,pos); }
2351XMLCSTR       XMLNode::updateName(XMLCSTR lpszName)
2352              { return updateName_WOSD(stringDup(lpszName)); }
2353XMLAttribute *XMLNode::updateAttribute(XMLAttribute *newAttribute, XMLAttribute *oldAttribute)
2354              { return updateAttribute_WOSD(stringDup(newAttribute->lpszValue),stringDup(newAttribute->lpszName),oldAttribute->lpszName); }
2355XMLAttribute *XMLNode::updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,int i)
2356              { return updateAttribute_WOSD(stringDup(lpszNewValue),stringDup(lpszNewName),i); }
2357XMLAttribute *XMLNode::updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,XMLCSTR lpszOldName)
2358              { return updateAttribute_WOSD(stringDup(lpszNewValue),stringDup(lpszNewName),lpszOldName); }
2359XMLCSTR       XMLNode::updateText(XMLCSTR lpszNewValue, int i)
2360              { return updateText_WOSD(stringDup(lpszNewValue),i); }
2361XMLCSTR       XMLNode::updateText(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue)
2362              { return updateText_WOSD(stringDup(lpszNewValue),lpszOldValue); }
2363XMLClear     *XMLNode::updateClear(XMLCSTR lpszNewContent, int i)
2364              { return updateClear_WOSD(stringDup(lpszNewContent),i); }
2365XMLClear     *XMLNode::updateClear(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue)
2366              { return updateClear_WOSD(stringDup(lpszNewValue),lpszOldValue); }
2367XMLClear     *XMLNode::updateClear(XMLClear *newP,XMLClear *oldP)
2368              { return updateClear_WOSD(stringDup(newP->lpszValue),oldP->lpszValue); }
2369
2370void XMLNode::setGlobalOptions(char _guessUnicodeChars, char _strictUTF8Parsing, char _dropWhiteSpace)
2371{
2372    guessUnicodeChars=_guessUnicodeChars; dropWhiteSpace=_dropWhiteSpace; strictUTF8Parsing=_strictUTF8Parsing;
2373#ifndef _XMLUNICODE
2374    if (strictUTF8Parsing) XML_ByteTable=XML_utf8ByteTable; else XML_ByteTable=XML_asciiByteTable;
2375#endif
2376}
2377
2378char XMLNode::guessUTF8ParsingParameterValue(void *buf,int l, char useXMLEncodingAttribute)
2379{
2380#ifdef _XMLUNICODE
2381    return 0;
2382#else
2383    if (l<25) return 0;
2384    if (myIsTextUnicode(buf,l)) return 0;
2385    unsigned char *b=(unsigned char*)buf;
2386    if ((b[0]==0xef)&&(b[1]==0xbb)&&(b[2]==0xbf)) return 1;
2387
2388    // Match utf-8 model ?
2389    int i=0;
2390    while (i<l)
2391        switch (XML_utf8ByteTable[b[i]])
2392        {
2393        case 4: i++; if ((i<l)&&(b[i]& 0xC0)!=0x80) return 0; // 10bbbbbb ?
2394        case 3: i++; if ((i<l)&&(b[i]& 0xC0)!=0x80) return 0; // 10bbbbbb ?
2395        case 2: i++; if ((i<l)&&(b[i]& 0xC0)!=0x80) return 0; // 10bbbbbb ?
2396        case 1: i++; break;
2397        case 0: i=l;
2398        }
2399    if (!useXMLEncodingAttribute) return 1;
2400    // if encoding is specified and different from utf-8 than it's non-utf8
2401    // otherwise it's utf-8
2402    char bb[201];
2403    l=mmin(l,200);
2404    memcpy(bb,buf,l); // copy buf into bb to be able to do "bb[l]=0"
2405    bb[l]=0;
2406    b=(unsigned char*)strstr(bb,"encoding");
2407    if (!b) return 1;
2408    b+=8; while XML_isSPACECHAR(*b) b++; if (*b!='=') return 1;
2409    b++;  while XML_isSPACECHAR(*b) b++; if ((*b!='\'')&&(*b!='"')) return 1;
2410    b++;  while XML_isSPACECHAR(*b) b++; if ((_strnicmp((char*)b,"utf-8",5)==0)||
2411                                             (_strnicmp((char*)b,"utf8",4)==0)) return 1;
2412    return 0;
2413#endif
2414}
2415#undef XML_isSPACECHAR
2416
2417//////////////////////////////////////////////////////////
2418//      Here starts the base64 conversion functions.    //
2419//////////////////////////////////////////////////////////
2420
2421static const char base64Fillchar = _T('='); // used to mark partial words at the end
2422
2423// this lookup table defines the base64 encoding
2424XMLCSTR base64EncodeTable=_T("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
2425
2426// Decode Table gives the index of any valid base64 character in the Base64 table]
2427// 96: '='  -   97: space char   -   98: illegal char   -   99: end of string
2428const unsigned char base64DecodeTable[] = {
2429    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
2430    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
2431    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
2432    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
2433    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
2434    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
2435    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
2436    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
2437    98,98,98,98,98,98,98,98,98,98,  98,98,98,98,98,98                                               //240 -255
2438};
2439
2440XMLParserBase64Tool::~XMLParserBase64Tool(){ freeBuffer(); }
2441
2442void XMLParserBase64Tool::freeBuffer(){ if (buf) free(buf); buf=NULL; buflen=0; }
2443
2444int XMLParserBase64Tool::encodeLength(int inlen, char formatted)
2445{
2446    unsigned int i=((inlen-1)/3*4+4+1);
2447    if (formatted) i+=inlen/54;
2448    return i;
2449}
2450
2451XMLSTR XMLParserBase64Tool::encode(unsigned char *inbuf, unsigned int inlen, char formatted)
2452{
2453    int i=encodeLength(inlen,formatted),k=17,eLen=inlen/3,j;
2454    alloc(i*sizeof(XMLCHAR));
2455    XMLSTR curr=(XMLSTR)buf;
2456    for(i=0;i<eLen;i++)
2457    {
2458        // Copy next three bytes into lower 24 bits of int, paying attention to sign.
2459        j=(inbuf[0]<<16)|(inbuf[1]<<8)|inbuf[2]; inbuf+=3;
2460        // Encode the int into four chars
2461        *(curr++)=base64EncodeTable[ j>>18      ];
2462        *(curr++)=base64EncodeTable[(j>>12)&0x3f];
2463        *(curr++)=base64EncodeTable[(j>> 6)&0x3f];
2464        *(curr++)=base64EncodeTable[(j    )&0x3f];
2465        if (formatted) { if (!k) { *(curr++)=_T('\n'); k=18; } k--; }
2466    }
2467    eLen=inlen-eLen*3; // 0 - 2.
2468    if (eLen==1)
2469    {
2470        *(curr++)=base64EncodeTable[ inbuf[0]>>2      ];
2471        *(curr++)=base64EncodeTable[(inbuf[0]<<4)&0x3F];
2472        *(curr++)=base64Fillchar;
2473        *(curr++)=base64Fillchar;
2474    } else if (eLen==2)
2475    {
2476        j=(inbuf[0]<<8)|inbuf[1];
2477        *(curr++)=base64EncodeTable[ j>>10      ];
2478        *(curr++)=base64EncodeTable[(j>> 4)&0x3f];
2479        *(curr++)=base64EncodeTable[(j<< 2)&0x3f];
2480        *(curr++)=base64Fillchar;
2481    }
2482    *(curr++)=0;
2483    return (XMLSTR)buf;
2484}
2485
2486unsigned int XMLParserBase64Tool::decodeSize(XMLCSTR data,XMLError *xe)
2487{
2488     if (xe) *xe=eXMLErrorNone;
2489    int size=0;
2490    unsigned char c;
2491    //skip any extra characters (e.g. newlines or spaces)
2492    while (*data)
2493    {
2494#ifdef _XMLUNICODE
2495        if (*data>255) { if (xe) *xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; }
2496#endif
2497        c=base64DecodeTable[(unsigned char)(*data)];
2498        if (c<97) size++;
2499        else if (c==98) { if (xe) *xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; }
2500        data++;
2501    }
2502    if (xe&&(size%4!=0)) *xe=eXMLErrorBase64DataSizeIsNotMultipleOf4;
2503    if (size==0) return 0;
2504    do { data--; size--; } while(*data==base64Fillchar); size++;
2505    return (unsigned int)((size*3)/4);
2506}
2507
2508unsigned char XMLParserBase64Tool::decode(XMLCSTR data, unsigned char *buf, int len, XMLError *xe)
2509{
2510    if (xe) *xe=eXMLErrorNone;
2511    int i=0,p=0;
2512    unsigned char d,c;
2513    for(;;)
2514    {
2515
2516#ifdef _XMLUNICODE
2517#define BASE64DECODE_READ_NEXT_CHAR(c)                                              \
2518        do {                                                                        \
2519            if (data[i]>255){ c=98; break; }                                        \
2520            c=base64DecodeTable[(unsigned char)data[i++]];                       \
2521        }while (c==97);                                                             \
2522        if(c==98){ if(xe)*xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; }
2523#else
2524#define BASE64DECODE_READ_NEXT_CHAR(c)                                           \
2525        do { c=base64DecodeTable[(unsigned char)data[i++]]; }while (c==97);   \
2526        if(c==98){ if(xe)*xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; }
2527#endif
2528
2529        BASE64DECODE_READ_NEXT_CHAR(c)
2530        if (c==99) { return 2; }
2531        if (c==96)
2532        {
2533            if (p==(int)len) return 2;
2534            if (xe) *xe=eXMLErrorBase64DecodeTruncatedData;
2535            return 1;
2536        }
2537
2538        BASE64DECODE_READ_NEXT_CHAR(d)
2539        if ((d==99)||(d==96)) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData;  return 1; }
2540        if (p==(int)len) {      if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall; return 0; }
2541        buf[p++]=(c<<2)|((d>>4)&0x3);
2542
2543        BASE64DECODE_READ_NEXT_CHAR(c)
2544        if (c==99) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData;  return 1; }
2545        if (p==(int)len)
2546        {
2547            if (c==96) return 2;
2548            if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall;
2549            return 0;
2550        }
2551        if (c==96) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData;  return 1; }
2552        buf[p++]=((d<<4)&0xf0)|((c>>2)&0xf);
2553
2554        BASE64DECODE_READ_NEXT_CHAR(d)
2555        if (d==99 ) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData;  return 1; }
2556        if (p==(int)len)
2557        {
2558            if (d==96) return 2;
2559            if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall;
2560            return 0;
2561        }
2562        if (d==96) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData;  return 1; }
2563        buf[p++]=((c<<6)&0xc0)|d;
2564    }
2565}
2566#undef BASE64DECODE_READ_NEXT_CHAR
2567
2568void XMLParserBase64Tool::alloc(int newsize)
2569{
2570    if ((!buf)&&(newsize)) { buf=malloc(newsize); buflen=newsize; return; }
2571    if (newsize>buflen) { buf=realloc(buf,newsize); buflen=newsize; }
2572}
2573
2574unsigned char *XMLParserBase64Tool::decode(XMLCSTR data, int *outlen, XMLError *xe)
2575{
2576    if (xe) *xe=eXMLErrorNone;
2577    unsigned int len=decodeSize(data,xe);
2578    if (outlen) *outlen=len;
2579    if (!len) return NULL;
2580    alloc(len+1);
2581    if(!decode(data,(unsigned char*)buf,len,xe)){ return NULL; }
2582    return (unsigned char*)buf;
2583}
2584
2585XMLNode XMLNode::operator/(XMLCSTR name)
2586{
2587        return getChildNode(name);
2588}
2589
2590XMLCSTR XMLNode::operator|(XMLCSTR name)
2591{
2592        return getAttribute(name);
2593}
Note: See TracBrowser for help on using the repository browser.