blob: fd27f7888d279e8f66d4fda506945f38a909bad8 [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
Elliott Hughesd173c992019-01-10 14:51:35 -0800763const char* XMLNode::Value() const
Narayan Kamath74c03bb2017-12-22 10:59:43 +0000764{
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
Elliott Hughesd173c992019-01-10 14:51:35 -08001007 XMLDocument::DepthTracker tracker(_document);
1008 if (_document->Error())
1009 return 0;
1010
1011 while( p && *p ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001012 XMLNode* node = 0;
1013
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001014 p = _document->Identify( p, &node );
1015 TIXMLASSERT( p );
1016 if ( node == 0 ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001017 break;
1018 }
1019
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001020 int initialLineNum = node->_parseLineNum;
1021
Matthew Xiec74b5462012-08-24 00:17:13 -07001022 StrPair endTag;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001023 p = node->ParseDeep( p, &endTag, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001024 if ( !p ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001025 DeleteNode( node );
1026 if ( !_document->Error() ) {
1027 _document->SetError( XML_ERROR_PARSING, initialLineNum, 0);
Matthew Xiec74b5462012-08-24 00:17:13 -07001028 }
1029 break;
1030 }
1031
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001032 XMLDeclaration* decl = node->ToDeclaration();
1033 if ( decl ) {
1034 // Declarations are only allowed at document level
Elliott Hughesd173c992019-01-10 14:51:35 -08001035 //
1036 // Multiple declarations are allowed but all declarations
1037 // must occur before anything else.
1038 //
1039 // Optimized due to a security test case. If the first node is
1040 // a declaration, and the last node is a declaration, then only
1041 // declarations have so far been addded.
1042 bool wellLocated = false;
1043
1044 if (ToDocument()) {
1045 if (FirstChild()) {
1046 wellLocated =
1047 FirstChild() &&
1048 FirstChild()->ToDeclaration() &&
1049 LastChild() &&
1050 LastChild()->ToDeclaration();
1051 }
1052 else {
1053 wellLocated = true;
Matthew Xiec74b5462012-08-24 00:17:13 -07001054 }
1055 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001056 if ( !wellLocated ) {
1057 _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value());
1058 DeleteNode( node );
1059 break;
1060 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001061 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001062
1063 XMLElement* ele = node->ToElement();
1064 if ( ele ) {
1065 // We read the end tag. Return it to the parent.
1066 if ( ele->ClosingType() == XMLElement::CLOSING ) {
1067 if ( parentEndTag ) {
1068 ele->_value.TransferTo( parentEndTag );
1069 }
1070 node->_memPool->SetTracked(); // created and then immediately deleted.
1071 DeleteNode( node );
1072 return p;
1073 }
1074
1075 // Handle an end tag returned to this level.
1076 // And handle a bunch of annoying errors.
1077 bool mismatch = false;
1078 if ( endTag.Empty() ) {
1079 if ( ele->ClosingType() == XMLElement::OPEN ) {
1080 mismatch = true;
1081 }
1082 }
1083 else {
1084 if ( ele->ClosingType() != XMLElement::OPEN ) {
1085 mismatch = true;
1086 }
1087 else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
1088 mismatch = true;
1089 }
1090 }
1091 if ( mismatch ) {
1092 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name());
1093 DeleteNode( node );
1094 break;
1095 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001096 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001097 InsertEndChild( node );
1098 }
1099 return 0;
1100}
1101
1102/*static*/ void XMLNode::DeleteNode( XMLNode* node )
1103{
1104 if ( node == 0 ) {
1105 return;
1106 }
1107 TIXMLASSERT(node->_document);
1108 if (!node->ToDocument()) {
1109 node->_document->MarkInUse(node);
1110 }
1111
1112 MemPool* pool = node->_memPool;
1113 node->~XMLNode();
1114 pool->Free( node );
1115}
1116
1117void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
1118{
1119 TIXMLASSERT( insertThis );
1120 TIXMLASSERT( insertThis->_document == _document );
1121
1122 if (insertThis->_parent) {
1123 insertThis->_parent->Unlink( insertThis );
1124 }
1125 else {
1126 insertThis->_document->MarkInUse(insertThis);
1127 insertThis->_memPool->SetTracked();
1128 }
1129}
1130
1131const XMLElement* XMLNode::ToElementWithName( const char* name ) const
1132{
1133 const XMLElement* element = this->ToElement();
1134 if ( element == 0 ) {
1135 return 0;
1136 }
1137 if ( name == 0 ) {
1138 return element;
1139 }
1140 if ( XMLUtil::StringEqual( element->Name(), name ) ) {
1141 return element;
Matthew Xiec74b5462012-08-24 00:17:13 -07001142 }
1143 return 0;
1144}
1145
1146// --------- XMLText ---------- //
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001147char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -07001148{
Matthew Xiec74b5462012-08-24 00:17:13 -07001149 if ( this->CData() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001150 p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001151 if ( !p ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001152 _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 );
Matthew Xiec74b5462012-08-24 00:17:13 -07001153 }
1154 return p;
1155 }
1156 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001157 int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
1158 if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
1159 flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
Matthew Xiec74b5462012-08-24 00:17:13 -07001160 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001161
1162 p = _value.ParseText( p, "<", flags, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001163 if ( p && *p ) {
1164 return p-1;
1165 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001166 if ( !p ) {
1167 _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 );
1168 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001169 }
1170 return 0;
1171}
1172
1173
1174XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
1175{
1176 if ( !doc ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001177 doc = _document;
Matthew Xiec74b5462012-08-24 00:17:13 -07001178 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001179 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
Matthew Xiec74b5462012-08-24 00:17:13 -07001180 text->SetCData( this->CData() );
1181 return text;
1182}
1183
1184
1185bool XMLText::ShallowEqual( const XMLNode* compare ) const
1186{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001187 TIXMLASSERT( compare );
1188 const XMLText* text = compare->ToText();
1189 return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
Matthew Xiec74b5462012-08-24 00:17:13 -07001190}
1191
1192
1193bool XMLText::Accept( XMLVisitor* visitor ) const
1194{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001195 TIXMLASSERT( visitor );
Matthew Xiec74b5462012-08-24 00:17:13 -07001196 return visitor->Visit( *this );
1197}
1198
1199
1200// --------- XMLComment ---------- //
1201
1202XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
1203{
1204}
1205
1206
1207XMLComment::~XMLComment()
1208{
Matthew Xiec74b5462012-08-24 00:17:13 -07001209}
1210
1211
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001212char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -07001213{
1214 // Comment parses as text.
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001215 p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001216 if ( p == 0 ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001217 _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 );
Matthew Xiec74b5462012-08-24 00:17:13 -07001218 }
1219 return p;
1220}
1221
1222
1223XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
1224{
1225 if ( !doc ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001226 doc = _document;
Matthew Xiec74b5462012-08-24 00:17:13 -07001227 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001228 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
Matthew Xiec74b5462012-08-24 00:17:13 -07001229 return comment;
1230}
1231
1232
1233bool XMLComment::ShallowEqual( const XMLNode* compare ) const
1234{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001235 TIXMLASSERT( compare );
1236 const XMLComment* comment = compare->ToComment();
1237 return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
Matthew Xiec74b5462012-08-24 00:17:13 -07001238}
1239
1240
1241bool XMLComment::Accept( XMLVisitor* visitor ) const
1242{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001243 TIXMLASSERT( visitor );
Matthew Xiec74b5462012-08-24 00:17:13 -07001244 return visitor->Visit( *this );
1245}
1246
1247
1248// --------- XMLDeclaration ---------- //
1249
1250XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
1251{
1252}
1253
1254
1255XMLDeclaration::~XMLDeclaration()
1256{
1257 //printf( "~XMLDeclaration\n" );
1258}
1259
1260
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001261char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -07001262{
1263 // Declaration parses as text.
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001264 p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001265 if ( p == 0 ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001266 _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 );
Matthew Xiec74b5462012-08-24 00:17:13 -07001267 }
1268 return p;
1269}
1270
1271
1272XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
1273{
1274 if ( !doc ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001275 doc = _document;
Matthew Xiec74b5462012-08-24 00:17:13 -07001276 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001277 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
Matthew Xiec74b5462012-08-24 00:17:13 -07001278 return dec;
1279}
1280
1281
1282bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
1283{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001284 TIXMLASSERT( compare );
1285 const XMLDeclaration* declaration = compare->ToDeclaration();
1286 return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
Matthew Xiec74b5462012-08-24 00:17:13 -07001287}
1288
1289
1290
1291bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
1292{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001293 TIXMLASSERT( visitor );
Matthew Xiec74b5462012-08-24 00:17:13 -07001294 return visitor->Visit( *this );
1295}
1296
1297// --------- XMLUnknown ---------- //
1298
1299XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
1300{
1301}
1302
1303
1304XMLUnknown::~XMLUnknown()
1305{
1306}
1307
1308
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001309char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -07001310{
1311 // Unknown parses as text.
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001312 p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001313 if ( !p ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001314 _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 );
Matthew Xiec74b5462012-08-24 00:17:13 -07001315 }
1316 return p;
1317}
1318
1319
1320XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
1321{
1322 if ( !doc ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001323 doc = _document;
Matthew Xiec74b5462012-08-24 00:17:13 -07001324 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001325 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
Matthew Xiec74b5462012-08-24 00:17:13 -07001326 return text;
1327}
1328
1329
1330bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
1331{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001332 TIXMLASSERT( compare );
1333 const XMLUnknown* unknown = compare->ToUnknown();
1334 return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
Matthew Xiec74b5462012-08-24 00:17:13 -07001335}
1336
1337
1338bool XMLUnknown::Accept( XMLVisitor* visitor ) const
1339{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001340 TIXMLASSERT( visitor );
Matthew Xiec74b5462012-08-24 00:17:13 -07001341 return visitor->Visit( *this );
1342}
1343
1344// --------- XMLAttribute ---------- //
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001345
Elliott Hughesd173c992019-01-10 14:51:35 -08001346const char* XMLAttribute::Name() const
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001347{
1348 return _name.GetStr();
1349}
1350
Elliott Hughesd173c992019-01-10 14:51:35 -08001351const char* XMLAttribute::Value() const
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001352{
1353 return _value.GetStr();
1354}
1355
1356char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -07001357{
1358 // Parse using the name rules: bug fix, was using ParseText before
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001359 p = _name.ParseName( p );
1360 if ( !p || !*p ) {
1361 return 0;
1362 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001363
1364 // Skip white space before =
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001365 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1366 if ( *p != '=' ) {
1367 return 0;
1368 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001369
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001370 ++p; // move up to opening quote
1371 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1372 if ( *p != '\"' && *p != '\'' ) {
1373 return 0;
1374 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001375
1376 char endTag[2] = { *p, 0 };
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001377 ++p; // move past opening quote
Matthew Xiec74b5462012-08-24 00:17:13 -07001378
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001379 p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001380 return p;
1381}
1382
1383
1384void XMLAttribute::SetName( const char* n )
1385{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001386 _name.SetStr( n );
Matthew Xiec74b5462012-08-24 00:17:13 -07001387}
1388
1389
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001390XMLError XMLAttribute::QueryIntValue( int* value ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001391{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001392 if ( XMLUtil::ToInt( Value(), value )) {
1393 return XML_SUCCESS;
Matthew Xiec74b5462012-08-24 00:17:13 -07001394 }
1395 return XML_WRONG_ATTRIBUTE_TYPE;
1396}
1397
1398
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001399XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001400{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001401 if ( XMLUtil::ToUnsigned( Value(), value )) {
1402 return XML_SUCCESS;
1403 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001404 return XML_WRONG_ATTRIBUTE_TYPE;
1405}
1406
1407
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001408XMLError XMLAttribute::QueryInt64Value(int64_t* value) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001409{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001410 if (XMLUtil::ToInt64(Value(), value)) {
1411 return XML_SUCCESS;
1412 }
1413 return XML_WRONG_ATTRIBUTE_TYPE;
1414}
1415
1416
1417XMLError XMLAttribute::QueryBoolValue( bool* value ) const
1418{
1419 if ( XMLUtil::ToBool( Value(), value )) {
1420 return XML_SUCCESS;
1421 }
1422 return XML_WRONG_ATTRIBUTE_TYPE;
1423}
1424
1425
1426XMLError XMLAttribute::QueryFloatValue( float* value ) const
1427{
1428 if ( XMLUtil::ToFloat( Value(), value )) {
1429 return XML_SUCCESS;
1430 }
1431 return XML_WRONG_ATTRIBUTE_TYPE;
1432}
1433
1434
1435XMLError XMLAttribute::QueryDoubleValue( double* value ) const
1436{
1437 if ( XMLUtil::ToDouble( Value(), value )) {
1438 return XML_SUCCESS;
1439 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001440 return XML_WRONG_ATTRIBUTE_TYPE;
1441}
1442
1443
1444void XMLAttribute::SetAttribute( const char* v )
1445{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001446 _value.SetStr( v );
Matthew Xiec74b5462012-08-24 00:17:13 -07001447}
1448
1449
1450void XMLAttribute::SetAttribute( int v )
1451{
1452 char buf[BUF_SIZE];
1453 XMLUtil::ToStr( v, buf, BUF_SIZE );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001454 _value.SetStr( buf );
Matthew Xiec74b5462012-08-24 00:17:13 -07001455}
1456
1457
1458void XMLAttribute::SetAttribute( unsigned v )
1459{
1460 char buf[BUF_SIZE];
1461 XMLUtil::ToStr( v, buf, BUF_SIZE );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001462 _value.SetStr( buf );
Matthew Xiec74b5462012-08-24 00:17:13 -07001463}
1464
1465
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001466void XMLAttribute::SetAttribute(int64_t v)
1467{
1468 char buf[BUF_SIZE];
1469 XMLUtil::ToStr(v, buf, BUF_SIZE);
1470 _value.SetStr(buf);
1471}
1472
1473
1474
Matthew Xiec74b5462012-08-24 00:17:13 -07001475void XMLAttribute::SetAttribute( bool 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
1482void XMLAttribute::SetAttribute( double v )
1483{
1484 char buf[BUF_SIZE];
1485 XMLUtil::ToStr( v, buf, BUF_SIZE );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001486 _value.SetStr( buf );
Matthew Xiec74b5462012-08-24 00:17:13 -07001487}
1488
1489void XMLAttribute::SetAttribute( float v )
1490{
1491 char buf[BUF_SIZE];
1492 XMLUtil::ToStr( v, buf, BUF_SIZE );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001493 _value.SetStr( buf );
Matthew Xiec74b5462012-08-24 00:17:13 -07001494}
1495
1496
1497// --------- XMLElement ---------- //
1498XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001499 _closingType( OPEN ),
1500 _rootAttribute( 0 )
Matthew Xiec74b5462012-08-24 00:17:13 -07001501{
1502}
1503
1504
1505XMLElement::~XMLElement()
1506{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001507 while( _rootAttribute ) {
1508 XMLAttribute* next = _rootAttribute->_next;
1509 DeleteAttribute( _rootAttribute );
1510 _rootAttribute = next;
Matthew Xiec74b5462012-08-24 00:17:13 -07001511 }
1512}
1513
1514
Matthew Xiec74b5462012-08-24 00:17:13 -07001515const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1516{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001517 for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
1518 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001519 return a;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001520 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001521 }
1522 return 0;
1523}
1524
1525
1526const char* XMLElement::Attribute( const char* name, const char* value ) const
1527{
1528 const XMLAttribute* a = FindAttribute( name );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001529 if ( !a ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001530 return 0;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001531 }
1532 if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001533 return a->Value();
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001534 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001535 return 0;
1536}
1537
Elliott Hughesd173c992019-01-10 14:51:35 -08001538int XMLElement::IntAttribute(const char* name, int defaultValue) const
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001539{
1540 int i = defaultValue;
1541 QueryIntAttribute(name, &i);
1542 return i;
1543}
1544
Elliott Hughesd173c992019-01-10 14:51:35 -08001545unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001546{
1547 unsigned i = defaultValue;
1548 QueryUnsignedAttribute(name, &i);
1549 return i;
1550}
1551
Elliott Hughesd173c992019-01-10 14:51:35 -08001552int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001553{
1554 int64_t i = defaultValue;
1555 QueryInt64Attribute(name, &i);
1556 return i;
1557}
1558
Elliott Hughesd173c992019-01-10 14:51:35 -08001559bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001560{
1561 bool b = defaultValue;
1562 QueryBoolAttribute(name, &b);
1563 return b;
1564}
1565
Elliott Hughesd173c992019-01-10 14:51:35 -08001566double XMLElement::DoubleAttribute(const char* name, double defaultValue) const
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001567{
1568 double d = defaultValue;
1569 QueryDoubleAttribute(name, &d);
1570 return d;
1571}
1572
Elliott Hughesd173c992019-01-10 14:51:35 -08001573float XMLElement::FloatAttribute(const char* name, float defaultValue) const
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001574{
1575 float f = defaultValue;
1576 QueryFloatAttribute(name, &f);
1577 return f;
1578}
Matthew Xiec74b5462012-08-24 00:17:13 -07001579
1580const char* XMLElement::GetText() const
1581{
1582 if ( FirstChild() && FirstChild()->ToText() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001583 return FirstChild()->Value();
Matthew Xiec74b5462012-08-24 00:17:13 -07001584 }
1585 return 0;
1586}
1587
1588
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001589void XMLElement::SetText( const char* inText )
1590{
1591 if ( FirstChild() && FirstChild()->ToText() )
1592 FirstChild()->SetValue( inText );
1593 else {
1594 XMLText* theText = GetDocument()->NewText( inText );
1595 InsertFirstChild( theText );
1596 }
1597}
1598
1599
Elliott Hughesd173c992019-01-10 14:51:35 -08001600void XMLElement::SetText( int v )
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001601{
1602 char buf[BUF_SIZE];
1603 XMLUtil::ToStr( v, buf, BUF_SIZE );
1604 SetText( buf );
1605}
1606
1607
Elliott Hughesd173c992019-01-10 14:51:35 -08001608void XMLElement::SetText( unsigned v )
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001609{
1610 char buf[BUF_SIZE];
1611 XMLUtil::ToStr( v, buf, BUF_SIZE );
1612 SetText( buf );
1613}
1614
1615
1616void XMLElement::SetText(int64_t v)
1617{
1618 char buf[BUF_SIZE];
1619 XMLUtil::ToStr(v, buf, BUF_SIZE);
1620 SetText(buf);
1621}
1622
1623
1624void XMLElement::SetText( bool v )
1625{
1626 char buf[BUF_SIZE];
1627 XMLUtil::ToStr( v, buf, BUF_SIZE );
1628 SetText( buf );
1629}
1630
1631
Elliott Hughesd173c992019-01-10 14:51:35 -08001632void XMLElement::SetText( float v )
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001633{
1634 char buf[BUF_SIZE];
1635 XMLUtil::ToStr( v, buf, BUF_SIZE );
1636 SetText( buf );
1637}
1638
1639
Elliott Hughesd173c992019-01-10 14:51:35 -08001640void XMLElement::SetText( double v )
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001641{
1642 char buf[BUF_SIZE];
1643 XMLUtil::ToStr( v, buf, BUF_SIZE );
1644 SetText( buf );
1645}
1646
1647
1648XMLError XMLElement::QueryIntText( int* ival ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001649{
1650 if ( FirstChild() && FirstChild()->ToText() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001651 const char* t = FirstChild()->Value();
1652 if ( XMLUtil::ToInt( t, ival ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001653 return XML_SUCCESS;
1654 }
1655 return XML_CAN_NOT_CONVERT_TEXT;
1656 }
1657 return XML_NO_TEXT_NODE;
1658}
1659
1660
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001661XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001662{
1663 if ( FirstChild() && FirstChild()->ToText() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001664 const char* t = FirstChild()->Value();
1665 if ( XMLUtil::ToUnsigned( t, uval ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001666 return XML_SUCCESS;
1667 }
1668 return XML_CAN_NOT_CONVERT_TEXT;
1669 }
1670 return XML_NO_TEXT_NODE;
1671}
1672
1673
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001674XMLError XMLElement::QueryInt64Text(int64_t* ival) const
1675{
1676 if (FirstChild() && FirstChild()->ToText()) {
1677 const char* t = FirstChild()->Value();
1678 if (XMLUtil::ToInt64(t, ival)) {
1679 return XML_SUCCESS;
1680 }
1681 return XML_CAN_NOT_CONVERT_TEXT;
1682 }
1683 return XML_NO_TEXT_NODE;
1684}
1685
1686
1687XMLError XMLElement::QueryBoolText( bool* bval ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001688{
1689 if ( FirstChild() && FirstChild()->ToText() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001690 const char* t = FirstChild()->Value();
1691 if ( XMLUtil::ToBool( t, bval ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001692 return XML_SUCCESS;
1693 }
1694 return XML_CAN_NOT_CONVERT_TEXT;
1695 }
1696 return XML_NO_TEXT_NODE;
1697}
1698
1699
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001700XMLError XMLElement::QueryDoubleText( double* dval ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001701{
1702 if ( FirstChild() && FirstChild()->ToText() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001703 const char* t = FirstChild()->Value();
1704 if ( XMLUtil::ToDouble( t, dval ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001705 return XML_SUCCESS;
1706 }
1707 return XML_CAN_NOT_CONVERT_TEXT;
1708 }
1709 return XML_NO_TEXT_NODE;
1710}
1711
1712
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001713XMLError XMLElement::QueryFloatText( float* fval ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07001714{
1715 if ( FirstChild() && FirstChild()->ToText() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001716 const char* t = FirstChild()->Value();
1717 if ( XMLUtil::ToFloat( t, fval ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001718 return XML_SUCCESS;
1719 }
1720 return XML_CAN_NOT_CONVERT_TEXT;
1721 }
1722 return XML_NO_TEXT_NODE;
1723}
1724
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001725int XMLElement::IntText(int defaultValue) const
1726{
1727 int i = defaultValue;
1728 QueryIntText(&i);
1729 return i;
1730}
1731
1732unsigned XMLElement::UnsignedText(unsigned defaultValue) const
1733{
1734 unsigned i = defaultValue;
1735 QueryUnsignedText(&i);
1736 return i;
1737}
1738
1739int64_t XMLElement::Int64Text(int64_t defaultValue) const
1740{
1741 int64_t i = defaultValue;
1742 QueryInt64Text(&i);
1743 return i;
1744}
1745
1746bool XMLElement::BoolText(bool defaultValue) const
1747{
1748 bool b = defaultValue;
1749 QueryBoolText(&b);
1750 return b;
1751}
1752
1753double XMLElement::DoubleText(double defaultValue) const
1754{
1755 double d = defaultValue;
1756 QueryDoubleText(&d);
1757 return d;
1758}
1759
1760float XMLElement::FloatText(float defaultValue) const
1761{
1762 float f = defaultValue;
1763 QueryFloatText(&f);
1764 return f;
1765}
Matthew Xiec74b5462012-08-24 00:17:13 -07001766
1767
1768XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1769{
1770 XMLAttribute* last = 0;
1771 XMLAttribute* attrib = 0;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001772 for( attrib = _rootAttribute;
1773 attrib;
1774 last = attrib, attrib = attrib->_next ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001775 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1776 break;
1777 }
1778 }
1779 if ( !attrib ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001780 attrib = CreateAttribute();
1781 TIXMLASSERT( attrib );
Matthew Xiec74b5462012-08-24 00:17:13 -07001782 if ( last ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001783 TIXMLASSERT( last->_next == 0 );
1784 last->_next = attrib;
Matthew Xiec74b5462012-08-24 00:17:13 -07001785 }
1786 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001787 TIXMLASSERT( _rootAttribute == 0 );
1788 _rootAttribute = attrib;
Matthew Xiec74b5462012-08-24 00:17:13 -07001789 }
1790 attrib->SetName( name );
1791 }
1792 return attrib;
1793}
1794
1795
1796void XMLElement::DeleteAttribute( const char* name )
1797{
1798 XMLAttribute* prev = 0;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001799 for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001800 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1801 if ( prev ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001802 prev->_next = a->_next;
Matthew Xiec74b5462012-08-24 00:17:13 -07001803 }
1804 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001805 _rootAttribute = a->_next;
Matthew Xiec74b5462012-08-24 00:17:13 -07001806 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001807 DeleteAttribute( a );
Matthew Xiec74b5462012-08-24 00:17:13 -07001808 break;
1809 }
1810 prev = a;
1811 }
1812}
1813
1814
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001815char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -07001816{
Matthew Xiec74b5462012-08-24 00:17:13 -07001817 XMLAttribute* prevAttribute = 0;
1818
1819 // Read the attributes.
1820 while( p ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001821 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1822 if ( !(*p) ) {
1823 _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() );
Matthew Xiec74b5462012-08-24 00:17:13 -07001824 return 0;
1825 }
1826
1827 // attribute.
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001828 if (XMLUtil::IsNameStartChar( *p ) ) {
1829 XMLAttribute* attrib = CreateAttribute();
1830 TIXMLASSERT( attrib );
1831 attrib->_parseLineNum = _document->_parseCurLineNum;
Matthew Xiec74b5462012-08-24 00:17:13 -07001832
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001833 int attrLineNum = attrib->_parseLineNum;
1834
1835 p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001836 if ( !p || Attribute( attrib->Name() ) ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001837 DeleteAttribute( attrib );
1838 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() );
Matthew Xiec74b5462012-08-24 00:17:13 -07001839 return 0;
1840 }
1841 // There is a minor bug here: if the attribute in the source xml
1842 // document is duplicated, it will not be detected and the
1843 // attribute will be doubly added. However, tracking the 'prevAttribute'
1844 // avoids re-scanning the attribute list. Preferring performance for
1845 // now, may reconsider in the future.
1846 if ( prevAttribute ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001847 TIXMLASSERT( prevAttribute->_next == 0 );
1848 prevAttribute->_next = attrib;
Matthew Xiec74b5462012-08-24 00:17:13 -07001849 }
1850 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001851 TIXMLASSERT( _rootAttribute == 0 );
1852 _rootAttribute = attrib;
Matthew Xiec74b5462012-08-24 00:17:13 -07001853 }
1854 prevAttribute = attrib;
1855 }
1856 // end of the tag
Matthew Xiec74b5462012-08-24 00:17:13 -07001857 else if ( *p == '>' ) {
1858 ++p;
1859 break;
1860 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001861 // end of the tag
1862 else if ( *p == '/' && *(p+1) == '>' ) {
1863 _closingType = CLOSED;
1864 return p+2; // done; sealed element.
1865 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001866 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001867 _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 );
Matthew Xiec74b5462012-08-24 00:17:13 -07001868 return 0;
1869 }
1870 }
1871 return p;
1872}
1873
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001874void XMLElement::DeleteAttribute( XMLAttribute* attribute )
1875{
1876 if ( attribute == 0 ) {
1877 return;
1878 }
1879 MemPool* pool = attribute->_memPool;
1880 attribute->~XMLAttribute();
1881 pool->Free( attribute );
1882}
1883
1884XMLAttribute* XMLElement::CreateAttribute()
1885{
1886 TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
1887 XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1888 TIXMLASSERT( attrib );
1889 attrib->_memPool = &_document->_attributePool;
1890 attrib->_memPool->SetTracked();
1891 return attrib;
1892}
Matthew Xiec74b5462012-08-24 00:17:13 -07001893
1894//
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001895// <ele></ele>
1896// <ele>foo<b>bar</b></ele>
Matthew Xiec74b5462012-08-24 00:17:13 -07001897//
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001898char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
Matthew Xiec74b5462012-08-24 00:17:13 -07001899{
1900 // Read the element name.
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001901 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001902
1903 // The closing element is the </element> form. It is
1904 // parsed just like a regular element then deleted from
1905 // the DOM.
1906 if ( *p == '/' ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001907 _closingType = CLOSING;
Matthew Xiec74b5462012-08-24 00:17:13 -07001908 ++p;
1909 }
1910
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001911 p = _value.ParseName( p );
1912 if ( _value.Empty() ) {
1913 return 0;
1914 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001915
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001916 p = ParseAttributes( p, curLineNumPtr );
1917 if ( !p || !*p || _closingType != OPEN ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001918 return p;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001919 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001920
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001921 p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr );
Matthew Xiec74b5462012-08-24 00:17:13 -07001922 return p;
1923}
1924
1925
1926
1927XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1928{
1929 if ( !doc ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001930 doc = _document;
Matthew Xiec74b5462012-08-24 00:17:13 -07001931 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001932 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
Matthew Xiec74b5462012-08-24 00:17:13 -07001933 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001934 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
Matthew Xiec74b5462012-08-24 00:17:13 -07001935 }
1936 return element;
1937}
1938
1939
1940bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1941{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001942 TIXMLASSERT( compare );
Matthew Xiec74b5462012-08-24 00:17:13 -07001943 const XMLElement* other = compare->ToElement();
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001944 if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001945
1946 const XMLAttribute* a=FirstAttribute();
1947 const XMLAttribute* b=other->FirstAttribute();
1948
1949 while ( a && b ) {
1950 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1951 return false;
1952 }
1953 a = a->Next();
1954 b = b->Next();
1955 }
1956 if ( a || b ) {
1957 // different count
1958 return false;
1959 }
1960 return true;
1961 }
1962 return false;
1963}
1964
1965
1966bool XMLElement::Accept( XMLVisitor* visitor ) const
1967{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001968 TIXMLASSERT( visitor );
1969 if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
1970 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
1971 if ( !node->Accept( visitor ) ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07001972 break;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001973 }
Matthew Xiec74b5462012-08-24 00:17:13 -07001974 }
1975 }
1976 return visitor->VisitExit( *this );
1977}
1978
1979
1980// --------- XMLDocument ----------- //
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001981
1982// Warning: List must match 'enum XMLError'
1983const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
1984 "XML_SUCCESS",
1985 "XML_NO_ATTRIBUTE",
1986 "XML_WRONG_ATTRIBUTE_TYPE",
1987 "XML_ERROR_FILE_NOT_FOUND",
1988 "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
1989 "XML_ERROR_FILE_READ_ERROR",
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001990 "XML_ERROR_PARSING_ELEMENT",
1991 "XML_ERROR_PARSING_ATTRIBUTE",
Narayan Kamath74c03bb2017-12-22 10:59:43 +00001992 "XML_ERROR_PARSING_TEXT",
1993 "XML_ERROR_PARSING_CDATA",
1994 "XML_ERROR_PARSING_COMMENT",
1995 "XML_ERROR_PARSING_DECLARATION",
1996 "XML_ERROR_PARSING_UNKNOWN",
1997 "XML_ERROR_EMPTY_DOCUMENT",
1998 "XML_ERROR_MISMATCHED_ELEMENT",
1999 "XML_ERROR_PARSING",
2000 "XML_CAN_NOT_CONVERT_TEXT",
Elliott Hughesd173c992019-01-10 14:51:35 -08002001 "XML_NO_TEXT_NODE",
2002 "XML_ELEMENT_DEPTH_EXCEEDED"
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002003};
2004
2005
2006XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) :
Matthew Xiec74b5462012-08-24 00:17:13 -07002007 XMLNode( 0 ),
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002008 _writeBOM( false ),
2009 _processEntities( processEntities ),
2010 _errorID(XML_SUCCESS),
2011 _whitespaceMode( whitespaceMode ),
2012 _errorStr(),
2013 _errorLineNum( 0 ),
2014 _charBuffer( 0 ),
2015 _parseCurLineNum( 0 ),
Elliott Hughesd173c992019-01-10 14:51:35 -08002016 _parsingDepth(0),
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002017 _unlinked(),
2018 _elementPool(),
2019 _attributePool(),
2020 _textPool(),
2021 _commentPool()
Matthew Xiec74b5462012-08-24 00:17:13 -07002022{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002023 // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
2024 _document = this;
Matthew Xiec74b5462012-08-24 00:17:13 -07002025}
2026
2027
2028XMLDocument::~XMLDocument()
2029{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002030 Clear();
2031}
2032
2033
2034void XMLDocument::MarkInUse(XMLNode* node)
2035{
2036 TIXMLASSERT(node);
2037 TIXMLASSERT(node->_parent == 0);
2038
2039 for (int i = 0; i < _unlinked.Size(); ++i) {
2040 if (node == _unlinked[i]) {
2041 _unlinked.SwapRemove(i);
2042 break;
2043 }
2044 }
2045}
2046
2047void XMLDocument::Clear()
2048{
Matthew Xiec74b5462012-08-24 00:17:13 -07002049 DeleteChildren();
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002050 while( _unlinked.Size()) {
2051 DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete.
2052 }
2053
Elliott Hughesd173c992019-01-10 14:51:35 -08002054#ifdef TINYXML2_DEBUG
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002055 const bool hadError = Error();
2056#endif
2057 ClearError();
2058
2059 delete [] _charBuffer;
2060 _charBuffer = 0;
Elliott Hughesd173c992019-01-10 14:51:35 -08002061 _parsingDepth = 0;
Matthew Xiec74b5462012-08-24 00:17:13 -07002062
2063#if 0
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002064 _textPool.Trace( "text" );
2065 _elementPool.Trace( "element" );
2066 _commentPool.Trace( "comment" );
2067 _attributePool.Trace( "attribute" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002068#endif
Elliott Hughesd173c992019-01-10 14:51:35 -08002069
2070#ifdef TINYXML2_DEBUG
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002071 if ( !hadError ) {
2072 TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() );
2073 TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
2074 TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() );
2075 TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() );
2076 }
2077#endif
Matthew Xiec74b5462012-08-24 00:17:13 -07002078}
2079
2080
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002081void XMLDocument::DeepCopy(XMLDocument* target) const
Matthew Xiec74b5462012-08-24 00:17:13 -07002082{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002083 TIXMLASSERT(target);
2084 if (target == this) {
2085 return; // technically success - a no-op.
2086 }
Matthew Xiec74b5462012-08-24 00:17:13 -07002087
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002088 target->Clear();
2089 for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) {
2090 target->InsertEndChild(node->DeepClone(target));
2091 }
Matthew Xiec74b5462012-08-24 00:17:13 -07002092}
2093
Matthew Xiec74b5462012-08-24 00:17:13 -07002094XMLElement* XMLDocument::NewElement( const char* name )
2095{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002096 XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool );
Matthew Xiec74b5462012-08-24 00:17:13 -07002097 ele->SetName( name );
2098 return ele;
2099}
2100
2101
2102XMLComment* XMLDocument::NewComment( const char* str )
2103{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002104 XMLComment* comment = CreateUnlinkedNode<XMLComment>( _commentPool );
Matthew Xiec74b5462012-08-24 00:17:13 -07002105 comment->SetValue( str );
2106 return comment;
2107}
2108
2109
2110XMLText* XMLDocument::NewText( const char* str )
2111{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002112 XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
Matthew Xiec74b5462012-08-24 00:17:13 -07002113 text->SetValue( str );
2114 return text;
2115}
2116
2117
2118XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
2119{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002120 XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
Matthew Xiec74b5462012-08-24 00:17:13 -07002121 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
2122 return dec;
2123}
2124
2125
2126XMLUnknown* XMLDocument::NewUnknown( const char* str )
2127{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002128 XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>( _commentPool );
Matthew Xiec74b5462012-08-24 00:17:13 -07002129 unk->SetValue( str );
2130 return unk;
2131}
2132
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002133static FILE* callfopen( const char* filepath, const char* mode )
Matthew Xiec74b5462012-08-24 00:17:13 -07002134{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002135 TIXMLASSERT( filepath );
2136 TIXMLASSERT( mode );
2137#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
2138 FILE* fp = 0;
2139 errno_t err = fopen_s( &fp, filepath, mode );
2140 if ( err ) {
2141 return 0;
2142 }
2143#else
2144 FILE* fp = fopen( filepath, mode );
2145#endif
2146 return fp;
2147}
Elliott Hughesd173c992019-01-10 14:51:35 -08002148
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002149void XMLDocument::DeleteNode( XMLNode* node ) {
2150 TIXMLASSERT( node );
2151 TIXMLASSERT(node->_document == this );
2152 if (node->_parent) {
2153 node->_parent->DeleteChild( node );
2154 }
2155 else {
2156 // Isn't in the tree.
2157 // Use the parent delete.
2158 // Also, we need to mark it tracked: we 'know'
2159 // it was never used.
2160 node->_memPool->SetTracked();
2161 // Call the static XMLNode version:
2162 XMLNode::DeleteNode(node);
2163 }
2164}
Matthew Xiec74b5462012-08-24 00:17:13 -07002165
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002166
2167XMLError XMLDocument::LoadFile( const char* filename )
2168{
Elliott Hughesd173c992019-01-10 14:51:35 -08002169 if ( !filename ) {
2170 TIXMLASSERT( false );
2171 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
2172 return _errorID;
2173 }
2174
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002175 Clear();
2176 FILE* fp = callfopen( filename, "rb" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002177 if ( !fp ) {
Elliott Hughesd173c992019-01-10 14:51:35 -08002178 SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002179 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002180 }
2181 LoadFile( fp );
2182 fclose( fp );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002183 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002184}
2185
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002186// This is likely overengineered template art to have a check that unsigned long value incremented
2187// by one still fits into size_t. If size_t type is larger than unsigned long type
2188// (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
2189// -Wtype-limits warning. This piece makes the compiler select code with a check when a check
2190// is useful and code with no check when a check is redundant depending on how size_t and unsigned long
2191// types sizes relate to each other.
2192template
2193<bool = (sizeof(unsigned long) >= sizeof(size_t))>
2194struct LongFitsIntoSizeTMinusOne {
2195 static bool Fits( unsigned long value )
2196 {
2197 return value < (size_t)-1;
2198 }
2199};
Matthew Xiec74b5462012-08-24 00:17:13 -07002200
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002201template <>
2202struct LongFitsIntoSizeTMinusOne<false> {
2203 static bool Fits( unsigned long )
2204 {
2205 return true;
2206 }
2207};
2208
2209XMLError XMLDocument::LoadFile( FILE* fp )
Matthew Xiec74b5462012-08-24 00:17:13 -07002210{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002211 Clear();
2212
2213 fseek( fp, 0, SEEK_SET );
2214 if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
2215 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2216 return _errorID;
2217 }
Matthew Xiec74b5462012-08-24 00:17:13 -07002218
2219 fseek( fp, 0, SEEK_END );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002220 const long filelength = ftell( fp );
Matthew Xiec74b5462012-08-24 00:17:13 -07002221 fseek( fp, 0, SEEK_SET );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002222 if ( filelength == -1L ) {
2223 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2224 return _errorID;
2225 }
2226 TIXMLASSERT( filelength >= 0 );
Matthew Xiec74b5462012-08-24 00:17:13 -07002227
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002228 if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
2229 // Cannot handle files which won't fit in buffer together with null terminator
2230 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2231 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002232 }
2233
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002234 if ( filelength == 0 ) {
2235 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2236 return _errorID;
2237 }
2238
2239 const size_t size = filelength;
2240 TIXMLASSERT( _charBuffer == 0 );
2241 _charBuffer = new char[size+1];
2242 size_t read = fread( _charBuffer, 1, size, fp );
Matthew Xiec74b5462012-08-24 00:17:13 -07002243 if ( read != size ) {
2244 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002245 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002246 }
2247
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002248 _charBuffer[size] = 0;
Matthew Xiec74b5462012-08-24 00:17:13 -07002249
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002250 Parse();
2251 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002252}
2253
2254
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002255XMLError XMLDocument::SaveFile( const char* filename, bool compact )
Matthew Xiec74b5462012-08-24 00:17:13 -07002256{
Elliott Hughesd173c992019-01-10 14:51:35 -08002257 if ( !filename ) {
2258 TIXMLASSERT( false );
2259 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
2260 return _errorID;
2261 }
2262
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002263 FILE* fp = callfopen( filename, "w" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002264 if ( !fp ) {
Elliott Hughesd173c992019-01-10 14:51:35 -08002265 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002266 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002267 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002268 SaveFile(fp, compact);
Matthew Xiec74b5462012-08-24 00:17:13 -07002269 fclose( fp );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002270 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002271}
2272
2273
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002274XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
Matthew Xiec74b5462012-08-24 00:17:13 -07002275{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002276 // Clear any error from the last save, otherwise it will get reported
2277 // for *this* call.
2278 ClearError();
2279 XMLPrinter stream( fp, compact );
Matthew Xiec74b5462012-08-24 00:17:13 -07002280 Print( &stream );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002281 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002282}
2283
2284
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002285XMLError XMLDocument::Parse( const char* p, size_t len )
Matthew Xiec74b5462012-08-24 00:17:13 -07002286{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002287 Clear();
Matthew Xiec74b5462012-08-24 00:17:13 -07002288
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002289 if ( len == 0 || !p || !*p ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07002290 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002291 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002292 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002293 if ( len == (size_t)(-1) ) {
2294 len = strlen( p );
Matthew Xiec74b5462012-08-24 00:17:13 -07002295 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002296 TIXMLASSERT( _charBuffer == 0 );
2297 _charBuffer = new char[ len+1 ];
2298 memcpy( _charBuffer, p, len );
2299 _charBuffer[len] = 0;
Matthew Xiec74b5462012-08-24 00:17:13 -07002300
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002301 Parse();
2302 if ( Error() ) {
2303 // clean up now essentially dangling memory.
2304 // and the parse fail can put objects in the
2305 // pools that are dead and inaccessible.
2306 DeleteChildren();
2307 _elementPool.Clear();
2308 _attributePool.Clear();
2309 _textPool.Clear();
2310 _commentPool.Clear();
2311 }
2312 return _errorID;
Matthew Xiec74b5462012-08-24 00:17:13 -07002313}
2314
2315
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002316void XMLDocument::Print( XMLPrinter* streamer ) const
Matthew Xiec74b5462012-08-24 00:17:13 -07002317{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002318 if ( streamer ) {
2319 Accept( streamer );
2320 }
2321 else {
2322 XMLPrinter stdoutStreamer( stdout );
2323 Accept( &stdoutStreamer );
2324 }
Matthew Xiec74b5462012-08-24 00:17:13 -07002325}
2326
2327
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002328void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... )
Matthew Xiec74b5462012-08-24 00:17:13 -07002329{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002330 TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
2331 _errorID = error;
2332 _errorLineNum = lineNum;
2333 _errorStr.Reset();
2334
Elliott Hughesd173c992019-01-10 14:51:35 -08002335 size_t BUFFER_SIZE = 1000;
2336 char* buffer = new char[BUFFER_SIZE];
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002337
Elliott Hughesd173c992019-01-10 14:51:35 -08002338 TIXMLASSERT(sizeof(error) <= sizeof(int));
2339 TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum);
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002340
Elliott Hughesd173c992019-01-10 14:51:35 -08002341 if (format) {
2342 size_t len = strlen(buffer);
2343 TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": ");
2344 len = strlen(buffer);
2345
2346 va_list va;
2347 va_start(va, format);
2348 TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va);
2349 va_end(va);
2350 }
2351 _errorStr.SetStr(buffer);
2352 delete[] buffer;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002353}
2354
2355
2356/*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID)
2357{
2358 TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT );
2359 const char* errorName = _errorNames[errorID];
2360 TIXMLASSERT( errorName && errorName[0] );
2361 return errorName;
2362}
2363
Elliott Hughesd173c992019-01-10 14:51:35 -08002364const char* XMLDocument::ErrorStr() const
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002365{
2366 return _errorStr.Empty() ? "" : _errorStr.GetStr();
Matthew Xiec74b5462012-08-24 00:17:13 -07002367}
2368
2369
2370void XMLDocument::PrintError() const
2371{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002372 printf("%s\n", ErrorStr());
Matthew Xiec74b5462012-08-24 00:17:13 -07002373}
2374
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002375const char* XMLDocument::ErrorName() const
2376{
2377 return ErrorIDToName(_errorID);
2378}
Matthew Xiec74b5462012-08-24 00:17:13 -07002379
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002380void XMLDocument::Parse()
2381{
2382 TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
2383 TIXMLASSERT( _charBuffer );
2384 _parseCurLineNum = 1;
2385 _parseLineNum = 1;
2386 char* p = _charBuffer;
2387 p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
2388 p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
2389 if ( !*p ) {
2390 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2391 return;
2392 }
2393 ParseDeep(p, 0, &_parseCurLineNum );
2394}
2395
Elliott Hughesd173c992019-01-10 14:51:35 -08002396void XMLDocument::PushDepth()
2397{
2398 _parsingDepth++;
2399 if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) {
2400 SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, "Element nesting is too deep." );
2401 }
2402}
2403
2404void XMLDocument::PopDepth()
2405{
2406 TIXMLASSERT(_parsingDepth > 0);
2407 --_parsingDepth;
2408}
2409
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002410XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
2411 _elementJustOpened( false ),
2412 _stack(),
2413 _firstElement( true ),
2414 _fp( file ),
2415 _depth( depth ),
2416 _textDepth( -1 ),
2417 _processEntities( true ),
2418 _compactMode( compact ),
2419 _buffer()
Matthew Xiec74b5462012-08-24 00:17:13 -07002420{
2421 for( int i=0; i<ENTITY_RANGE; ++i ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002422 _entityFlag[i] = false;
2423 _restrictedEntityFlag[i] = false;
Matthew Xiec74b5462012-08-24 00:17:13 -07002424 }
2425 for( int i=0; i<NUM_ENTITIES; ++i ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002426 const char entityValue = entities[i].value;
2427 const unsigned char flagIndex = (unsigned char)entityValue;
2428 TIXMLASSERT( flagIndex < ENTITY_RANGE );
2429 _entityFlag[flagIndex] = true;
Matthew Xiec74b5462012-08-24 00:17:13 -07002430 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002431 _restrictedEntityFlag[(unsigned char)'&'] = true;
2432 _restrictedEntityFlag[(unsigned char)'<'] = true;
2433 _restrictedEntityFlag[(unsigned char)'>'] = true; // not required, but consistency is nice
2434 _buffer.Push( 0 );
Matthew Xiec74b5462012-08-24 00:17:13 -07002435}
2436
2437
2438void XMLPrinter::Print( const char* format, ... )
2439{
2440 va_list va;
2441 va_start( va, format );
2442
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002443 if ( _fp ) {
2444 vfprintf( _fp, format, va );
Matthew Xiec74b5462012-08-24 00:17:13 -07002445 }
2446 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002447 const int len = TIXML_VSCPRINTF( format, va );
2448 // Close out and re-start the va-args
2449 va_end( va );
2450 TIXMLASSERT( len >= 0 );
2451 va_start( va, format );
2452 TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
2453 char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator.
2454 TIXML_VSNPRINTF( p, len+1, format, va );
Matthew Xiec74b5462012-08-24 00:17:13 -07002455 }
2456 va_end( va );
2457}
2458
2459
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002460void XMLPrinter::Write( const char* data, size_t size )
2461{
2462 if ( _fp ) {
2463 fwrite ( data , sizeof(char), size, _fp);
2464 }
2465 else {
2466 char* p = _buffer.PushArr( static_cast<int>(size) ) - 1; // back up over the null terminator.
2467 memcpy( p, data, size );
2468 p[size] = 0;
2469 }
2470}
2471
2472
2473void XMLPrinter::Putc( char ch )
2474{
2475 if ( _fp ) {
2476 fputc ( ch, _fp);
2477 }
2478 else {
2479 char* p = _buffer.PushArr( sizeof(char) ) - 1; // back up over the null terminator.
2480 p[0] = ch;
2481 p[1] = 0;
2482 }
2483}
2484
2485
Matthew Xiec74b5462012-08-24 00:17:13 -07002486void XMLPrinter::PrintSpace( int depth )
2487{
2488 for( int i=0; i<depth; ++i ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002489 Write( " " );
Matthew Xiec74b5462012-08-24 00:17:13 -07002490 }
2491}
2492
2493
2494void XMLPrinter::PrintString( const char* p, bool restricted )
2495{
2496 // Look for runs of bytes between entities to print.
2497 const char* q = p;
Matthew Xiec74b5462012-08-24 00:17:13 -07002498
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002499 if ( _processEntities ) {
2500 const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
Matthew Xiec74b5462012-08-24 00:17:13 -07002501 while ( *q ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002502 TIXMLASSERT( p <= q );
Matthew Xiec74b5462012-08-24 00:17:13 -07002503 // Remember, char is sometimes signed. (How many times has that bitten me?)
2504 if ( *q > 0 && *q < ENTITY_RANGE ) {
2505 // Check for entities. If one is found, flush
2506 // the stream up until the entity, write the
2507 // entity, and keep looking.
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002508 if ( flag[(unsigned char)(*q)] ) {
Matthew Xiec74b5462012-08-24 00:17:13 -07002509 while ( p < q ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002510 const size_t delta = q - p;
2511 const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
2512 Write( p, toPrint );
2513 p += toPrint;
Matthew Xiec74b5462012-08-24 00:17:13 -07002514 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002515 bool entityPatternPrinted = false;
Matthew Xiec74b5462012-08-24 00:17:13 -07002516 for( int i=0; i<NUM_ENTITIES; ++i ) {
2517 if ( entities[i].value == *q ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002518 Putc( '&' );
2519 Write( entities[i].pattern, entities[i].length );
2520 Putc( ';' );
2521 entityPatternPrinted = true;
Matthew Xiec74b5462012-08-24 00:17:13 -07002522 break;
2523 }
2524 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002525 if ( !entityPatternPrinted ) {
2526 // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
2527 TIXMLASSERT( false );
2528 }
Matthew Xiec74b5462012-08-24 00:17:13 -07002529 ++p;
2530 }
2531 }
2532 ++q;
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002533 TIXMLASSERT( p <= q );
Matthew Xiec74b5462012-08-24 00:17:13 -07002534 }
Elliott Hughesd173c992019-01-10 14:51:35 -08002535 // Flush the remaining string. This will be the entire
2536 // string if an entity wasn't found.
2537 if ( p < q ) {
2538 const size_t delta = q - p;
2539 const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
2540 Write( p, toPrint );
2541 }
Matthew Xiec74b5462012-08-24 00:17:13 -07002542 }
Elliott Hughesd173c992019-01-10 14:51:35 -08002543 else {
2544 Write( p );
Matthew Xiec74b5462012-08-24 00:17:13 -07002545 }
2546}
2547
2548
2549void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
2550{
Matthew Xiec74b5462012-08-24 00:17:13 -07002551 if ( writeBOM ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002552 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
2553 Write( reinterpret_cast< const char* >( bom ) );
Matthew Xiec74b5462012-08-24 00:17:13 -07002554 }
2555 if ( writeDec ) {
2556 PushDeclaration( "xml version=\"1.0\"" );
2557 }
2558}
2559
2560
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002561void XMLPrinter::OpenElement( const char* name, bool compactMode )
Matthew Xiec74b5462012-08-24 00:17:13 -07002562{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002563 SealElementIfJustOpened();
2564 _stack.Push( name );
Matthew Xiec74b5462012-08-24 00:17:13 -07002565
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002566 if ( _textDepth < 0 && !_firstElement && !compactMode ) {
2567 Putc( '\n' );
2568 }
2569 if ( !compactMode ) {
2570 PrintSpace( _depth );
Matthew Xiec74b5462012-08-24 00:17:13 -07002571 }
2572
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002573 Write ( "<" );
2574 Write ( name );
2575
2576 _elementJustOpened = true;
2577 _firstElement = false;
2578 ++_depth;
Matthew Xiec74b5462012-08-24 00:17:13 -07002579}
2580
2581
2582void XMLPrinter::PushAttribute( const char* name, const char* value )
2583{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002584 TIXMLASSERT( _elementJustOpened );
2585 Putc ( ' ' );
2586 Write( name );
2587 Write( "=\"" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002588 PrintString( value, false );
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002589 Putc ( '\"' );
Matthew Xiec74b5462012-08-24 00:17:13 -07002590}
2591
2592
2593void XMLPrinter::PushAttribute( const char* name, int v )
2594{
2595 char buf[BUF_SIZE];
2596 XMLUtil::ToStr( v, buf, BUF_SIZE );
2597 PushAttribute( name, buf );
2598}
2599
2600
2601void XMLPrinter::PushAttribute( const char* name, unsigned v )
2602{
2603 char buf[BUF_SIZE];
2604 XMLUtil::ToStr( v, buf, BUF_SIZE );
2605 PushAttribute( name, buf );
2606}
2607
2608
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002609void XMLPrinter::PushAttribute(const char* name, int64_t v)
2610{
2611 char buf[BUF_SIZE];
2612 XMLUtil::ToStr(v, buf, BUF_SIZE);
2613 PushAttribute(name, buf);
2614}
2615
2616
Matthew Xiec74b5462012-08-24 00:17:13 -07002617void XMLPrinter::PushAttribute( const char* name, bool v )
2618{
2619 char buf[BUF_SIZE];
2620 XMLUtil::ToStr( v, buf, BUF_SIZE );
2621 PushAttribute( name, buf );
2622}
2623
2624
2625void XMLPrinter::PushAttribute( const char* name, double v )
2626{
2627 char buf[BUF_SIZE];
2628 XMLUtil::ToStr( v, buf, BUF_SIZE );
2629 PushAttribute( name, buf );
2630}
2631
2632
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002633void XMLPrinter::CloseElement( bool compactMode )
Matthew Xiec74b5462012-08-24 00:17:13 -07002634{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002635 --_depth;
2636 const char* name = _stack.Pop();
Matthew Xiec74b5462012-08-24 00:17:13 -07002637
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002638 if ( _elementJustOpened ) {
2639 Write( "/>" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002640 }
2641 else {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002642 if ( _textDepth < 0 && !compactMode) {
2643 Putc( '\n' );
2644 PrintSpace( _depth );
Matthew Xiec74b5462012-08-24 00:17:13 -07002645 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002646 Write ( "</" );
2647 Write ( name );
2648 Write ( ">" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002649 }
2650
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002651 if ( _textDepth == _depth ) {
2652 _textDepth = -1;
2653 }
2654 if ( _depth == 0 && !compactMode) {
2655 Putc( '\n' );
2656 }
2657 _elementJustOpened = false;
Matthew Xiec74b5462012-08-24 00:17:13 -07002658}
2659
2660
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002661void XMLPrinter::SealElementIfJustOpened()
Matthew Xiec74b5462012-08-24 00:17:13 -07002662{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002663 if ( !_elementJustOpened ) {
2664 return;
2665 }
2666 _elementJustOpened = false;
2667 Putc( '>' );
Matthew Xiec74b5462012-08-24 00:17:13 -07002668}
2669
2670
2671void XMLPrinter::PushText( const char* text, bool cdata )
2672{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002673 _textDepth = _depth-1;
Matthew Xiec74b5462012-08-24 00:17:13 -07002674
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002675 SealElementIfJustOpened();
Matthew Xiec74b5462012-08-24 00:17:13 -07002676 if ( cdata ) {
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002677 Write( "<![CDATA[" );
2678 Write( text );
2679 Write( "]]>" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002680 }
2681 else {
2682 PrintString( text, true );
2683 }
2684}
2685
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002686void XMLPrinter::PushText( int64_t value )
2687{
2688 char buf[BUF_SIZE];
2689 XMLUtil::ToStr( value, buf, BUF_SIZE );
2690 PushText( buf, false );
2691}
2692
Matthew Xiec74b5462012-08-24 00:17:13 -07002693void XMLPrinter::PushText( int value )
2694{
2695 char buf[BUF_SIZE];
2696 XMLUtil::ToStr( value, buf, BUF_SIZE );
2697 PushText( buf, false );
2698}
2699
2700
2701void XMLPrinter::PushText( unsigned value )
2702{
2703 char buf[BUF_SIZE];
2704 XMLUtil::ToStr( value, buf, BUF_SIZE );
2705 PushText( buf, false );
2706}
2707
2708
2709void XMLPrinter::PushText( bool value )
2710{
2711 char buf[BUF_SIZE];
2712 XMLUtil::ToStr( value, buf, BUF_SIZE );
2713 PushText( buf, false );
2714}
2715
2716
2717void XMLPrinter::PushText( float value )
2718{
2719 char buf[BUF_SIZE];
2720 XMLUtil::ToStr( value, buf, BUF_SIZE );
2721 PushText( buf, false );
2722}
2723
2724
2725void XMLPrinter::PushText( double value )
2726{
2727 char buf[BUF_SIZE];
2728 XMLUtil::ToStr( value, buf, BUF_SIZE );
2729 PushText( buf, false );
2730}
2731
2732
2733void XMLPrinter::PushComment( const char* comment )
2734{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002735 SealElementIfJustOpened();
2736 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2737 Putc( '\n' );
2738 PrintSpace( _depth );
Matthew Xiec74b5462012-08-24 00:17:13 -07002739 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002740 _firstElement = false;
2741
2742 Write( "<!--" );
2743 Write( comment );
2744 Write( "-->" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002745}
2746
2747
2748void XMLPrinter::PushDeclaration( const char* value )
2749{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002750 SealElementIfJustOpened();
2751 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2752 Putc( '\n' );
2753 PrintSpace( _depth );
Matthew Xiec74b5462012-08-24 00:17:13 -07002754 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002755 _firstElement = false;
2756
2757 Write( "<?" );
2758 Write( value );
2759 Write( "?>" );
Matthew Xiec74b5462012-08-24 00:17:13 -07002760}
2761
2762
2763void XMLPrinter::PushUnknown( const char* value )
2764{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002765 SealElementIfJustOpened();
2766 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2767 Putc( '\n' );
2768 PrintSpace( _depth );
Matthew Xiec74b5462012-08-24 00:17:13 -07002769 }
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002770 _firstElement = false;
2771
2772 Write( "<!" );
2773 Write( value );
2774 Putc( '>' );
Matthew Xiec74b5462012-08-24 00:17:13 -07002775}
2776
2777
2778bool XMLPrinter::VisitEnter( const XMLDocument& doc )
2779{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002780 _processEntities = doc.ProcessEntities();
Matthew Xiec74b5462012-08-24 00:17:13 -07002781 if ( doc.HasBOM() ) {
2782 PushHeader( true, false );
2783 }
2784 return true;
2785}
2786
2787
2788bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
2789{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002790 const XMLElement* parentElem = 0;
2791 if ( element.Parent() ) {
2792 parentElem = element.Parent()->ToElement();
2793 }
2794 const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
2795 OpenElement( element.Name(), compactMode );
Matthew Xiec74b5462012-08-24 00:17:13 -07002796 while ( attribute ) {
2797 PushAttribute( attribute->Name(), attribute->Value() );
2798 attribute = attribute->Next();
2799 }
2800 return true;
2801}
2802
2803
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002804bool XMLPrinter::VisitExit( const XMLElement& element )
Matthew Xiec74b5462012-08-24 00:17:13 -07002805{
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002806 CloseElement( CompactMode(element) );
Matthew Xiec74b5462012-08-24 00:17:13 -07002807 return true;
2808}
2809
2810
2811bool XMLPrinter::Visit( const XMLText& text )
2812{
2813 PushText( text.Value(), text.CData() );
2814 return true;
2815}
2816
2817
2818bool XMLPrinter::Visit( const XMLComment& comment )
2819{
2820 PushComment( comment.Value() );
2821 return true;
2822}
2823
2824bool XMLPrinter::Visit( const XMLDeclaration& declaration )
2825{
2826 PushDeclaration( declaration.Value() );
2827 return true;
2828}
2829
2830
2831bool XMLPrinter::Visit( const XMLUnknown& unknown )
2832{
2833 PushUnknown( unknown.Value() );
2834 return true;
2835}
Narayan Kamath74c03bb2017-12-22 10:59:43 +00002836
2837} // namespace tinyxml2