blob: 04cf6974952069fbfcacbd1bc4758cfdb4d52477 [file] [log] [blame]
Matthew Xiec74b5462012-08-24 00:17:13 -07001/*
2Original code by Lee Thomason (www.grinninglizard.com)
3
4This software is provided 'as-is', without any express or implied
5warranty. In no event will the authors be held liable for any
6damages arising from the use of this software.
7
8Permission is granted to anyone to use this software for any
9purpose, including commercial applications, and to alter it and
10redistribute it freely, subject to the following restrictions:
11
121. The origin of this software must not be misrepresented; you must
13not claim that you wrote the original software. If you use this
14software in a product, an acknowledgment in the product documentation
15would be appreciated but is not required.
16
172. Altered source versions must be plainly marked as such, and
18must not be misrepresented as being the original software.
19
203. This notice may not be removed or altered from any source
21distribution.
22*/
Narayan Kamath74c03bb2017-12-22 10:59:43 +000023
Matthew Xiec74b5462012-08-24 00:17:13 -070024#include "tinyxml2.h"
25
Narayan Kamath74c03bb2017-12-22 10:59:43 +000026#include <new> // yes, this one new style header, is in the Android SDK.
27#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
28# include <stddef.h>
29# include <stdarg.h>
30#else
31# include <cstddef>
32# include <cstdarg>
33#endif
Matthew Xiec74b5462012-08-24 00:17:13 -070034
Narayan Kamath74c03bb2017-12-22 10:59:43 +000035#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
36 // Microsoft Visual Studio, version 2005 and higher. Not WinCE.
37 /*int _snprintf_s(
38 char *buffer,
39 size_t sizeOfBuffer,
40 size_t count,
41 const char *format [,
42 argument] ...
43 );*/
44 static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... )
45 {
46 va_list va;
47 va_start( va, format );
48 int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
49 va_end( va );
50 return result;
51 }
Matthew Xiec74b5462012-08-24 00:17:13 -070052
Narayan Kamath74c03bb2017-12-22 10:59:43 +000053 static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va )
54 {
55 int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
56 return result;
57 }
58
59 #define TIXML_VSCPRINTF _vscprintf
60 #define TIXML_SSCANF sscanf_s
61#elif defined _MSC_VER
62 // Microsoft Visual Studio 2003 and earlier or WinCE
63 #define TIXML_SNPRINTF _snprintf
64 #define TIXML_VSNPRINTF _vsnprintf
65 #define TIXML_SSCANF sscanf
66 #if (_MSC_VER < 1400 ) && (!defined WINCE)
67 // Microsoft Visual Studio 2003 and not WinCE.
68 #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have.
69 #else
70 // Microsoft Visual Studio 2003 and earlier or WinCE.
71 static inline int TIXML_VSCPRINTF( const char* format, va_list va )
72 {
73 int len = 512;
74 for (;;) {
75 len = len*2;
76 char* str = new char[len]();
77 const int required = _vsnprintf(str, len, format, va);
78 delete[] str;
79 if ( required != -1 ) {
80 TIXMLASSERT( required >= 0 );
81 len = required;
82 break;
83 }
84 }
85 TIXMLASSERT( len >= 0 );
86 return len;
87 }
88 #endif
89#else
90 // GCC version 3 and higher
91 //#warning( "Using sn* functions." )
92 #define TIXML_SNPRINTF snprintf
93 #define TIXML_VSNPRINTF vsnprintf
94 static inline int TIXML_VSCPRINTF( const char* format, va_list va )
95 {
96 int len = vsnprintf( 0, 0, format, va );
97 TIXMLASSERT( len >= 0 );
98 return len;
99 }
100 #define TIXML_SSCANF sscanf
101#endif
102
103
104static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
Matthew Xiec74b5462012-08-24 00:17:13 -0700105static const char LF = LINE_FEED;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000106static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
Matthew Xiec74b5462012-08-24 00:17:13 -0700107static const char CR = CARRIAGE_RETURN;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000108static const char SINGLE_QUOTE = '\'';
109static const char DOUBLE_QUOTE = '\"';
Matthew Xiec74b5462012-08-24 00:17:13 -0700110
111// Bunch of unicode info at:
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000112// http://www.unicode.org/faq/utf_bom.html
113// ef bb bf (Microsoft "lead bytes") - designates UTF-8
Matthew Xiec74b5462012-08-24 00:17:13 -0700114
115static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
116static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
117static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
118
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000119namespace tinyxml2
120{
Matthew Xiec74b5462012-08-24 00:17:13 -0700121
122struct Entity {
123 const char* pattern;
124 int length;
125 char value;
126};
127
128static const int NUM_ENTITIES = 5;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000129static const Entity entities[NUM_ENTITIES] = {
130 { "quot", 4, DOUBLE_QUOTE },
131 { "amp", 3, '&' },
132 { "apos", 4, SINGLE_QUOTE },
133 { "lt", 2, '<' },
134 { "gt", 2, '>' }
Matthew Xiec74b5462012-08-24 00:17:13 -0700135};
136
137
138StrPair::~StrPair()
139{
140 Reset();
141}
142
143
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000144void StrPair::TransferTo( StrPair* other )
145{
146 if ( this == other ) {
147 return;
148 }
149 // This in effect implements the assignment operator by "moving"
150 // ownership (as in auto_ptr).
151
152 TIXMLASSERT( other != 0 );
153 TIXMLASSERT( other->_flags == 0 );
154 TIXMLASSERT( other->_start == 0 );
155 TIXMLASSERT( other->_end == 0 );
156
157 other->Reset();
158
159 other->_flags = _flags;
160 other->_start = _start;
161 other->_end = _end;
162
163 _flags = 0;
164 _start = 0;
165 _end = 0;
166}
167
168
Matthew Xiec74b5462012-08-24 00:17:13 -0700169void StrPair::Reset()
170{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000171 if ( _flags & NEEDS_DELETE ) {
172 delete [] _start;
Matthew Xiec74b5462012-08-24 00:17:13 -0700173 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000174 _flags = 0;
175 _start = 0;
176 _end = 0;
Matthew Xiec74b5462012-08-24 00:17:13 -0700177}
178
179
180void StrPair::SetStr( const char* str, int flags )
181{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000182 TIXMLASSERT( str );
Matthew Xiec74b5462012-08-24 00:17:13 -0700183 Reset();
184 size_t len = strlen( str );
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000185 TIXMLASSERT( _start == 0 );
186 _start = new char[ len+1 ];
187 memcpy( _start, str, len+1 );
188 _end = _start + len;
189 _flags = flags | NEEDS_DELETE;
Matthew Xiec74b5462012-08-24 00:17:13 -0700190}
191
192
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000193char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -0700194{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000195 TIXMLASSERT( p );
Matthew Xiec74b5462012-08-24 00:17:13 -0700196 TIXMLASSERT( endTag && *endTag );
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000197 TIXMLASSERT(curLineNumPtr);
Matthew Xiec74b5462012-08-24 00:17:13 -0700198
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000199 char* start = p;
Matthew Xiec74b5462012-08-24 00:17:13 -0700200 char endChar = *endTag;
201 size_t length = strlen( endTag );
202
203 // Inner loop of text parsing.
204 while ( *p ) {
205 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
206 Set( start, p, strFlags );
207 return p + length;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000208 } else if (*p == '\n') {
209 ++(*curLineNumPtr);
Matthew Xiec74b5462012-08-24 00:17:13 -0700210 }
211 ++p;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000212 TIXMLASSERT( p );
Matthew Xiec74b5462012-08-24 00:17:13 -0700213 }
214 return 0;
215}
216
217
218char* StrPair::ParseName( char* p )
219{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000220 if ( !p || !(*p) ) {
221 return 0;
222 }
223 if ( !XMLUtil::IsNameStartChar( *p ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700224 return 0;
225 }
226
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000227 char* const start = p;
228 ++p;
229 while ( *p && XMLUtil::IsNameChar( *p ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700230 ++p;
231 }
232
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000233 Set( start, p, 0 );
234 return p;
Matthew Xiec74b5462012-08-24 00:17:13 -0700235}
236
237
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000238void StrPair::CollapseWhitespace()
239{
240 // Adjusting _start would cause undefined behavior on delete[]
241 TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );
242 // Trim leading space.
243 _start = XMLUtil::SkipWhiteSpace( _start, 0 );
244
245 if ( *_start ) {
246 const char* p = _start; // the read pointer
247 char* q = _start; // the write pointer
248
249 while( *p ) {
250 if ( XMLUtil::IsWhiteSpace( *p )) {
251 p = XMLUtil::SkipWhiteSpace( p, 0 );
252 if ( *p == 0 ) {
253 break; // don't write to q; this trims the trailing space.
254 }
255 *q = ' ';
256 ++q;
257 }
258 *q = *p;
259 ++q;
260 ++p;
261 }
262 *q = 0;
263 }
264}
265
Matthew Xiec74b5462012-08-24 00:17:13 -0700266
267const char* StrPair::GetStr()
268{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000269 TIXMLASSERT( _start );
270 TIXMLASSERT( _end );
271 if ( _flags & NEEDS_FLUSH ) {
272 *_end = 0;
273 _flags ^= NEEDS_FLUSH;
Matthew Xiec74b5462012-08-24 00:17:13 -0700274
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000275 if ( _flags ) {
276 const char* p = _start; // the read pointer
277 char* q = _start; // the write pointer
Matthew Xiec74b5462012-08-24 00:17:13 -0700278
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000279 while( p < _end ) {
280 if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700281 // CR-LF pair becomes LF
282 // CR alone becomes LF
283 // LF-CR becomes LF
284 if ( *(p+1) == LF ) {
285 p += 2;
286 }
287 else {
288 ++p;
289 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000290 *q = LF;
291 ++q;
Matthew Xiec74b5462012-08-24 00:17:13 -0700292 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000293 else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700294 if ( *(p+1) == CR ) {
295 p += 2;
296 }
297 else {
298 ++p;
299 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000300 *q = LF;
301 ++q;
Matthew Xiec74b5462012-08-24 00:17:13 -0700302 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000303 else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700304 // Entities handled by tinyXML2:
305 // - special entities in the entity table [in/out]
306 // - numeric character reference [in]
307 // &#20013; or &#x4e2d;
308
309 if ( *(p+1) == '#' ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000310 const int buflen = 10;
311 char buf[buflen] = { 0 };
312 int len = 0;
313 char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
314 if ( adjusted == 0 ) {
315 *q = *p;
316 ++p;
317 ++q;
Matthew Xiec74b5462012-08-24 00:17:13 -0700318 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000319 else {
320 TIXMLASSERT( 0 <= len && len <= buflen );
321 TIXMLASSERT( q + len <= adjusted );
322 p = adjusted;
323 memcpy( q, buf, len );
324 q += len;
325 }
Matthew Xiec74b5462012-08-24 00:17:13 -0700326 }
327 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000328 bool entityFound = false;
329 for( int i = 0; i < NUM_ENTITIES; ++i ) {
330 const Entity& entity = entities[i];
331 if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
332 && *( p + entity.length + 1 ) == ';' ) {
333 // Found an entity - convert.
334 *q = entity.value;
Matthew Xiec74b5462012-08-24 00:17:13 -0700335 ++q;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000336 p += entity.length + 2;
337 entityFound = true;
Matthew Xiec74b5462012-08-24 00:17:13 -0700338 break;
339 }
340 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000341 if ( !entityFound ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700342 // fixme: treat as error?
343 ++p;
344 ++q;
345 }
346 }
347 }
348 else {
349 *q = *p;
350 ++p;
351 ++q;
352 }
353 }
354 *q = 0;
355 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000356 // The loop below has plenty going on, and this
357 // is a less useful mode. Break it out.
358 if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {
359 CollapseWhitespace();
360 }
361 _flags = (_flags & NEEDS_DELETE);
Matthew Xiec74b5462012-08-24 00:17:13 -0700362 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000363 TIXMLASSERT( _start );
364 return _start;
Matthew Xiec74b5462012-08-24 00:17:13 -0700365}
366
367
368
369
370// --------- XMLUtil ----------- //
371
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000372const char* XMLUtil::writeBoolTrue = "true";
373const char* XMLUtil::writeBoolFalse = "false";
374
375void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse)
376{
377 static const char* defTrue = "true";
378 static const char* defFalse = "false";
379
380 writeBoolTrue = (writeTrue) ? writeTrue : defTrue;
381 writeBoolFalse = (writeFalse) ? writeFalse : defFalse;
382}
383
384
Matthew Xiec74b5462012-08-24 00:17:13 -0700385const char* XMLUtil::ReadBOM( const char* p, bool* bom )
386{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000387 TIXMLASSERT( p );
388 TIXMLASSERT( bom );
Matthew Xiec74b5462012-08-24 00:17:13 -0700389 *bom = false;
390 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
391 // Check for BOM:
392 if ( *(pu+0) == TIXML_UTF_LEAD_0
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000393 && *(pu+1) == TIXML_UTF_LEAD_1
394 && *(pu+2) == TIXML_UTF_LEAD_2 ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700395 *bom = true;
396 p += 3;
397 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000398 TIXMLASSERT( p );
Matthew Xiec74b5462012-08-24 00:17:13 -0700399 return p;
400}
401
402
403void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
404{
405 const unsigned long BYTE_MASK = 0xBF;
406 const unsigned long BYTE_MARK = 0x80;
407 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
408
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000409 if (input < 0x80) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700410 *length = 1;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000411 }
412 else if ( input < 0x800 ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700413 *length = 2;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000414 }
415 else if ( input < 0x10000 ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700416 *length = 3;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000417 }
418 else if ( input < 0x200000 ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700419 *length = 4;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000420 }
421 else {
422 *length = 0; // This code won't convert this correctly anyway.
423 return;
424 }
Matthew Xiec74b5462012-08-24 00:17:13 -0700425
426 output += *length;
427
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000428 // Scary scary fall throughs are annotated with carefully designed comments
429 // to suppress compiler warnings such as -Wimplicit-fallthrough in gcc
430 switch (*length) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700431 case 4:
432 --output;
433 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
434 input >>= 6;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000435 //fall through
Matthew Xiec74b5462012-08-24 00:17:13 -0700436 case 3:
437 --output;
438 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
439 input >>= 6;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000440 //fall through
Matthew Xiec74b5462012-08-24 00:17:13 -0700441 case 2:
442 --output;
443 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
444 input >>= 6;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000445 //fall through
Matthew Xiec74b5462012-08-24 00:17:13 -0700446 case 1:
447 --output;
448 *output = (char)(input | FIRST_BYTE_MARK[*length]);
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000449 break;
450 default:
451 TIXMLASSERT( false );
Matthew Xiec74b5462012-08-24 00:17:13 -0700452 }
453}
454
455
456const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
457{
458 // Presume an entity, and pull it out.
459 *length = 0;
460
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000461 if ( *(p+1) == '#' && *(p+2) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700462 unsigned long ucs = 0;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000463 TIXMLASSERT( sizeof( ucs ) >= 4 );
Matthew Xiec74b5462012-08-24 00:17:13 -0700464 ptrdiff_t delta = 0;
465 unsigned mult = 1;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000466 static const char SEMICOLON = ';';
Matthew Xiec74b5462012-08-24 00:17:13 -0700467
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000468 if ( *(p+2) == 'x' ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700469 // Hexadecimal.
Matthew Xiec74b5462012-08-24 00:17:13 -0700470 const char* q = p+3;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000471 if ( !(*q) ) {
472 return 0;
473 }
Matthew Xiec74b5462012-08-24 00:17:13 -0700474
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000475 q = strchr( q, SEMICOLON );
476
477 if ( !q ) {
478 return 0;
479 }
480 TIXMLASSERT( *q == SEMICOLON );
Matthew Xiec74b5462012-08-24 00:17:13 -0700481
482 delta = q-p;
483 --q;
484
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000485 while ( *q != 'x' ) {
486 unsigned int digit = 0;
487
488 if ( *q >= '0' && *q <= '9' ) {
489 digit = *q - '0';
490 }
491 else if ( *q >= 'a' && *q <= 'f' ) {
492 digit = *q - 'a' + 10;
493 }
494 else if ( *q >= 'A' && *q <= 'F' ) {
495 digit = *q - 'A' + 10;
496 }
497 else {
Matthew Xiec74b5462012-08-24 00:17:13 -0700498 return 0;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000499 }
500 TIXMLASSERT( digit < 16 );
501 TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
502 const unsigned int digitScaled = mult * digit;
503 TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
504 ucs += digitScaled;
505 TIXMLASSERT( mult <= UINT_MAX / 16 );
Matthew Xiec74b5462012-08-24 00:17:13 -0700506 mult *= 16;
507 --q;
508 }
509 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000510 else {
Matthew Xiec74b5462012-08-24 00:17:13 -0700511 // Decimal.
Matthew Xiec74b5462012-08-24 00:17:13 -0700512 const char* q = p+2;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000513 if ( !(*q) ) {
514 return 0;
515 }
Matthew Xiec74b5462012-08-24 00:17:13 -0700516
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000517 q = strchr( q, SEMICOLON );
518
519 if ( !q ) {
520 return 0;
521 }
522 TIXMLASSERT( *q == SEMICOLON );
Matthew Xiec74b5462012-08-24 00:17:13 -0700523
524 delta = q-p;
525 --q;
526
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000527 while ( *q != '#' ) {
528 if ( *q >= '0' && *q <= '9' ) {
529 const unsigned int digit = *q - '0';
530 TIXMLASSERT( digit < 10 );
531 TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
532 const unsigned int digitScaled = mult * digit;
533 TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
534 ucs += digitScaled;
535 }
536 else {
Matthew Xiec74b5462012-08-24 00:17:13 -0700537 return 0;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000538 }
539 TIXMLASSERT( mult <= UINT_MAX / 10 );
Matthew Xiec74b5462012-08-24 00:17:13 -0700540 mult *= 10;
541 --q;
542 }
543 }
544 // convert the UCS to UTF-8
545 ConvertUTF32ToUTF8( ucs, value, length );
546 return p + delta + 1;
547 }
548 return p+1;
549}
550
551
552void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
553{
554 TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
555}
556
557
558void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
559{
560 TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
561}
562
563
564void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
565{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000566 TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse);
Matthew Xiec74b5462012-08-24 00:17:13 -0700567}
568
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000569/*
570 ToStr() of a number is a very tricky topic.
571 https://github.com/leethomason/tinyxml2/issues/106
572*/
Matthew Xiec74b5462012-08-24 00:17:13 -0700573void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
574{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000575 TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );
Matthew Xiec74b5462012-08-24 00:17:13 -0700576}
577
578
579void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
580{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000581 TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );
582}
583
584
585void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize)
586{
587 // horrible syntax trick to make the compiler happy about %lld
588 TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v);
Matthew Xiec74b5462012-08-24 00:17:13 -0700589}
590
591
592bool XMLUtil::ToInt( const char* str, int* value )
593{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000594 if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700595 return true;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000596 }
Matthew Xiec74b5462012-08-24 00:17:13 -0700597 return false;
598}
599
600bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
601{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000602 if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700603 return true;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000604 }
Matthew Xiec74b5462012-08-24 00:17:13 -0700605 return false;
606}
607
608bool XMLUtil::ToBool( const char* str, bool* value )
609{
610 int ival = 0;
611 if ( ToInt( str, &ival )) {
612 *value = (ival==0) ? false : true;
613 return true;
614 }
615 if ( StringEqual( str, "true" ) ) {
616 *value = true;
617 return true;
618 }
619 else if ( StringEqual( str, "false" ) ) {
620 *value = false;
621 return true;
622 }
623 return false;
624}
625
626
627bool XMLUtil::ToFloat( const char* str, float* value )
628{
629 if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
630 return true;
631 }
632 return false;
633}
634
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000635
Matthew Xiec74b5462012-08-24 00:17:13 -0700636bool XMLUtil::ToDouble( const char* str, double* value )
637{
638 if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
639 return true;
640 }
641 return false;
642}
643
644
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000645bool XMLUtil::ToInt64(const char* str, int64_t* value)
646{
647 long long v = 0; // horrible syntax trick to make the compiler happy about %lld
648 if (TIXML_SSCANF(str, "%lld", &v) == 1) {
649 *value = (int64_t)v;
650 return true;
651 }
652 return false;
653}
654
655
Matthew Xiec74b5462012-08-24 00:17:13 -0700656char* XMLDocument::Identify( char* p, XMLNode** node )
657{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000658 TIXMLASSERT( node );
659 TIXMLASSERT( p );
660 char* const start = p;
661 int const startLine = _parseCurLineNum;
662 p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
663 if( !*p ) {
664 *node = 0;
665 TIXMLASSERT( p );
Matthew Xiec74b5462012-08-24 00:17:13 -0700666 return p;
667 }
668
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000669 // These strings define the matching patterns:
670 static const char* xmlHeader = { "<?" };
671 static const char* commentHeader = { "<!--" };
672 static const char* cdataHeader = { "<![CDATA[" };
673 static const char* dtdHeader = { "<!" };
674 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Matthew Xiec74b5462012-08-24 00:17:13 -0700675
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000676 static const int xmlHeaderLen = 2;
677 static const int commentHeaderLen = 4;
678 static const int cdataHeaderLen = 9;
679 static const int dtdHeaderLen = 2;
680 static const int elementHeaderLen = 1;
Matthew Xiec74b5462012-08-24 00:17:13 -0700681
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000682 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
683 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
684 XMLNode* returnNode = 0;
Matthew Xiec74b5462012-08-24 00:17:13 -0700685 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000686 returnNode = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
687 returnNode->_parseLineNum = _parseCurLineNum;
Matthew Xiec74b5462012-08-24 00:17:13 -0700688 p += xmlHeaderLen;
689 }
690 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000691 returnNode = CreateUnlinkedNode<XMLComment>( _commentPool );
692 returnNode->_parseLineNum = _parseCurLineNum;
Matthew Xiec74b5462012-08-24 00:17:13 -0700693 p += commentHeaderLen;
694 }
695 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000696 XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
Matthew Xiec74b5462012-08-24 00:17:13 -0700697 returnNode = text;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000698 returnNode->_parseLineNum = _parseCurLineNum;
Matthew Xiec74b5462012-08-24 00:17:13 -0700699 p += cdataHeaderLen;
700 text->SetCData( true );
701 }
702 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000703 returnNode = CreateUnlinkedNode<XMLUnknown>( _commentPool );
704 returnNode->_parseLineNum = _parseCurLineNum;
Matthew Xiec74b5462012-08-24 00:17:13 -0700705 p += dtdHeaderLen;
706 }
707 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000708 returnNode = CreateUnlinkedNode<XMLElement>( _elementPool );
709 returnNode->_parseLineNum = _parseCurLineNum;
Matthew Xiec74b5462012-08-24 00:17:13 -0700710 p += elementHeaderLen;
711 }
712 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000713 returnNode = CreateUnlinkedNode<XMLText>( _textPool );
714 returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character
715 p = start; // Back it up, all the text counts.
716 _parseCurLineNum = startLine;
Matthew Xiec74b5462012-08-24 00:17:13 -0700717 }
718
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000719 TIXMLASSERT( returnNode );
720 TIXMLASSERT( p );
Matthew Xiec74b5462012-08-24 00:17:13 -0700721 *node = returnNode;
722 return p;
723}
724
725
726bool XMLDocument::Accept( XMLVisitor* visitor ) const
727{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000728 TIXMLASSERT( visitor );
729 if ( visitor->VisitEnter( *this ) ) {
730 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
731 if ( !node->Accept( visitor ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700732 break;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000733 }
Matthew Xiec74b5462012-08-24 00:17:13 -0700734 }
735 }
736 return visitor->VisitExit( *this );
737}
738
739
740// --------- XMLNode ----------- //
741
742XMLNode::XMLNode( XMLDocument* doc ) :
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000743 _document( doc ),
744 _parent( 0 ),
745 _value(),
746 _parseLineNum( 0 ),
747 _firstChild( 0 ), _lastChild( 0 ),
748 _prev( 0 ), _next( 0 ),
749 _userData( 0 ),
750 _memPool( 0 )
Matthew Xiec74b5462012-08-24 00:17:13 -0700751{
752}
753
754
755XMLNode::~XMLNode()
756{
757 DeleteChildren();
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000758 if ( _parent ) {
759 _parent->Unlink( this );
Matthew Xiec74b5462012-08-24 00:17:13 -0700760 }
761}
762
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000763const char* XMLNode::Value() const
764{
765 // Edge case: XMLDocuments don't have a Value. Return null.
766 if ( this->ToDocument() )
767 return 0;
768 return _value.GetStr();
769}
Matthew Xiec74b5462012-08-24 00:17:13 -0700770
771void XMLNode::SetValue( const char* str, bool staticMem )
772{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000773 if ( staticMem ) {
774 _value.SetInternedStr( str );
775 }
776 else {
777 _value.SetStr( str );
778 }
Matthew Xiec74b5462012-08-24 00:17:13 -0700779}
780
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000781XMLNode* XMLNode::DeepClone(XMLDocument* target) const
782{
783 XMLNode* clone = this->ShallowClone(target);
784 if (!clone) return 0;
785
786 for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) {
787 XMLNode* childClone = child->DeepClone(target);
788 TIXMLASSERT(childClone);
789 clone->InsertEndChild(childClone);
790 }
791 return clone;
792}
Matthew Xiec74b5462012-08-24 00:17:13 -0700793
794void XMLNode::DeleteChildren()
795{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000796 while( _firstChild ) {
797 TIXMLASSERT( _lastChild );
798 DeleteChild( _firstChild );
Matthew Xiec74b5462012-08-24 00:17:13 -0700799 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000800 _firstChild = _lastChild = 0;
Matthew Xiec74b5462012-08-24 00:17:13 -0700801}
802
803
804void XMLNode::Unlink( XMLNode* child )
805{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000806 TIXMLASSERT( child );
807 TIXMLASSERT( child->_document == _document );
808 TIXMLASSERT( child->_parent == this );
809 if ( child == _firstChild ) {
810 _firstChild = _firstChild->_next;
811 }
812 if ( child == _lastChild ) {
813 _lastChild = _lastChild->_prev;
814 }
Matthew Xiec74b5462012-08-24 00:17:13 -0700815
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000816 if ( child->_prev ) {
817 child->_prev->_next = child->_next;
Matthew Xiec74b5462012-08-24 00:17:13 -0700818 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000819 if ( child->_next ) {
820 child->_next->_prev = child->_prev;
Matthew Xiec74b5462012-08-24 00:17:13 -0700821 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000822 child->_next = 0;
823 child->_prev = 0;
824 child->_parent = 0;
Matthew Xiec74b5462012-08-24 00:17:13 -0700825}
826
827
828void XMLNode::DeleteChild( XMLNode* node )
829{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000830 TIXMLASSERT( node );
831 TIXMLASSERT( node->_document == _document );
832 TIXMLASSERT( node->_parent == this );
833 Unlink( node );
834 TIXMLASSERT(node->_prev == 0);
835 TIXMLASSERT(node->_next == 0);
836 TIXMLASSERT(node->_parent == 0);
837 DeleteNode( node );
Matthew Xiec74b5462012-08-24 00:17:13 -0700838}
839
840
841XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
842{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000843 TIXMLASSERT( addThis );
844 if ( addThis->_document != _document ) {
845 TIXMLASSERT( false );
846 return 0;
847 }
848 InsertChildPreamble( addThis );
Matthew Xiec74b5462012-08-24 00:17:13 -0700849
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000850 if ( _lastChild ) {
851 TIXMLASSERT( _firstChild );
852 TIXMLASSERT( _lastChild->_next == 0 );
853 _lastChild->_next = addThis;
854 addThis->_prev = _lastChild;
855 _lastChild = addThis;
856
857 addThis->_next = 0;
Matthew Xiec74b5462012-08-24 00:17:13 -0700858 }
859 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000860 TIXMLASSERT( _firstChild == 0 );
861 _firstChild = _lastChild = addThis;
Matthew Xiec74b5462012-08-24 00:17:13 -0700862
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000863 addThis->_prev = 0;
864 addThis->_next = 0;
Matthew Xiec74b5462012-08-24 00:17:13 -0700865 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000866 addThis->_parent = this;
Matthew Xiec74b5462012-08-24 00:17:13 -0700867 return addThis;
868}
869
870
871XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
872{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000873 TIXMLASSERT( addThis );
874 if ( addThis->_document != _document ) {
875 TIXMLASSERT( false );
876 return 0;
877 }
878 InsertChildPreamble( addThis );
Matthew Xiec74b5462012-08-24 00:17:13 -0700879
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000880 if ( _firstChild ) {
881 TIXMLASSERT( _lastChild );
882 TIXMLASSERT( _firstChild->_prev == 0 );
Matthew Xiec74b5462012-08-24 00:17:13 -0700883
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000884 _firstChild->_prev = addThis;
885 addThis->_next = _firstChild;
886 _firstChild = addThis;
887
888 addThis->_prev = 0;
Matthew Xiec74b5462012-08-24 00:17:13 -0700889 }
890 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000891 TIXMLASSERT( _lastChild == 0 );
892 _firstChild = _lastChild = addThis;
Matthew Xiec74b5462012-08-24 00:17:13 -0700893
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000894 addThis->_prev = 0;
895 addThis->_next = 0;
Matthew Xiec74b5462012-08-24 00:17:13 -0700896 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000897 addThis->_parent = this;
Matthew Xiec74b5462012-08-24 00:17:13 -0700898 return addThis;
899}
900
901
902XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
903{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000904 TIXMLASSERT( addThis );
905 if ( addThis->_document != _document ) {
906 TIXMLASSERT( false );
Matthew Xiec74b5462012-08-24 00:17:13 -0700907 return 0;
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000908 }
Matthew Xiec74b5462012-08-24 00:17:13 -0700909
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000910 TIXMLASSERT( afterThis );
911
912 if ( afterThis->_parent != this ) {
913 TIXMLASSERT( false );
914 return 0;
915 }
916 if ( afterThis == addThis ) {
917 // Current state: BeforeThis -> AddThis -> OneAfterAddThis
918 // Now AddThis must disappear from it's location and then
919 // reappear between BeforeThis and OneAfterAddThis.
920 // So just leave it where it is.
921 return addThis;
922 }
923
924 if ( afterThis->_next == 0 ) {
Matthew Xiec74b5462012-08-24 00:17:13 -0700925 // The last node or the only node.
926 return InsertEndChild( addThis );
927 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000928 InsertChildPreamble( addThis );
929 addThis->_prev = afterThis;
930 addThis->_next = afterThis->_next;
931 afterThis->_next->_prev = addThis;
932 afterThis->_next = addThis;
933 addThis->_parent = this;
Matthew Xiec74b5462012-08-24 00:17:13 -0700934 return addThis;
935}
936
937
938
939
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000940const XMLElement* XMLNode::FirstChildElement( const char* name ) const
Matthew Xiec74b5462012-08-24 00:17:13 -0700941{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000942 for( const XMLNode* node = _firstChild; node; node = node->_next ) {
943 const XMLElement* element = node->ToElementWithName( name );
Matthew Xiec74b5462012-08-24 00:17:13 -0700944 if ( element ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000945 return element;
Matthew Xiec74b5462012-08-24 00:17:13 -0700946 }
947 }
948 return 0;
949}
950
951
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000952const XMLElement* XMLNode::LastChildElement( const char* name ) const
Matthew Xiec74b5462012-08-24 00:17:13 -0700953{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000954 for( const XMLNode* node = _lastChild; node; node = node->_prev ) {
955 const XMLElement* element = node->ToElementWithName( name );
Matthew Xiec74b5462012-08-24 00:17:13 -0700956 if ( element ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000957 return element;
Matthew Xiec74b5462012-08-24 00:17:13 -0700958 }
959 }
960 return 0;
961}
962
963
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000964const XMLElement* XMLNode::NextSiblingElement( const char* name ) const
Matthew Xiec74b5462012-08-24 00:17:13 -0700965{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000966 for( const XMLNode* node = _next; node; node = node->_next ) {
967 const XMLElement* element = node->ToElementWithName( name );
968 if ( element ) {
969 return element;
Matthew Xiec74b5462012-08-24 00:17:13 -0700970 }
971 }
972 return 0;
973}
974
975
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000976const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const
Matthew Xiec74b5462012-08-24 00:17:13 -0700977{
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000978 for( const XMLNode* node = _prev; node; node = node->_prev ) {
979 const XMLElement* element = node->ToElementWithName( name );
980 if ( element ) {
981 return element;
Matthew Xiec74b5462012-08-24 00:17:13 -0700982 }
983 }
984 return 0;
985}
986
987
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000988char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -0700989{
990 // This is a recursive method, but thinking about it "at the current level"
991 // it is a pretty simple flat list:
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000992 // <foo/>
993 // <!-- comment -->
Matthew Xiec74b5462012-08-24 00:17:13 -0700994 //
995 // With a special case:
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000996 // <foo>
997 // </foo>
998 // <!-- comment -->
Matthew Xiec74b5462012-08-24 00:17:13 -0700999 //
1000 // Where the closing element (/foo) *must* be the next thing after the opening
1001 // element, and the names must match. BUT the tricky bit is that the closing
1002 // element will be read by the child.
1003 //
1004 // 'endTag' is the end tag for this node, it is returned by a call to a child.
1005 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
1006
1007 while( p && *p ) {
1008 XMLNode* node = 0;
1009
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001010 p = _document->Identify( p, &node );
1011 TIXMLASSERT( p );
1012 if ( node == 0 ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001013 break;
1014 }
1015
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001016 int initialLineNum = node->_parseLineNum;
1017
Matthew Xiec74b5462012-08-24 00:17:13 -07001018 StrPair endTag;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001019 p = node->ParseDeep( p, &endTag, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001020 if ( !p ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001021 DeleteNode( node );
1022 if ( !_document->Error() ) {
1023 _document->SetError( XML_ERROR_PARSING, initialLineNum, 0);
Matthew Xiec74b5462012-08-24 00:17:13 -07001024 }
1025 break;
1026 }
1027
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001028 XMLDeclaration* decl = node->ToDeclaration();
1029 if ( decl ) {
1030 // Declarations are only allowed at document level
1031 bool wellLocated = ( ToDocument() != 0 );
1032 if ( wellLocated ) {
1033 // Multiple declarations are allowed but all declarations
1034 // must occur before anything else
1035 for ( const XMLNode* existingNode = _document->FirstChild(); existingNode; existingNode = existingNode->NextSibling() ) {
1036 if ( !existingNode->ToDeclaration() ) {
1037 wellLocated = false;
1038 break;
1039 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001040 }
1041 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001042 if ( !wellLocated ) {
1043 _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value());
1044 DeleteNode( node );
1045 break;
1046 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001047 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001048
1049 XMLElement* ele = node->ToElement();
1050 if ( ele ) {
1051 // We read the end tag. Return it to the parent.
1052 if ( ele->ClosingType() == XMLElement::CLOSING ) {
1053 if ( parentEndTag ) {
1054 ele->_value.TransferTo( parentEndTag );
1055 }
1056 node->_memPool->SetTracked(); // created and then immediately deleted.
1057 DeleteNode( node );
1058 return p;
1059 }
1060
1061 // Handle an end tag returned to this level.
1062 // And handle a bunch of annoying errors.
1063 bool mismatch = false;
1064 if ( endTag.Empty() ) {
1065 if ( ele->ClosingType() == XMLElement::OPEN ) {
1066 mismatch = true;
1067 }
1068 }
1069 else {
1070 if ( ele->ClosingType() != XMLElement::OPEN ) {
1071 mismatch = true;
1072 }
1073 else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
1074 mismatch = true;
1075 }
1076 }
1077 if ( mismatch ) {
1078 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name());
1079 DeleteNode( node );
1080 break;
1081 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001082 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001083 InsertEndChild( node );
1084 }
1085 return 0;
1086}
1087
1088/*static*/ void XMLNode::DeleteNode( XMLNode* node )
1089{
1090 if ( node == 0 ) {
1091 return;
1092 }
1093 TIXMLASSERT(node->_document);
1094 if (!node->ToDocument()) {
1095 node->_document->MarkInUse(node);
1096 }
1097
1098 MemPool* pool = node->_memPool;
1099 node->~XMLNode();
1100 pool->Free( node );
1101}
1102
1103void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
1104{
1105 TIXMLASSERT( insertThis );
1106 TIXMLASSERT( insertThis->_document == _document );
1107
1108 if (insertThis->_parent) {
1109 insertThis->_parent->Unlink( insertThis );
1110 }
1111 else {
1112 insertThis->_document->MarkInUse(insertThis);
1113 insertThis->_memPool->SetTracked();
1114 }
1115}
1116
1117const XMLElement* XMLNode::ToElementWithName( const char* name ) const
1118{
1119 const XMLElement* element = this->ToElement();
1120 if ( element == 0 ) {
1121 return 0;
1122 }
1123 if ( name == 0 ) {
1124 return element;
1125 }
1126 if ( XMLUtil::StringEqual( element->Name(), name ) ) {
1127 return element;
Matthew Xiec74b5462012-08-24 00:17:13 -07001128 }
1129 return 0;
1130}
1131
1132// --------- XMLText ---------- //
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001133char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -07001134{
Matthew Xiec74b5462012-08-24 00:17:13 -07001135 if ( this->CData() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001136 p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001137 if ( !p ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001138 _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 );
Matthew Xiec74b5462012-08-24 00:17:13 -07001139 }
1140 return p;
1141 }
1142 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001143 int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
1144 if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
1145 flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
Matthew Xiec74b5462012-08-24 00:17:13 -07001146 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001147
1148 p = _value.ParseText( p, "<", flags, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001149 if ( p && *p ) {
1150 return p-1;
1151 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001152 if ( !p ) {
1153 _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 );
1154 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001155 }
1156 return 0;
1157}
1158
1159
1160XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
1161{
1162 if ( !doc ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001163 doc = _document;
Matthew Xiec74b5462012-08-24 00:17:13 -07001164 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001165 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
Matthew Xiec74b5462012-08-24 00:17:13 -07001166 text->SetCData( this->CData() );
1167 return text;
1168}
1169
1170
1171bool XMLText::ShallowEqual( const XMLNode* compare ) const
1172{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001173 TIXMLASSERT( compare );
1174 const XMLText* text = compare->ToText();
1175 return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
Matthew Xiec74b5462012-08-24 00:17:13 -07001176}
1177
1178
1179bool XMLText::Accept( XMLVisitor* visitor ) const
1180{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001181 TIXMLASSERT( visitor );
Matthew Xiec74b5462012-08-24 00:17:13 -07001182 return visitor->Visit( *this );
1183}
1184
1185
1186// --------- XMLComment ---------- //
1187
1188XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
1189{
1190}
1191
1192
1193XMLComment::~XMLComment()
1194{
Matthew Xiec74b5462012-08-24 00:17:13 -07001195}
1196
1197
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001198char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -07001199{
1200 // Comment parses as text.
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001201 p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001202 if ( p == 0 ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001203 _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 );
Matthew Xiec74b5462012-08-24 00:17:13 -07001204 }
1205 return p;
1206}
1207
1208
1209XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
1210{
1211 if ( !doc ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001212 doc = _document;
Matthew Xiec74b5462012-08-24 00:17:13 -07001213 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001214 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
Matthew Xiec74b5462012-08-24 00:17:13 -07001215 return comment;
1216}
1217
1218
1219bool XMLComment::ShallowEqual( const XMLNode* compare ) const
1220{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001221 TIXMLASSERT( compare );
1222 const XMLComment* comment = compare->ToComment();
1223 return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
Matthew Xiec74b5462012-08-24 00:17:13 -07001224}
1225
1226
1227bool XMLComment::Accept( XMLVisitor* visitor ) const
1228{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001229 TIXMLASSERT( visitor );
Matthew Xiec74b5462012-08-24 00:17:13 -07001230 return visitor->Visit( *this );
1231}
1232
1233
1234// --------- XMLDeclaration ---------- //
1235
1236XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
1237{
1238}
1239
1240
1241XMLDeclaration::~XMLDeclaration()
1242{
1243 //printf( "~XMLDeclaration\n" );
1244}
1245
1246
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001247char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -07001248{
1249 // Declaration parses as text.
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001250 p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001251 if ( p == 0 ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001252 _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 );
Matthew Xiec74b5462012-08-24 00:17:13 -07001253 }
1254 return p;
1255}
1256
1257
1258XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
1259{
1260 if ( !doc ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001261 doc = _document;
Matthew Xiec74b5462012-08-24 00:17:13 -07001262 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001263 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
Matthew Xiec74b5462012-08-24 00:17:13 -07001264 return dec;
1265}
1266
1267
1268bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
1269{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001270 TIXMLASSERT( compare );
1271 const XMLDeclaration* declaration = compare->ToDeclaration();
1272 return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
Matthew Xiec74b5462012-08-24 00:17:13 -07001273}
1274
1275
1276
1277bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
1278{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001279 TIXMLASSERT( visitor );
Matthew Xiec74b5462012-08-24 00:17:13 -07001280 return visitor->Visit( *this );
1281}
1282
1283// --------- XMLUnknown ---------- //
1284
1285XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
1286{
1287}
1288
1289
1290XMLUnknown::~XMLUnknown()
1291{
1292}
1293
1294
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001295char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -07001296{
1297 // Unknown parses as text.
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001298 p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001299 if ( !p ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001300 _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 );
Matthew Xiec74b5462012-08-24 00:17:13 -07001301 }
1302 return p;
1303}
1304
1305
1306XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
1307{
1308 if ( !doc ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001309 doc = _document;
Matthew Xiec74b5462012-08-24 00:17:13 -07001310 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001311 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
Matthew Xiec74b5462012-08-24 00:17:13 -07001312 return text;
1313}
1314
1315
1316bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
1317{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001318 TIXMLASSERT( compare );
1319 const XMLUnknown* unknown = compare->ToUnknown();
1320 return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
Matthew Xiec74b5462012-08-24 00:17:13 -07001321}
1322
1323
1324bool XMLUnknown::Accept( XMLVisitor* visitor ) const
1325{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001326 TIXMLASSERT( visitor );
Matthew Xiec74b5462012-08-24 00:17:13 -07001327 return visitor->Visit( *this );
1328}
1329
1330// --------- XMLAttribute ---------- //
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001331
1332const char* XMLAttribute::Name() const
1333{
1334 return _name.GetStr();
1335}
1336
1337const char* XMLAttribute::Value() const
1338{
1339 return _value.GetStr();
1340}
1341
1342char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -07001343{
1344 // Parse using the name rules: bug fix, was using ParseText before
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001345 p = _name.ParseName( p );
1346 if ( !p || !*p ) {
1347 return 0;
1348 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001349
1350 // Skip white space before =
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001351 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1352 if ( *p != '=' ) {
1353 return 0;
1354 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001355
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001356 ++p; // move up to opening quote
1357 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1358 if ( *p != '\"' && *p != '\'' ) {
1359 return 0;
1360 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001361
1362 char endTag[2] = { *p, 0 };
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001363 ++p; // move past opening quote
Matthew Xiec74b5462012-08-24 00:17:13 -07001364
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001365 p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001366 return p;
1367}
1368
1369
1370void XMLAttribute::SetName( const char* n )
1371{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001372 _name.SetStr( n );
Matthew Xiec74b5462012-08-24 00:17:13 -07001373}
1374
1375
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001376XMLError XMLAttribute::QueryIntValue( int* value ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001377{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001378 if ( XMLUtil::ToInt( Value(), value )) {
1379 return XML_SUCCESS;
Matthew Xiec74b5462012-08-24 00:17:13 -07001380 }
1381 return XML_WRONG_ATTRIBUTE_TYPE;
1382}
1383
1384
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001385XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001386{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001387 if ( XMLUtil::ToUnsigned( Value(), value )) {
1388 return XML_SUCCESS;
1389 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001390 return XML_WRONG_ATTRIBUTE_TYPE;
1391}
1392
1393
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001394XMLError XMLAttribute::QueryInt64Value(int64_t* value) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001395{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001396 if (XMLUtil::ToInt64(Value(), value)) {
1397 return XML_SUCCESS;
1398 }
1399 return XML_WRONG_ATTRIBUTE_TYPE;
1400}
1401
1402
1403XMLError XMLAttribute::QueryBoolValue( bool* value ) const
1404{
1405 if ( XMLUtil::ToBool( Value(), value )) {
1406 return XML_SUCCESS;
1407 }
1408 return XML_WRONG_ATTRIBUTE_TYPE;
1409}
1410
1411
1412XMLError XMLAttribute::QueryFloatValue( float* value ) const
1413{
1414 if ( XMLUtil::ToFloat( Value(), value )) {
1415 return XML_SUCCESS;
1416 }
1417 return XML_WRONG_ATTRIBUTE_TYPE;
1418}
1419
1420
1421XMLError XMLAttribute::QueryDoubleValue( double* value ) const
1422{
1423 if ( XMLUtil::ToDouble( Value(), value )) {
1424 return XML_SUCCESS;
1425 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001426 return XML_WRONG_ATTRIBUTE_TYPE;
1427}
1428
1429
1430void XMLAttribute::SetAttribute( const char* v )
1431{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001432 _value.SetStr( v );
Matthew Xiec74b5462012-08-24 00:17:13 -07001433}
1434
1435
1436void XMLAttribute::SetAttribute( int v )
1437{
1438 char buf[BUF_SIZE];
1439 XMLUtil::ToStr( v, buf, BUF_SIZE );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001440 _value.SetStr( buf );
Matthew Xiec74b5462012-08-24 00:17:13 -07001441}
1442
1443
1444void XMLAttribute::SetAttribute( unsigned v )
1445{
1446 char buf[BUF_SIZE];
1447 XMLUtil::ToStr( v, buf, BUF_SIZE );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001448 _value.SetStr( buf );
Matthew Xiec74b5462012-08-24 00:17:13 -07001449}
1450
1451
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001452void XMLAttribute::SetAttribute(int64_t v)
1453{
1454 char buf[BUF_SIZE];
1455 XMLUtil::ToStr(v, buf, BUF_SIZE);
1456 _value.SetStr(buf);
1457}
1458
1459
1460
Matthew Xiec74b5462012-08-24 00:17:13 -07001461void XMLAttribute::SetAttribute( bool v )
1462{
1463 char buf[BUF_SIZE];
1464 XMLUtil::ToStr( v, buf, BUF_SIZE );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001465 _value.SetStr( buf );
Matthew Xiec74b5462012-08-24 00:17:13 -07001466}
1467
1468void XMLAttribute::SetAttribute( double v )
1469{
1470 char buf[BUF_SIZE];
1471 XMLUtil::ToStr( v, buf, BUF_SIZE );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001472 _value.SetStr( buf );
Matthew Xiec74b5462012-08-24 00:17:13 -07001473}
1474
1475void XMLAttribute::SetAttribute( float v )
1476{
1477 char buf[BUF_SIZE];
1478 XMLUtil::ToStr( v, buf, BUF_SIZE );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001479 _value.SetStr( buf );
Matthew Xiec74b5462012-08-24 00:17:13 -07001480}
1481
1482
1483// --------- XMLElement ---------- //
1484XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001485 _closingType( OPEN ),
1486 _rootAttribute( 0 )
Matthew Xiec74b5462012-08-24 00:17:13 -07001487{
1488}
1489
1490
1491XMLElement::~XMLElement()
1492{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001493 while( _rootAttribute ) {
1494 XMLAttribute* next = _rootAttribute->_next;
1495 DeleteAttribute( _rootAttribute );
1496 _rootAttribute = next;
Matthew Xiec74b5462012-08-24 00:17:13 -07001497 }
1498}
1499
1500
Matthew Xiec74b5462012-08-24 00:17:13 -07001501const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1502{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001503 for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
1504 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001505 return a;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001506 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001507 }
1508 return 0;
1509}
1510
1511
1512const char* XMLElement::Attribute( const char* name, const char* value ) const
1513{
1514 const XMLAttribute* a = FindAttribute( name );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001515 if ( !a ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001516 return 0;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001517 }
1518 if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001519 return a->Value();
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001520 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001521 return 0;
1522}
1523
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001524int XMLElement::IntAttribute(const char* name, int defaultValue) const
1525{
1526 int i = defaultValue;
1527 QueryIntAttribute(name, &i);
1528 return i;
1529}
1530
1531unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const
1532{
1533 unsigned i = defaultValue;
1534 QueryUnsignedAttribute(name, &i);
1535 return i;
1536}
1537
1538int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const
1539{
1540 int64_t i = defaultValue;
1541 QueryInt64Attribute(name, &i);
1542 return i;
1543}
1544
1545bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const
1546{
1547 bool b = defaultValue;
1548 QueryBoolAttribute(name, &b);
1549 return b;
1550}
1551
1552double XMLElement::DoubleAttribute(const char* name, double defaultValue) const
1553{
1554 double d = defaultValue;
1555 QueryDoubleAttribute(name, &d);
1556 return d;
1557}
1558
1559float XMLElement::FloatAttribute(const char* name, float defaultValue) const
1560{
1561 float f = defaultValue;
1562 QueryFloatAttribute(name, &f);
1563 return f;
1564}
Matthew Xiec74b5462012-08-24 00:17:13 -07001565
1566const char* XMLElement::GetText() const
1567{
1568 if ( FirstChild() && FirstChild()->ToText() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001569 return FirstChild()->Value();
Matthew Xiec74b5462012-08-24 00:17:13 -07001570 }
1571 return 0;
1572}
1573
1574
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001575void XMLElement::SetText( const char* inText )
1576{
1577 if ( FirstChild() && FirstChild()->ToText() )
1578 FirstChild()->SetValue( inText );
1579 else {
1580 XMLText* theText = GetDocument()->NewText( inText );
1581 InsertFirstChild( theText );
1582 }
1583}
1584
1585
1586void XMLElement::SetText( int v )
1587{
1588 char buf[BUF_SIZE];
1589 XMLUtil::ToStr( v, buf, BUF_SIZE );
1590 SetText( buf );
1591}
1592
1593
1594void XMLElement::SetText( unsigned v )
1595{
1596 char buf[BUF_SIZE];
1597 XMLUtil::ToStr( v, buf, BUF_SIZE );
1598 SetText( buf );
1599}
1600
1601
1602void XMLElement::SetText(int64_t v)
1603{
1604 char buf[BUF_SIZE];
1605 XMLUtil::ToStr(v, buf, BUF_SIZE);
1606 SetText(buf);
1607}
1608
1609
1610void XMLElement::SetText( bool v )
1611{
1612 char buf[BUF_SIZE];
1613 XMLUtil::ToStr( v, buf, BUF_SIZE );
1614 SetText( buf );
1615}
1616
1617
1618void XMLElement::SetText( float v )
1619{
1620 char buf[BUF_SIZE];
1621 XMLUtil::ToStr( v, buf, BUF_SIZE );
1622 SetText( buf );
1623}
1624
1625
1626void XMLElement::SetText( double v )
1627{
1628 char buf[BUF_SIZE];
1629 XMLUtil::ToStr( v, buf, BUF_SIZE );
1630 SetText( buf );
1631}
1632
1633
1634XMLError XMLElement::QueryIntText( int* ival ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001635{
1636 if ( FirstChild() && FirstChild()->ToText() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001637 const char* t = FirstChild()->Value();
1638 if ( XMLUtil::ToInt( t, ival ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001639 return XML_SUCCESS;
1640 }
1641 return XML_CAN_NOT_CONVERT_TEXT;
1642 }
1643 return XML_NO_TEXT_NODE;
1644}
1645
1646
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001647XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001648{
1649 if ( FirstChild() && FirstChild()->ToText() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001650 const char* t = FirstChild()->Value();
1651 if ( XMLUtil::ToUnsigned( t, uval ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001652 return XML_SUCCESS;
1653 }
1654 return XML_CAN_NOT_CONVERT_TEXT;
1655 }
1656 return XML_NO_TEXT_NODE;
1657}
1658
1659
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001660XMLError XMLElement::QueryInt64Text(int64_t* ival) const
1661{
1662 if (FirstChild() && FirstChild()->ToText()) {
1663 const char* t = FirstChild()->Value();
1664 if (XMLUtil::ToInt64(t, ival)) {
1665 return XML_SUCCESS;
1666 }
1667 return XML_CAN_NOT_CONVERT_TEXT;
1668 }
1669 return XML_NO_TEXT_NODE;
1670}
1671
1672
1673XMLError XMLElement::QueryBoolText( bool* bval ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001674{
1675 if ( FirstChild() && FirstChild()->ToText() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001676 const char* t = FirstChild()->Value();
1677 if ( XMLUtil::ToBool( t, bval ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001678 return XML_SUCCESS;
1679 }
1680 return XML_CAN_NOT_CONVERT_TEXT;
1681 }
1682 return XML_NO_TEXT_NODE;
1683}
1684
1685
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001686XMLError XMLElement::QueryDoubleText( double* dval ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001687{
1688 if ( FirstChild() && FirstChild()->ToText() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001689 const char* t = FirstChild()->Value();
1690 if ( XMLUtil::ToDouble( t, dval ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001691 return XML_SUCCESS;
1692 }
1693 return XML_CAN_NOT_CONVERT_TEXT;
1694 }
1695 return XML_NO_TEXT_NODE;
1696}
1697
1698
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001699XMLError XMLElement::QueryFloatText( float* fval ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001700{
1701 if ( FirstChild() && FirstChild()->ToText() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001702 const char* t = FirstChild()->Value();
1703 if ( XMLUtil::ToFloat( t, fval ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001704 return XML_SUCCESS;
1705 }
1706 return XML_CAN_NOT_CONVERT_TEXT;
1707 }
1708 return XML_NO_TEXT_NODE;
1709}
1710
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001711int XMLElement::IntText(int defaultValue) const
1712{
1713 int i = defaultValue;
1714 QueryIntText(&i);
1715 return i;
1716}
1717
1718unsigned XMLElement::UnsignedText(unsigned defaultValue) const
1719{
1720 unsigned i = defaultValue;
1721 QueryUnsignedText(&i);
1722 return i;
1723}
1724
1725int64_t XMLElement::Int64Text(int64_t defaultValue) const
1726{
1727 int64_t i = defaultValue;
1728 QueryInt64Text(&i);
1729 return i;
1730}
1731
1732bool XMLElement::BoolText(bool defaultValue) const
1733{
1734 bool b = defaultValue;
1735 QueryBoolText(&b);
1736 return b;
1737}
1738
1739double XMLElement::DoubleText(double defaultValue) const
1740{
1741 double d = defaultValue;
1742 QueryDoubleText(&d);
1743 return d;
1744}
1745
1746float XMLElement::FloatText(float defaultValue) const
1747{
1748 float f = defaultValue;
1749 QueryFloatText(&f);
1750 return f;
1751}
Matthew Xiec74b5462012-08-24 00:17:13 -07001752
1753
1754XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1755{
1756 XMLAttribute* last = 0;
1757 XMLAttribute* attrib = 0;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001758 for( attrib = _rootAttribute;
1759 attrib;
1760 last = attrib, attrib = attrib->_next ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001761 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1762 break;
1763 }
1764 }
1765 if ( !attrib ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001766 attrib = CreateAttribute();
1767 TIXMLASSERT( attrib );
Matthew Xiec74b5462012-08-24 00:17:13 -07001768 if ( last ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001769 TIXMLASSERT( last->_next == 0 );
1770 last->_next = attrib;
Matthew Xiec74b5462012-08-24 00:17:13 -07001771 }
1772 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001773 TIXMLASSERT( _rootAttribute == 0 );
1774 _rootAttribute = attrib;
Matthew Xiec74b5462012-08-24 00:17:13 -07001775 }
1776 attrib->SetName( name );
1777 }
1778 return attrib;
1779}
1780
1781
1782void XMLElement::DeleteAttribute( const char* name )
1783{
1784 XMLAttribute* prev = 0;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001785 for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001786 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1787 if ( prev ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001788 prev->_next = a->_next;
Matthew Xiec74b5462012-08-24 00:17:13 -07001789 }
1790 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001791 _rootAttribute = a->_next;
Matthew Xiec74b5462012-08-24 00:17:13 -07001792 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001793 DeleteAttribute( a );
Matthew Xiec74b5462012-08-24 00:17:13 -07001794 break;
1795 }
1796 prev = a;
1797 }
1798}
1799
1800
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001801char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -07001802{
Matthew Xiec74b5462012-08-24 00:17:13 -07001803 XMLAttribute* prevAttribute = 0;
1804
1805 // Read the attributes.
1806 while( p ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001807 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1808 if ( !(*p) ) {
1809 _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() );
Matthew Xiec74b5462012-08-24 00:17:13 -07001810 return 0;
1811 }
1812
1813 // attribute.
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001814 if (XMLUtil::IsNameStartChar( *p ) ) {
1815 XMLAttribute* attrib = CreateAttribute();
1816 TIXMLASSERT( attrib );
1817 attrib->_parseLineNum = _document->_parseCurLineNum;
Matthew Xiec74b5462012-08-24 00:17:13 -07001818
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001819 int attrLineNum = attrib->_parseLineNum;
1820
1821 p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001822 if ( !p || Attribute( attrib->Name() ) ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001823 DeleteAttribute( attrib );
1824 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() );
Matthew Xiec74b5462012-08-24 00:17:13 -07001825 return 0;
1826 }
1827 // There is a minor bug here: if the attribute in the source xml
1828 // document is duplicated, it will not be detected and the
1829 // attribute will be doubly added. However, tracking the 'prevAttribute'
1830 // avoids re-scanning the attribute list. Preferring performance for
1831 // now, may reconsider in the future.
1832 if ( prevAttribute ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001833 TIXMLASSERT( prevAttribute->_next == 0 );
1834 prevAttribute->_next = attrib;
Matthew Xiec74b5462012-08-24 00:17:13 -07001835 }
1836 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001837 TIXMLASSERT( _rootAttribute == 0 );
1838 _rootAttribute = attrib;
Matthew Xiec74b5462012-08-24 00:17:13 -07001839 }
1840 prevAttribute = attrib;
1841 }
1842 // end of the tag
Matthew Xiec74b5462012-08-24 00:17:13 -07001843 else if ( *p == '>' ) {
1844 ++p;
1845 break;
1846 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001847 // end of the tag
1848 else if ( *p == '/' && *(p+1) == '>' ) {
1849 _closingType = CLOSED;
1850 return p+2; // done; sealed element.
1851 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001852 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001853 _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 );
Matthew Xiec74b5462012-08-24 00:17:13 -07001854 return 0;
1855 }
1856 }
1857 return p;
1858}
1859
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001860void XMLElement::DeleteAttribute( XMLAttribute* attribute )
1861{
1862 if ( attribute == 0 ) {
1863 return;
1864 }
1865 MemPool* pool = attribute->_memPool;
1866 attribute->~XMLAttribute();
1867 pool->Free( attribute );
1868}
1869
1870XMLAttribute* XMLElement::CreateAttribute()
1871{
1872 TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
1873 XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1874 TIXMLASSERT( attrib );
1875 attrib->_memPool = &_document->_attributePool;
1876 attrib->_memPool->SetTracked();
1877 return attrib;
1878}
Matthew Xiec74b5462012-08-24 00:17:13 -07001879
1880//
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001881// <ele></ele>
1882// <ele>foo<b>bar</b></ele>
Matthew Xiec74b5462012-08-24 00:17:13 -07001883//
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001884char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -07001885{
1886 // Read the element name.
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001887 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001888
1889 // The closing element is the </element> form. It is
1890 // parsed just like a regular element then deleted from
1891 // the DOM.
1892 if ( *p == '/' ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001893 _closingType = CLOSING;
Matthew Xiec74b5462012-08-24 00:17:13 -07001894 ++p;
1895 }
1896
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001897 p = _value.ParseName( p );
1898 if ( _value.Empty() ) {
1899 return 0;
1900 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001901
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001902 p = ParseAttributes( p, curLineNumPtr );
1903 if ( !p || !*p || _closingType != OPEN ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001904 return p;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001905 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001906
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001907 p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001908 return p;
1909}
1910
1911
1912
1913XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1914{
1915 if ( !doc ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001916 doc = _document;
Matthew Xiec74b5462012-08-24 00:17:13 -07001917 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001918 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
Matthew Xiec74b5462012-08-24 00:17:13 -07001919 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001920 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
Matthew Xiec74b5462012-08-24 00:17:13 -07001921 }
1922 return element;
1923}
1924
1925
1926bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1927{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001928 TIXMLASSERT( compare );
Matthew Xiec74b5462012-08-24 00:17:13 -07001929 const XMLElement* other = compare->ToElement();
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001930 if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001931
1932 const XMLAttribute* a=FirstAttribute();
1933 const XMLAttribute* b=other->FirstAttribute();
1934
1935 while ( a && b ) {
1936 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1937 return false;
1938 }
1939 a = a->Next();
1940 b = b->Next();
1941 }
1942 if ( a || b ) {
1943 // different count
1944 return false;
1945 }
1946 return true;
1947 }
1948 return false;
1949}
1950
1951
1952bool XMLElement::Accept( XMLVisitor* visitor ) const
1953{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001954 TIXMLASSERT( visitor );
1955 if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
1956 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
1957 if ( !node->Accept( visitor ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001958 break;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001959 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001960 }
1961 }
1962 return visitor->VisitExit( *this );
1963}
1964
1965
1966// --------- XMLDocument ----------- //
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001967
1968// Warning: List must match 'enum XMLError'
1969const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
1970 "XML_SUCCESS",
1971 "XML_NO_ATTRIBUTE",
1972 "XML_WRONG_ATTRIBUTE_TYPE",
1973 "XML_ERROR_FILE_NOT_FOUND",
1974 "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
1975 "XML_ERROR_FILE_READ_ERROR",
1976 "UNUSED_XML_ERROR_ELEMENT_MISMATCH",
1977 "XML_ERROR_PARSING_ELEMENT",
1978 "XML_ERROR_PARSING_ATTRIBUTE",
1979 "UNUSED_XML_ERROR_IDENTIFYING_TAG",
1980 "XML_ERROR_PARSING_TEXT",
1981 "XML_ERROR_PARSING_CDATA",
1982 "XML_ERROR_PARSING_COMMENT",
1983 "XML_ERROR_PARSING_DECLARATION",
1984 "XML_ERROR_PARSING_UNKNOWN",
1985 "XML_ERROR_EMPTY_DOCUMENT",
1986 "XML_ERROR_MISMATCHED_ELEMENT",
1987 "XML_ERROR_PARSING",
1988 "XML_CAN_NOT_CONVERT_TEXT",
1989 "XML_NO_TEXT_NODE"
1990};
1991
1992
1993XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) :
Matthew Xiec74b5462012-08-24 00:17:13 -07001994 XMLNode( 0 ),
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001995 _writeBOM( false ),
1996 _processEntities( processEntities ),
1997 _errorID(XML_SUCCESS),
1998 _whitespaceMode( whitespaceMode ),
1999 _errorStr(),
2000 _errorLineNum( 0 ),
2001 _charBuffer( 0 ),
2002 _parseCurLineNum( 0 ),
2003 _unlinked(),
2004 _elementPool(),
2005 _attributePool(),
2006 _textPool(),
2007 _commentPool()
Matthew Xiec74b5462012-08-24 00:17:13 -07002008{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002009 // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
2010 _document = this;
Matthew Xiec74b5462012-08-24 00:17:13 -07002011}
2012
2013
2014XMLDocument::~XMLDocument()
2015{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002016 Clear();
2017}
2018
2019
2020void XMLDocument::MarkInUse(XMLNode* node)
2021{
2022 TIXMLASSERT(node);
2023 TIXMLASSERT(node->_parent == 0);
2024
2025 for (int i = 0; i < _unlinked.Size(); ++i) {
2026 if (node == _unlinked[i]) {
2027 _unlinked.SwapRemove(i);
2028 break;
2029 }
2030 }
2031}
2032
2033void XMLDocument::Clear()
2034{
Matthew Xiec74b5462012-08-24 00:17:13 -07002035 DeleteChildren();
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002036 while( _unlinked.Size()) {
2037 DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete.
2038 }
2039
2040#ifdef DEBUG
2041 const bool hadError = Error();
2042#endif
2043 ClearError();
2044
2045 delete [] _charBuffer;
2046 _charBuffer = 0;
Matthew Xiec74b5462012-08-24 00:17:13 -07002047
2048#if 0
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002049 _textPool.Trace( "text" );
2050 _elementPool.Trace( "element" );
2051 _commentPool.Trace( "comment" );
2052 _attributePool.Trace( "attribute" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002053#endif
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002054
2055#ifdef DEBUG
2056 if ( !hadError ) {
2057 TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() );
2058 TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
2059 TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() );
2060 TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() );
2061 }
2062#endif
Matthew Xiec74b5462012-08-24 00:17:13 -07002063}
2064
2065
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002066void XMLDocument::DeepCopy(XMLDocument* target) const
Matthew Xiec74b5462012-08-24 00:17:13 -07002067{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002068 TIXMLASSERT(target);
2069 if (target == this) {
2070 return; // technically success - a no-op.
2071 }
Matthew Xiec74b5462012-08-24 00:17:13 -07002072
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002073 target->Clear();
2074 for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) {
2075 target->InsertEndChild(node->DeepClone(target));
2076 }
Matthew Xiec74b5462012-08-24 00:17:13 -07002077}
2078
Matthew Xiec74b5462012-08-24 00:17:13 -07002079XMLElement* XMLDocument::NewElement( const char* name )
2080{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002081 XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool );
Matthew Xiec74b5462012-08-24 00:17:13 -07002082 ele->SetName( name );
2083 return ele;
2084}
2085
2086
2087XMLComment* XMLDocument::NewComment( const char* str )
2088{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002089 XMLComment* comment = CreateUnlinkedNode<XMLComment>( _commentPool );
Matthew Xiec74b5462012-08-24 00:17:13 -07002090 comment->SetValue( str );
2091 return comment;
2092}
2093
2094
2095XMLText* XMLDocument::NewText( const char* str )
2096{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002097 XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
Matthew Xiec74b5462012-08-24 00:17:13 -07002098 text->SetValue( str );
2099 return text;
2100}
2101
2102
2103XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
2104{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002105 XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
Matthew Xiec74b5462012-08-24 00:17:13 -07002106 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
2107 return dec;
2108}
2109
2110
2111XMLUnknown* XMLDocument::NewUnknown( const char* str )
2112{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002113 XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>( _commentPool );
Matthew Xiec74b5462012-08-24 00:17:13 -07002114 unk->SetValue( str );
2115 return unk;
2116}
2117
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002118static FILE* callfopen( const char* filepath, const char* mode )
Matthew Xiec74b5462012-08-24 00:17:13 -07002119{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002120 TIXMLASSERT( filepath );
2121 TIXMLASSERT( mode );
2122#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
2123 FILE* fp = 0;
2124 errno_t err = fopen_s( &fp, filepath, mode );
2125 if ( err ) {
2126 return 0;
2127 }
2128#else
2129 FILE* fp = fopen( filepath, mode );
2130#endif
2131 return fp;
2132}
2133
2134void XMLDocument::DeleteNode( XMLNode* node ) {
2135 TIXMLASSERT( node );
2136 TIXMLASSERT(node->_document == this );
2137 if (node->_parent) {
2138 node->_parent->DeleteChild( node );
2139 }
2140 else {
2141 // Isn't in the tree.
2142 // Use the parent delete.
2143 // Also, we need to mark it tracked: we 'know'
2144 // it was never used.
2145 node->_memPool->SetTracked();
2146 // Call the static XMLNode version:
2147 XMLNode::DeleteNode(node);
2148 }
2149}
Matthew Xiec74b5462012-08-24 00:17:13 -07002150
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002151
2152XMLError XMLDocument::LoadFile( const char* filename )
2153{
2154 Clear();
2155 FILE* fp = callfopen( filename, "rb" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002156 if ( !fp ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002157 SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename ? filename : "<null>");
2158 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002159 }
2160 LoadFile( fp );
2161 fclose( fp );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002162 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002163}
2164
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002165// This is likely overengineered template art to have a check that unsigned long value incremented
2166// by one still fits into size_t. If size_t type is larger than unsigned long type
2167// (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
2168// -Wtype-limits warning. This piece makes the compiler select code with a check when a check
2169// is useful and code with no check when a check is redundant depending on how size_t and unsigned long
2170// types sizes relate to each other.
2171template
2172<bool = (sizeof(unsigned long) >= sizeof(size_t))>
2173struct LongFitsIntoSizeTMinusOne {
2174 static bool Fits( unsigned long value )
2175 {
2176 return value < (size_t)-1;
2177 }
2178};
Matthew Xiec74b5462012-08-24 00:17:13 -07002179
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002180template <>
2181struct LongFitsIntoSizeTMinusOne<false> {
2182 static bool Fits( unsigned long )
2183 {
2184 return true;
2185 }
2186};
2187
2188XMLError XMLDocument::LoadFile( FILE* fp )
Matthew Xiec74b5462012-08-24 00:17:13 -07002189{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002190 Clear();
2191
2192 fseek( fp, 0, SEEK_SET );
2193 if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
2194 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2195 return _errorID;
2196 }
Matthew Xiec74b5462012-08-24 00:17:13 -07002197
2198 fseek( fp, 0, SEEK_END );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002199 const long filelength = ftell( fp );
Matthew Xiec74b5462012-08-24 00:17:13 -07002200 fseek( fp, 0, SEEK_SET );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002201 if ( filelength == -1L ) {
2202 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2203 return _errorID;
2204 }
2205 TIXMLASSERT( filelength >= 0 );
Matthew Xiec74b5462012-08-24 00:17:13 -07002206
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002207 if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
2208 // Cannot handle files which won't fit in buffer together with null terminator
2209 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2210 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002211 }
2212
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002213 if ( filelength == 0 ) {
2214 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2215 return _errorID;
2216 }
2217
2218 const size_t size = filelength;
2219 TIXMLASSERT( _charBuffer == 0 );
2220 _charBuffer = new char[size+1];
2221 size_t read = fread( _charBuffer, 1, size, fp );
Matthew Xiec74b5462012-08-24 00:17:13 -07002222 if ( read != size ) {
2223 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002224 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002225 }
2226
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002227 _charBuffer[size] = 0;
Matthew Xiec74b5462012-08-24 00:17:13 -07002228
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002229 Parse();
2230 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002231}
2232
2233
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002234XMLError XMLDocument::SaveFile( const char* filename, bool compact )
Matthew Xiec74b5462012-08-24 00:17:13 -07002235{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002236 FILE* fp = callfopen( filename, "w" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002237 if ( !fp ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002238 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename ? filename : "<null>");
2239 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002240 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002241 SaveFile(fp, compact);
Matthew Xiec74b5462012-08-24 00:17:13 -07002242 fclose( fp );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002243 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002244}
2245
2246
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002247XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
Matthew Xiec74b5462012-08-24 00:17:13 -07002248{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002249 // Clear any error from the last save, otherwise it will get reported
2250 // for *this* call.
2251 ClearError();
2252 XMLPrinter stream( fp, compact );
Matthew Xiec74b5462012-08-24 00:17:13 -07002253 Print( &stream );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002254 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002255}
2256
2257
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002258XMLError XMLDocument::Parse( const char* p, size_t len )
Matthew Xiec74b5462012-08-24 00:17:13 -07002259{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002260 Clear();
Matthew Xiec74b5462012-08-24 00:17:13 -07002261
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002262 if ( len == 0 || !p || !*p ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07002263 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002264 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002265 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002266 if ( len == (size_t)(-1) ) {
2267 len = strlen( p );
Matthew Xiec74b5462012-08-24 00:17:13 -07002268 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002269 TIXMLASSERT( _charBuffer == 0 );
2270 _charBuffer = new char[ len+1 ];
2271 memcpy( _charBuffer, p, len );
2272 _charBuffer[len] = 0;
Matthew Xiec74b5462012-08-24 00:17:13 -07002273
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002274 Parse();
2275 if ( Error() ) {
2276 // clean up now essentially dangling memory.
2277 // and the parse fail can put objects in the
2278 // pools that are dead and inaccessible.
2279 DeleteChildren();
2280 _elementPool.Clear();
2281 _attributePool.Clear();
2282 _textPool.Clear();
2283 _commentPool.Clear();
2284 }
2285 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002286}
2287
2288
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002289void XMLDocument::Print( XMLPrinter* streamer ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07002290{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002291 if ( streamer ) {
2292 Accept( streamer );
2293 }
2294 else {
2295 XMLPrinter stdoutStreamer( stdout );
2296 Accept( &stdoutStreamer );
2297 }
Matthew Xiec74b5462012-08-24 00:17:13 -07002298}
2299
2300
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002301void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... )
Matthew Xiec74b5462012-08-24 00:17:13 -07002302{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002303 TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
2304 _errorID = error;
2305 _errorLineNum = lineNum;
2306 _errorStr.Reset();
2307
2308 if (format) {
2309 size_t BUFFER_SIZE = 1000;
2310 char* buffer = new char[BUFFER_SIZE];
2311 TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d: ", ErrorIDToName(error), int(error), int(error), lineNum);
2312 size_t len = strlen(buffer);
2313
2314 va_list va;
2315 va_start( va, format );
2316 TIXML_VSNPRINTF( buffer + len, BUFFER_SIZE - len, format, va );
2317 va_end( va );
2318
2319 _errorStr.SetStr(buffer);
2320 delete [] buffer;
2321 }
2322}
2323
2324
2325/*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID)
2326{
2327 TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT );
2328 const char* errorName = _errorNames[errorID];
2329 TIXMLASSERT( errorName && errorName[0] );
2330 return errorName;
2331}
2332
2333const char* XMLDocument::ErrorStr() const
2334{
2335 return _errorStr.Empty() ? "" : _errorStr.GetStr();
Matthew Xiec74b5462012-08-24 00:17:13 -07002336}
2337
2338
2339void XMLDocument::PrintError() const
2340{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002341 printf("%s\n", ErrorStr());
Matthew Xiec74b5462012-08-24 00:17:13 -07002342}
2343
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002344const char* XMLDocument::ErrorName() const
2345{
2346 return ErrorIDToName(_errorID);
2347}
Matthew Xiec74b5462012-08-24 00:17:13 -07002348
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002349void XMLDocument::Parse()
2350{
2351 TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
2352 TIXMLASSERT( _charBuffer );
2353 _parseCurLineNum = 1;
2354 _parseLineNum = 1;
2355 char* p = _charBuffer;
2356 p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
2357 p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
2358 if ( !*p ) {
2359 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2360 return;
2361 }
2362 ParseDeep(p, 0, &_parseCurLineNum );
2363}
2364
2365XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
2366 _elementJustOpened( false ),
2367 _stack(),
2368 _firstElement( true ),
2369 _fp( file ),
2370 _depth( depth ),
2371 _textDepth( -1 ),
2372 _processEntities( true ),
2373 _compactMode( compact ),
2374 _buffer()
Matthew Xiec74b5462012-08-24 00:17:13 -07002375{
2376 for( int i=0; i<ENTITY_RANGE; ++i ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002377 _entityFlag[i] = false;
2378 _restrictedEntityFlag[i] = false;
Matthew Xiec74b5462012-08-24 00:17:13 -07002379 }
2380 for( int i=0; i<NUM_ENTITIES; ++i ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002381 const char entityValue = entities[i].value;
2382 const unsigned char flagIndex = (unsigned char)entityValue;
2383 TIXMLASSERT( flagIndex < ENTITY_RANGE );
2384 _entityFlag[flagIndex] = true;
Matthew Xiec74b5462012-08-24 00:17:13 -07002385 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002386 _restrictedEntityFlag[(unsigned char)'&'] = true;
2387 _restrictedEntityFlag[(unsigned char)'<'] = true;
2388 _restrictedEntityFlag[(unsigned char)'>'] = true; // not required, but consistency is nice
2389 _buffer.Push( 0 );
Matthew Xiec74b5462012-08-24 00:17:13 -07002390}
2391
2392
2393void XMLPrinter::Print( const char* format, ... )
2394{
2395 va_list va;
2396 va_start( va, format );
2397
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002398 if ( _fp ) {
2399 vfprintf( _fp, format, va );
Matthew Xiec74b5462012-08-24 00:17:13 -07002400 }
2401 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002402 const int len = TIXML_VSCPRINTF( format, va );
2403 // Close out and re-start the va-args
2404 va_end( va );
2405 TIXMLASSERT( len >= 0 );
2406 va_start( va, format );
2407 TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
2408 char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator.
2409 TIXML_VSNPRINTF( p, len+1, format, va );
Matthew Xiec74b5462012-08-24 00:17:13 -07002410 }
2411 va_end( va );
2412}
2413
2414
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002415void XMLPrinter::Write( const char* data, size_t size )
2416{
2417 if ( _fp ) {
2418 fwrite ( data , sizeof(char), size, _fp);
2419 }
2420 else {
2421 char* p = _buffer.PushArr( static_cast<int>(size) ) - 1; // back up over the null terminator.
2422 memcpy( p, data, size );
2423 p[size] = 0;
2424 }
2425}
2426
2427
2428void XMLPrinter::Putc( char ch )
2429{
2430 if ( _fp ) {
2431 fputc ( ch, _fp);
2432 }
2433 else {
2434 char* p = _buffer.PushArr( sizeof(char) ) - 1; // back up over the null terminator.
2435 p[0] = ch;
2436 p[1] = 0;
2437 }
2438}
2439
2440
Matthew Xiec74b5462012-08-24 00:17:13 -07002441void XMLPrinter::PrintSpace( int depth )
2442{
2443 for( int i=0; i<depth; ++i ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002444 Write( " " );
Matthew Xiec74b5462012-08-24 00:17:13 -07002445 }
2446}
2447
2448
2449void XMLPrinter::PrintString( const char* p, bool restricted )
2450{
2451 // Look for runs of bytes between entities to print.
2452 const char* q = p;
Matthew Xiec74b5462012-08-24 00:17:13 -07002453
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002454 if ( _processEntities ) {
2455 const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
Matthew Xiec74b5462012-08-24 00:17:13 -07002456 while ( *q ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002457 TIXMLASSERT( p <= q );
Matthew Xiec74b5462012-08-24 00:17:13 -07002458 // Remember, char is sometimes signed. (How many times has that bitten me?)
2459 if ( *q > 0 && *q < ENTITY_RANGE ) {
2460 // Check for entities. If one is found, flush
2461 // the stream up until the entity, write the
2462 // entity, and keep looking.
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002463 if ( flag[(unsigned char)(*q)] ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07002464 while ( p < q ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002465 const size_t delta = q - p;
2466 const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
2467 Write( p, toPrint );
2468 p += toPrint;
Matthew Xiec74b5462012-08-24 00:17:13 -07002469 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002470 bool entityPatternPrinted = false;
Matthew Xiec74b5462012-08-24 00:17:13 -07002471 for( int i=0; i<NUM_ENTITIES; ++i ) {
2472 if ( entities[i].value == *q ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002473 Putc( '&' );
2474 Write( entities[i].pattern, entities[i].length );
2475 Putc( ';' );
2476 entityPatternPrinted = true;
Matthew Xiec74b5462012-08-24 00:17:13 -07002477 break;
2478 }
2479 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002480 if ( !entityPatternPrinted ) {
2481 // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
2482 TIXMLASSERT( false );
2483 }
Matthew Xiec74b5462012-08-24 00:17:13 -07002484 ++p;
2485 }
2486 }
2487 ++q;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002488 TIXMLASSERT( p <= q );
Matthew Xiec74b5462012-08-24 00:17:13 -07002489 }
2490 }
2491 // Flush the remaining string. This will be the entire
2492 // string if an entity wasn't found.
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002493 TIXMLASSERT( p <= q );
2494 if ( !_processEntities || ( p < q ) ) {
2495 const size_t delta = q - p;
2496 const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
2497 Write( p, toPrint );
Matthew Xiec74b5462012-08-24 00:17:13 -07002498 }
2499}
2500
2501
2502void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
2503{
Matthew Xiec74b5462012-08-24 00:17:13 -07002504 if ( writeBOM ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002505 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
2506 Write( reinterpret_cast< const char* >( bom ) );
Matthew Xiec74b5462012-08-24 00:17:13 -07002507 }
2508 if ( writeDec ) {
2509 PushDeclaration( "xml version=\"1.0\"" );
2510 }
2511}
2512
2513
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002514void XMLPrinter::OpenElement( const char* name, bool compactMode )
Matthew Xiec74b5462012-08-24 00:17:13 -07002515{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002516 SealElementIfJustOpened();
2517 _stack.Push( name );
Matthew Xiec74b5462012-08-24 00:17:13 -07002518
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002519 if ( _textDepth < 0 && !_firstElement && !compactMode ) {
2520 Putc( '\n' );
2521 }
2522 if ( !compactMode ) {
2523 PrintSpace( _depth );
Matthew Xiec74b5462012-08-24 00:17:13 -07002524 }
2525
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002526 Write ( "<" );
2527 Write ( name );
2528
2529 _elementJustOpened = true;
2530 _firstElement = false;
2531 ++_depth;
Matthew Xiec74b5462012-08-24 00:17:13 -07002532}
2533
2534
2535void XMLPrinter::PushAttribute( const char* name, const char* value )
2536{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002537 TIXMLASSERT( _elementJustOpened );
2538 Putc ( ' ' );
2539 Write( name );
2540 Write( "=\"" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002541 PrintString( value, false );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002542 Putc ( '\"' );
Matthew Xiec74b5462012-08-24 00:17:13 -07002543}
2544
2545
2546void XMLPrinter::PushAttribute( const char* name, int v )
2547{
2548 char buf[BUF_SIZE];
2549 XMLUtil::ToStr( v, buf, BUF_SIZE );
2550 PushAttribute( name, buf );
2551}
2552
2553
2554void XMLPrinter::PushAttribute( const char* name, unsigned v )
2555{
2556 char buf[BUF_SIZE];
2557 XMLUtil::ToStr( v, buf, BUF_SIZE );
2558 PushAttribute( name, buf );
2559}
2560
2561
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002562void XMLPrinter::PushAttribute(const char* name, int64_t v)
2563{
2564 char buf[BUF_SIZE];
2565 XMLUtil::ToStr(v, buf, BUF_SIZE);
2566 PushAttribute(name, buf);
2567}
2568
2569
Matthew Xiec74b5462012-08-24 00:17:13 -07002570void XMLPrinter::PushAttribute( const char* name, bool v )
2571{
2572 char buf[BUF_SIZE];
2573 XMLUtil::ToStr( v, buf, BUF_SIZE );
2574 PushAttribute( name, buf );
2575}
2576
2577
2578void XMLPrinter::PushAttribute( const char* name, double v )
2579{
2580 char buf[BUF_SIZE];
2581 XMLUtil::ToStr( v, buf, BUF_SIZE );
2582 PushAttribute( name, buf );
2583}
2584
2585
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002586void XMLPrinter::CloseElement( bool compactMode )
Matthew Xiec74b5462012-08-24 00:17:13 -07002587{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002588 --_depth;
2589 const char* name = _stack.Pop();
Matthew Xiec74b5462012-08-24 00:17:13 -07002590
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002591 if ( _elementJustOpened ) {
2592 Write( "/>" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002593 }
2594 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002595 if ( _textDepth < 0 && !compactMode) {
2596 Putc( '\n' );
2597 PrintSpace( _depth );
Matthew Xiec74b5462012-08-24 00:17:13 -07002598 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002599 Write ( "</" );
2600 Write ( name );
2601 Write ( ">" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002602 }
2603
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002604 if ( _textDepth == _depth ) {
2605 _textDepth = -1;
2606 }
2607 if ( _depth == 0 && !compactMode) {
2608 Putc( '\n' );
2609 }
2610 _elementJustOpened = false;
Matthew Xiec74b5462012-08-24 00:17:13 -07002611}
2612
2613
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002614void XMLPrinter::SealElementIfJustOpened()
Matthew Xiec74b5462012-08-24 00:17:13 -07002615{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002616 if ( !_elementJustOpened ) {
2617 return;
2618 }
2619 _elementJustOpened = false;
2620 Putc( '>' );
Matthew Xiec74b5462012-08-24 00:17:13 -07002621}
2622
2623
2624void XMLPrinter::PushText( const char* text, bool cdata )
2625{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002626 _textDepth = _depth-1;
Matthew Xiec74b5462012-08-24 00:17:13 -07002627
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002628 SealElementIfJustOpened();
Matthew Xiec74b5462012-08-24 00:17:13 -07002629 if ( cdata ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002630 Write( "<![CDATA[" );
2631 Write( text );
2632 Write( "]]>" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002633 }
2634 else {
2635 PrintString( text, true );
2636 }
2637}
2638
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002639void XMLPrinter::PushText( int64_t value )
2640{
2641 char buf[BUF_SIZE];
2642 XMLUtil::ToStr( value, buf, BUF_SIZE );
2643 PushText( buf, false );
2644}
2645
Matthew Xiec74b5462012-08-24 00:17:13 -07002646void XMLPrinter::PushText( int value )
2647{
2648 char buf[BUF_SIZE];
2649 XMLUtil::ToStr( value, buf, BUF_SIZE );
2650 PushText( buf, false );
2651}
2652
2653
2654void XMLPrinter::PushText( unsigned value )
2655{
2656 char buf[BUF_SIZE];
2657 XMLUtil::ToStr( value, buf, BUF_SIZE );
2658 PushText( buf, false );
2659}
2660
2661
2662void XMLPrinter::PushText( bool value )
2663{
2664 char buf[BUF_SIZE];
2665 XMLUtil::ToStr( value, buf, BUF_SIZE );
2666 PushText( buf, false );
2667}
2668
2669
2670void XMLPrinter::PushText( float value )
2671{
2672 char buf[BUF_SIZE];
2673 XMLUtil::ToStr( value, buf, BUF_SIZE );
2674 PushText( buf, false );
2675}
2676
2677
2678void XMLPrinter::PushText( double value )
2679{
2680 char buf[BUF_SIZE];
2681 XMLUtil::ToStr( value, buf, BUF_SIZE );
2682 PushText( buf, false );
2683}
2684
2685
2686void XMLPrinter::PushComment( const char* comment )
2687{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002688 SealElementIfJustOpened();
2689 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2690 Putc( '\n' );
2691 PrintSpace( _depth );
Matthew Xiec74b5462012-08-24 00:17:13 -07002692 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002693 _firstElement = false;
2694
2695 Write( "<!--" );
2696 Write( comment );
2697 Write( "-->" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002698}
2699
2700
2701void XMLPrinter::PushDeclaration( const char* value )
2702{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002703 SealElementIfJustOpened();
2704 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2705 Putc( '\n' );
2706 PrintSpace( _depth );
Matthew Xiec74b5462012-08-24 00:17:13 -07002707 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002708 _firstElement = false;
2709
2710 Write( "<?" );
2711 Write( value );
2712 Write( "?>" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002713}
2714
2715
2716void XMLPrinter::PushUnknown( const char* value )
2717{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002718 SealElementIfJustOpened();
2719 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2720 Putc( '\n' );
2721 PrintSpace( _depth );
Matthew Xiec74b5462012-08-24 00:17:13 -07002722 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002723 _firstElement = false;
2724
2725 Write( "<!" );
2726 Write( value );
2727 Putc( '>' );
Matthew Xiec74b5462012-08-24 00:17:13 -07002728}
2729
2730
2731bool XMLPrinter::VisitEnter( const XMLDocument& doc )
2732{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002733 _processEntities = doc.ProcessEntities();
Matthew Xiec74b5462012-08-24 00:17:13 -07002734 if ( doc.HasBOM() ) {
2735 PushHeader( true, false );
2736 }
2737 return true;
2738}
2739
2740
2741bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
2742{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002743 const XMLElement* parentElem = 0;
2744 if ( element.Parent() ) {
2745 parentElem = element.Parent()->ToElement();
2746 }
2747 const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
2748 OpenElement( element.Name(), compactMode );
Matthew Xiec74b5462012-08-24 00:17:13 -07002749 while ( attribute ) {
2750 PushAttribute( attribute->Name(), attribute->Value() );
2751 attribute = attribute->Next();
2752 }
2753 return true;
2754}
2755
2756
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002757bool XMLPrinter::VisitExit( const XMLElement& element )
Matthew Xiec74b5462012-08-24 00:17:13 -07002758{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002759 CloseElement( CompactMode(element) );
Matthew Xiec74b5462012-08-24 00:17:13 -07002760 return true;
2761}
2762
2763
2764bool XMLPrinter::Visit( const XMLText& text )
2765{
2766 PushText( text.Value(), text.CData() );
2767 return true;
2768}
2769
2770
2771bool XMLPrinter::Visit( const XMLComment& comment )
2772{
2773 PushComment( comment.Value() );
2774 return true;
2775}
2776
2777bool XMLPrinter::Visit( const XMLDeclaration& declaration )
2778{
2779 PushDeclaration( declaration.Value() );
2780 return true;
2781}
2782
2783
2784bool XMLPrinter::Visit( const XMLUnknown& unknown )
2785{
2786 PushUnknown( unknown.Value() );
2787 return true;
2788}
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002789
2790} // namespace tinyxml2
2791