]> Creatis software - cpPlugins.git/blob - lib/tinyxml2/tinyxml2.cpp
...
[cpPlugins.git] / lib / tinyxml2 / tinyxml2.cpp
1 /*\r
2 Original code by Lee Thomason (www.grinninglizard.com)\r
3 \r
4 This software is provided 'as-is', without any express or implied\r
5 warranty. In no event will the authors be held liable for any\r
6 damages arising from the use of this software.\r
7 \r
8 Permission is granted to anyone to use this software for any\r
9 purpose, including commercial applications, and to alter it and\r
10 redistribute it freely, subject to the following restrictions:\r
11 \r
12 1. The origin of this software must not be misrepresented; you must\r
13 not claim that you wrote the original software. If you use this\r
14 software in a product, an acknowledgment in the product documentation\r
15 would be appreciated but is not required.\r
16 \r
17 2. Altered source versions must be plainly marked as such, and\r
18 must not be misrepresented as being the original software.\r
19 \r
20 3. This notice may not be removed or altered from any source\r
21 distribution.\r
22 */\r
23 \r
24 #include "tinyxml2.h"\r
25 \r
26 #include <new>          // yes, this one new style header, is in the Android SDK.\r
27 #if defined(ANDROID_NDK) || defined(__QNXNTO__)\r
28 #   include <stddef.h>\r
29 #   include <stdarg.h>\r
30 #else\r
31 #   include <cstddef>\r
32 #   include <cstdarg>\r
33 #endif\r
34 \r
35 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)\r
36         // Microsoft Visual Studio, version 2005 and higher. Not WinCE.\r
37         /*int _snprintf_s(\r
38            char *buffer,\r
39            size_t sizeOfBuffer,\r
40            size_t count,\r
41            const char *format [,\r
42                   argument] ...\r
43         );*/\r
44         static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... )\r
45         {\r
46                 va_list va;\r
47                 va_start( va, format );\r
48                 int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );\r
49                 va_end( va );\r
50                 return result;\r
51         }\r
52 \r
53         static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va )\r
54         {\r
55                 int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );\r
56                 return result;\r
57         }\r
58 \r
59         #define TIXML_VSCPRINTF _vscprintf\r
60         #define TIXML_SSCANF    sscanf_s\r
61 #elif defined _MSC_VER\r
62         // Microsoft Visual Studio 2003 and earlier or WinCE\r
63         #define TIXML_SNPRINTF  _snprintf\r
64         #define TIXML_VSNPRINTF _vsnprintf\r
65         #define TIXML_SSCANF    sscanf\r
66         #if (_MSC_VER < 1400 ) && (!defined WINCE)\r
67                 // Microsoft Visual Studio 2003 and not WinCE.\r
68                 #define TIXML_VSCPRINTF   _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have.\r
69         #else\r
70                 // Microsoft Visual Studio 2003 and earlier or WinCE.\r
71                 static inline int TIXML_VSCPRINTF( const char* format, va_list va )\r
72                 {\r
73                         int len = 512;\r
74                         for (;;) {\r
75                                 len = len*2;\r
76                                 char* str = new char[len]();\r
77                                 const int required = _vsnprintf(str, len, format, va);\r
78                                 delete[] str;\r
79                                 if ( required != -1 ) {\r
80                                         TIXMLASSERT( required >= 0 );\r
81                                         len = required;\r
82                                         break;\r
83                                 }\r
84                         }\r
85                         TIXMLASSERT( len >= 0 );\r
86                         return len;\r
87                 }\r
88         #endif\r
89 #else\r
90         // GCC version 3 and higher\r
91         //#warning( "Using sn* functions." )\r
92         #define TIXML_SNPRINTF  snprintf\r
93         #define TIXML_VSNPRINTF vsnprintf\r
94         static inline int TIXML_VSCPRINTF( const char* format, va_list va )\r
95         {\r
96                 int len = vsnprintf( 0, 0, format, va );\r
97                 TIXMLASSERT( len >= 0 );\r
98                 return len;\r
99         }\r
100         #define TIXML_SSCANF   sscanf\r
101 #endif\r
102 \r
103 \r
104 static const char LINE_FEED                             = (char)0x0a;                   // all line endings are normalized to LF\r
105 static const char LF = LINE_FEED;\r
106 static const char CARRIAGE_RETURN               = (char)0x0d;                   // CR gets filtered out\r
107 static const char CR = CARRIAGE_RETURN;\r
108 static const char SINGLE_QUOTE                  = '\'';\r
109 static const char DOUBLE_QUOTE                  = '\"';\r
110 \r
111 // Bunch of unicode info at:\r
112 //              http://www.unicode.org/faq/utf_bom.html\r
113 //      ef bb bf (Microsoft "lead bytes") - designates UTF-8\r
114 \r
115 static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;\r
116 static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;\r
117 static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;\r
118 \r
119 namespace tinyxml2\r
120 {\r
121 \r
122 struct Entity {\r
123     const char* pattern;\r
124     int length;\r
125     char value;\r
126 };\r
127 \r
128 static const int NUM_ENTITIES = 5;\r
129 static const Entity entities[NUM_ENTITIES] = {\r
130     { "quot", 4,        DOUBLE_QUOTE },\r
131     { "amp", 3,         '&'  },\r
132     { "apos", 4,        SINGLE_QUOTE },\r
133     { "lt",     2,              '<'      },\r
134     { "gt",     2,              '>'      }\r
135 };\r
136 \r
137 \r
138 StrPair::~StrPair()\r
139 {\r
140     Reset();\r
141 }\r
142 \r
143 \r
144 void StrPair::TransferTo( StrPair* other )\r
145 {\r
146     if ( this == other ) {\r
147         return;\r
148     }\r
149     // This in effect implements the assignment operator by "moving"\r
150     // ownership (as in auto_ptr).\r
151 \r
152     TIXMLASSERT( other->_flags == 0 );\r
153     TIXMLASSERT( other->_start == 0 );\r
154     TIXMLASSERT( other->_end == 0 );\r
155 \r
156     other->Reset();\r
157 \r
158     other->_flags = _flags;\r
159     other->_start = _start;\r
160     other->_end = _end;\r
161 \r
162     _flags = 0;\r
163     _start = 0;\r
164     _end = 0;\r
165 }\r
166 \r
167 void StrPair::Reset()\r
168 {\r
169     if ( _flags & NEEDS_DELETE ) {\r
170         delete [] _start;\r
171     }\r
172     _flags = 0;\r
173     _start = 0;\r
174     _end = 0;\r
175 }\r
176 \r
177 \r
178 void StrPair::SetStr( const char* str, int flags )\r
179 {\r
180     TIXMLASSERT( str );\r
181     Reset();\r
182     size_t len = strlen( str );\r
183     TIXMLASSERT( _start == 0 );\r
184     _start = new char[ len+1 ];\r
185     memcpy( _start, str, len+1 );\r
186     _end = _start + len;\r
187     _flags = flags | NEEDS_DELETE;\r
188 }\r
189 \r
190 \r
191 char* StrPair::ParseText( char* p, const char* endTag, int strFlags )\r
192 {\r
193     TIXMLASSERT( endTag && *endTag );\r
194 \r
195     char* start = p;\r
196     char  endChar = *endTag;\r
197     size_t length = strlen( endTag );\r
198 \r
199     // Inner loop of text parsing.\r
200     while ( *p ) {\r
201         if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {\r
202             Set( start, p, strFlags );\r
203             return p + length;\r
204         }\r
205         ++p;\r
206     }\r
207     return 0;\r
208 }\r
209 \r
210 \r
211 char* StrPair::ParseName( char* p )\r
212 {\r
213     if ( !p || !(*p) ) {\r
214         return 0;\r
215     }\r
216     if ( !XMLUtil::IsNameStartChar( *p ) ) {\r
217         return 0;\r
218     }\r
219 \r
220     char* const start = p;\r
221     ++p;\r
222     while ( *p && XMLUtil::IsNameChar( *p ) ) {\r
223         ++p;\r
224     }\r
225 \r
226     Set( start, p, 0 );\r
227     return p;\r
228 }\r
229 \r
230 \r
231 void StrPair::CollapseWhitespace()\r
232 {\r
233     // Adjusting _start would cause undefined behavior on delete[]\r
234     TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );\r
235     // Trim leading space.\r
236     _start = XMLUtil::SkipWhiteSpace( _start );\r
237 \r
238     if ( *_start ) {\r
239         char* p = _start;       // the read pointer\r
240         char* q = _start;       // the write pointer\r
241 \r
242         while( *p ) {\r
243             if ( XMLUtil::IsWhiteSpace( *p )) {\r
244                 p = XMLUtil::SkipWhiteSpace( p );\r
245                 if ( *p == 0 ) {\r
246                     break;    // don't write to q; this trims the trailing space.\r
247                 }\r
248                 *q = ' ';\r
249                 ++q;\r
250             }\r
251             *q = *p;\r
252             ++q;\r
253             ++p;\r
254         }\r
255         *q = 0;\r
256     }\r
257 }\r
258 \r
259 \r
260 const char* StrPair::GetStr()\r
261 {\r
262     TIXMLASSERT( _start );\r
263     TIXMLASSERT( _end );\r
264     if ( _flags & NEEDS_FLUSH ) {\r
265         *_end = 0;\r
266         _flags ^= NEEDS_FLUSH;\r
267 \r
268         if ( _flags ) {\r
269             char* p = _start;   // the read pointer\r
270             char* q = _start;   // the write pointer\r
271 \r
272             while( p < _end ) {\r
273                 if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {\r
274                     // CR-LF pair becomes LF\r
275                     // CR alone becomes LF\r
276                     // LF-CR becomes LF\r
277                     if ( *(p+1) == LF ) {\r
278                         p += 2;\r
279                     }\r
280                     else {\r
281                         ++p;\r
282                     }\r
283                     *q++ = LF;\r
284                 }\r
285                 else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {\r
286                     if ( *(p+1) == CR ) {\r
287                         p += 2;\r
288                     }\r
289                     else {\r
290                         ++p;\r
291                     }\r
292                     *q++ = LF;\r
293                 }\r
294                 else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {\r
295                     // Entities handled by tinyXML2:\r
296                     // - special entities in the entity table [in/out]\r
297                     // - numeric character reference [in]\r
298                     //   &#20013; or &#x4e2d;\r
299 \r
300                     if ( *(p+1) == '#' ) {\r
301                         const int buflen = 10;\r
302                         char buf[buflen] = { 0 };\r
303                         int len = 0;\r
304                         char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );\r
305                         if ( adjusted == 0 ) {\r
306                             *q = *p;\r
307                             ++p;\r
308                             ++q;\r
309                         }\r
310                         else {\r
311                             TIXMLASSERT( 0 <= len && len <= buflen );\r
312                             TIXMLASSERT( q + len <= adjusted );\r
313                             p = adjusted;\r
314                             memcpy( q, buf, len );\r
315                             q += len;\r
316                         }\r
317                     }\r
318                     else {\r
319                         bool entityFound = false;\r
320                         for( int i = 0; i < NUM_ENTITIES; ++i ) {\r
321                             const Entity& entity = entities[i];\r
322                             if ( strncmp( p + 1, entity.pattern, entity.length ) == 0\r
323                                     && *( p + entity.length + 1 ) == ';' ) {\r
324                                 // Found an entity - convert.\r
325                                 *q = entity.value;\r
326                                 ++q;\r
327                                 p += entity.length + 2;\r
328                                 entityFound = true;\r
329                                 break;\r
330                             }\r
331                         }\r
332                         if ( !entityFound ) {\r
333                             // fixme: treat as error?\r
334                             ++p;\r
335                             ++q;\r
336                         }\r
337                     }\r
338                 }\r
339                 else {\r
340                     *q = *p;\r
341                     ++p;\r
342                     ++q;\r
343                 }\r
344             }\r
345             *q = 0;\r
346         }\r
347         // The loop below has plenty going on, and this\r
348         // is a less useful mode. Break it out.\r
349         if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {\r
350             CollapseWhitespace();\r
351         }\r
352         _flags = (_flags & NEEDS_DELETE);\r
353     }\r
354     TIXMLASSERT( _start );\r
355     return _start;\r
356 }\r
357 \r
358 \r
359 \r
360 \r
361 // --------- XMLUtil ----------- //\r
362 \r
363 const char* XMLUtil::ReadBOM( const char* p, bool* bom )\r
364 {\r
365     TIXMLASSERT( p );\r
366     TIXMLASSERT( bom );\r
367     *bom = false;\r
368     const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);\r
369     // Check for BOM:\r
370     if (    *(pu+0) == TIXML_UTF_LEAD_0\r
371             && *(pu+1) == TIXML_UTF_LEAD_1\r
372             && *(pu+2) == TIXML_UTF_LEAD_2 ) {\r
373         *bom = true;\r
374         p += 3;\r
375     }\r
376     TIXMLASSERT( p );\r
377     return p;\r
378 }\r
379 \r
380 \r
381 void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )\r
382 {\r
383     const unsigned long BYTE_MASK = 0xBF;\r
384     const unsigned long BYTE_MARK = 0x80;\r
385     const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };\r
386 \r
387     if (input < 0x80) {\r
388         *length = 1;\r
389     }\r
390     else if ( input < 0x800 ) {\r
391         *length = 2;\r
392     }\r
393     else if ( input < 0x10000 ) {\r
394         *length = 3;\r
395     }\r
396     else if ( input < 0x200000 ) {\r
397         *length = 4;\r
398     }\r
399     else {\r
400         *length = 0;    // This code won't convert this correctly anyway.\r
401         return;\r
402     }\r
403 \r
404     output += *length;\r
405 \r
406     // Scary scary fall throughs.\r
407     switch (*length) {\r
408         case 4:\r
409             --output;\r
410             *output = (char)((input | BYTE_MARK) & BYTE_MASK);\r
411             input >>= 6;\r
412         case 3:\r
413             --output;\r
414             *output = (char)((input | BYTE_MARK) & BYTE_MASK);\r
415             input >>= 6;\r
416         case 2:\r
417             --output;\r
418             *output = (char)((input | BYTE_MARK) & BYTE_MASK);\r
419             input >>= 6;\r
420         case 1:\r
421             --output;\r
422             *output = (char)(input | FIRST_BYTE_MARK[*length]);\r
423             break;\r
424         default:\r
425             TIXMLASSERT( false );\r
426     }\r
427 }\r
428 \r
429 \r
430 const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )\r
431 {\r
432     // Presume an entity, and pull it out.\r
433     *length = 0;\r
434 \r
435     if ( *(p+1) == '#' && *(p+2) ) {\r
436         unsigned long ucs = 0;\r
437         TIXMLASSERT( sizeof( ucs ) >= 4 );\r
438         ptrdiff_t delta = 0;\r
439         unsigned mult = 1;\r
440         static const char SEMICOLON = ';';\r
441 \r
442         if ( *(p+2) == 'x' ) {\r
443             // Hexadecimal.\r
444             const char* q = p+3;\r
445             if ( !(*q) ) {\r
446                 return 0;\r
447             }\r
448 \r
449             q = strchr( q, SEMICOLON );\r
450 \r
451             if ( !q ) {\r
452                 return 0;\r
453             }\r
454             TIXMLASSERT( *q == SEMICOLON );\r
455 \r
456             delta = q-p;\r
457             --q;\r
458 \r
459             while ( *q != 'x' ) {\r
460                 unsigned int digit = 0;\r
461 \r
462                 if ( *q >= '0' && *q <= '9' ) {\r
463                     digit = *q - '0';\r
464                 }\r
465                 else if ( *q >= 'a' && *q <= 'f' ) {\r
466                     digit = *q - 'a' + 10;\r
467                 }\r
468                 else if ( *q >= 'A' && *q <= 'F' ) {\r
469                     digit = *q - 'A' + 10;\r
470                 }\r
471                 else {\r
472                     return 0;\r
473                 }\r
474                 TIXMLASSERT( digit >= 0 && digit < 16);\r
475                 TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );\r
476                 const unsigned int digitScaled = mult * digit;\r
477                 TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );\r
478                 ucs += digitScaled;\r
479                 TIXMLASSERT( mult <= UINT_MAX / 16 );\r
480                 mult *= 16;\r
481                 --q;\r
482             }\r
483         }\r
484         else {\r
485             // Decimal.\r
486             const char* q = p+2;\r
487             if ( !(*q) ) {\r
488                 return 0;\r
489             }\r
490 \r
491             q = strchr( q, SEMICOLON );\r
492 \r
493             if ( !q ) {\r
494                 return 0;\r
495             }\r
496             TIXMLASSERT( *q == SEMICOLON );\r
497 \r
498             delta = q-p;\r
499             --q;\r
500 \r
501             while ( *q != '#' ) {\r
502                 if ( *q >= '0' && *q <= '9' ) {\r
503                     const unsigned int digit = *q - '0';\r
504                     TIXMLASSERT( digit >= 0 && digit < 10);\r
505                     TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );\r
506                     const unsigned int digitScaled = mult * digit;\r
507                     TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );\r
508                     ucs += digitScaled;\r
509                 }\r
510                 else {\r
511                     return 0;\r
512                 }\r
513                 TIXMLASSERT( mult <= UINT_MAX / 10 );\r
514                 mult *= 10;\r
515                 --q;\r
516             }\r
517         }\r
518         // convert the UCS to UTF-8\r
519         ConvertUTF32ToUTF8( ucs, value, length );\r
520         return p + delta + 1;\r
521     }\r
522     return p+1;\r
523 }\r
524 \r
525 \r
526 void XMLUtil::ToStr( int v, char* buffer, int bufferSize )\r
527 {\r
528     TIXML_SNPRINTF( buffer, bufferSize, "%d", v );\r
529 }\r
530 \r
531 \r
532 void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )\r
533 {\r
534     TIXML_SNPRINTF( buffer, bufferSize, "%u", v );\r
535 }\r
536 \r
537 \r
538 void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )\r
539 {\r
540     TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 );\r
541 }\r
542 \r
543 /*\r
544         ToStr() of a number is a very tricky topic.\r
545         https://github.com/leethomason/tinyxml2/issues/106\r
546 */\r
547 void XMLUtil::ToStr( float v, char* buffer, int bufferSize )\r
548 {\r
549     TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );\r
550 }\r
551 \r
552 \r
553 void XMLUtil::ToStr( double v, char* buffer, int bufferSize )\r
554 {\r
555     TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );\r
556 }\r
557 \r
558 \r
559 bool XMLUtil::ToInt( const char* str, int* value )\r
560 {\r
561     if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {\r
562         return true;\r
563     }\r
564     return false;\r
565 }\r
566 \r
567 bool XMLUtil::ToUnsigned( const char* str, unsigned *value )\r
568 {\r
569     if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {\r
570         return true;\r
571     }\r
572     return false;\r
573 }\r
574 \r
575 bool XMLUtil::ToBool( const char* str, bool* value )\r
576 {\r
577     int ival = 0;\r
578     if ( ToInt( str, &ival )) {\r
579         *value = (ival==0) ? false : true;\r
580         return true;\r
581     }\r
582     if ( StringEqual( str, "true" ) ) {\r
583         *value = true;\r
584         return true;\r
585     }\r
586     else if ( StringEqual( str, "false" ) ) {\r
587         *value = false;\r
588         return true;\r
589     }\r
590     return false;\r
591 }\r
592 \r
593 \r
594 bool XMLUtil::ToFloat( const char* str, float* value )\r
595 {\r
596     if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {\r
597         return true;\r
598     }\r
599     return false;\r
600 }\r
601 \r
602 bool XMLUtil::ToDouble( const char* str, double* value )\r
603 {\r
604     if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {\r
605         return true;\r
606     }\r
607     return false;\r
608 }\r
609 \r
610 \r
611 char* XMLDocument::Identify( char* p, XMLNode** node )\r
612 {\r
613     TIXMLASSERT( node );\r
614     TIXMLASSERT( p );\r
615     char* const start = p;\r
616     p = XMLUtil::SkipWhiteSpace( p );\r
617     if( !*p ) {\r
618         *node = 0;\r
619         TIXMLASSERT( p );\r
620         return p;\r
621     }\r
622 \r
623     // These strings define the matching patterns:\r
624     static const char* xmlHeader                = { "<?" };\r
625     static const char* commentHeader    = { "<!--" };\r
626     static const char* cdataHeader              = { "<![CDATA[" };\r
627     static const char* dtdHeader                = { "<!" };\r
628     static const char* elementHeader    = { "<" };      // and a header for everything else; check last.\r
629 \r
630     static const int xmlHeaderLen               = 2;\r
631     static const int commentHeaderLen   = 4;\r
632     static const int cdataHeaderLen             = 9;\r
633     static const int dtdHeaderLen               = 2;\r
634     static const int elementHeaderLen   = 1;\r
635 \r
636     TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) );                // use same memory pool\r
637     TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) );    // use same memory pool\r
638     XMLNode* returnNode = 0;\r
639     if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {\r
640         TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );\r
641         returnNode = new (_commentPool.Alloc()) XMLDeclaration( this );\r
642         returnNode->_memPool = &_commentPool;\r
643         p += xmlHeaderLen;\r
644     }\r
645     else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {\r
646         TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );\r
647         returnNode = new (_commentPool.Alloc()) XMLComment( this );\r
648         returnNode->_memPool = &_commentPool;\r
649         p += commentHeaderLen;\r
650     }\r
651     else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {\r
652         TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );\r
653         XMLText* text = new (_textPool.Alloc()) XMLText( this );\r
654         returnNode = text;\r
655         returnNode->_memPool = &_textPool;\r
656         p += cdataHeaderLen;\r
657         text->SetCData( true );\r
658     }\r
659     else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {\r
660         TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );\r
661         returnNode = new (_commentPool.Alloc()) XMLUnknown( this );\r
662         returnNode->_memPool = &_commentPool;\r
663         p += dtdHeaderLen;\r
664     }\r
665     else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {\r
666         TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );\r
667         returnNode = new (_elementPool.Alloc()) XMLElement( this );\r
668         returnNode->_memPool = &_elementPool;\r
669         p += elementHeaderLen;\r
670     }\r
671     else {\r
672         TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );\r
673         returnNode = new (_textPool.Alloc()) XMLText( this );\r
674         returnNode->_memPool = &_textPool;\r
675         p = start;      // Back it up, all the text counts.\r
676     }\r
677 \r
678     TIXMLASSERT( returnNode );\r
679     TIXMLASSERT( p );\r
680     *node = returnNode;\r
681     return p;\r
682 }\r
683 \r
684 \r
685 bool XMLDocument::Accept( XMLVisitor* visitor ) const\r
686 {\r
687     TIXMLASSERT( visitor );\r
688     if ( visitor->VisitEnter( *this ) ) {\r
689         for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {\r
690             if ( !node->Accept( visitor ) ) {\r
691                 break;\r
692             }\r
693         }\r
694     }\r
695     return visitor->VisitExit( *this );\r
696 }\r
697 \r
698 \r
699 // --------- XMLNode ----------- //\r
700 \r
701 XMLNode::XMLNode( XMLDocument* doc ) :\r
702     _document( doc ),\r
703     _parent( 0 ),\r
704     _firstChild( 0 ), _lastChild( 0 ),\r
705     _prev( 0 ), _next( 0 ),\r
706     _memPool( 0 )\r
707 {\r
708 }\r
709 \r
710 \r
711 XMLNode::~XMLNode()\r
712 {\r
713     DeleteChildren();\r
714     if ( _parent ) {\r
715         _parent->Unlink( this );\r
716     }\r
717 }\r
718 \r
719 const char* XMLNode::Value() const \r
720 {\r
721     // Catch an edge case: XMLDocuments don't have a a Value. Carefully return nullptr.\r
722     if ( this->ToDocument() )\r
723         return 0;\r
724     return _value.GetStr();\r
725 }\r
726 \r
727 void XMLNode::SetValue( const char* str, bool staticMem )\r
728 {\r
729     if ( staticMem ) {\r
730         _value.SetInternedStr( str );\r
731     }\r
732     else {\r
733         _value.SetStr( str );\r
734     }\r
735 }\r
736 \r
737 \r
738 void XMLNode::DeleteChildren()\r
739 {\r
740     while( _firstChild ) {\r
741         TIXMLASSERT( _lastChild );\r
742         TIXMLASSERT( _firstChild->_document == _document );\r
743         XMLNode* node = _firstChild;\r
744         Unlink( node );\r
745 \r
746         DeleteNode( node );\r
747     }\r
748     _firstChild = _lastChild = 0;\r
749 }\r
750 \r
751 \r
752 void XMLNode::Unlink( XMLNode* child )\r
753 {\r
754     TIXMLASSERT( child );\r
755     TIXMLASSERT( child->_document == _document );\r
756     TIXMLASSERT( child->_parent == this );\r
757     if ( child == _firstChild ) {\r
758         _firstChild = _firstChild->_next;\r
759     }\r
760     if ( child == _lastChild ) {\r
761         _lastChild = _lastChild->_prev;\r
762     }\r
763 \r
764     if ( child->_prev ) {\r
765         child->_prev->_next = child->_next;\r
766     }\r
767     if ( child->_next ) {\r
768         child->_next->_prev = child->_prev;\r
769     }\r
770         child->_parent = 0;\r
771 }\r
772 \r
773 \r
774 void XMLNode::DeleteChild( XMLNode* node )\r
775 {\r
776     TIXMLASSERT( node );\r
777     TIXMLASSERT( node->_document == _document );\r
778     TIXMLASSERT( node->_parent == this );\r
779     Unlink( node );\r
780     DeleteNode( node );\r
781 }\r
782 \r
783 \r
784 XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )\r
785 {\r
786     TIXMLASSERT( addThis );\r
787     if ( addThis->_document != _document ) {\r
788         TIXMLASSERT( false );\r
789         return 0;\r
790     }\r
791     InsertChildPreamble( addThis );\r
792 \r
793     if ( _lastChild ) {\r
794         TIXMLASSERT( _firstChild );\r
795         TIXMLASSERT( _lastChild->_next == 0 );\r
796         _lastChild->_next = addThis;\r
797         addThis->_prev = _lastChild;\r
798         _lastChild = addThis;\r
799 \r
800         addThis->_next = 0;\r
801     }\r
802     else {\r
803         TIXMLASSERT( _firstChild == 0 );\r
804         _firstChild = _lastChild = addThis;\r
805 \r
806         addThis->_prev = 0;\r
807         addThis->_next = 0;\r
808     }\r
809     addThis->_parent = this;\r
810     return addThis;\r
811 }\r
812 \r
813 \r
814 XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )\r
815 {\r
816     TIXMLASSERT( addThis );\r
817     if ( addThis->_document != _document ) {\r
818         TIXMLASSERT( false );\r
819         return 0;\r
820     }\r
821     InsertChildPreamble( addThis );\r
822 \r
823     if ( _firstChild ) {\r
824         TIXMLASSERT( _lastChild );\r
825         TIXMLASSERT( _firstChild->_prev == 0 );\r
826 \r
827         _firstChild->_prev = addThis;\r
828         addThis->_next = _firstChild;\r
829         _firstChild = addThis;\r
830 \r
831         addThis->_prev = 0;\r
832     }\r
833     else {\r
834         TIXMLASSERT( _lastChild == 0 );\r
835         _firstChild = _lastChild = addThis;\r
836 \r
837         addThis->_prev = 0;\r
838         addThis->_next = 0;\r
839     }\r
840     addThis->_parent = this;\r
841     return addThis;\r
842 }\r
843 \r
844 \r
845 XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )\r
846 {\r
847     TIXMLASSERT( addThis );\r
848     if ( addThis->_document != _document ) {\r
849         TIXMLASSERT( false );\r
850         return 0;\r
851     }\r
852 \r
853     TIXMLASSERT( afterThis );\r
854 \r
855     if ( afterThis->_parent != this ) {\r
856         TIXMLASSERT( false );\r
857         return 0;\r
858     }\r
859 \r
860     if ( afterThis->_next == 0 ) {\r
861         // The last node or the only node.\r
862         return InsertEndChild( addThis );\r
863     }\r
864     InsertChildPreamble( addThis );\r
865     addThis->_prev = afterThis;\r
866     addThis->_next = afterThis->_next;\r
867     afterThis->_next->_prev = addThis;\r
868     afterThis->_next = addThis;\r
869     addThis->_parent = this;\r
870     return addThis;\r
871 }\r
872 \r
873 \r
874 \r
875 \r
876 const XMLElement* XMLNode::FirstChildElement( const char* name ) const\r
877 {\r
878     for( const XMLNode* node = _firstChild; node; node = node->_next ) {\r
879         const XMLElement* element = node->ToElement();\r
880         if ( element ) {\r
881             if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) {\r
882                 return element;\r
883             }\r
884         }\r
885     }\r
886     return 0;\r
887 }\r
888 \r
889 \r
890 const XMLElement* XMLNode::LastChildElement( const char* name ) const\r
891 {\r
892     for( const XMLNode* node = _lastChild; node; node = node->_prev ) {\r
893         const XMLElement* element = node->ToElement();\r
894         if ( element ) {\r
895             if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) {\r
896                 return element;\r
897             }\r
898         }\r
899     }\r
900     return 0;\r
901 }\r
902 \r
903 \r
904 const XMLElement* XMLNode::NextSiblingElement( const char* name ) const\r
905 {\r
906     for( const XMLNode* node = _next; node; node = node->_next ) {\r
907         const XMLElement* element = node->ToElement();\r
908         if ( element\r
909                 && (!name || XMLUtil::StringEqual( name, element->Name() ))) {\r
910             return element;\r
911         }\r
912     }\r
913     return 0;\r
914 }\r
915 \r
916 \r
917 const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const\r
918 {\r
919     for( const XMLNode* node = _prev; node; node = node->_prev ) {\r
920         const XMLElement* element = node->ToElement();\r
921         if ( element\r
922                 && (!name || XMLUtil::StringEqual( name, element->Name() ))) {\r
923             return element;\r
924         }\r
925     }\r
926     return 0;\r
927 }\r
928 \r
929 \r
930 char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )\r
931 {\r
932     // This is a recursive method, but thinking about it "at the current level"\r
933     // it is a pretty simple flat list:\r
934     //          <foo/>\r
935     //          <!-- comment -->\r
936     //\r
937     // With a special case:\r
938     //          <foo>\r
939     //          </foo>\r
940     //          <!-- comment -->\r
941     //\r
942     // Where the closing element (/foo) *must* be the next thing after the opening\r
943     // element, and the names must match. BUT the tricky bit is that the closing\r
944     // element will be read by the child.\r
945     //\r
946     // 'endTag' is the end tag for this node, it is returned by a call to a child.\r
947     // 'parentEnd' is the end tag for the parent, which is filled in and returned.\r
948 \r
949     while( p && *p ) {\r
950         XMLNode* node = 0;\r
951 \r
952         p = _document->Identify( p, &node );\r
953         if ( node == 0 ) {\r
954             break;\r
955         }\r
956 \r
957         StrPair endTag;\r
958         p = node->ParseDeep( p, &endTag );\r
959         if ( !p ) {\r
960             DeleteNode( node );\r
961             if ( !_document->Error() ) {\r
962                 _document->SetError( XML_ERROR_PARSING, 0, 0 );\r
963             }\r
964             break;\r
965         }\r
966 \r
967         XMLDeclaration* decl = node->ToDeclaration();\r
968         if ( decl ) {\r
969                 // A declaration can only be the first child of a document.\r
970                 // Set error, if document already has children.\r
971                 if ( !_document->NoChildren() ) {\r
972                         _document->SetError( XML_ERROR_PARSING_DECLARATION, decl->Value(), 0);\r
973                         DeleteNode( decl );\r
974                         break;\r
975                 }\r
976         }\r
977 \r
978         XMLElement* ele = node->ToElement();\r
979         if ( ele ) {\r
980             // We read the end tag. Return it to the parent.\r
981             if ( ele->ClosingType() == XMLElement::CLOSING ) {\r
982                 if ( parentEnd ) {\r
983                     ele->_value.TransferTo( parentEnd );\r
984                 }\r
985                 node->_memPool->SetTracked();   // created and then immediately deleted.\r
986                 DeleteNode( node );\r
987                 return p;\r
988             }\r
989 \r
990             // Handle an end tag returned to this level.\r
991             // And handle a bunch of annoying errors.\r
992             bool mismatch = false;\r
993             if ( endTag.Empty() ) {\r
994                 if ( ele->ClosingType() == XMLElement::OPEN ) {\r
995                     mismatch = true;\r
996                 }\r
997             }\r
998             else {\r
999                 if ( ele->ClosingType() != XMLElement::OPEN ) {\r
1000                     mismatch = true;\r
1001                 }\r
1002                 else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {\r
1003                     mismatch = true;\r
1004                 }\r
1005             }\r
1006             if ( mismatch ) {\r
1007                 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, ele->Name(), 0 );\r
1008                 DeleteNode( node );\r
1009                 break;\r
1010             }\r
1011         }\r
1012         InsertEndChild( node );\r
1013     }\r
1014     return 0;\r
1015 }\r
1016 \r
1017 void XMLNode::DeleteNode( XMLNode* node )\r
1018 {\r
1019     if ( node == 0 ) {\r
1020         return;\r
1021     }\r
1022     MemPool* pool = node->_memPool;\r
1023     node->~XMLNode();\r
1024     pool->Free( node );\r
1025 }\r
1026 \r
1027 void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const\r
1028 {\r
1029     TIXMLASSERT( insertThis );\r
1030     TIXMLASSERT( insertThis->_document == _document );\r
1031 \r
1032     if ( insertThis->_parent )\r
1033         insertThis->_parent->Unlink( insertThis );\r
1034     else\r
1035         insertThis->_memPool->SetTracked();\r
1036 }\r
1037 \r
1038 // --------- XMLText ---------- //\r
1039 char* XMLText::ParseDeep( char* p, StrPair* )\r
1040 {\r
1041     const char* start = p;\r
1042     if ( this->CData() ) {\r
1043         p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );\r
1044         if ( !p ) {\r
1045             _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );\r
1046         }\r
1047         return p;\r
1048     }\r
1049     else {\r
1050         int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;\r
1051         if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {\r
1052             flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;\r
1053         }\r
1054 \r
1055         p = _value.ParseText( p, "<", flags );\r
1056         if ( p && *p ) {\r
1057             return p-1;\r
1058         }\r
1059         if ( !p ) {\r
1060             _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );\r
1061         }\r
1062     }\r
1063     return 0;\r
1064 }\r
1065 \r
1066 \r
1067 XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const\r
1068 {\r
1069     if ( !doc ) {\r
1070         doc = _document;\r
1071     }\r
1072     XMLText* text = doc->NewText( Value() );    // fixme: this will always allocate memory. Intern?\r
1073     text->SetCData( this->CData() );\r
1074     return text;\r
1075 }\r
1076 \r
1077 \r
1078 bool XMLText::ShallowEqual( const XMLNode* compare ) const\r
1079 {\r
1080     const XMLText* text = compare->ToText();\r
1081     return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );\r
1082 }\r
1083 \r
1084 \r
1085 bool XMLText::Accept( XMLVisitor* visitor ) const\r
1086 {\r
1087     TIXMLASSERT( visitor );\r
1088     return visitor->Visit( *this );\r
1089 }\r
1090 \r
1091 \r
1092 // --------- XMLComment ---------- //\r
1093 \r
1094 XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )\r
1095 {\r
1096 }\r
1097 \r
1098 \r
1099 XMLComment::~XMLComment()\r
1100 {\r
1101 }\r
1102 \r
1103 \r
1104 char* XMLComment::ParseDeep( char* p, StrPair* )\r
1105 {\r
1106     // Comment parses as text.\r
1107     const char* start = p;\r
1108     p = _value.ParseText( p, "-->", StrPair::COMMENT );\r
1109     if ( p == 0 ) {\r
1110         _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );\r
1111     }\r
1112     return p;\r
1113 }\r
1114 \r
1115 \r
1116 XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const\r
1117 {\r
1118     if ( !doc ) {\r
1119         doc = _document;\r
1120     }\r
1121     XMLComment* comment = doc->NewComment( Value() );   // fixme: this will always allocate memory. Intern?\r
1122     return comment;\r
1123 }\r
1124 \r
1125 \r
1126 bool XMLComment::ShallowEqual( const XMLNode* compare ) const\r
1127 {\r
1128     TIXMLASSERT( compare );\r
1129     const XMLComment* comment = compare->ToComment();\r
1130     return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));\r
1131 }\r
1132 \r
1133 \r
1134 bool XMLComment::Accept( XMLVisitor* visitor ) const\r
1135 {\r
1136     TIXMLASSERT( visitor );\r
1137     return visitor->Visit( *this );\r
1138 }\r
1139 \r
1140 \r
1141 // --------- XMLDeclaration ---------- //\r
1142 \r
1143 XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )\r
1144 {\r
1145 }\r
1146 \r
1147 \r
1148 XMLDeclaration::~XMLDeclaration()\r
1149 {\r
1150     //printf( "~XMLDeclaration\n" );\r
1151 }\r
1152 \r
1153 \r
1154 char* XMLDeclaration::ParseDeep( char* p, StrPair* )\r
1155 {\r
1156     // Declaration parses as text.\r
1157     const char* start = p;\r
1158     p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );\r
1159     if ( p == 0 ) {\r
1160         _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );\r
1161     }\r
1162     return p;\r
1163 }\r
1164 \r
1165 \r
1166 XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const\r
1167 {\r
1168     if ( !doc ) {\r
1169         doc = _document;\r
1170     }\r
1171     XMLDeclaration* dec = doc->NewDeclaration( Value() );       // fixme: this will always allocate memory. Intern?\r
1172     return dec;\r
1173 }\r
1174 \r
1175 \r
1176 bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const\r
1177 {\r
1178     TIXMLASSERT( compare );\r
1179     const XMLDeclaration* declaration = compare->ToDeclaration();\r
1180     return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));\r
1181 }\r
1182 \r
1183 \r
1184 \r
1185 bool XMLDeclaration::Accept( XMLVisitor* visitor ) const\r
1186 {\r
1187     TIXMLASSERT( visitor );\r
1188     return visitor->Visit( *this );\r
1189 }\r
1190 \r
1191 // --------- XMLUnknown ---------- //\r
1192 \r
1193 XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )\r
1194 {\r
1195 }\r
1196 \r
1197 \r
1198 XMLUnknown::~XMLUnknown()\r
1199 {\r
1200 }\r
1201 \r
1202 \r
1203 char* XMLUnknown::ParseDeep( char* p, StrPair* )\r
1204 {\r
1205     // Unknown parses as text.\r
1206     const char* start = p;\r
1207 \r
1208     p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );\r
1209     if ( !p ) {\r
1210         _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );\r
1211     }\r
1212     return p;\r
1213 }\r
1214 \r
1215 \r
1216 XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const\r
1217 {\r
1218     if ( !doc ) {\r
1219         doc = _document;\r
1220     }\r
1221     XMLUnknown* text = doc->NewUnknown( Value() );      // fixme: this will always allocate memory. Intern?\r
1222     return text;\r
1223 }\r
1224 \r
1225 \r
1226 bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const\r
1227 {\r
1228     TIXMLASSERT( compare );\r
1229     const XMLUnknown* unknown = compare->ToUnknown();\r
1230     return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));\r
1231 }\r
1232 \r
1233 \r
1234 bool XMLUnknown::Accept( XMLVisitor* visitor ) const\r
1235 {\r
1236     TIXMLASSERT( visitor );\r
1237     return visitor->Visit( *this );\r
1238 }\r
1239 \r
1240 // --------- XMLAttribute ---------- //\r
1241 \r
1242 const char* XMLAttribute::Name() const \r
1243 {\r
1244     return _name.GetStr();\r
1245 }\r
1246 \r
1247 const char* XMLAttribute::Value() const \r
1248 {\r
1249     return _value.GetStr();\r
1250 }\r
1251 \r
1252 char* XMLAttribute::ParseDeep( char* p, bool processEntities )\r
1253 {\r
1254     // Parse using the name rules: bug fix, was using ParseText before\r
1255     p = _name.ParseName( p );\r
1256     if ( !p || !*p ) {\r
1257         return 0;\r
1258     }\r
1259 \r
1260     // Skip white space before =\r
1261     p = XMLUtil::SkipWhiteSpace( p );\r
1262     if ( *p != '=' ) {\r
1263         return 0;\r
1264     }\r
1265 \r
1266     ++p;        // move up to opening quote\r
1267     p = XMLUtil::SkipWhiteSpace( p );\r
1268     if ( *p != '\"' && *p != '\'' ) {\r
1269         return 0;\r
1270     }\r
1271 \r
1272     char endTag[2] = { *p, 0 };\r
1273     ++p;        // move past opening quote\r
1274 \r
1275     p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );\r
1276     return p;\r
1277 }\r
1278 \r
1279 \r
1280 void XMLAttribute::SetName( const char* n )\r
1281 {\r
1282     _name.SetStr( n );\r
1283 }\r
1284 \r
1285 \r
1286 XMLError XMLAttribute::QueryIntValue( int* value ) const\r
1287 {\r
1288     if ( XMLUtil::ToInt( Value(), value )) {\r
1289         return XML_NO_ERROR;\r
1290     }\r
1291     return XML_WRONG_ATTRIBUTE_TYPE;\r
1292 }\r
1293 \r
1294 \r
1295 XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const\r
1296 {\r
1297     if ( XMLUtil::ToUnsigned( Value(), value )) {\r
1298         return XML_NO_ERROR;\r
1299     }\r
1300     return XML_WRONG_ATTRIBUTE_TYPE;\r
1301 }\r
1302 \r
1303 \r
1304 XMLError XMLAttribute::QueryBoolValue( bool* value ) const\r
1305 {\r
1306     if ( XMLUtil::ToBool( Value(), value )) {\r
1307         return XML_NO_ERROR;\r
1308     }\r
1309     return XML_WRONG_ATTRIBUTE_TYPE;\r
1310 }\r
1311 \r
1312 \r
1313 XMLError XMLAttribute::QueryFloatValue( float* value ) const\r
1314 {\r
1315     if ( XMLUtil::ToFloat( Value(), value )) {\r
1316         return XML_NO_ERROR;\r
1317     }\r
1318     return XML_WRONG_ATTRIBUTE_TYPE;\r
1319 }\r
1320 \r
1321 \r
1322 XMLError XMLAttribute::QueryDoubleValue( double* value ) const\r
1323 {\r
1324     if ( XMLUtil::ToDouble( Value(), value )) {\r
1325         return XML_NO_ERROR;\r
1326     }\r
1327     return XML_WRONG_ATTRIBUTE_TYPE;\r
1328 }\r
1329 \r
1330 \r
1331 void XMLAttribute::SetAttribute( const char* v )\r
1332 {\r
1333     _value.SetStr( v );\r
1334 }\r
1335 \r
1336 \r
1337 void XMLAttribute::SetAttribute( int v )\r
1338 {\r
1339     char buf[BUF_SIZE];\r
1340     XMLUtil::ToStr( v, buf, BUF_SIZE );\r
1341     _value.SetStr( buf );\r
1342 }\r
1343 \r
1344 \r
1345 void XMLAttribute::SetAttribute( unsigned v )\r
1346 {\r
1347     char buf[BUF_SIZE];\r
1348     XMLUtil::ToStr( v, buf, BUF_SIZE );\r
1349     _value.SetStr( buf );\r
1350 }\r
1351 \r
1352 \r
1353 void XMLAttribute::SetAttribute( bool v )\r
1354 {\r
1355     char buf[BUF_SIZE];\r
1356     XMLUtil::ToStr( v, buf, BUF_SIZE );\r
1357     _value.SetStr( buf );\r
1358 }\r
1359 \r
1360 void XMLAttribute::SetAttribute( double v )\r
1361 {\r
1362     char buf[BUF_SIZE];\r
1363     XMLUtil::ToStr( v, buf, BUF_SIZE );\r
1364     _value.SetStr( buf );\r
1365 }\r
1366 \r
1367 void XMLAttribute::SetAttribute( float v )\r
1368 {\r
1369     char buf[BUF_SIZE];\r
1370     XMLUtil::ToStr( v, buf, BUF_SIZE );\r
1371     _value.SetStr( buf );\r
1372 }\r
1373 \r
1374 \r
1375 // --------- XMLElement ---------- //\r
1376 XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),\r
1377     _closingType( 0 ),\r
1378     _rootAttribute( 0 )\r
1379 {\r
1380 }\r
1381 \r
1382 \r
1383 XMLElement::~XMLElement()\r
1384 {\r
1385     while( _rootAttribute ) {\r
1386         XMLAttribute* next = _rootAttribute->_next;\r
1387         DeleteAttribute( _rootAttribute );\r
1388         _rootAttribute = next;\r
1389     }\r
1390 }\r
1391 \r
1392 \r
1393 const XMLAttribute* XMLElement::FindAttribute( const char* name ) const\r
1394 {\r
1395     for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {\r
1396         if ( XMLUtil::StringEqual( a->Name(), name ) ) {\r
1397             return a;\r
1398         }\r
1399     }\r
1400     return 0;\r
1401 }\r
1402 \r
1403 \r
1404 const char* XMLElement::Attribute( const char* name, const char* value ) const\r
1405 {\r
1406     const XMLAttribute* a = FindAttribute( name );\r
1407     if ( !a ) {\r
1408         return 0;\r
1409     }\r
1410     if ( !value || XMLUtil::StringEqual( a->Value(), value )) {\r
1411         return a->Value();\r
1412     }\r
1413     return 0;\r
1414 }\r
1415 \r
1416 \r
1417 const char* XMLElement::GetText() const\r
1418 {\r
1419     if ( FirstChild() && FirstChild()->ToText() ) {\r
1420         return FirstChild()->Value();\r
1421     }\r
1422     return 0;\r
1423 }\r
1424 \r
1425 \r
1426 void    XMLElement::SetText( const char* inText )\r
1427 {\r
1428         if ( FirstChild() && FirstChild()->ToText() )\r
1429                 FirstChild()->SetValue( inText );\r
1430         else {\r
1431                 XMLText*        theText = GetDocument()->NewText( inText );\r
1432                 InsertFirstChild( theText );\r
1433         }\r
1434 }\r
1435 \r
1436 \r
1437 void XMLElement::SetText( int v ) \r
1438 {\r
1439     char buf[BUF_SIZE];\r
1440     XMLUtil::ToStr( v, buf, BUF_SIZE );\r
1441     SetText( buf );\r
1442 }\r
1443 \r
1444 \r
1445 void XMLElement::SetText( unsigned v ) \r
1446 {\r
1447     char buf[BUF_SIZE];\r
1448     XMLUtil::ToStr( v, buf, BUF_SIZE );\r
1449     SetText( buf );\r
1450 }\r
1451 \r
1452 \r
1453 void XMLElement::SetText( bool v ) \r
1454 {\r
1455     char buf[BUF_SIZE];\r
1456     XMLUtil::ToStr( v, buf, BUF_SIZE );\r
1457     SetText( buf );\r
1458 }\r
1459 \r
1460 \r
1461 void XMLElement::SetText( float v ) \r
1462 {\r
1463     char buf[BUF_SIZE];\r
1464     XMLUtil::ToStr( v, buf, BUF_SIZE );\r
1465     SetText( buf );\r
1466 }\r
1467 \r
1468 \r
1469 void XMLElement::SetText( double v ) \r
1470 {\r
1471     char buf[BUF_SIZE];\r
1472     XMLUtil::ToStr( v, buf, BUF_SIZE );\r
1473     SetText( buf );\r
1474 }\r
1475 \r
1476 \r
1477 XMLError XMLElement::QueryIntText( int* ival ) const\r
1478 {\r
1479     if ( FirstChild() && FirstChild()->ToText() ) {\r
1480         const char* t = FirstChild()->Value();\r
1481         if ( XMLUtil::ToInt( t, ival ) ) {\r
1482             return XML_SUCCESS;\r
1483         }\r
1484         return XML_CAN_NOT_CONVERT_TEXT;\r
1485     }\r
1486     return XML_NO_TEXT_NODE;\r
1487 }\r
1488 \r
1489 \r
1490 XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const\r
1491 {\r
1492     if ( FirstChild() && FirstChild()->ToText() ) {\r
1493         const char* t = FirstChild()->Value();\r
1494         if ( XMLUtil::ToUnsigned( t, uval ) ) {\r
1495             return XML_SUCCESS;\r
1496         }\r
1497         return XML_CAN_NOT_CONVERT_TEXT;\r
1498     }\r
1499     return XML_NO_TEXT_NODE;\r
1500 }\r
1501 \r
1502 \r
1503 XMLError XMLElement::QueryBoolText( bool* bval ) const\r
1504 {\r
1505     if ( FirstChild() && FirstChild()->ToText() ) {\r
1506         const char* t = FirstChild()->Value();\r
1507         if ( XMLUtil::ToBool( t, bval ) ) {\r
1508             return XML_SUCCESS;\r
1509         }\r
1510         return XML_CAN_NOT_CONVERT_TEXT;\r
1511     }\r
1512     return XML_NO_TEXT_NODE;\r
1513 }\r
1514 \r
1515 \r
1516 XMLError XMLElement::QueryDoubleText( double* dval ) const\r
1517 {\r
1518     if ( FirstChild() && FirstChild()->ToText() ) {\r
1519         const char* t = FirstChild()->Value();\r
1520         if ( XMLUtil::ToDouble( t, dval ) ) {\r
1521             return XML_SUCCESS;\r
1522         }\r
1523         return XML_CAN_NOT_CONVERT_TEXT;\r
1524     }\r
1525     return XML_NO_TEXT_NODE;\r
1526 }\r
1527 \r
1528 \r
1529 XMLError XMLElement::QueryFloatText( float* fval ) const\r
1530 {\r
1531     if ( FirstChild() && FirstChild()->ToText() ) {\r
1532         const char* t = FirstChild()->Value();\r
1533         if ( XMLUtil::ToFloat( t, fval ) ) {\r
1534             return XML_SUCCESS;\r
1535         }\r
1536         return XML_CAN_NOT_CONVERT_TEXT;\r
1537     }\r
1538     return XML_NO_TEXT_NODE;\r
1539 }\r
1540 \r
1541 \r
1542 \r
1543 XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )\r
1544 {\r
1545     XMLAttribute* last = 0;\r
1546     XMLAttribute* attrib = 0;\r
1547     for( attrib = _rootAttribute;\r
1548             attrib;\r
1549             last = attrib, attrib = attrib->_next ) {\r
1550         if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {\r
1551             break;\r
1552         }\r
1553     }\r
1554     if ( !attrib ) {\r
1555         TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );\r
1556         attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();\r
1557         attrib->_memPool = &_document->_attributePool;\r
1558         if ( last ) {\r
1559             last->_next = attrib;\r
1560         }\r
1561         else {\r
1562             _rootAttribute = attrib;\r
1563         }\r
1564         attrib->SetName( name );\r
1565         attrib->_memPool->SetTracked(); // always created and linked.\r
1566     }\r
1567     return attrib;\r
1568 }\r
1569 \r
1570 \r
1571 void XMLElement::DeleteAttribute( const char* name )\r
1572 {\r
1573     XMLAttribute* prev = 0;\r
1574     for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {\r
1575         if ( XMLUtil::StringEqual( name, a->Name() ) ) {\r
1576             if ( prev ) {\r
1577                 prev->_next = a->_next;\r
1578             }\r
1579             else {\r
1580                 _rootAttribute = a->_next;\r
1581             }\r
1582             DeleteAttribute( a );\r
1583             break;\r
1584         }\r
1585         prev = a;\r
1586     }\r
1587 }\r
1588 \r
1589 \r
1590 char* XMLElement::ParseAttributes( char* p )\r
1591 {\r
1592     const char* start = p;\r
1593     XMLAttribute* prevAttribute = 0;\r
1594 \r
1595     // Read the attributes.\r
1596     while( p ) {\r
1597         p = XMLUtil::SkipWhiteSpace( p );\r
1598         if ( !(*p) ) {\r
1599             _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );\r
1600             return 0;\r
1601         }\r
1602 \r
1603         // attribute.\r
1604         if (XMLUtil::IsNameStartChar( *p ) ) {\r
1605             TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );\r
1606             XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();\r
1607             attrib->_memPool = &_document->_attributePool;\r
1608                         attrib->_memPool->SetTracked();\r
1609 \r
1610             p = attrib->ParseDeep( p, _document->ProcessEntities() );\r
1611             if ( !p || Attribute( attrib->Name() ) ) {\r
1612                 DeleteAttribute( attrib );\r
1613                 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );\r
1614                 return 0;\r
1615             }\r
1616             // There is a minor bug here: if the attribute in the source xml\r
1617             // document is duplicated, it will not be detected and the\r
1618             // attribute will be doubly added. However, tracking the 'prevAttribute'\r
1619             // avoids re-scanning the attribute list. Preferring performance for\r
1620             // now, may reconsider in the future.\r
1621             if ( prevAttribute ) {\r
1622                 prevAttribute->_next = attrib;\r
1623             }\r
1624             else {\r
1625                 _rootAttribute = attrib;\r
1626             }\r
1627             prevAttribute = attrib;\r
1628         }\r
1629         // end of the tag\r
1630         else if ( *p == '>' ) {\r
1631             ++p;\r
1632             break;\r
1633         }\r
1634         // end of the tag\r
1635         else if ( *p == '/' && *(p+1) == '>' ) {\r
1636             _closingType = CLOSED;\r
1637             return p+2; // done; sealed element.\r
1638         }\r
1639         else {\r
1640             _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );\r
1641             return 0;\r
1642         }\r
1643     }\r
1644     return p;\r
1645 }\r
1646 \r
1647 void XMLElement::DeleteAttribute( XMLAttribute* attribute )\r
1648 {\r
1649     if ( attribute == 0 ) {\r
1650         return;\r
1651     }\r
1652     MemPool* pool = attribute->_memPool;\r
1653     attribute->~XMLAttribute();\r
1654     pool->Free( attribute );\r
1655 }\r
1656 \r
1657 //\r
1658 //      <ele></ele>\r
1659 //      <ele>foo<b>bar</b></ele>\r
1660 //\r
1661 char* XMLElement::ParseDeep( char* p, StrPair* strPair )\r
1662 {\r
1663     // Read the element name.\r
1664     p = XMLUtil::SkipWhiteSpace( p );\r
1665 \r
1666     // The closing element is the </element> form. It is\r
1667     // parsed just like a regular element then deleted from\r
1668     // the DOM.\r
1669     if ( *p == '/' ) {\r
1670         _closingType = CLOSING;\r
1671         ++p;\r
1672     }\r
1673 \r
1674     p = _value.ParseName( p );\r
1675     if ( _value.Empty() ) {\r
1676         return 0;\r
1677     }\r
1678 \r
1679     p = ParseAttributes( p );\r
1680     if ( !p || !*p || _closingType ) {\r
1681         return p;\r
1682     }\r
1683 \r
1684     p = XMLNode::ParseDeep( p, strPair );\r
1685     return p;\r
1686 }\r
1687 \r
1688 \r
1689 \r
1690 XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const\r
1691 {\r
1692     if ( !doc ) {\r
1693         doc = _document;\r
1694     }\r
1695     XMLElement* element = doc->NewElement( Value() );                                   // fixme: this will always allocate memory. Intern?\r
1696     for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {\r
1697         element->SetAttribute( a->Name(), a->Value() );                                 // fixme: this will always allocate memory. Intern?\r
1698     }\r
1699     return element;\r
1700 }\r
1701 \r
1702 \r
1703 bool XMLElement::ShallowEqual( const XMLNode* compare ) const\r
1704 {\r
1705     TIXMLASSERT( compare );\r
1706     const XMLElement* other = compare->ToElement();\r
1707     if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {\r
1708 \r
1709         const XMLAttribute* a=FirstAttribute();\r
1710         const XMLAttribute* b=other->FirstAttribute();\r
1711 \r
1712         while ( a && b ) {\r
1713             if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {\r
1714                 return false;\r
1715             }\r
1716             a = a->Next();\r
1717             b = b->Next();\r
1718         }\r
1719         if ( a || b ) {\r
1720             // different count\r
1721             return false;\r
1722         }\r
1723         return true;\r
1724     }\r
1725     return false;\r
1726 }\r
1727 \r
1728 \r
1729 bool XMLElement::Accept( XMLVisitor* visitor ) const\r
1730 {\r
1731     TIXMLASSERT( visitor );\r
1732     if ( visitor->VisitEnter( *this, _rootAttribute ) ) {\r
1733         for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {\r
1734             if ( !node->Accept( visitor ) ) {\r
1735                 break;\r
1736             }\r
1737         }\r
1738     }\r
1739     return visitor->VisitExit( *this );\r
1740 }\r
1741 \r
1742 \r
1743 // --------- XMLDocument ----------- //\r
1744 \r
1745 // Warning: List must match 'enum XMLError'\r
1746 const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {\r
1747     "XML_SUCCESS",\r
1748     "XML_NO_ATTRIBUTE",\r
1749     "XML_WRONG_ATTRIBUTE_TYPE",\r
1750     "XML_ERROR_FILE_NOT_FOUND",\r
1751     "XML_ERROR_FILE_COULD_NOT_BE_OPENED",\r
1752     "XML_ERROR_FILE_READ_ERROR",\r
1753     "XML_ERROR_ELEMENT_MISMATCH",\r
1754     "XML_ERROR_PARSING_ELEMENT",\r
1755     "XML_ERROR_PARSING_ATTRIBUTE",\r
1756     "XML_ERROR_IDENTIFYING_TAG",\r
1757     "XML_ERROR_PARSING_TEXT",\r
1758     "XML_ERROR_PARSING_CDATA",\r
1759     "XML_ERROR_PARSING_COMMENT",\r
1760     "XML_ERROR_PARSING_DECLARATION",\r
1761     "XML_ERROR_PARSING_UNKNOWN",\r
1762     "XML_ERROR_EMPTY_DOCUMENT",\r
1763     "XML_ERROR_MISMATCHED_ELEMENT",\r
1764     "XML_ERROR_PARSING",\r
1765     "XML_CAN_NOT_CONVERT_TEXT",\r
1766     "XML_NO_TEXT_NODE"\r
1767 };\r
1768 \r
1769 \r
1770 XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) :\r
1771     XMLNode( 0 ),\r
1772     _writeBOM( false ),\r
1773     _processEntities( processEntities ),\r
1774     _errorID( XML_NO_ERROR ),\r
1775     _whitespace( whitespace ),\r
1776     _errorStr1( 0 ),\r
1777     _errorStr2( 0 ),\r
1778     _charBuffer( 0 )\r
1779 {\r
1780     // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)\r
1781     _document = this;\r
1782 }\r
1783 \r
1784 \r
1785 XMLDocument::~XMLDocument()\r
1786 {\r
1787     Clear();\r
1788 }\r
1789 \r
1790 \r
1791 void XMLDocument::Clear()\r
1792 {\r
1793     DeleteChildren();\r
1794 \r
1795 #ifdef DEBUG\r
1796     const bool hadError = Error();\r
1797 #endif\r
1798     _errorID = XML_NO_ERROR;\r
1799     _errorStr1 = 0;\r
1800     _errorStr2 = 0;\r
1801 \r
1802     delete [] _charBuffer;\r
1803     _charBuffer = 0;\r
1804 \r
1805 #if 0\r
1806     _textPool.Trace( "text" );\r
1807     _elementPool.Trace( "element" );\r
1808     _commentPool.Trace( "comment" );\r
1809     _attributePool.Trace( "attribute" );\r
1810 #endif\r
1811     \r
1812 #ifdef DEBUG\r
1813     if ( !hadError ) {\r
1814         TIXMLASSERT( _elementPool.CurrentAllocs()   == _elementPool.Untracked() );\r
1815         TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );\r
1816         TIXMLASSERT( _textPool.CurrentAllocs()      == _textPool.Untracked() );\r
1817         TIXMLASSERT( _commentPool.CurrentAllocs()   == _commentPool.Untracked() );\r
1818     }\r
1819 #endif\r
1820 }\r
1821 \r
1822 \r
1823 XMLElement* XMLDocument::NewElement( const char* name )\r
1824 {\r
1825     TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );\r
1826     XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this );\r
1827     ele->_memPool = &_elementPool;\r
1828     ele->SetName( name );\r
1829     return ele;\r
1830 }\r
1831 \r
1832 \r
1833 XMLComment* XMLDocument::NewComment( const char* str )\r
1834 {\r
1835     TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );\r
1836     XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this );\r
1837     comment->_memPool = &_commentPool;\r
1838     comment->SetValue( str );\r
1839     return comment;\r
1840 }\r
1841 \r
1842 \r
1843 XMLText* XMLDocument::NewText( const char* str )\r
1844 {\r
1845     TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );\r
1846     XMLText* text = new (_textPool.Alloc()) XMLText( this );\r
1847     text->_memPool = &_textPool;\r
1848     text->SetValue( str );\r
1849     return text;\r
1850 }\r
1851 \r
1852 \r
1853 XMLDeclaration* XMLDocument::NewDeclaration( const char* str )\r
1854 {\r
1855     TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );\r
1856     XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this );\r
1857     dec->_memPool = &_commentPool;\r
1858     dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );\r
1859     return dec;\r
1860 }\r
1861 \r
1862 \r
1863 XMLUnknown* XMLDocument::NewUnknown( const char* str )\r
1864 {\r
1865     TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );\r
1866     XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this );\r
1867     unk->_memPool = &_commentPool;\r
1868     unk->SetValue( str );\r
1869     return unk;\r
1870 }\r
1871 \r
1872 static FILE* callfopen( const char* filepath, const char* mode )\r
1873 {\r
1874     TIXMLASSERT( filepath );\r
1875     TIXMLASSERT( mode );\r
1876 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)\r
1877     FILE* fp = 0;\r
1878     errno_t err = fopen_s( &fp, filepath, mode );\r
1879     if ( err ) {\r
1880         return 0;\r
1881     }\r
1882 #else\r
1883     FILE* fp = fopen( filepath, mode );\r
1884 #endif\r
1885     return fp;\r
1886 }\r
1887     \r
1888 void XMLDocument::DeleteNode( XMLNode* node )   {\r
1889     TIXMLASSERT( node );\r
1890     TIXMLASSERT(node->_document == this );\r
1891     if (node->_parent) {\r
1892         node->_parent->DeleteChild( node );\r
1893     }\r
1894     else {\r
1895         // Isn't in the tree.\r
1896         // Use the parent delete.\r
1897         // Also, we need to mark it tracked: we 'know'\r
1898         // it was never used.\r
1899         node->_memPool->SetTracked();\r
1900         // Call the static XMLNode version:\r
1901         XMLNode::DeleteNode(node);\r
1902     }\r
1903 }\r
1904 \r
1905 \r
1906 XMLError XMLDocument::LoadFile( const char* filename )\r
1907 {\r
1908     Clear();\r
1909     FILE* fp = callfopen( filename, "rb" );\r
1910     if ( !fp ) {\r
1911         SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );\r
1912         return _errorID;\r
1913     }\r
1914     LoadFile( fp );\r
1915     fclose( fp );\r
1916     return _errorID;\r
1917 }\r
1918 \r
1919 // This is likely overengineered template art to have a check that unsigned long value incremented\r
1920 // by one still fits into size_t. If size_t type is larger than unsigned long type\r
1921 // (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit\r
1922 // -Wtype-limits warning. This piece makes the compiler select code with a check when a check\r
1923 // is useful and code with no check when a check is redundant depending on how size_t and unsigned long\r
1924 // types sizes relate to each other.\r
1925 template\r
1926 <bool = (sizeof(unsigned long) >= sizeof(size_t))>\r
1927 struct LongFitsIntoSizeTMinusOne {\r
1928     static bool Fits( unsigned long value )\r
1929     {\r
1930         return value < (size_t)-1;\r
1931     }\r
1932 };\r
1933 \r
1934 template <>\r
1935 bool LongFitsIntoSizeTMinusOne<false>::Fits( unsigned long /*value*/ )\r
1936 {\r
1937     return true;\r
1938 }\r
1939 \r
1940 XMLError XMLDocument::LoadFile( FILE* fp )\r
1941 {\r
1942     Clear();\r
1943 \r
1944     fseek( fp, 0, SEEK_SET );\r
1945     if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {\r
1946         SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );\r
1947         return _errorID;\r
1948     }\r
1949 \r
1950     fseek( fp, 0, SEEK_END );\r
1951     const long filelength = ftell( fp );\r
1952     fseek( fp, 0, SEEK_SET );\r
1953     if ( filelength == -1L ) {\r
1954         SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );\r
1955         return _errorID;\r
1956     }\r
1957     TIXMLASSERT( filelength >= 0 );\r
1958 \r
1959     if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {\r
1960         // Cannot handle files which won't fit in buffer together with null terminator\r
1961         SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );\r
1962         return _errorID;\r
1963     }\r
1964 \r
1965     if ( filelength == 0 ) {\r
1966         SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );\r
1967         return _errorID;\r
1968     }\r
1969 \r
1970     const size_t size = filelength;\r
1971     TIXMLASSERT( _charBuffer == 0 );\r
1972     _charBuffer = new char[size+1];\r
1973     size_t read = fread( _charBuffer, 1, size, fp );\r
1974     if ( read != size ) {\r
1975         SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );\r
1976         return _errorID;\r
1977     }\r
1978 \r
1979     _charBuffer[size] = 0;\r
1980 \r
1981     Parse();\r
1982     return _errorID;\r
1983 }\r
1984 \r
1985 \r
1986 XMLError XMLDocument::SaveFile( const char* filename, bool compact )\r
1987 {\r
1988     FILE* fp = callfopen( filename, "w" );\r
1989     if ( !fp ) {\r
1990         SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );\r
1991         return _errorID;\r
1992     }\r
1993     SaveFile(fp, compact);\r
1994     fclose( fp );\r
1995     return _errorID;\r
1996 }\r
1997 \r
1998 \r
1999 XMLError XMLDocument::SaveFile( FILE* fp, bool compact )\r
2000 {\r
2001     // Clear any error from the last save, otherwise it will get reported\r
2002     // for *this* call.\r
2003     SetError( XML_NO_ERROR, 0, 0 );\r
2004     XMLPrinter stream( fp, compact );\r
2005     Print( &stream );\r
2006     return _errorID;\r
2007 }\r
2008 \r
2009 \r
2010 XMLError XMLDocument::Parse( const char* p, size_t len )\r
2011 {\r
2012     Clear();\r
2013 \r
2014     if ( len == 0 || !p || !*p ) {\r
2015         SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );\r
2016         return _errorID;\r
2017     }\r
2018     if ( len == (size_t)(-1) ) {\r
2019         len = strlen( p );\r
2020     }\r
2021     TIXMLASSERT( _charBuffer == 0 );\r
2022     _charBuffer = new char[ len+1 ];\r
2023     memcpy( _charBuffer, p, len );\r
2024     _charBuffer[len] = 0;\r
2025 \r
2026     Parse();\r
2027     if ( Error() ) {\r
2028         // clean up now essentially dangling memory.\r
2029         // and the parse fail can put objects in the\r
2030         // pools that are dead and inaccessible.\r
2031         DeleteChildren();\r
2032         _elementPool.Clear();\r
2033         _attributePool.Clear();\r
2034         _textPool.Clear();\r
2035         _commentPool.Clear();\r
2036     }\r
2037     return _errorID;\r
2038 }\r
2039 \r
2040 \r
2041 void XMLDocument::Print( XMLPrinter* streamer ) const\r
2042 {\r
2043     if ( streamer ) {\r
2044         Accept( streamer );\r
2045     }\r
2046     else {\r
2047         XMLPrinter stdoutStreamer( stdout );\r
2048         Accept( &stdoutStreamer );\r
2049     }\r
2050 }\r
2051 \r
2052 \r
2053 void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 )\r
2054 {\r
2055     TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );\r
2056     _errorID = error;\r
2057     _errorStr1 = str1;\r
2058     _errorStr2 = str2;\r
2059 }\r
2060 \r
2061 const char* XMLDocument::ErrorName() const\r
2062 {\r
2063         TIXMLASSERT( _errorID >= 0 && _errorID < XML_ERROR_COUNT );\r
2064     const char* errorName = _errorNames[_errorID];\r
2065     TIXMLASSERT( errorName && errorName[0] );\r
2066     return errorName;\r
2067 }\r
2068 \r
2069 void XMLDocument::PrintError() const\r
2070 {\r
2071     if ( Error() ) {\r
2072         static const int LEN = 20;\r
2073         char buf1[LEN] = { 0 };\r
2074         char buf2[LEN] = { 0 };\r
2075 \r
2076         if ( _errorStr1 ) {\r
2077             TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 );\r
2078         }\r
2079         if ( _errorStr2 ) {\r
2080             TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 );\r
2081         }\r
2082 \r
2083         // Should check INT_MIN <= _errorID && _errorId <= INT_MAX, but that\r
2084         // causes a clang "always true" -Wtautological-constant-out-of-range-compare warning\r
2085         TIXMLASSERT( 0 <= _errorID && XML_ERROR_COUNT - 1 <= INT_MAX );\r
2086         printf( "XMLDocument error id=%d '%s' str1=%s str2=%s\n",\r
2087                 static_cast<int>( _errorID ), ErrorName(), buf1, buf2 );\r
2088     }\r
2089 }\r
2090 \r
2091 void XMLDocument::Parse()\r
2092 {\r
2093     TIXMLASSERT( NoChildren() ); // Clear() must have been called previously\r
2094     TIXMLASSERT( _charBuffer );\r
2095     char* p = _charBuffer;\r
2096     p = XMLUtil::SkipWhiteSpace( p );\r
2097     p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );\r
2098     if ( !*p ) {\r
2099         SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );\r
2100         return;\r
2101     }\r
2102     ParseDeep(p, 0 );\r
2103 }\r
2104 \r
2105 XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :\r
2106     _elementJustOpened( false ),\r
2107     _firstElement( true ),\r
2108     _fp( file ),\r
2109     _depth( depth ),\r
2110     _textDepth( -1 ),\r
2111     _processEntities( true ),\r
2112     _compactMode( compact )\r
2113 {\r
2114     for( int i=0; i<ENTITY_RANGE; ++i ) {\r
2115         _entityFlag[i] = false;\r
2116         _restrictedEntityFlag[i] = false;\r
2117     }\r
2118     for( int i=0; i<NUM_ENTITIES; ++i ) {\r
2119         const char entityValue = entities[i].value;\r
2120         TIXMLASSERT( 0 <= entityValue && entityValue < ENTITY_RANGE );\r
2121         _entityFlag[ (unsigned char)entityValue ] = true;\r
2122     }\r
2123     _restrictedEntityFlag[(unsigned char)'&'] = true;\r
2124     _restrictedEntityFlag[(unsigned char)'<'] = true;\r
2125     _restrictedEntityFlag[(unsigned char)'>'] = true;   // not required, but consistency is nice\r
2126     _buffer.Push( 0 );\r
2127 }\r
2128 \r
2129 \r
2130 void XMLPrinter::Print( const char* format, ... )\r
2131 {\r
2132     va_list     va;\r
2133     va_start( va, format );\r
2134 \r
2135     if ( _fp ) {\r
2136         vfprintf( _fp, format, va );\r
2137     }\r
2138     else {\r
2139         const int len = TIXML_VSCPRINTF( format, va );\r
2140         // Close out and re-start the va-args\r
2141         va_end( va );\r
2142         TIXMLASSERT( len >= 0 );\r
2143         va_start( va, format );\r
2144         TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );\r
2145         char* p = _buffer.PushArr( len ) - 1;   // back up over the null terminator.\r
2146                 TIXML_VSNPRINTF( p, len+1, format, va );\r
2147     }\r
2148     va_end( va );\r
2149 }\r
2150 \r
2151 \r
2152 void XMLPrinter::PrintSpace( int depth )\r
2153 {\r
2154     for( int i=0; i<depth; ++i ) {\r
2155         Print( "    " );\r
2156     }\r
2157 }\r
2158 \r
2159 \r
2160 void XMLPrinter::PrintString( const char* p, bool restricted )\r
2161 {\r
2162     // Look for runs of bytes between entities to print.\r
2163     const char* q = p;\r
2164 \r
2165     if ( _processEntities ) {\r
2166         const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;\r
2167         while ( *q ) {\r
2168             TIXMLASSERT( p <= q );\r
2169             // Remember, char is sometimes signed. (How many times has that bitten me?)\r
2170             if ( *q > 0 && *q < ENTITY_RANGE ) {\r
2171                 // Check for entities. If one is found, flush\r
2172                 // the stream up until the entity, write the\r
2173                 // entity, and keep looking.\r
2174                 if ( flag[(unsigned char)(*q)] ) {\r
2175                     while ( p < q ) {\r
2176                         const size_t delta = q - p;\r
2177                         // %.*s accepts type int as "precision"\r
2178                         const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;\r
2179                         Print( "%.*s", toPrint, p );\r
2180                         p += toPrint;\r
2181                     }\r
2182                     bool entityPatternPrinted = false;\r
2183                     for( int i=0; i<NUM_ENTITIES; ++i ) {\r
2184                         if ( entities[i].value == *q ) {\r
2185                             Print( "&%s;", entities[i].pattern );\r
2186                             entityPatternPrinted = true;\r
2187                             break;\r
2188                         }\r
2189                     }\r
2190                     if ( !entityPatternPrinted ) {\r
2191                         // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release\r
2192                         TIXMLASSERT( false );\r
2193                     }\r
2194                     ++p;\r
2195                 }\r
2196             }\r
2197             ++q;\r
2198             TIXMLASSERT( p <= q );\r
2199         }\r
2200     }\r
2201     // Flush the remaining string. This will be the entire\r
2202     // string if an entity wasn't found.\r
2203     TIXMLASSERT( p <= q );\r
2204     if ( !_processEntities || ( p < q ) ) {\r
2205         Print( "%s", p );\r
2206     }\r
2207 }\r
2208 \r
2209 \r
2210 void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )\r
2211 {\r
2212     if ( writeBOM ) {\r
2213         static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };\r
2214         Print( "%s", bom );\r
2215     }\r
2216     if ( writeDec ) {\r
2217         PushDeclaration( "xml version=\"1.0\"" );\r
2218     }\r
2219 }\r
2220 \r
2221 \r
2222 void XMLPrinter::OpenElement( const char* name, bool compactMode )\r
2223 {\r
2224     SealElementIfJustOpened();\r
2225     _stack.Push( name );\r
2226 \r
2227     if ( _textDepth < 0 && !_firstElement && !compactMode ) {\r
2228         Print( "\n" );\r
2229     }\r
2230     if ( !compactMode ) {\r
2231         PrintSpace( _depth );\r
2232     }\r
2233 \r
2234     Print( "<%s", name );\r
2235     _elementJustOpened = true;\r
2236     _firstElement = false;\r
2237     ++_depth;\r
2238 }\r
2239 \r
2240 \r
2241 void XMLPrinter::PushAttribute( const char* name, const char* value )\r
2242 {\r
2243     TIXMLASSERT( _elementJustOpened );\r
2244     Print( " %s=\"", name );\r
2245     PrintString( value, false );\r
2246     Print( "\"" );\r
2247 }\r
2248 \r
2249 \r
2250 void XMLPrinter::PushAttribute( const char* name, int v )\r
2251 {\r
2252     char buf[BUF_SIZE];\r
2253     XMLUtil::ToStr( v, buf, BUF_SIZE );\r
2254     PushAttribute( name, buf );\r
2255 }\r
2256 \r
2257 \r
2258 void XMLPrinter::PushAttribute( const char* name, unsigned v )\r
2259 {\r
2260     char buf[BUF_SIZE];\r
2261     XMLUtil::ToStr( v, buf, BUF_SIZE );\r
2262     PushAttribute( name, buf );\r
2263 }\r
2264 \r
2265 \r
2266 void XMLPrinter::PushAttribute( const char* name, bool v )\r
2267 {\r
2268     char buf[BUF_SIZE];\r
2269     XMLUtil::ToStr( v, buf, BUF_SIZE );\r
2270     PushAttribute( name, buf );\r
2271 }\r
2272 \r
2273 \r
2274 void XMLPrinter::PushAttribute( const char* name, double v )\r
2275 {\r
2276     char buf[BUF_SIZE];\r
2277     XMLUtil::ToStr( v, buf, BUF_SIZE );\r
2278     PushAttribute( name, buf );\r
2279 }\r
2280 \r
2281 \r
2282 void XMLPrinter::CloseElement( bool compactMode )\r
2283 {\r
2284     --_depth;\r
2285     const char* name = _stack.Pop();\r
2286 \r
2287     if ( _elementJustOpened ) {\r
2288         Print( "/>" );\r
2289     }\r
2290     else {\r
2291         if ( _textDepth < 0 && !compactMode) {\r
2292             Print( "\n" );\r
2293             PrintSpace( _depth );\r
2294         }\r
2295         Print( "</%s>", name );\r
2296     }\r
2297 \r
2298     if ( _textDepth == _depth ) {\r
2299         _textDepth = -1;\r
2300     }\r
2301     if ( _depth == 0 && !compactMode) {\r
2302         Print( "\n" );\r
2303     }\r
2304     _elementJustOpened = false;\r
2305 }\r
2306 \r
2307 \r
2308 void XMLPrinter::SealElementIfJustOpened()\r
2309 {\r
2310     if ( !_elementJustOpened ) {\r
2311         return;\r
2312     }\r
2313     _elementJustOpened = false;\r
2314     Print( ">" );\r
2315 }\r
2316 \r
2317 \r
2318 void XMLPrinter::PushText( const char* text, bool cdata )\r
2319 {\r
2320     _textDepth = _depth-1;\r
2321 \r
2322     SealElementIfJustOpened();\r
2323     if ( cdata ) {\r
2324         Print( "<![CDATA[%s]]>", text );\r
2325     }\r
2326     else {\r
2327         PrintString( text, true );\r
2328     }\r
2329 }\r
2330 \r
2331 void XMLPrinter::PushText( int value )\r
2332 {\r
2333     char buf[BUF_SIZE];\r
2334     XMLUtil::ToStr( value, buf, BUF_SIZE );\r
2335     PushText( buf, false );\r
2336 }\r
2337 \r
2338 \r
2339 void XMLPrinter::PushText( unsigned value )\r
2340 {\r
2341     char buf[BUF_SIZE];\r
2342     XMLUtil::ToStr( value, buf, BUF_SIZE );\r
2343     PushText( buf, false );\r
2344 }\r
2345 \r
2346 \r
2347 void XMLPrinter::PushText( bool value )\r
2348 {\r
2349     char buf[BUF_SIZE];\r
2350     XMLUtil::ToStr( value, buf, BUF_SIZE );\r
2351     PushText( buf, false );\r
2352 }\r
2353 \r
2354 \r
2355 void XMLPrinter::PushText( float value )\r
2356 {\r
2357     char buf[BUF_SIZE];\r
2358     XMLUtil::ToStr( value, buf, BUF_SIZE );\r
2359     PushText( buf, false );\r
2360 }\r
2361 \r
2362 \r
2363 void XMLPrinter::PushText( double value )\r
2364 {\r
2365     char buf[BUF_SIZE];\r
2366     XMLUtil::ToStr( value, buf, BUF_SIZE );\r
2367     PushText( buf, false );\r
2368 }\r
2369 \r
2370 \r
2371 void XMLPrinter::PushComment( const char* comment )\r
2372 {\r
2373     SealElementIfJustOpened();\r
2374     if ( _textDepth < 0 && !_firstElement && !_compactMode) {\r
2375         Print( "\n" );\r
2376         PrintSpace( _depth );\r
2377     }\r
2378     _firstElement = false;\r
2379     Print( "<!--%s-->", comment );\r
2380 }\r
2381 \r
2382 \r
2383 void XMLPrinter::PushDeclaration( const char* value )\r
2384 {\r
2385     SealElementIfJustOpened();\r
2386     if ( _textDepth < 0 && !_firstElement && !_compactMode) {\r
2387         Print( "\n" );\r
2388         PrintSpace( _depth );\r
2389     }\r
2390     _firstElement = false;\r
2391     Print( "<?%s?>", value );\r
2392 }\r
2393 \r
2394 \r
2395 void XMLPrinter::PushUnknown( const char* value )\r
2396 {\r
2397     SealElementIfJustOpened();\r
2398     if ( _textDepth < 0 && !_firstElement && !_compactMode) {\r
2399         Print( "\n" );\r
2400         PrintSpace( _depth );\r
2401     }\r
2402     _firstElement = false;\r
2403     Print( "<!%s>", value );\r
2404 }\r
2405 \r
2406 \r
2407 bool XMLPrinter::VisitEnter( const XMLDocument& doc )\r
2408 {\r
2409     _processEntities = doc.ProcessEntities();\r
2410     if ( doc.HasBOM() ) {\r
2411         PushHeader( true, false );\r
2412     }\r
2413     return true;\r
2414 }\r
2415 \r
2416 \r
2417 bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )\r
2418 {\r
2419     const XMLElement* parentElem = 0;\r
2420     if ( element.Parent() ) {\r
2421         parentElem = element.Parent()->ToElement();\r
2422     }\r
2423     const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;\r
2424     OpenElement( element.Name(), compactMode );\r
2425     while ( attribute ) {\r
2426         PushAttribute( attribute->Name(), attribute->Value() );\r
2427         attribute = attribute->Next();\r
2428     }\r
2429     return true;\r
2430 }\r
2431 \r
2432 \r
2433 bool XMLPrinter::VisitExit( const XMLElement& element )\r
2434 {\r
2435     CloseElement( CompactMode(element) );\r
2436     return true;\r
2437 }\r
2438 \r
2439 \r
2440 bool XMLPrinter::Visit( const XMLText& text )\r
2441 {\r
2442     PushText( text.Value(), text.CData() );\r
2443     return true;\r
2444 }\r
2445 \r
2446 \r
2447 bool XMLPrinter::Visit( const XMLComment& comment )\r
2448 {\r
2449     PushComment( comment.Value() );\r
2450     return true;\r
2451 }\r
2452 \r
2453 bool XMLPrinter::Visit( const XMLDeclaration& declaration )\r
2454 {\r
2455     PushDeclaration( declaration.Value() );\r
2456     return true;\r
2457 }\r
2458 \r
2459 \r
2460 bool XMLPrinter::Visit( const XMLUnknown& unknown )\r
2461 {\r
2462     PushUnknown( unknown.Value() );\r
2463     return true;\r
2464 }\r
2465 \r
2466 }   // namespace tinyxml2\r
2467 \r