Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/xml/xmlParser.cc @ 164

Last change on this file since 164 was 164, checked in by nicolasc, 16 years ago

Brute force megre of xml branch

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