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 // 中 or 中
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