1 /*
2 Original code by Lee Thomason (www.grinninglizard.com)
3 
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any
6 damages arising from the use of this software.
7 
8 Permission is granted to anyone to use this software for any
9 purpose, including commercial applications, and to alter it and
10 redistribute it freely, subject to the following restrictions:
11 
12 1. The origin of this software must not be misrepresented; you must
13 not claim that you wrote the original software. If you use this
14 software in a product, an acknowledgment in the product documentation
15 would be appreciated but is not required.
16 
17 2. Altered source versions must be plainly marked as such, and
18 must not be misrepresented as being the original software.
19 
20 3. This notice may not be removed or altered from any source
21 distribution.
22 */
23 
24 #include "tinyxml2.h"
25 
26 #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
34 
35 #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 	);*/
TIXML_SNPRINTF(char * buffer,size_t size,const char * format,...)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 }
52 
TIXML_VSNPRINTF(char * buffer,size_t size,const char * format,va_list va)53 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.
TIXML_VSCPRINTF(const char * format,va_list va)71 static inline int TIXML_VSCPRINTF(const char* format, va_list va)
72 {
73 	int len = 512;
74 	for (;;)
75 	{
76 		len = len * 2;
77 		char* str = new char[len]();
78 		const int required = _vsnprintf(str, len, format, va);
79 		delete[] str;
80 		if (required != -1)
81 		{
82 			TIXMLASSERT(required >= 0);
83 			len = required;
84 			break;
85 		}
86 	}
87 	TIXMLASSERT(len >= 0);
88 	return len;
89 }
90 #endif
91 #else
92 // GCC version 3 and higher
93 //#warning( "Using sn* functions." )
94 #define TIXML_SNPRINTF snprintf
95 #define TIXML_VSNPRINTF vsnprintf
TIXML_VSCPRINTF(const char * format,va_list va)96 static inline int TIXML_VSCPRINTF(const char* format, va_list va)
97 {
98 	int len = vsnprintf(0, 0, format, va);
99 	TIXMLASSERT(len >= 0);
100 	return len;
101 }
102 #define TIXML_SSCANF sscanf
103 #endif
104 
105 static const char LINE_FEED = (char)0x0a;  // all line endings are normalized to LF
106 static const char LF = LINE_FEED;
107 static const char CARRIAGE_RETURN = (char)0x0d;  // CR gets filtered out
108 static const char CR = CARRIAGE_RETURN;
109 static const char SINGLE_QUOTE = '\'';
110 static const char DOUBLE_QUOTE = '\"';
111 
112 // Bunch of unicode info at:
113 //		http://www.unicode.org/faq/utf_bom.html
114 //	ef bb bf (Microsoft "lead bytes") - designates UTF-8
115 
116 static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
117 static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
118 static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
119 
120 namespace tinyxml2
121 {
122 struct Entity
123 {
124 	const char* pattern;
125 	int length;
126 	char value;
127 };
128 
129 static const int NUM_ENTITIES = 5;
130 static const Entity entities[NUM_ENTITIES] = {
131 	{"quot", 4, DOUBLE_QUOTE},
132 	{"amp", 3, '&'},
133 	{"apos", 4, SINGLE_QUOTE},
134 	{"lt", 2, '<'},
135 	{"gt", 2, '>'}};
136 
~StrPair()137 StrPair::~StrPair()
138 {
139 	Reset();
140 }
141 
TransferTo(StrPair * other)142 void StrPair::TransferTo(StrPair* other)
143 {
144 	if (this == other)
145 	{
146 		return;
147 	}
148 	// This in effect implements the assignment operator by "moving"
149 	// ownership (as in auto_ptr).
150 
151 	TIXMLASSERT(other != 0);
152 	TIXMLASSERT(other->_flags == 0);
153 	TIXMLASSERT(other->_start == 0);
154 	TIXMLASSERT(other->_end == 0);
155 
156 	other->Reset();
157 
158 	other->_flags = _flags;
159 	other->_start = _start;
160 	other->_end = _end;
161 
162 	_flags = 0;
163 	_start = 0;
164 	_end = 0;
165 }
166 
Reset()167 void StrPair::Reset()
168 {
169 	if (_flags & NEEDS_DELETE)
170 	{
171 		delete[] _start;
172 	}
173 	_flags = 0;
174 	_start = 0;
175 	_end = 0;
176 }
177 
SetStr(const char * str,int flags)178 void StrPair::SetStr(const char* str, int flags)
179 {
180 	TIXMLASSERT(str);
181 	Reset();
182 	size_t len = strlen(str);
183 	TIXMLASSERT(_start == 0);
184 	_start = new char[len + 1];
185 	memcpy(_start, str, len + 1);
186 	_end = _start + len;
187 	_flags = flags | NEEDS_DELETE;
188 }
189 
ParseText(char * p,const char * endTag,int strFlags,int * curLineNumPtr)190 char* StrPair::ParseText(char* p, const char* endTag, int strFlags, int* curLineNumPtr)
191 {
192 	TIXMLASSERT(p);
193 	TIXMLASSERT(endTag && *endTag);
194 	TIXMLASSERT(curLineNumPtr);
195 
196 	char* start = p;
197 	char endChar = *endTag;
198 	size_t length = strlen(endTag);
199 
200 	// Inner loop of text parsing.
201 	while (*p)
202 	{
203 		if (*p == endChar && strncmp(p, endTag, length) == 0)
204 		{
205 			Set(start, p, strFlags);
206 			return p + length;
207 		}
208 		else if (*p == '\n')
209 		{
210 			++(*curLineNumPtr);
211 		}
212 		++p;
213 		TIXMLASSERT(p);
214 	}
215 	return 0;
216 }
217 
ParseName(char * p)218 char* StrPair::ParseName(char* p)
219 {
220 	if (!p || !(*p))
221 	{
222 		return 0;
223 	}
224 	if (!XMLUtil::IsNameStartChar(*p))
225 	{
226 		return 0;
227 	}
228 
229 	char* const start = p;
230 	++p;
231 	while (*p && XMLUtil::IsNameChar(*p))
232 	{
233 		++p;
234 	}
235 
236 	Set(start, p, 0);
237 	return p;
238 }
239 
CollapseWhitespace()240 void StrPair::CollapseWhitespace()
241 {
242 	// Adjusting _start would cause undefined behavior on delete[]
243 	TIXMLASSERT((_flags & NEEDS_DELETE) == 0);
244 	// Trim leading space.
245 	_start = XMLUtil::SkipWhiteSpace(_start, 0);
246 
247 	if (*_start)
248 	{
249 		const char* p = _start;  // the read pointer
250 		char* q = _start;        // the write pointer
251 
252 		while (*p)
253 		{
254 			if (XMLUtil::IsWhiteSpace(*p))
255 			{
256 				p = XMLUtil::SkipWhiteSpace(p, 0);
257 				if (*p == 0)
258 				{
259 					break;  // don't write to q; this trims the trailing space.
260 				}
261 				*q = ' ';
262 				++q;
263 			}
264 			*q = *p;
265 			++q;
266 			++p;
267 		}
268 		*q = 0;
269 	}
270 }
271 
GetStr()272 const char* StrPair::GetStr()
273 {
274 	TIXMLASSERT(_start);
275 	TIXMLASSERT(_end);
276 	if (_flags & NEEDS_FLUSH)
277 	{
278 		*_end = 0;
279 		_flags ^= NEEDS_FLUSH;
280 
281 		if (_flags)
282 		{
283 			const char* p = _start;  // the read pointer
284 			char* q = _start;        // the write pointer
285 
286 			while (p < _end)
287 			{
288 				if ((_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR)
289 				{
290 					// CR-LF pair becomes LF
291 					// CR alone becomes LF
292 					// LF-CR becomes LF
293 					if (*(p + 1) == LF)
294 					{
295 						p += 2;
296 					}
297 					else
298 					{
299 						++p;
300 					}
301 					*q = LF;
302 					++q;
303 				}
304 				else if ((_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF)
305 				{
306 					if (*(p + 1) == CR)
307 					{
308 						p += 2;
309 					}
310 					else
311 					{
312 						++p;
313 					}
314 					*q = LF;
315 					++q;
316 				}
317 				else if ((_flags & NEEDS_ENTITY_PROCESSING) && *p == '&')
318 				{
319 					// Entities handled by tinyXML2:
320 					// - special entities in the entity table [in/out]
321 					// - numeric character reference [in]
322 					//   &#20013; or &#x4e2d;
323 
324 					if (*(p + 1) == '#')
325 					{
326 						const int buflen = 10;
327 						char buf[buflen] = {0};
328 						int len = 0;
329 						char* adjusted = const_cast<char*>(XMLUtil::GetCharacterRef(p, buf, &len));
330 						if (adjusted == 0)
331 						{
332 							*q = *p;
333 							++p;
334 							++q;
335 						}
336 						else
337 						{
338 							TIXMLASSERT(0 <= len && len <= buflen);
339 							TIXMLASSERT(q + len <= adjusted);
340 							p = adjusted;
341 							memcpy(q, buf, len);
342 							q += len;
343 						}
344 					}
345 					else
346 					{
347 						bool entityFound = false;
348 						for (int i = 0; i < NUM_ENTITIES; ++i)
349 						{
350 							const Entity& entity = entities[i];
351 							if (strncmp(p + 1, entity.pattern, entity.length) == 0 && *(p + entity.length + 1) == ';')
352 							{
353 								// Found an entity - convert.
354 								*q = entity.value;
355 								++q;
356 								p += entity.length + 2;
357 								entityFound = true;
358 								break;
359 							}
360 						}
361 						if (!entityFound)
362 						{
363 							// fixme: treat as error?
364 							++p;
365 							++q;
366 						}
367 					}
368 				}
369 				else
370 				{
371 					*q = *p;
372 					++p;
373 					++q;
374 				}
375 			}
376 			*q = 0;
377 		}
378 		// The loop below has plenty going on, and this
379 		// is a less useful mode. Break it out.
380 		if (_flags & NEEDS_WHITESPACE_COLLAPSING)
381 		{
382 			CollapseWhitespace();
383 		}
384 		_flags = (_flags & NEEDS_DELETE);
385 	}
386 	TIXMLASSERT(_start);
387 	return _start;
388 }
389 
390 // --------- XMLUtil ----------- //
391 
392 const char* XMLUtil::writeBoolTrue = "true";
393 const char* XMLUtil::writeBoolFalse = "false";
394 
SetBoolSerialization(const char * writeTrue,const char * writeFalse)395 void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse)
396 {
397 	static const char* defTrue = "true";
398 	static const char* defFalse = "false";
399 
400 	writeBoolTrue = (writeTrue) ? writeTrue : defTrue;
401 	writeBoolFalse = (writeFalse) ? writeFalse : defFalse;
402 }
403 
ReadBOM(const char * p,bool * bom)404 const char* XMLUtil::ReadBOM(const char* p, bool* bom)
405 {
406 	TIXMLASSERT(p);
407 	TIXMLASSERT(bom);
408 	*bom = false;
409 	const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
410 	// Check for BOM:
411 	if (*(pu + 0) == TIXML_UTF_LEAD_0 && *(pu + 1) == TIXML_UTF_LEAD_1 && *(pu + 2) == TIXML_UTF_LEAD_2)
412 	{
413 		*bom = true;
414 		p += 3;
415 	}
416 	TIXMLASSERT(p);
417 	return p;
418 }
419 
ConvertUTF32ToUTF8(unsigned long input,char * output,int * length)420 void XMLUtil::ConvertUTF32ToUTF8(unsigned long input, char* output, int* length)
421 {
422 	const unsigned long BYTE_MASK = 0xBF;
423 	const unsigned long BYTE_MARK = 0x80;
424 	const unsigned long FIRST_BYTE_MARK[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC};
425 
426 	if (input < 0x80)
427 	{
428 		*length = 1;
429 	}
430 	else if (input < 0x800)
431 	{
432 		*length = 2;
433 	}
434 	else if (input < 0x10000)
435 	{
436 		*length = 3;
437 	}
438 	else if (input < 0x200000)
439 	{
440 		*length = 4;
441 	}
442 	else
443 	{
444 		*length = 0;  // This code won't convert this correctly anyway.
445 		return;
446 	}
447 
448 	output += *length;
449 
450 	// Scary scary fall throughs are annotated with carefully designed comments
451 	// to suppress compiler warnings such as -Wimplicit-fallthrough in gcc
452 	switch (*length)
453 	{
454 		case 4:
455 			--output;
456 			*output = (char)((input | BYTE_MARK) & BYTE_MASK);
457 			input >>= 6;
458 			//fall through
459 		case 3:
460 			--output;
461 			*output = (char)((input | BYTE_MARK) & BYTE_MASK);
462 			input >>= 6;
463 			//fall through
464 		case 2:
465 			--output;
466 			*output = (char)((input | BYTE_MARK) & BYTE_MASK);
467 			input >>= 6;
468 			//fall through
469 		case 1:
470 			--output;
471 			*output = (char)(input | FIRST_BYTE_MARK[*length]);
472 			break;
473 		default:
474 			TIXMLASSERT(false);
475 	}
476 }
477 
GetCharacterRef(const char * p,char * value,int * length)478 const char* XMLUtil::GetCharacterRef(const char* p, char* value, int* length)
479 {
480 	// Presume an entity, and pull it out.
481 	*length = 0;
482 
483 	if (*(p + 1) == '#' && *(p + 2))
484 	{
485 		unsigned long ucs = 0;
486 		TIXMLASSERT(sizeof(ucs) >= 4);
487 		ptrdiff_t delta = 0;
488 		unsigned mult = 1;
489 		static const char SEMICOLON = ';';
490 
491 		if (*(p + 2) == 'x')
492 		{
493 			// Hexadecimal.
494 			const char* q = p + 3;
495 			if (!(*q))
496 			{
497 				return 0;
498 			}
499 
500 			q = strchr(q, SEMICOLON);
501 
502 			if (!q)
503 			{
504 				return 0;
505 			}
506 			TIXMLASSERT(*q == SEMICOLON);
507 
508 			delta = q - p;
509 			--q;
510 
511 			while (*q != 'x')
512 			{
513 				unsigned int digit = 0;
514 
515 				if (*q >= '0' && *q <= '9')
516 				{
517 					digit = *q - '0';
518 				}
519 				else if (*q >= 'a' && *q <= 'f')
520 				{
521 					digit = *q - 'a' + 10;
522 				}
523 				else if (*q >= 'A' && *q <= 'F')
524 				{
525 					digit = *q - 'A' + 10;
526 				}
527 				else
528 				{
529 					return 0;
530 				}
531 				TIXMLASSERT(digit < 16);
532 				TIXMLASSERT(digit == 0 || mult <= UINT_MAX / digit);
533 				const unsigned int digitScaled = mult * digit;
534 				TIXMLASSERT(ucs <= ULONG_MAX - digitScaled);
535 				ucs += digitScaled;
536 				TIXMLASSERT(mult <= UINT_MAX / 16);
537 				mult *= 16;
538 				--q;
539 			}
540 		}
541 		else
542 		{
543 			// Decimal.
544 			const char* q = p + 2;
545 			if (!(*q))
546 			{
547 				return 0;
548 			}
549 
550 			q = strchr(q, SEMICOLON);
551 
552 			if (!q)
553 			{
554 				return 0;
555 			}
556 			TIXMLASSERT(*q == SEMICOLON);
557 
558 			delta = q - p;
559 			--q;
560 
561 			while (*q != '#')
562 			{
563 				if (*q >= '0' && *q <= '9')
564 				{
565 					const unsigned int digit = *q - '0';
566 					TIXMLASSERT(digit < 10);
567 					TIXMLASSERT(digit == 0 || mult <= UINT_MAX / digit);
568 					const unsigned int digitScaled = mult * digit;
569 					TIXMLASSERT(ucs <= ULONG_MAX - digitScaled);
570 					ucs += digitScaled;
571 				}
572 				else
573 				{
574 					return 0;
575 				}
576 				TIXMLASSERT(mult <= UINT_MAX / 10);
577 				mult *= 10;
578 				--q;
579 			}
580 		}
581 		// convert the UCS to UTF-8
582 		ConvertUTF32ToUTF8(ucs, value, length);
583 		return p + delta + 1;
584 	}
585 	return p + 1;
586 }
587 
ToStr(int v,char * buffer,int bufferSize)588 void XMLUtil::ToStr(int v, char* buffer, int bufferSize)
589 {
590 	TIXML_SNPRINTF(buffer, bufferSize, "%d", v);
591 }
592 
ToStr(unsigned v,char * buffer,int bufferSize)593 void XMLUtil::ToStr(unsigned v, char* buffer, int bufferSize)
594 {
595 	TIXML_SNPRINTF(buffer, bufferSize, "%u", v);
596 }
597 
ToStr(bool v,char * buffer,int bufferSize)598 void XMLUtil::ToStr(bool v, char* buffer, int bufferSize)
599 {
600 	TIXML_SNPRINTF(buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse);
601 }
602 
603 /*
604 	ToStr() of a number is a very tricky topic.
605 	https://github.com/leethomason/tinyxml2/issues/106
606 */
ToStr(float v,char * buffer,int bufferSize)607 void XMLUtil::ToStr(float v, char* buffer, int bufferSize)
608 {
609 	TIXML_SNPRINTF(buffer, bufferSize, "%.8g", v);
610 }
611 
ToStr(double v,char * buffer,int bufferSize)612 void XMLUtil::ToStr(double v, char* buffer, int bufferSize)
613 {
614 	TIXML_SNPRINTF(buffer, bufferSize, "%.17g", v);
615 }
616 
ToStr(xml_Int64a_t v,char * buffer,int bufferSize)617 void XMLUtil::ToStr(xml_Int64a_t v, char* buffer, int bufferSize)
618 {
619 	// horrible syntax trick to make the compiler happy about %lld
620 	TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v);
621 }
622 
ToInt(const char * str,int * value)623 bool XMLUtil::ToInt(const char* str, int* value)
624 {
625 	if (TIXML_SSCANF(str, "%d", value) == 1)
626 	{
627 		return true;
628 	}
629 	return false;
630 }
631 
ToUnsigned(const char * str,unsigned * value)632 bool XMLUtil::ToUnsigned(const char* str, unsigned* value)
633 {
634 	if (TIXML_SSCANF(str, "%u", value) == 1)
635 	{
636 		return true;
637 	}
638 	return false;
639 }
640 
ToBool(const char * str,bool * value)641 bool XMLUtil::ToBool(const char* str, bool* value)
642 {
643 	int ival = 0;
644 	if (ToInt(str, &ival))
645 	{
646 		*value = (ival == 0) ? false : true;
647 		return true;
648 	}
649 	if (StringEqual(str, "true"))
650 	{
651 		*value = true;
652 		return true;
653 	}
654 	else if (StringEqual(str, "false"))
655 	{
656 		*value = false;
657 		return true;
658 	}
659 	return false;
660 }
661 
ToFloat(const char * str,float * value)662 bool XMLUtil::ToFloat(const char* str, float* value)
663 {
664 	if (TIXML_SSCANF(str, "%f", value) == 1)
665 	{
666 		return true;
667 	}
668 	return false;
669 }
670 
ToDouble(const char * str,double * value)671 bool XMLUtil::ToDouble(const char* str, double* value)
672 {
673 	if (TIXML_SSCANF(str, "%lf", value) == 1)
674 	{
675 		return true;
676 	}
677 	return false;
678 }
679 
ToInt64(const char * str,xml_Int64a_t * value)680 bool XMLUtil::ToInt64(const char* str, xml_Int64a_t* value)
681 {
682 	long long v = 0;  // horrible syntax trick to make the compiler happy about %lld
683 	if (TIXML_SSCANF(str, "%lld", &v) == 1)
684 	{
685 		*value = (xml_Int64a_t)v;
686 		return true;
687 	}
688 	return false;
689 }
690 
Identify(char * p,XMLNode ** node)691 char* XMLDocument::Identify(char* p, XMLNode** node)
692 {
693 	TIXMLASSERT(node);
694 	TIXMLASSERT(p);
695 	char* const start = p;
696 	int const startLine = _parseCurLineNum;
697 	p = XMLUtil::SkipWhiteSpace(p, &_parseCurLineNum);
698 	if (!*p)
699 	{
700 		*node = 0;
701 		TIXMLASSERT(p);
702 		return p;
703 	}
704 
705 	// These strings define the matching patterns:
706 	static const char* xmlHeader = {"<?"};
707 	static const char* commentHeader = {"<!--"};
708 	static const char* cdataHeader = {"<![CDATA["};
709 	static const char* dtdHeader = {"<!"};
710 	static const char* elementHeader = {"<"};  // and a header for everything else; check last.
711 
712 	static const int xmlHeaderLen = 2;
713 	static const int commentHeaderLen = 4;
714 	static const int cdataHeaderLen = 9;
715 	static const int dtdHeaderLen = 2;
716 	static const int elementHeaderLen = 1;
717 
718 	TIXMLASSERT(sizeof(XMLComment) == sizeof(XMLUnknown));      // use same memory pool
719 	TIXMLASSERT(sizeof(XMLComment) == sizeof(XMLDeclaration));  // use same memory pool
720 	XMLNode* returnNode = 0;
721 	if (XMLUtil::StringEqual(p, xmlHeader, xmlHeaderLen))
722 	{
723 		returnNode = CreateUnlinkedNode<XMLDeclaration>(_commentPool);
724 		returnNode->_parseLineNum = _parseCurLineNum;
725 		p += xmlHeaderLen;
726 	}
727 	else if (XMLUtil::StringEqual(p, commentHeader, commentHeaderLen))
728 	{
729 		returnNode = CreateUnlinkedNode<XMLComment>(_commentPool);
730 		returnNode->_parseLineNum = _parseCurLineNum;
731 		p += commentHeaderLen;
732 	}
733 	else if (XMLUtil::StringEqual(p, cdataHeader, cdataHeaderLen))
734 	{
735 		XMLText* text = CreateUnlinkedNode<XMLText>(_textPool);
736 		returnNode = text;
737 		returnNode->_parseLineNum = _parseCurLineNum;
738 		p += cdataHeaderLen;
739 		text->SetCData(true);
740 	}
741 	else if (XMLUtil::StringEqual(p, dtdHeader, dtdHeaderLen))
742 	{
743 		returnNode = CreateUnlinkedNode<XMLUnknown>(_commentPool);
744 		returnNode->_parseLineNum = _parseCurLineNum;
745 		p += dtdHeaderLen;
746 	}
747 	else if (XMLUtil::StringEqual(p, elementHeader, elementHeaderLen))
748 	{
749 		returnNode = CreateUnlinkedNode<XMLElement>(_elementPool);
750 		returnNode->_parseLineNum = _parseCurLineNum;
751 		p += elementHeaderLen;
752 	}
753 	else
754 	{
755 		returnNode = CreateUnlinkedNode<XMLText>(_textPool);
756 		returnNode->_parseLineNum = _parseCurLineNum;  // Report line of first non-whitespace character
757 		p = start;                                     // Back it up, all the text counts.
758 		_parseCurLineNum = startLine;
759 	}
760 
761 	TIXMLASSERT(returnNode);
762 	TIXMLASSERT(p);
763 	*node = returnNode;
764 	return p;
765 }
766 
Accept(XMLVisitor * visitor) const767 bool XMLDocument::Accept(XMLVisitor* visitor) const
768 {
769 	TIXMLASSERT(visitor);
770 	if (visitor->VisitEnter(*this))
771 	{
772 		for (const XMLNode* node = FirstChild(); node; node = node->NextSibling())
773 		{
774 			if (!node->Accept(visitor))
775 			{
776 				break;
777 			}
778 		}
779 	}
780 	return visitor->VisitExit(*this);
781 }
782 
783 // --------- XMLNode ----------- //
784 
XMLNode(XMLDocument * doc)785 XMLNode::XMLNode(XMLDocument* doc) : _document(doc),
786 									 _parent(0),
787 									 _value(),
788 									 _parseLineNum(0),
789 									 _firstChild(0),
790 									 _lastChild(0),
791 									 _prev(0),
792 									 _next(0),
793 									 _userData(0),
794 									 _memPool(0)
795 {
796 }
797 
~XMLNode()798 XMLNode::~XMLNode()
799 {
800 	DeleteChildren();
801 	if (_parent)
802 	{
803 		_parent->Unlink(this);
804 	}
805 }
806 
Value() const807 const char* XMLNode::Value() const
808 {
809 	// Edge case: XMLDocuments don't have a Value. Return null.
810 	if (this->ToDocument())
811 		return 0;
812 	return _value.GetStr();
813 }
814 
SetValue(const char * str,bool staticMem)815 void XMLNode::SetValue(const char* str, bool staticMem)
816 {
817 	if (staticMem)
818 	{
819 		_value.SetInternedStr(str);
820 	}
821 	else
822 	{
823 		_value.SetStr(str);
824 	}
825 }
826 
DeepClone(XMLDocument * target) const827 XMLNode* XMLNode::DeepClone(XMLDocument* target) const
828 {
829 	XMLNode* clone = this->ShallowClone(target);
830 	if (!clone) return 0;
831 
832 	for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling())
833 	{
834 		XMLNode* childClone = child->DeepClone(target);
835 		TIXMLASSERT(childClone);
836 		clone->InsertEndChild(childClone);
837 	}
838 	return clone;
839 }
840 
DeleteChildren()841 void XMLNode::DeleteChildren()
842 {
843 	while (_firstChild)
844 	{
845 		TIXMLASSERT(_lastChild);
846 		DeleteChild(_firstChild);
847 	}
848 	_firstChild = _lastChild = 0;
849 }
850 
Unlink(XMLNode * child)851 void XMLNode::Unlink(XMLNode* child)
852 {
853 	TIXMLASSERT(child);
854 	TIXMLASSERT(child->_document == _document);
855 	TIXMLASSERT(child->_parent == this);
856 	if (child == _firstChild)
857 	{
858 		_firstChild = _firstChild->_next;
859 	}
860 	if (child == _lastChild)
861 	{
862 		_lastChild = _lastChild->_prev;
863 	}
864 
865 	if (child->_prev)
866 	{
867 		child->_prev->_next = child->_next;
868 	}
869 	if (child->_next)
870 	{
871 		child->_next->_prev = child->_prev;
872 	}
873 	child->_next = 0;
874 	child->_prev = 0;
875 	child->_parent = 0;
876 }
877 
DeleteChild(XMLNode * node)878 void XMLNode::DeleteChild(XMLNode* node)
879 {
880 	TIXMLASSERT(node);
881 	TIXMLASSERT(node->_document == _document);
882 	TIXMLASSERT(node->_parent == this);
883 	Unlink(node);
884 	TIXMLASSERT(node->_prev == 0);
885 	TIXMLASSERT(node->_next == 0);
886 	TIXMLASSERT(node->_parent == 0);
887 	DeleteNode(node);
888 }
889 
InsertEndChild(XMLNode * addThis)890 XMLNode* XMLNode::InsertEndChild(XMLNode* addThis)
891 {
892 	TIXMLASSERT(addThis);
893 	if (addThis->_document != _document)
894 	{
895 		TIXMLASSERT(false);
896 		return 0;
897 	}
898 	InsertChildPreamble(addThis);
899 
900 	if (_lastChild)
901 	{
902 		TIXMLASSERT(_firstChild);
903 		TIXMLASSERT(_lastChild->_next == 0);
904 		_lastChild->_next = addThis;
905 		addThis->_prev = _lastChild;
906 		_lastChild = addThis;
907 
908 		addThis->_next = 0;
909 	}
910 	else
911 	{
912 		TIXMLASSERT(_firstChild == 0);
913 		_firstChild = _lastChild = addThis;
914 
915 		addThis->_prev = 0;
916 		addThis->_next = 0;
917 	}
918 	addThis->_parent = this;
919 	return addThis;
920 }
921 
InsertFirstChild(XMLNode * addThis)922 XMLNode* XMLNode::InsertFirstChild(XMLNode* addThis)
923 {
924 	TIXMLASSERT(addThis);
925 	if (addThis->_document != _document)
926 	{
927 		TIXMLASSERT(false);
928 		return 0;
929 	}
930 	InsertChildPreamble(addThis);
931 
932 	if (_firstChild)
933 	{
934 		TIXMLASSERT(_lastChild);
935 		TIXMLASSERT(_firstChild->_prev == 0);
936 
937 		_firstChild->_prev = addThis;
938 		addThis->_next = _firstChild;
939 		_firstChild = addThis;
940 
941 		addThis->_prev = 0;
942 	}
943 	else
944 	{
945 		TIXMLASSERT(_lastChild == 0);
946 		_firstChild = _lastChild = addThis;
947 
948 		addThis->_prev = 0;
949 		addThis->_next = 0;
950 	}
951 	addThis->_parent = this;
952 	return addThis;
953 }
954 
InsertAfterChild(XMLNode * afterThis,XMLNode * addThis)955 XMLNode* XMLNode::InsertAfterChild(XMLNode* afterThis, XMLNode* addThis)
956 {
957 	TIXMLASSERT(addThis);
958 	if (addThis->_document != _document)
959 	{
960 		TIXMLASSERT(false);
961 		return 0;
962 	}
963 
964 	TIXMLASSERT(afterThis);
965 
966 	if (afterThis->_parent != this)
967 	{
968 		TIXMLASSERT(false);
969 		return 0;
970 	}
971 	if (afterThis == addThis)
972 	{
973 		// Current state: BeforeThis -> AddThis -> OneAfterAddThis
974 		// Now AddThis must disappear from it's location and then
975 		// reappear between BeforeThis and OneAfterAddThis.
976 		// So just leave it where it is.
977 		return addThis;
978 	}
979 
980 	if (afterThis->_next == 0)
981 	{
982 		// The last node or the only node.
983 		return InsertEndChild(addThis);
984 	}
985 	InsertChildPreamble(addThis);
986 	addThis->_prev = afterThis;
987 	addThis->_next = afterThis->_next;
988 	afterThis->_next->_prev = addThis;
989 	afterThis->_next = addThis;
990 	addThis->_parent = this;
991 	return addThis;
992 }
993 
FirstChildElement(const char * name) const994 const XMLElement* XMLNode::FirstChildElement(const char* name) const
995 {
996 	for (const XMLNode* node = _firstChild; node; node = node->_next)
997 	{
998 		const XMLElement* element = node->ToElementWithName(name);
999 		if (element)
1000 		{
1001 			return element;
1002 		}
1003 	}
1004 	return 0;
1005 }
1006 
LastChildElement(const char * name) const1007 const XMLElement* XMLNode::LastChildElement(const char* name) const
1008 {
1009 	for (const XMLNode* node = _lastChild; node; node = node->_prev)
1010 	{
1011 		const XMLElement* element = node->ToElementWithName(name);
1012 		if (element)
1013 		{
1014 			return element;
1015 		}
1016 	}
1017 	return 0;
1018 }
1019 
NextSiblingElement(const char * name) const1020 const XMLElement* XMLNode::NextSiblingElement(const char* name) const
1021 {
1022 	for (const XMLNode* node = _next; node; node = node->_next)
1023 	{
1024 		const XMLElement* element = node->ToElementWithName(name);
1025 		if (element)
1026 		{
1027 			return element;
1028 		}
1029 	}
1030 	return 0;
1031 }
1032 
PreviousSiblingElement(const char * name) const1033 const XMLElement* XMLNode::PreviousSiblingElement(const char* name) const
1034 {
1035 	for (const XMLNode* node = _prev; node; node = node->_prev)
1036 	{
1037 		const XMLElement* element = node->ToElementWithName(name);
1038 		if (element)
1039 		{
1040 			return element;
1041 		}
1042 	}
1043 	return 0;
1044 }
1045 
ParseDeep(char * p,StrPair * parentEndTag,int * curLineNumPtr)1046 char* XMLNode::ParseDeep(char* p, StrPair* parentEndTag, int* curLineNumPtr)
1047 {
1048 	// This is a recursive method, but thinking about it "at the current level"
1049 	// it is a pretty simple flat list:
1050 	//		<foo/>
1051 	//		<!-- comment -->
1052 	//
1053 	// With a special case:
1054 	//		<foo>
1055 	//		</foo>
1056 	//		<!-- comment -->
1057 	//
1058 	// Where the closing element (/foo) *must* be the next thing after the opening
1059 	// element, and the names must match. BUT the tricky bit is that the closing
1060 	// element will be read by the child.
1061 	//
1062 	// 'endTag' is the end tag for this node, it is returned by a call to a child.
1063 	// 'parentEnd' is the end tag for the parent, which is filled in and returned.
1064 
1065 	XMLDocument::DepthTracker tracker(_document);
1066 	if (_document->Error())
1067 		return 0;
1068 
1069 	while (p && *p)
1070 	{
1071 		XMLNode* node = 0;
1072 
1073 		p = _document->Identify(p, &node);
1074 		TIXMLASSERT(p);
1075 		if (node == 0)
1076 		{
1077 			break;
1078 		}
1079 
1080 		int initialLineNum = node->_parseLineNum;
1081 
1082 		StrPair endTag;
1083 		p = node->ParseDeep(p, &endTag, curLineNumPtr);
1084 		if (!p)
1085 		{
1086 			DeleteNode(node);
1087 			if (!_document->Error())
1088 			{
1089 				_document->SetError(XML_ERROR_PARSING, initialLineNum, 0);
1090 			}
1091 			break;
1092 		}
1093 
1094 		XMLDeclaration* decl = node->ToDeclaration();
1095 		if (decl)
1096 		{
1097 			// Declarations are only allowed at document level
1098 			bool wellLocated = (ToDocument() != 0);
1099 			if (wellLocated)
1100 			{
1101 				// Multiple declarations are allowed but all declarations
1102 				// must occur before anything else
1103 				for (const XMLNode* existingNode = _document->FirstChild(); existingNode; existingNode = existingNode->NextSibling())
1104 				{
1105 					if (!existingNode->ToDeclaration())
1106 					{
1107 						wellLocated = false;
1108 						break;
1109 					}
1110 				}
1111 			}
1112 			if (!wellLocated)
1113 			{
1114 				_document->SetError(XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value());
1115 				DeleteNode(node);
1116 				break;
1117 			}
1118 		}
1119 
1120 		XMLElement* ele = node->ToElement();
1121 		if (ele)
1122 		{
1123 			// We read the end tag. Return it to the parent.
1124 			if (ele->ClosingType() == XMLElement::CLOSING)
1125 			{
1126 				if (parentEndTag)
1127 				{
1128 					ele->_value.TransferTo(parentEndTag);
1129 				}
1130 				node->_memPool->SetTracked();  // created and then immediately deleted.
1131 				DeleteNode(node);
1132 				return p;
1133 			}
1134 
1135 			// Handle an end tag returned to this level.
1136 			// And handle a bunch of annoying errors.
1137 			bool mismatch = false;
1138 			if (endTag.Empty())
1139 			{
1140 				if (ele->ClosingType() == XMLElement::OPEN)
1141 				{
1142 					mismatch = true;
1143 				}
1144 			}
1145 			else
1146 			{
1147 				if (ele->ClosingType() != XMLElement::OPEN)
1148 				{
1149 					mismatch = true;
1150 				}
1151 				else if (!XMLUtil::StringEqual(endTag.GetStr(), ele->Name()))
1152 				{
1153 					mismatch = true;
1154 				}
1155 			}
1156 			if (mismatch)
1157 			{
1158 				_document->SetError(XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name());
1159 				DeleteNode(node);
1160 				break;
1161 			}
1162 		}
1163 		InsertEndChild(node);
1164 	}
1165 	return 0;
1166 }
1167 
DeleteNode(XMLNode * node)1168 /*static*/ void XMLNode::DeleteNode(XMLNode* node)
1169 {
1170 	if (node == 0)
1171 	{
1172 		return;
1173 	}
1174 	TIXMLASSERT(node->_document);
1175 	if (!node->ToDocument())
1176 	{
1177 		node->_document->MarkInUse(node);
1178 	}
1179 
1180 	MemPool* pool = node->_memPool;
1181 	node->~XMLNode();
1182 	pool->Free(node);
1183 }
1184 
InsertChildPreamble(XMLNode * insertThis) const1185 void XMLNode::InsertChildPreamble(XMLNode* insertThis) const
1186 {
1187 	TIXMLASSERT(insertThis);
1188 	TIXMLASSERT(insertThis->_document == _document);
1189 
1190 	if (insertThis->_parent)
1191 	{
1192 		insertThis->_parent->Unlink(insertThis);
1193 	}
1194 	else
1195 	{
1196 		insertThis->_document->MarkInUse(insertThis);
1197 		insertThis->_memPool->SetTracked();
1198 	}
1199 }
1200 
ToElementWithName(const char * name) const1201 const XMLElement* XMLNode::ToElementWithName(const char* name) const
1202 {
1203 	const XMLElement* element = this->ToElement();
1204 	if (element == 0)
1205 	{
1206 		return 0;
1207 	}
1208 	if (name == 0)
1209 	{
1210 		return element;
1211 	}
1212 	if (XMLUtil::StringEqual(element->Name(), name))
1213 	{
1214 		return element;
1215 	}
1216 	return 0;
1217 }
1218 
1219 // --------- XMLText ---------- //
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1220 char* XMLText::ParseDeep(char* p, StrPair*, int* curLineNumPtr)
1221 {
1222 	if (this->CData())
1223 	{
1224 		p = _value.ParseText(p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr);
1225 		if (!p)
1226 		{
1227 			_document->SetError(XML_ERROR_PARSING_CDATA, _parseLineNum, 0);
1228 		}
1229 		return p;
1230 	}
1231 	else
1232 	{
1233 		int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
1234 		if (_document->WhitespaceMode() == COLLAPSE_WHITESPACE)
1235 		{
1236 			flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
1237 		}
1238 
1239 		p = _value.ParseText(p, "<", flags, curLineNumPtr);
1240 		if (p && *p)
1241 		{
1242 			return p - 1;
1243 		}
1244 		if (!p)
1245 		{
1246 			_document->SetError(XML_ERROR_PARSING_TEXT, _parseLineNum, 0);
1247 		}
1248 	}
1249 	return 0;
1250 }
1251 
ShallowClone(XMLDocument * doc) const1252 XMLNode* XMLText::ShallowClone(XMLDocument* doc) const
1253 {
1254 	if (!doc)
1255 	{
1256 		doc = _document;
1257 	}
1258 	XMLText* text = doc->NewText(Value());  // fixme: this will always allocate memory. Intern?
1259 	text->SetCData(this->CData());
1260 	return text;
1261 }
1262 
ShallowEqual(const XMLNode * compare) const1263 bool XMLText::ShallowEqual(const XMLNode* compare) const
1264 {
1265 	TIXMLASSERT(compare);
1266 	const XMLText* text = compare->ToText();
1267 	return (text && XMLUtil::StringEqual(text->Value(), Value()));
1268 }
1269 
Accept(XMLVisitor * visitor) const1270 bool XMLText::Accept(XMLVisitor* visitor) const
1271 {
1272 	TIXMLASSERT(visitor);
1273 	return visitor->Visit(*this);
1274 }
1275 
1276 // --------- XMLComment ---------- //
1277 
XMLComment(XMLDocument * doc)1278 XMLComment::XMLComment(XMLDocument* doc) : XMLNode(doc)
1279 {
1280 }
1281 
~XMLComment()1282 XMLComment::~XMLComment()
1283 {
1284 }
1285 
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1286 char* XMLComment::ParseDeep(char* p, StrPair*, int* curLineNumPtr)
1287 {
1288 	// Comment parses as text.
1289 	p = _value.ParseText(p, "-->", StrPair::COMMENT, curLineNumPtr);
1290 	if (p == 0)
1291 	{
1292 		_document->SetError(XML_ERROR_PARSING_COMMENT, _parseLineNum, 0);
1293 	}
1294 	return p;
1295 }
1296 
ShallowClone(XMLDocument * doc) const1297 XMLNode* XMLComment::ShallowClone(XMLDocument* doc) const
1298 {
1299 	if (!doc)
1300 	{
1301 		doc = _document;
1302 	}
1303 	XMLComment* comment = doc->NewComment(Value());  // fixme: this will always allocate memory. Intern?
1304 	return comment;
1305 }
1306 
ShallowEqual(const XMLNode * compare) const1307 bool XMLComment::ShallowEqual(const XMLNode* compare) const
1308 {
1309 	TIXMLASSERT(compare);
1310 	const XMLComment* comment = compare->ToComment();
1311 	return (comment && XMLUtil::StringEqual(comment->Value(), Value()));
1312 }
1313 
Accept(XMLVisitor * visitor) const1314 bool XMLComment::Accept(XMLVisitor* visitor) const
1315 {
1316 	TIXMLASSERT(visitor);
1317 	return visitor->Visit(*this);
1318 }
1319 
1320 // --------- XMLDeclaration ---------- //
1321 
XMLDeclaration(XMLDocument * doc)1322 XMLDeclaration::XMLDeclaration(XMLDocument* doc) : XMLNode(doc)
1323 {
1324 }
1325 
~XMLDeclaration()1326 XMLDeclaration::~XMLDeclaration()
1327 {
1328 	//printf( "~XMLDeclaration\n" );
1329 }
1330 
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1331 char* XMLDeclaration::ParseDeep(char* p, StrPair*, int* curLineNumPtr)
1332 {
1333 	// Declaration parses as text.
1334 	p = _value.ParseText(p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr);
1335 	if (p == 0)
1336 	{
1337 		_document->SetError(XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0);
1338 	}
1339 	return p;
1340 }
1341 
ShallowClone(XMLDocument * doc) const1342 XMLNode* XMLDeclaration::ShallowClone(XMLDocument* doc) const
1343 {
1344 	if (!doc)
1345 	{
1346 		doc = _document;
1347 	}
1348 	XMLDeclaration* dec = doc->NewDeclaration(Value());  // fixme: this will always allocate memory. Intern?
1349 	return dec;
1350 }
1351 
ShallowEqual(const XMLNode * compare) const1352 bool XMLDeclaration::ShallowEqual(const XMLNode* compare) const
1353 {
1354 	TIXMLASSERT(compare);
1355 	const XMLDeclaration* declaration = compare->ToDeclaration();
1356 	return (declaration && XMLUtil::StringEqual(declaration->Value(), Value()));
1357 }
1358 
Accept(XMLVisitor * visitor) const1359 bool XMLDeclaration::Accept(XMLVisitor* visitor) const
1360 {
1361 	TIXMLASSERT(visitor);
1362 	return visitor->Visit(*this);
1363 }
1364 
1365 // --------- XMLUnknown ---------- //
1366 
XMLUnknown(XMLDocument * doc)1367 XMLUnknown::XMLUnknown(XMLDocument* doc) : XMLNode(doc)
1368 {
1369 }
1370 
~XMLUnknown()1371 XMLUnknown::~XMLUnknown()
1372 {
1373 }
1374 
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1375 char* XMLUnknown::ParseDeep(char* p, StrPair*, int* curLineNumPtr)
1376 {
1377 	// Unknown parses as text.
1378 	p = _value.ParseText(p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr);
1379 	if (!p)
1380 	{
1381 		_document->SetError(XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0);
1382 	}
1383 	return p;
1384 }
1385 
ShallowClone(XMLDocument * doc) const1386 XMLNode* XMLUnknown::ShallowClone(XMLDocument* doc) const
1387 {
1388 	if (!doc)
1389 	{
1390 		doc = _document;
1391 	}
1392 	XMLUnknown* text = doc->NewUnknown(Value());  // fixme: this will always allocate memory. Intern?
1393 	return text;
1394 }
1395 
ShallowEqual(const XMLNode * compare) const1396 bool XMLUnknown::ShallowEqual(const XMLNode* compare) const
1397 {
1398 	TIXMLASSERT(compare);
1399 	const XMLUnknown* unknown = compare->ToUnknown();
1400 	return (unknown && XMLUtil::StringEqual(unknown->Value(), Value()));
1401 }
1402 
Accept(XMLVisitor * visitor) const1403 bool XMLUnknown::Accept(XMLVisitor* visitor) const
1404 {
1405 	TIXMLASSERT(visitor);
1406 	return visitor->Visit(*this);
1407 }
1408 
1409 // --------- XMLAttribute ---------- //
1410 
Name() const1411 const char* XMLAttribute::Name() const
1412 {
1413 	return _name.GetStr();
1414 }
1415 
Value() const1416 const char* XMLAttribute::Value() const
1417 {
1418 	return _value.GetStr();
1419 }
1420 
ParseDeep(char * p,bool processEntities,int * curLineNumPtr)1421 char* XMLAttribute::ParseDeep(char* p, bool processEntities, int* curLineNumPtr)
1422 {
1423 	// Parse using the name rules: bug fix, was using ParseText before
1424 	p = _name.ParseName(p);
1425 	if (!p || !*p)
1426 	{
1427 		return 0;
1428 	}
1429 
1430 	// Skip white space before =
1431 	p = XMLUtil::SkipWhiteSpace(p, curLineNumPtr);
1432 	if (*p != '=')
1433 	{
1434 		return 0;
1435 	}
1436 
1437 	++p;  // move up to opening quote
1438 	p = XMLUtil::SkipWhiteSpace(p, curLineNumPtr);
1439 	if (*p != '\"' && *p != '\'')
1440 	{
1441 		return 0;
1442 	}
1443 
1444 	char endTag[2] = {*p, 0};
1445 	++p;  // move past opening quote
1446 
1447 	p = _value.ParseText(p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr);
1448 	return p;
1449 }
1450 
SetName(const char * n)1451 void XMLAttribute::SetName(const char* n)
1452 {
1453 	_name.SetStr(n);
1454 }
1455 
QueryIntValue(int * value) const1456 XMLError XMLAttribute::QueryIntValue(int* value) const
1457 {
1458 	if (XMLUtil::ToInt(Value(), value))
1459 	{
1460 		return XML_SUCCESS;
1461 	}
1462 	return XML_WRONG_ATTRIBUTE_TYPE;
1463 }
1464 
QueryUnsignedValue(unsigned int * value) const1465 XMLError XMLAttribute::QueryUnsignedValue(unsigned int* value) const
1466 {
1467 	if (XMLUtil::ToUnsigned(Value(), value))
1468 	{
1469 		return XML_SUCCESS;
1470 	}
1471 	return XML_WRONG_ATTRIBUTE_TYPE;
1472 }
1473 
QueryInt64Value(xml_Int64a_t * value) const1474 XMLError XMLAttribute::QueryInt64Value(xml_Int64a_t* value) const
1475 {
1476 	if (XMLUtil::ToInt64(Value(), value))
1477 	{
1478 		return XML_SUCCESS;
1479 	}
1480 	return XML_WRONG_ATTRIBUTE_TYPE;
1481 }
1482 
QueryBoolValue(bool * value) const1483 XMLError XMLAttribute::QueryBoolValue(bool* value) const
1484 {
1485 	if (XMLUtil::ToBool(Value(), value))
1486 	{
1487 		return XML_SUCCESS;
1488 	}
1489 	return XML_WRONG_ATTRIBUTE_TYPE;
1490 }
1491 
QueryFloatValue(float * value) const1492 XMLError XMLAttribute::QueryFloatValue(float* value) const
1493 {
1494 	if (XMLUtil::ToFloat(Value(), value))
1495 	{
1496 		return XML_SUCCESS;
1497 	}
1498 	return XML_WRONG_ATTRIBUTE_TYPE;
1499 }
1500 
QueryDoubleValue(double * value) const1501 XMLError XMLAttribute::QueryDoubleValue(double* value) const
1502 {
1503 	if (XMLUtil::ToDouble(Value(), value))
1504 	{
1505 		return XML_SUCCESS;
1506 	}
1507 	return XML_WRONG_ATTRIBUTE_TYPE;
1508 }
1509 
SetAttribute(const char * v)1510 void XMLAttribute::SetAttribute(const char* v)
1511 {
1512 	_value.SetStr(v);
1513 }
1514 
SetAttribute(int v)1515 void XMLAttribute::SetAttribute(int v)
1516 {
1517 	char buf[BUF_SIZE];
1518 	XMLUtil::ToStr(v, buf, BUF_SIZE);
1519 	_value.SetStr(buf);
1520 }
1521 
SetAttribute(unsigned v)1522 void XMLAttribute::SetAttribute(unsigned v)
1523 {
1524 	char buf[BUF_SIZE];
1525 	XMLUtil::ToStr(v, buf, BUF_SIZE);
1526 	_value.SetStr(buf);
1527 }
1528 
SetAttribute(xml_Int64a_t v)1529 void XMLAttribute::SetAttribute(xml_Int64a_t v)
1530 {
1531 	char buf[BUF_SIZE];
1532 	XMLUtil::ToStr(v, buf, BUF_SIZE);
1533 	_value.SetStr(buf);
1534 }
1535 
SetAttribute(bool v)1536 void XMLAttribute::SetAttribute(bool v)
1537 {
1538 	char buf[BUF_SIZE];
1539 	XMLUtil::ToStr(v, buf, BUF_SIZE);
1540 	_value.SetStr(buf);
1541 }
1542 
SetAttribute(double v)1543 void XMLAttribute::SetAttribute(double v)
1544 {
1545 	char buf[BUF_SIZE];
1546 	XMLUtil::ToStr(v, buf, BUF_SIZE);
1547 	_value.SetStr(buf);
1548 }
1549 
SetAttribute(float v)1550 void XMLAttribute::SetAttribute(float v)
1551 {
1552 	char buf[BUF_SIZE];
1553 	XMLUtil::ToStr(v, buf, BUF_SIZE);
1554 	_value.SetStr(buf);
1555 }
1556 
1557 // --------- XMLElement ---------- //
XMLElement(XMLDocument * doc)1558 XMLElement::XMLElement(XMLDocument* doc) : XMLNode(doc),
1559 										   _closingType(OPEN),
1560 										   _rootAttribute(0)
1561 {
1562 }
1563 
~XMLElement()1564 XMLElement::~XMLElement()
1565 {
1566 	while (_rootAttribute)
1567 	{
1568 		XMLAttribute* next = _rootAttribute->_next;
1569 		DeleteAttribute(_rootAttribute);
1570 		_rootAttribute = next;
1571 	}
1572 }
1573 
FindAttribute(const char * name) const1574 const XMLAttribute* XMLElement::FindAttribute(const char* name) const
1575 {
1576 	for (XMLAttribute* a = _rootAttribute; a; a = a->_next)
1577 	{
1578 		if (XMLUtil::StringEqual(a->Name(), name))
1579 		{
1580 			return a;
1581 		}
1582 	}
1583 	return 0;
1584 }
1585 
Attribute(const char * name,const char * value) const1586 const char* XMLElement::Attribute(const char* name, const char* value) const
1587 {
1588 	const XMLAttribute* a = FindAttribute(name);
1589 	if (!a)
1590 	{
1591 		return 0;
1592 	}
1593 	if (!value || XMLUtil::StringEqual(a->Value(), value))
1594 	{
1595 		return a->Value();
1596 	}
1597 	return 0;
1598 }
1599 
IntAttribute(const char * name,int defaultValue) const1600 int XMLElement::IntAttribute(const char* name, int defaultValue) const
1601 {
1602 	int i = defaultValue;
1603 	QueryIntAttribute(name, &i);
1604 	return i;
1605 }
1606 
UnsignedAttribute(const char * name,unsigned defaultValue) const1607 unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const
1608 {
1609 	unsigned i = defaultValue;
1610 	QueryUnsignedAttribute(name, &i);
1611 	return i;
1612 }
1613 
Int64Attribute(const char * name,xml_Int64a_t defaultValue) const1614 xml_Int64a_t XMLElement::Int64Attribute(const char* name, xml_Int64a_t defaultValue) const
1615 {
1616 	xml_Int64a_t i = defaultValue;
1617 	QueryInt64Attribute(name, &i);
1618 	return i;
1619 }
1620 
BoolAttribute(const char * name,bool defaultValue) const1621 bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const
1622 {
1623 	bool b = defaultValue;
1624 	QueryBoolAttribute(name, &b);
1625 	return b;
1626 }
1627 
DoubleAttribute(const char * name,double defaultValue) const1628 double XMLElement::DoubleAttribute(const char* name, double defaultValue) const
1629 {
1630 	double d = defaultValue;
1631 	QueryDoubleAttribute(name, &d);
1632 	return d;
1633 }
1634 
FloatAttribute(const char * name,float defaultValue) const1635 float XMLElement::FloatAttribute(const char* name, float defaultValue) const
1636 {
1637 	float f = defaultValue;
1638 	QueryFloatAttribute(name, &f);
1639 	return f;
1640 }
1641 
GetText() const1642 const char* XMLElement::GetText() const
1643 {
1644 	if (FirstChild() && FirstChild()->ToText())
1645 	{
1646 		return FirstChild()->Value();
1647 	}
1648 	return 0;
1649 }
1650 
SetText(const char * inText)1651 void XMLElement::SetText(const char* inText)
1652 {
1653 	if (FirstChild() && FirstChild()->ToText())
1654 		FirstChild()->SetValue(inText);
1655 	else
1656 	{
1657 		XMLText* theText = GetDocument()->NewText(inText);
1658 		InsertFirstChild(theText);
1659 	}
1660 }
1661 
SetText(int v)1662 void XMLElement::SetText(int v)
1663 {
1664 	char buf[BUF_SIZE];
1665 	XMLUtil::ToStr(v, buf, BUF_SIZE);
1666 	SetText(buf);
1667 }
1668 
SetText(unsigned v)1669 void XMLElement::SetText(unsigned v)
1670 {
1671 	char buf[BUF_SIZE];
1672 	XMLUtil::ToStr(v, buf, BUF_SIZE);
1673 	SetText(buf);
1674 }
1675 
SetText(xml_Int64a_t v)1676 void XMLElement::SetText(xml_Int64a_t v)
1677 {
1678 	char buf[BUF_SIZE];
1679 	XMLUtil::ToStr(v, buf, BUF_SIZE);
1680 	SetText(buf);
1681 }
1682 
SetText(bool v)1683 void XMLElement::SetText(bool v)
1684 {
1685 	char buf[BUF_SIZE];
1686 	XMLUtil::ToStr(v, buf, BUF_SIZE);
1687 	SetText(buf);
1688 }
1689 
SetText(float v)1690 void XMLElement::SetText(float v)
1691 {
1692 	char buf[BUF_SIZE];
1693 	XMLUtil::ToStr(v, buf, BUF_SIZE);
1694 	SetText(buf);
1695 }
1696 
SetText(double v)1697 void XMLElement::SetText(double v)
1698 {
1699 	char buf[BUF_SIZE];
1700 	XMLUtil::ToStr(v, buf, BUF_SIZE);
1701 	SetText(buf);
1702 }
1703 
QueryIntText(int * ival) const1704 XMLError XMLElement::QueryIntText(int* ival) const
1705 {
1706 	if (FirstChild() && FirstChild()->ToText())
1707 	{
1708 		const char* t = FirstChild()->Value();
1709 		if (XMLUtil::ToInt(t, ival))
1710 		{
1711 			return XML_SUCCESS;
1712 		}
1713 		return XML_CAN_NOT_CONVERT_TEXT;
1714 	}
1715 	return XML_NO_TEXT_NODE;
1716 }
1717 
QueryUnsignedText(unsigned * uval) const1718 XMLError XMLElement::QueryUnsignedText(unsigned* uval) const
1719 {
1720 	if (FirstChild() && FirstChild()->ToText())
1721 	{
1722 		const char* t = FirstChild()->Value();
1723 		if (XMLUtil::ToUnsigned(t, uval))
1724 		{
1725 			return XML_SUCCESS;
1726 		}
1727 		return XML_CAN_NOT_CONVERT_TEXT;
1728 	}
1729 	return XML_NO_TEXT_NODE;
1730 }
1731 
QueryInt64Text(xml_Int64a_t * ival) const1732 XMLError XMLElement::QueryInt64Text(xml_Int64a_t* ival) const
1733 {
1734 	if (FirstChild() && FirstChild()->ToText())
1735 	{
1736 		const char* t = FirstChild()->Value();
1737 		if (XMLUtil::ToInt64(t, ival))
1738 		{
1739 			return XML_SUCCESS;
1740 		}
1741 		return XML_CAN_NOT_CONVERT_TEXT;
1742 	}
1743 	return XML_NO_TEXT_NODE;
1744 }
1745 
QueryBoolText(bool * bval) const1746 XMLError XMLElement::QueryBoolText(bool* bval) const
1747 {
1748 	if (FirstChild() && FirstChild()->ToText())
1749 	{
1750 		const char* t = FirstChild()->Value();
1751 		if (XMLUtil::ToBool(t, bval))
1752 		{
1753 			return XML_SUCCESS;
1754 		}
1755 		return XML_CAN_NOT_CONVERT_TEXT;
1756 	}
1757 	return XML_NO_TEXT_NODE;
1758 }
1759 
QueryDoubleText(double * dval) const1760 XMLError XMLElement::QueryDoubleText(double* dval) const
1761 {
1762 	if (FirstChild() && FirstChild()->ToText())
1763 	{
1764 		const char* t = FirstChild()->Value();
1765 		if (XMLUtil::ToDouble(t, dval))
1766 		{
1767 			return XML_SUCCESS;
1768 		}
1769 		return XML_CAN_NOT_CONVERT_TEXT;
1770 	}
1771 	return XML_NO_TEXT_NODE;
1772 }
1773 
QueryFloatText(float * fval) const1774 XMLError XMLElement::QueryFloatText(float* fval) const
1775 {
1776 	if (FirstChild() && FirstChild()->ToText())
1777 	{
1778 		const char* t = FirstChild()->Value();
1779 		if (XMLUtil::ToFloat(t, fval))
1780 		{
1781 			return XML_SUCCESS;
1782 		}
1783 		return XML_CAN_NOT_CONVERT_TEXT;
1784 	}
1785 	return XML_NO_TEXT_NODE;
1786 }
1787 
IntText(int defaultValue) const1788 int XMLElement::IntText(int defaultValue) const
1789 {
1790 	int i = defaultValue;
1791 	QueryIntText(&i);
1792 	return i;
1793 }
1794 
UnsignedText(unsigned defaultValue) const1795 unsigned XMLElement::UnsignedText(unsigned defaultValue) const
1796 {
1797 	unsigned i = defaultValue;
1798 	QueryUnsignedText(&i);
1799 	return i;
1800 }
1801 
Int64Text(xml_Int64a_t defaultValue) const1802 xml_Int64a_t XMLElement::Int64Text(xml_Int64a_t defaultValue) const
1803 {
1804 	xml_Int64a_t i = defaultValue;
1805 	QueryInt64Text(&i);
1806 	return i;
1807 }
1808 
BoolText(bool defaultValue) const1809 bool XMLElement::BoolText(bool defaultValue) const
1810 {
1811 	bool b = defaultValue;
1812 	QueryBoolText(&b);
1813 	return b;
1814 }
1815 
DoubleText(double defaultValue) const1816 double XMLElement::DoubleText(double defaultValue) const
1817 {
1818 	double d = defaultValue;
1819 	QueryDoubleText(&d);
1820 	return d;
1821 }
1822 
FloatText(float defaultValue) const1823 float XMLElement::FloatText(float defaultValue) const
1824 {
1825 	float f = defaultValue;
1826 	QueryFloatText(&f);
1827 	return f;
1828 }
1829 
FindOrCreateAttribute(const char * name)1830 XMLAttribute* XMLElement::FindOrCreateAttribute(const char* name)
1831 {
1832 	XMLAttribute* last = 0;
1833 	XMLAttribute* attrib = 0;
1834 	for (attrib = _rootAttribute;
1835 		 attrib;
1836 		 last = attrib, attrib = attrib->_next)
1837 	{
1838 		if (XMLUtil::StringEqual(attrib->Name(), name))
1839 		{
1840 			break;
1841 		}
1842 	}
1843 	if (!attrib)
1844 	{
1845 		attrib = CreateAttribute();
1846 		TIXMLASSERT(attrib);
1847 		if (last)
1848 		{
1849 			TIXMLASSERT(last->_next == 0);
1850 			last->_next = attrib;
1851 		}
1852 		else
1853 		{
1854 			TIXMLASSERT(_rootAttribute == 0);
1855 			_rootAttribute = attrib;
1856 		}
1857 		attrib->SetName(name);
1858 	}
1859 	return attrib;
1860 }
1861 
DeleteAttribute(const char * name)1862 void XMLElement::DeleteAttribute(const char* name)
1863 {
1864 	XMLAttribute* prev = 0;
1865 	for (XMLAttribute* a = _rootAttribute; a; a = a->_next)
1866 	{
1867 		if (XMLUtil::StringEqual(name, a->Name()))
1868 		{
1869 			if (prev)
1870 			{
1871 				prev->_next = a->_next;
1872 			}
1873 			else
1874 			{
1875 				_rootAttribute = a->_next;
1876 			}
1877 			DeleteAttribute(a);
1878 			break;
1879 		}
1880 		prev = a;
1881 	}
1882 }
1883 
ParseAttributes(char * p,int * curLineNumPtr)1884 char* XMLElement::ParseAttributes(char* p, int* curLineNumPtr)
1885 {
1886 	XMLAttribute* prevAttribute = 0;
1887 
1888 	// Read the attributes.
1889 	while (p)
1890 	{
1891 		p = XMLUtil::SkipWhiteSpace(p, curLineNumPtr);
1892 		if (!(*p))
1893 		{
1894 			_document->SetError(XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name());
1895 			return 0;
1896 		}
1897 
1898 		// attribute.
1899 		if (XMLUtil::IsNameStartChar(*p))
1900 		{
1901 			XMLAttribute* attrib = CreateAttribute();
1902 			TIXMLASSERT(attrib);
1903 			attrib->_parseLineNum = _document->_parseCurLineNum;
1904 
1905 			int attrLineNum = attrib->_parseLineNum;
1906 
1907 			p = attrib->ParseDeep(p, _document->ProcessEntities(), curLineNumPtr);
1908 			if (!p || Attribute(attrib->Name()))
1909 			{
1910 				DeleteAttribute(attrib);
1911 				_document->SetError(XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name());
1912 				return 0;
1913 			}
1914 			// There is a minor bug here: if the attribute in the source xml
1915 			// document is duplicated, it will not be detected and the
1916 			// attribute will be doubly added. However, tracking the 'prevAttribute'
1917 			// avoids re-scanning the attribute list. Preferring performance for
1918 			// now, may reconsider in the future.
1919 			if (prevAttribute)
1920 			{
1921 				TIXMLASSERT(prevAttribute->_next == 0);
1922 				prevAttribute->_next = attrib;
1923 			}
1924 			else
1925 			{
1926 				TIXMLASSERT(_rootAttribute == 0);
1927 				_rootAttribute = attrib;
1928 			}
1929 			prevAttribute = attrib;
1930 		}
1931 		// end of the tag
1932 		else if (*p == '>')
1933 		{
1934 			++p;
1935 			break;
1936 		}
1937 		// end of the tag
1938 		else if (*p == '/' && *(p + 1) == '>')
1939 		{
1940 			_closingType = CLOSED;
1941 			return p + 2;  // done; sealed element.
1942 		}
1943 		else
1944 		{
1945 			_document->SetError(XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0);
1946 			return 0;
1947 		}
1948 	}
1949 	return p;
1950 }
1951 
DeleteAttribute(XMLAttribute * attribute)1952 void XMLElement::DeleteAttribute(XMLAttribute* attribute)
1953 {
1954 	if (attribute == 0)
1955 	{
1956 		return;
1957 	}
1958 	MemPool* pool = attribute->_memPool;
1959 	attribute->~XMLAttribute();
1960 	pool->Free(attribute);
1961 }
1962 
CreateAttribute()1963 XMLAttribute* XMLElement::CreateAttribute()
1964 {
1965 	TIXMLASSERT(sizeof(XMLAttribute) == _document->_attributePool.ItemSize());
1966 	XMLAttribute* attrib = new (_document->_attributePool.Alloc()) XMLAttribute();
1967 	TIXMLASSERT(attrib);
1968 	attrib->_memPool = &_document->_attributePool;
1969 	attrib->_memPool->SetTracked();
1970 	return attrib;
1971 }
1972 
1973 //
1974 //	<ele></ele>
1975 //	<ele>foo<b>bar</b></ele>
1976 //
ParseDeep(char * p,StrPair * parentEndTag,int * curLineNumPtr)1977 char* XMLElement::ParseDeep(char* p, StrPair* parentEndTag, int* curLineNumPtr)
1978 {
1979 	// Read the element name.
1980 	p = XMLUtil::SkipWhiteSpace(p, curLineNumPtr);
1981 
1982 	// The closing element is the </element> form. It is
1983 	// parsed just like a regular element then deleted from
1984 	// the DOM.
1985 	if (*p == '/')
1986 	{
1987 		_closingType = CLOSING;
1988 		++p;
1989 	}
1990 
1991 	p = _value.ParseName(p);
1992 	if (_value.Empty())
1993 	{
1994 		return 0;
1995 	}
1996 
1997 	p = ParseAttributes(p, curLineNumPtr);
1998 	if (!p || !*p || _closingType != OPEN)
1999 	{
2000 		return p;
2001 	}
2002 
2003 	p = XMLNode::ParseDeep(p, parentEndTag, curLineNumPtr);
2004 	return p;
2005 }
2006 
ShallowClone(XMLDocument * doc) const2007 XMLNode* XMLElement::ShallowClone(XMLDocument* doc) const
2008 {
2009 	if (!doc)
2010 	{
2011 		doc = _document;
2012 	}
2013 	XMLElement* element = doc->NewElement(Value());  // fixme: this will always allocate memory. Intern?
2014 	for (const XMLAttribute* a = FirstAttribute(); a; a = a->Next())
2015 	{
2016 		element->SetAttribute(a->Name(), a->Value());  // fixme: this will always allocate memory. Intern?
2017 	}
2018 	return element;
2019 }
2020 
ShallowEqual(const XMLNode * compare) const2021 bool XMLElement::ShallowEqual(const XMLNode* compare) const
2022 {
2023 	TIXMLASSERT(compare);
2024 	const XMLElement* other = compare->ToElement();
2025 	if (other && XMLUtil::StringEqual(other->Name(), Name()))
2026 	{
2027 		const XMLAttribute* a = FirstAttribute();
2028 		const XMLAttribute* b = other->FirstAttribute();
2029 
2030 		while (a && b)
2031 		{
2032 			if (!XMLUtil::StringEqual(a->Value(), b->Value()))
2033 			{
2034 				return false;
2035 			}
2036 			a = a->Next();
2037 			b = b->Next();
2038 		}
2039 		if (a || b)
2040 		{
2041 			// different count
2042 			return false;
2043 		}
2044 		return true;
2045 	}
2046 	return false;
2047 }
2048 
Accept(XMLVisitor * visitor) const2049 bool XMLElement::Accept(XMLVisitor* visitor) const
2050 {
2051 	TIXMLASSERT(visitor);
2052 	if (visitor->VisitEnter(*this, _rootAttribute))
2053 	{
2054 		for (const XMLNode* node = FirstChild(); node; node = node->NextSibling())
2055 		{
2056 			if (!node->Accept(visitor))
2057 			{
2058 				break;
2059 			}
2060 		}
2061 	}
2062 	return visitor->VisitExit(*this);
2063 }
2064 
2065 // --------- XMLDocument ----------- //
2066 
2067 // Warning: List must match 'enum XMLError'
2068 const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
2069 	"XML_SUCCESS",
2070 	"XML_NO_ATTRIBUTE",
2071 	"XML_WRONG_ATTRIBUTE_TYPE",
2072 	"XML_ERROR_FILE_NOT_FOUND",
2073 	"XML_ERROR_FILE_COULD_NOT_BE_OPENED",
2074 	"XML_ERROR_FILE_READ_ERROR",
2075 	"UNUSED_XML_ERROR_ELEMENT_MISMATCH",
2076 	"XML_ERROR_PARSING_ELEMENT",
2077 	"XML_ERROR_PARSING_ATTRIBUTE",
2078 	"UNUSED_XML_ERROR_IDENTIFYING_TAG",
2079 	"XML_ERROR_PARSING_TEXT",
2080 	"XML_ERROR_PARSING_CDATA",
2081 	"XML_ERROR_PARSING_COMMENT",
2082 	"XML_ERROR_PARSING_DECLARATION",
2083 	"XML_ERROR_PARSING_UNKNOWN",
2084 	"XML_ERROR_EMPTY_DOCUMENT",
2085 	"XML_ERROR_MISMATCHED_ELEMENT",
2086 	"XML_ERROR_PARSING",
2087 	"XML_CAN_NOT_CONVERT_TEXT",
2088 	"XML_NO_TEXT_NODE",
2089 	"XML_ELEMENT_DEPTH_EXCEEDED"};
2090 
XMLDocument(bool processEntities,Whitespace whitespaceMode)2091 XMLDocument::XMLDocument(bool processEntities, Whitespace whitespaceMode) : XMLNode(0),
2092 																			_writeBOM(false),
2093 																			_processEntities(processEntities),
2094 																			_errorID(XML_SUCCESS),
2095 																			_whitespaceMode(whitespaceMode),
2096 																			_errorStr(),
2097 																			_errorLineNum(0),
2098 																			_charBuffer(0),
2099 																			_parseCurLineNum(0),
2100 																			_parsingDepth(0),
2101 																			_unlinked(),
2102 																			_elementPool(),
2103 																			_attributePool(),
2104 																			_textPool(),
2105 																			_commentPool()
2106 {
2107 	// avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
2108 	_document = this;
2109 }
2110 
~XMLDocument()2111 XMLDocument::~XMLDocument()
2112 {
2113 	Clear();
2114 }
2115 
MarkInUse(XMLNode * node)2116 void XMLDocument::MarkInUse(XMLNode* node)
2117 {
2118 	TIXMLASSERT(node);
2119 	TIXMLASSERT(node->_parent == 0);
2120 
2121 	for (int i = 0; i < _unlinked.Size(); ++i)
2122 	{
2123 		if (node == _unlinked[i])
2124 		{
2125 			_unlinked.SwapRemove(i);
2126 			break;
2127 		}
2128 	}
2129 }
2130 
Clear()2131 void XMLDocument::Clear()
2132 {
2133 	DeleteChildren();
2134 	while (_unlinked.Size())
2135 	{
2136 		DeleteNode(_unlinked[0]);  // Will remove from _unlinked as part of delete.
2137 	}
2138 
2139 #ifdef TINYXML2_DEBUG
2140 	const bool hadError = Error();
2141 #endif
2142 	ClearError();
2143 
2144 	delete[] _charBuffer;
2145 	_charBuffer = 0;
2146 	_parsingDepth = 0;
2147 
2148 #if 0
2149     _textPool.Trace( "text" );
2150     _elementPool.Trace( "element" );
2151     _commentPool.Trace( "comment" );
2152     _attributePool.Trace( "attribute" );
2153 #endif
2154 
2155 #ifdef TINYXML2_DEBUG
2156 	if (!hadError)
2157 	{
2158 		TIXMLASSERT(_elementPool.CurrentAllocs() == _elementPool.Untracked());
2159 		TIXMLASSERT(_attributePool.CurrentAllocs() == _attributePool.Untracked());
2160 		TIXMLASSERT(_textPool.CurrentAllocs() == _textPool.Untracked());
2161 		TIXMLASSERT(_commentPool.CurrentAllocs() == _commentPool.Untracked());
2162 	}
2163 #endif
2164 }
2165 
DeepCopy(XMLDocument * target) const2166 void XMLDocument::DeepCopy(XMLDocument* target) const
2167 {
2168 	TIXMLASSERT(target);
2169 	if (target == this)
2170 	{
2171 		return;  // technically success - a no-op.
2172 	}
2173 
2174 	target->Clear();
2175 	for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling())
2176 	{
2177 		target->InsertEndChild(node->DeepClone(target));
2178 	}
2179 }
2180 
NewElement(const char * name)2181 XMLElement* XMLDocument::NewElement(const char* name)
2182 {
2183 	XMLElement* ele = CreateUnlinkedNode<XMLElement>(_elementPool);
2184 	ele->SetName(name);
2185 	return ele;
2186 }
2187 
NewComment(const char * str)2188 XMLComment* XMLDocument::NewComment(const char* str)
2189 {
2190 	XMLComment* comment = CreateUnlinkedNode<XMLComment>(_commentPool);
2191 	comment->SetValue(str);
2192 	return comment;
2193 }
2194 
NewText(const char * str)2195 XMLText* XMLDocument::NewText(const char* str)
2196 {
2197 	XMLText* text = CreateUnlinkedNode<XMLText>(_textPool);
2198 	text->SetValue(str);
2199 	return text;
2200 }
2201 
NewDeclaration(const char * str)2202 XMLDeclaration* XMLDocument::NewDeclaration(const char* str)
2203 {
2204 	XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>(_commentPool);
2205 	dec->SetValue(str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"");
2206 	return dec;
2207 }
2208 
NewUnknown(const char * str)2209 XMLUnknown* XMLDocument::NewUnknown(const char* str)
2210 {
2211 	XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>(_commentPool);
2212 	unk->SetValue(str);
2213 	return unk;
2214 }
2215 
callfopen(const char * filepath,const char * mode)2216 static FILE* callfopen(const char* filepath, const char* mode)
2217 {
2218 	TIXMLASSERT(filepath);
2219 	TIXMLASSERT(mode);
2220 #if defined(_MSC_VER) && (_MSC_VER >= 1400) && (!defined WINCE)
2221 	FILE* fp = 0;
2222 	errno_t err = fopen_s(&fp, filepath, mode);
2223 	if (err)
2224 	{
2225 		return 0;
2226 	}
2227 #else
2228 	FILE* fp = fopen(filepath, mode);
2229 #endif
2230 	return fp;
2231 }
2232 
DeleteNode(XMLNode * node)2233 void XMLDocument::DeleteNode(XMLNode* node)
2234 {
2235 	TIXMLASSERT(node);
2236 	TIXMLASSERT(node->_document == this);
2237 	if (node->_parent)
2238 	{
2239 		node->_parent->DeleteChild(node);
2240 	}
2241 	else
2242 	{
2243 		// Isn't in the tree.
2244 		// Use the parent delete.
2245 		// Also, we need to mark it tracked: we 'know'
2246 		// it was never used.
2247 		node->_memPool->SetTracked();
2248 		// Call the static XMLNode version:
2249 		XMLNode::DeleteNode(node);
2250 	}
2251 }
2252 
LoadFile(const char * filename)2253 XMLError XMLDocument::LoadFile(const char* filename)
2254 {
2255 	if (!filename)
2256 	{
2257 		TIXMLASSERT(false);
2258 		SetError(XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>");
2259 		return _errorID;
2260 	}
2261 
2262 	Clear();
2263 	FILE* fp = callfopen(filename, "rb");
2264 	if (!fp)
2265 	{
2266 		SetError(XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename);
2267 		return _errorID;
2268 	}
2269 	LoadFile(fp);
2270 	fclose(fp);
2271 	return _errorID;
2272 }
2273 
2274 // This is likely overengineered template art to have a check that unsigned long value incremented
2275 // by one still fits into size_t. If size_t type is larger than unsigned long type
2276 // (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
2277 // -Wtype-limits warning. This piece makes the compiler select code with a check when a check
2278 // is useful and code with no check when a check is redundant depending on how size_t and unsigned long
2279 // types sizes relate to each other.
2280 template <bool = (sizeof(unsigned long) >= sizeof(size_t))>
2281 struct LongFitsIntoSizeTMinusOne
2282 {
Fitstinyxml2::LongFitsIntoSizeTMinusOne2283 	static bool Fits(unsigned long value)
2284 	{
2285 		return value < (size_t)-1;
2286 	}
2287 };
2288 
2289 template <>
2290 struct LongFitsIntoSizeTMinusOne<false>
2291 {
Fitstinyxml2::LongFitsIntoSizeTMinusOne2292 	static bool Fits(unsigned long)
2293 	{
2294 		return true;
2295 	}
2296 };
2297 
LoadFile(FILE * fp)2298 XMLError XMLDocument::LoadFile(FILE* fp)
2299 {
2300 	Clear();
2301 
2302 	fseek(fp, 0, SEEK_SET);
2303 	if (fgetc(fp) == EOF && ferror(fp) != 0)
2304 	{
2305 		SetError(XML_ERROR_FILE_READ_ERROR, 0, 0);
2306 		return _errorID;
2307 	}
2308 
2309 	fseek(fp, 0, SEEK_END);
2310 	const long filelength = ftell(fp);
2311 	fseek(fp, 0, SEEK_SET);
2312 	if (filelength == -1L)
2313 	{
2314 		SetError(XML_ERROR_FILE_READ_ERROR, 0, 0);
2315 		return _errorID;
2316 	}
2317 	TIXMLASSERT(filelength >= 0);
2318 
2319 	if (!LongFitsIntoSizeTMinusOne<>::Fits(filelength))
2320 	{
2321 		// Cannot handle files which won't fit in buffer together with null terminator
2322 		SetError(XML_ERROR_FILE_READ_ERROR, 0, 0);
2323 		return _errorID;
2324 	}
2325 
2326 	if (filelength == 0)
2327 	{
2328 		SetError(XML_ERROR_EMPTY_DOCUMENT, 0, 0);
2329 		return _errorID;
2330 	}
2331 
2332 	const size_t size = filelength;
2333 	TIXMLASSERT(_charBuffer == 0);
2334 	_charBuffer = new char[size + 1];
2335 	size_t read = fread(_charBuffer, 1, size, fp);
2336 	if (read != size)
2337 	{
2338 		SetError(XML_ERROR_FILE_READ_ERROR, 0, 0);
2339 		return _errorID;
2340 	}
2341 
2342 	_charBuffer[size] = 0;
2343 
2344 	Parse();
2345 	return _errorID;
2346 }
2347 
SaveFile(const char * filename,bool compact)2348 XMLError XMLDocument::SaveFile(const char* filename, bool compact)
2349 {
2350 	if (!filename)
2351 	{
2352 		TIXMLASSERT(false);
2353 		SetError(XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>");
2354 		return _errorID;
2355 	}
2356 
2357 	FILE* fp = callfopen(filename, "w");
2358 	if (!fp)
2359 	{
2360 		SetError(XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename);
2361 		return _errorID;
2362 	}
2363 	SaveFile(fp, compact);
2364 	fclose(fp);
2365 	return _errorID;
2366 }
2367 
SaveFile(FILE * fp,bool compact)2368 XMLError XMLDocument::SaveFile(FILE* fp, bool compact)
2369 {
2370 	// Clear any error from the last save, otherwise it will get reported
2371 	// for *this* call.
2372 	ClearError();
2373 	XMLPrinter stream(fp, compact);
2374 	Print(&stream);
2375 	return _errorID;
2376 }
2377 
Parse(const char * p,size_t len)2378 XMLError XMLDocument::Parse(const char* p, size_t len)
2379 {
2380 	Clear();
2381 
2382 	if (len == 0 || !p || !*p)
2383 	{
2384 		SetError(XML_ERROR_EMPTY_DOCUMENT, 0, 0);
2385 		return _errorID;
2386 	}
2387 	if (len == (size_t)(-1))
2388 	{
2389 		len = strlen(p);
2390 	}
2391 	TIXMLASSERT(_charBuffer == 0);
2392 	_charBuffer = new char[len + 1];
2393 	memcpy(_charBuffer, p, len);
2394 	_charBuffer[len] = 0;
2395 
2396 	Parse();
2397 	if (Error())
2398 	{
2399 		// clean up now essentially dangling memory.
2400 		// and the parse fail can put objects in the
2401 		// pools that are dead and inaccessible.
2402 		DeleteChildren();
2403 		_elementPool.Clear();
2404 		_attributePool.Clear();
2405 		_textPool.Clear();
2406 		_commentPool.Clear();
2407 	}
2408 	return _errorID;
2409 }
2410 
Print(XMLPrinter * streamer) const2411 void XMLDocument::Print(XMLPrinter* streamer) const
2412 {
2413 	if (streamer)
2414 	{
2415 		Accept(streamer);
2416 	}
2417 	else
2418 	{
2419 		XMLPrinter stdoutStreamer(stdout);
2420 		Accept(&stdoutStreamer);
2421 	}
2422 }
2423 
SetError(XMLError error,int lineNum,const char * format,...)2424 void XMLDocument::SetError(XMLError error, int lineNum, const char* format, ...)
2425 {
2426 	TIXMLASSERT(error >= 0 && error < XML_ERROR_COUNT);
2427 	_errorID = error;
2428 	_errorLineNum = lineNum;
2429 	_errorStr.Reset();
2430 
2431 	size_t BUFFER_SIZE = 1000;
2432 	char* buffer = new char[BUFFER_SIZE];
2433 
2434 	TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum);
2435 
2436 	if (format)
2437 	{
2438 		size_t len = strlen(buffer);
2439 		TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": ");
2440 		len = strlen(buffer);
2441 
2442 		va_list va;
2443 		va_start(va, format);
2444 		TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va);
2445 		va_end(va);
2446 	}
2447 	_errorStr.SetStr(buffer);
2448 	delete[] buffer;
2449 }
2450 
ErrorIDToName(XMLError errorID)2451 /*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID)
2452 {
2453 	TIXMLASSERT(errorID >= 0 && errorID < XML_ERROR_COUNT);
2454 	const char* errorName = _errorNames[errorID];
2455 	TIXMLASSERT(errorName && errorName[0]);
2456 	return errorName;
2457 }
2458 
ErrorStr() const2459 const char* XMLDocument::ErrorStr() const
2460 {
2461 	return _errorStr.Empty() ? "" : _errorStr.GetStr();
2462 }
2463 
PrintError() const2464 void XMLDocument::PrintError() const
2465 {
2466 	printf("%s\n", ErrorStr());
2467 }
2468 
ErrorName() const2469 const char* XMLDocument::ErrorName() const
2470 {
2471 	return ErrorIDToName(_errorID);
2472 }
2473 
Parse()2474 void XMLDocument::Parse()
2475 {
2476 	TIXMLASSERT(NoChildren());  // Clear() must have been called previously
2477 	TIXMLASSERT(_charBuffer);
2478 	_parseCurLineNum = 1;
2479 	_parseLineNum = 1;
2480 	char* p = _charBuffer;
2481 	p = XMLUtil::SkipWhiteSpace(p, &_parseCurLineNum);
2482 	p = const_cast<char*>(XMLUtil::ReadBOM(p, &_writeBOM));
2483 	if (!*p)
2484 	{
2485 		SetError(XML_ERROR_EMPTY_DOCUMENT, 0, 0);
2486 		return;
2487 	}
2488 	ParseDeep(p, 0, &_parseCurLineNum);
2489 }
2490 
PushDepth()2491 void XMLDocument::PushDepth()
2492 {
2493 	_parsingDepth++;
2494 	if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH)
2495 	{
2496 		SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, "Element nesting is too deep.");
2497 	}
2498 }
2499 
PopDepth()2500 void XMLDocument::PopDepth()
2501 {
2502 	TIXMLASSERT(_parsingDepth > 0);
2503 	--_parsingDepth;
2504 }
2505 
XMLPrinter(FILE * file,bool compact,int depth)2506 XMLPrinter::XMLPrinter(FILE* file, bool compact, int depth) : _elementJustOpened(false),
2507 															  _stack(),
2508 															  _firstElement(true),
2509 															  _fp(file),
2510 															  _depth(depth),
2511 															  _textDepth(-1),
2512 															  _processEntities(true),
2513 															  _compactMode(compact),
2514 															  _buffer()
2515 {
2516 	for (int i = 0; i < ENTITY_RANGE; ++i)
2517 	{
2518 		_entityFlag[i] = false;
2519 		_restrictedEntityFlag[i] = false;
2520 	}
2521 	for (int i = 0; i < NUM_ENTITIES; ++i)
2522 	{
2523 		const char entityValue = entities[i].value;
2524 		const unsigned char flagIndex = (unsigned char)entityValue;
2525 		TIXMLASSERT(flagIndex < ENTITY_RANGE);
2526 		_entityFlag[flagIndex] = true;
2527 	}
2528 	_restrictedEntityFlag[(unsigned char)'&'] = true;
2529 	_restrictedEntityFlag[(unsigned char)'<'] = true;
2530 	_restrictedEntityFlag[(unsigned char)'>'] = true;  // not required, but consistency is nice
2531 	_buffer.Push(0);
2532 }
2533 
Print(const char * format,...)2534 void XMLPrinter::Print(const char* format, ...)
2535 {
2536 	va_list va;
2537 	va_start(va, format);
2538 
2539 	if (_fp)
2540 	{
2541 		vfprintf(_fp, format, va);
2542 	}
2543 	else
2544 	{
2545 		const int len = TIXML_VSCPRINTF(format, va);
2546 		// Close out and re-start the va-args
2547 		va_end(va);
2548 		TIXMLASSERT(len >= 0);
2549 		va_start(va, format);
2550 		TIXMLASSERT(_buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0);
2551 		char* p = _buffer.PushArr(len) - 1;  // back up over the null terminator.
2552 		TIXML_VSNPRINTF(p, len + 1, format, va);
2553 	}
2554 	va_end(va);
2555 }
2556 
Write(const char * data,size_t size)2557 void XMLPrinter::Write(const char* data, size_t size)
2558 {
2559 	if (_fp)
2560 	{
2561 		fwrite(data, sizeof(char), size, _fp);
2562 	}
2563 	else
2564 	{
2565 		char* p = _buffer.PushArr(static_cast<int>(size)) - 1;  // back up over the null terminator.
2566 		memcpy(p, data, size);
2567 		p[size] = 0;
2568 	}
2569 }
2570 
Putc(char ch)2571 void XMLPrinter::Putc(char ch)
2572 {
2573 	if (_fp)
2574 	{
2575 		fputc(ch, _fp);
2576 	}
2577 	else
2578 	{
2579 		char* p = _buffer.PushArr(sizeof(char)) - 1;  // back up over the null terminator.
2580 		p[0] = ch;
2581 		p[1] = 0;
2582 	}
2583 }
2584 
PrintSpace(int depth)2585 void XMLPrinter::PrintSpace(int depth)
2586 {
2587 	for (int i = 0; i < depth; ++i)
2588 	{
2589 		Write("    ");
2590 	}
2591 }
2592 
PrintString(const char * p,bool restricted)2593 void XMLPrinter::PrintString(const char* p, bool restricted)
2594 {
2595 	// Look for runs of bytes between entities to print.
2596 	const char* q = p;
2597 
2598 	if (_processEntities)
2599 	{
2600 		const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
2601 		while (*q)
2602 		{
2603 			TIXMLASSERT(p <= q);
2604 			// Remember, char is sometimes signed. (How many times has that bitten me?)
2605 			if (*q > 0 && *q < ENTITY_RANGE)
2606 			{
2607 				// Check for entities. If one is found, flush
2608 				// the stream up until the entity, write the
2609 				// entity, and keep looking.
2610 				if (flag[(unsigned char)(*q)])
2611 				{
2612 					while (p < q)
2613 					{
2614 						const size_t delta = q - p;
2615 						const int toPrint = (INT_MAX < delta) ? INT_MAX : (int)delta;
2616 						Write(p, toPrint);
2617 						p += toPrint;
2618 					}
2619 					bool entityPatternPrinted = false;
2620 					for (int i = 0; i < NUM_ENTITIES; ++i)
2621 					{
2622 						if (entities[i].value == *q)
2623 						{
2624 							Putc('&');
2625 							Write(entities[i].pattern, entities[i].length);
2626 							Putc(';');
2627 							entityPatternPrinted = true;
2628 							break;
2629 						}
2630 					}
2631 					if (!entityPatternPrinted)
2632 					{
2633 						// TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
2634 						TIXMLASSERT(false);
2635 					}
2636 					++p;
2637 				}
2638 			}
2639 			++q;
2640 			TIXMLASSERT(p <= q);
2641 		}
2642 	}
2643 	// Flush the remaining string. This will be the entire
2644 	// string if an entity wasn't found.
2645 	TIXMLASSERT(p <= q);
2646 	if (!_processEntities || (p < q))
2647 	{
2648 		const size_t delta = q - p;
2649 		const int toPrint = (INT_MAX < delta) ? INT_MAX : (int)delta;
2650 		Write(p, toPrint);
2651 	}
2652 }
2653 
PushHeader(bool writeBOM,bool writeDec)2654 void XMLPrinter::PushHeader(bool writeBOM, bool writeDec)
2655 {
2656 	if (writeBOM)
2657 	{
2658 		static const unsigned char bom[] = {TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0};
2659 		Write(reinterpret_cast<const char*>(bom));
2660 	}
2661 	if (writeDec)
2662 	{
2663 		PushDeclaration("xml version=\"1.0\"");
2664 	}
2665 }
2666 
OpenElement(const char * name,bool compactMode)2667 void XMLPrinter::OpenElement(const char* name, bool compactMode)
2668 {
2669 	SealElementIfJustOpened();
2670 	_stack.Push(name);
2671 
2672 	if (_textDepth < 0 && !_firstElement && !compactMode)
2673 	{
2674 		Putc('\n');
2675 	}
2676 	if (!compactMode)
2677 	{
2678 		PrintSpace(_depth);
2679 	}
2680 
2681 	Write("<");
2682 	Write(name);
2683 
2684 	_elementJustOpened = true;
2685 	_firstElement = false;
2686 	++_depth;
2687 }
2688 
PushAttribute(const char * name,const char * value)2689 void XMLPrinter::PushAttribute(const char* name, const char* value)
2690 {
2691 	TIXMLASSERT(_elementJustOpened);
2692 	Putc(' ');
2693 	Write(name);
2694 	Write("=\"");
2695 	PrintString(value, false);
2696 	Putc('\"');
2697 }
2698 
PushAttribute(const char * name,int v)2699 void XMLPrinter::PushAttribute(const char* name, int v)
2700 {
2701 	char buf[BUF_SIZE];
2702 	XMLUtil::ToStr(v, buf, BUF_SIZE);
2703 	PushAttribute(name, buf);
2704 }
2705 
PushAttribute(const char * name,unsigned v)2706 void XMLPrinter::PushAttribute(const char* name, unsigned v)
2707 {
2708 	char buf[BUF_SIZE];
2709 	XMLUtil::ToStr(v, buf, BUF_SIZE);
2710 	PushAttribute(name, buf);
2711 }
2712 
PushAttribute(const char * name,xml_Int64a_t v)2713 void XMLPrinter::PushAttribute(const char* name, xml_Int64a_t v)
2714 {
2715 	char buf[BUF_SIZE];
2716 	XMLUtil::ToStr(v, buf, BUF_SIZE);
2717 	PushAttribute(name, buf);
2718 }
2719 
PushAttribute(const char * name,bool v)2720 void XMLPrinter::PushAttribute(const char* name, bool v)
2721 {
2722 	char buf[BUF_SIZE];
2723 	XMLUtil::ToStr(v, buf, BUF_SIZE);
2724 	PushAttribute(name, buf);
2725 }
2726 
PushAttribute(const char * name,double v)2727 void XMLPrinter::PushAttribute(const char* name, double v)
2728 {
2729 	char buf[BUF_SIZE];
2730 	XMLUtil::ToStr(v, buf, BUF_SIZE);
2731 	PushAttribute(name, buf);
2732 }
2733 
CloseElement(bool compactMode)2734 void XMLPrinter::CloseElement(bool compactMode)
2735 {
2736 	--_depth;
2737 	const char* name = _stack.Pop();
2738 
2739 	if (_elementJustOpened)
2740 	{
2741 		Write("/>");
2742 	}
2743 	else
2744 	{
2745 		if (_textDepth < 0 && !compactMode)
2746 		{
2747 			Putc('\n');
2748 			PrintSpace(_depth);
2749 		}
2750 		Write("</");
2751 		Write(name);
2752 		Write(">");
2753 	}
2754 
2755 	if (_textDepth == _depth)
2756 	{
2757 		_textDepth = -1;
2758 	}
2759 	if (_depth == 0 && !compactMode)
2760 	{
2761 		Putc('\n');
2762 	}
2763 	_elementJustOpened = false;
2764 }
2765 
SealElementIfJustOpened()2766 void XMLPrinter::SealElementIfJustOpened()
2767 {
2768 	if (!_elementJustOpened)
2769 	{
2770 		return;
2771 	}
2772 	_elementJustOpened = false;
2773 	Putc('>');
2774 }
2775 
PushText(const char * text,bool cdata)2776 void XMLPrinter::PushText(const char* text, bool cdata)
2777 {
2778 	_textDepth = _depth - 1;
2779 
2780 	SealElementIfJustOpened();
2781 	if (cdata)
2782 	{
2783 		Write("<![CDATA[");
2784 		Write(text);
2785 		Write("]]>");
2786 	}
2787 	else
2788 	{
2789 		PrintString(text, true);
2790 	}
2791 }
2792 
PushText(xml_Int64a_t value)2793 void XMLPrinter::PushText(xml_Int64a_t value)
2794 {
2795 	char buf[BUF_SIZE];
2796 	XMLUtil::ToStr(value, buf, BUF_SIZE);
2797 	PushText(buf, false);
2798 }
2799 
PushText(int value)2800 void XMLPrinter::PushText(int value)
2801 {
2802 	char buf[BUF_SIZE];
2803 	XMLUtil::ToStr(value, buf, BUF_SIZE);
2804 	PushText(buf, false);
2805 }
2806 
PushText(unsigned value)2807 void XMLPrinter::PushText(unsigned value)
2808 {
2809 	char buf[BUF_SIZE];
2810 	XMLUtil::ToStr(value, buf, BUF_SIZE);
2811 	PushText(buf, false);
2812 }
2813 
PushText(bool value)2814 void XMLPrinter::PushText(bool value)
2815 {
2816 	char buf[BUF_SIZE];
2817 	XMLUtil::ToStr(value, buf, BUF_SIZE);
2818 	PushText(buf, false);
2819 }
2820 
PushText(float value)2821 void XMLPrinter::PushText(float value)
2822 {
2823 	char buf[BUF_SIZE];
2824 	XMLUtil::ToStr(value, buf, BUF_SIZE);
2825 	PushText(buf, false);
2826 }
2827 
PushText(double value)2828 void XMLPrinter::PushText(double value)
2829 {
2830 	char buf[BUF_SIZE];
2831 	XMLUtil::ToStr(value, buf, BUF_SIZE);
2832 	PushText(buf, false);
2833 }
2834 
PushComment(const char * comment)2835 void XMLPrinter::PushComment(const char* comment)
2836 {
2837 	SealElementIfJustOpened();
2838 	if (_textDepth < 0 && !_firstElement && !_compactMode)
2839 	{
2840 		Putc('\n');
2841 		PrintSpace(_depth);
2842 	}
2843 	_firstElement = false;
2844 
2845 	Write("<!--");
2846 	Write(comment);
2847 	Write("-->");
2848 }
2849 
PushDeclaration(const char * value)2850 void XMLPrinter::PushDeclaration(const char* value)
2851 {
2852 	SealElementIfJustOpened();
2853 	if (_textDepth < 0 && !_firstElement && !_compactMode)
2854 	{
2855 		Putc('\n');
2856 		PrintSpace(_depth);
2857 	}
2858 	_firstElement = false;
2859 
2860 	Write("<?");
2861 	Write(value);
2862 	Write("?>");
2863 }
2864 
PushUnknown(const char * value)2865 void XMLPrinter::PushUnknown(const char* value)
2866 {
2867 	SealElementIfJustOpened();
2868 	if (_textDepth < 0 && !_firstElement && !_compactMode)
2869 	{
2870 		Putc('\n');
2871 		PrintSpace(_depth);
2872 	}
2873 	_firstElement = false;
2874 
2875 	Write("<!");
2876 	Write(value);
2877 	Putc('>');
2878 }
2879 
VisitEnter(const XMLDocument & doc)2880 bool XMLPrinter::VisitEnter(const XMLDocument& doc)
2881 {
2882 	_processEntities = doc.ProcessEntities();
2883 	if (doc.HasBOM())
2884 	{
2885 		PushHeader(true, false);
2886 	}
2887 	return true;
2888 }
2889 
VisitEnter(const XMLElement & element,const XMLAttribute * attribute)2890 bool XMLPrinter::VisitEnter(const XMLElement& element, const XMLAttribute* attribute)
2891 {
2892 	const XMLElement* parentElem = 0;
2893 	if (element.Parent())
2894 	{
2895 		parentElem = element.Parent()->ToElement();
2896 	}
2897 	const bool compactMode = parentElem ? CompactMode(*parentElem) : _compactMode;
2898 	OpenElement(element.Name(), compactMode);
2899 	while (attribute)
2900 	{
2901 		PushAttribute(attribute->Name(), attribute->Value());
2902 		attribute = attribute->Next();
2903 	}
2904 	return true;
2905 }
2906 
VisitExit(const XMLElement & element)2907 bool XMLPrinter::VisitExit(const XMLElement& element)
2908 {
2909 	CloseElement(CompactMode(element));
2910 	return true;
2911 }
2912 
Visit(const XMLText & text)2913 bool XMLPrinter::Visit(const XMLText& text)
2914 {
2915 	PushText(text.Value(), text.CData());
2916 	return true;
2917 }
2918 
Visit(const XMLComment & comment)2919 bool XMLPrinter::Visit(const XMLComment& comment)
2920 {
2921 	PushComment(comment.Value());
2922 	return true;
2923 }
2924 
Visit(const XMLDeclaration & declaration)2925 bool XMLPrinter::Visit(const XMLDeclaration& declaration)
2926 {
2927 	PushDeclaration(declaration.Value());
2928 	return true;
2929 }
2930 
Visit(const XMLUnknown & unknown)2931 bool XMLPrinter::Visit(const XMLUnknown& unknown)
2932 {
2933 	PushUnknown(unknown.Value());
2934 	return true;
2935 }
2936 
2937 }  // namespace tinyxml2
2938