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