SO3Engine
tinyxml2.cpp
Go to the documentation of this file.
1/*
2Original code by Lee Thomason (www.grinninglizard.com)
3
4This software is provided 'as-is', without any express or implied
5warranty. In no event will the authors be held liable for any
6damages arising from the use of this software.
7
8Permission is granted to anyone to use this software for any
9purpose, including commercial applications, and to alter it and
10redistribute it freely, subject to the following restrictions:
11
121. The origin of this software must not be misrepresented; you must
13not claim that you wrote the original software. If you use this
14software in a product, an acknowledgment in the product documentation
15would be appreciated but is not required.
16
172. Altered source versions must be plainly marked as such, and
18must not be misrepresented as being the original software.
19
203. This notice may not be removed or altered from any source
21distribution.
22*/
23
25
26#include <new> // yes, this one new style header, is in the Android SDK.
27# ifdef ANDROID_NDK
28# include <stddef.h>
29#else
30# include <cstddef>
31#endif
32
33static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
34static const char LF = LINE_FEED;
35static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
36static const char CR = CARRIAGE_RETURN;
37static const char SINGLE_QUOTE = '\'';
38static const char DOUBLE_QUOTE = '\"';
39
40// Bunch of unicode info at:
41// http://www.unicode.org/faq/utf_bom.html
42// ef bb bf (Microsoft "lead bytes") - designates UTF-8
43
44static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
45static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
46static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
47
48
49#define DELETE_NODE( node ) { \
50 if ( node ) { \
51 MemPool* pool = node->_memPool; \
52 node->~XMLNode(); \
53 pool->Free( node ); \
54 } \
55 }
56#define DELETE_ATTRIBUTE( attrib ) { \
57 if ( attrib ) { \
58 MemPool* pool = attrib->_memPool; \
59 attrib->~XMLAttribute(); \
60 pool->Free( attrib ); \
61 } \
62 }
63
64namespace tinyxml2
65{
66
67struct Entity {
68 const char* pattern;
69 int length;
70 char value;
71};
72
73static const int NUM_ENTITIES = 5;
74static const Entity entities[NUM_ENTITIES] = {
75 { "quot", 4, DOUBLE_QUOTE },
76 { "amp", 3, '&' },
77 { "apos", 4, SINGLE_QUOTE },
78 { "lt", 2, '<' },
79 { "gt", 2, '>' }
80};
81
82
84{
85 Reset();
86}
87
88
89void StrPair::Reset()
90{
91 if ( _flags & NEEDS_DELETE ) {
92 delete [] _start;
93 }
94 _flags = 0;
95 _start = 0;
96 _end = 0;
97}
98
99
100void StrPair::SetStr( const char* str, int flags )
101{
102 Reset();
103 size_t len = strlen( str );
104 _start = new char[ len+1 ];
105 memcpy( _start, str, len+1 );
106 _end = _start + len;
107 _flags = flags | NEEDS_DELETE;
108}
109
110
111char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
112{
113 TIXMLASSERT( endTag && *endTag );
114
115 char* start = p; // fixme: hides a member
116 char endChar = *endTag;
117 size_t length = strlen( endTag );
118
119 // Inner loop of text parsing.
120 while ( *p ) {
121 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
122 Set( start, p, strFlags );
123 return p + length;
124 }
125 ++p;
126 }
127 return 0;
128}
129
130
131char* StrPair::ParseName( char* p )
132{
133 char* start = p;
134
135 if ( !start || !(*start) ) {
136 return 0;
137 }
138
139 while( *p && ( p == start ? XMLUtil::IsNameStartChar( *p ) : XMLUtil::IsNameChar( *p ) )) {
140 ++p;
141 }
142
143 if ( p > start ) {
144 Set( start, p, 0 );
145 return p;
146 }
147 return 0;
148}
149
150
151void StrPair::CollapseWhitespace()
152{
153 // Trim leading space.
154 _start = XMLUtil::SkipWhiteSpace( _start );
155
156 if ( _start && *_start ) {
157 char* p = _start; // the read pointer
158 char* q = _start; // the write pointer
159
160 while( *p ) {
161 if ( XMLUtil::IsWhiteSpace( *p )) {
163 if ( *p == 0 ) {
164 break; // don't write to q; this trims the trailing space.
165 }
166 *q = ' ';
167 ++q;
168 }
169 *q = *p;
170 ++q;
171 ++p;
172 }
173 *q = 0;
174 }
175}
176
177
178const char* StrPair::GetStr()
179{
180 if ( _flags & NEEDS_FLUSH ) {
181 *_end = 0;
182 _flags ^= NEEDS_FLUSH;
183
184 if ( _flags ) {
185 char* p = _start; // the read pointer
186 char* q = _start; // the write pointer
187
188 while( p < _end ) {
189 if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
190 // CR-LF pair becomes LF
191 // CR alone becomes LF
192 // LF-CR becomes LF
193 if ( *(p+1) == LF ) {
194 p += 2;
195 }
196 else {
197 ++p;
198 }
199 *q++ = LF;
200 }
201 else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
202 if ( *(p+1) == CR ) {
203 p += 2;
204 }
205 else {
206 ++p;
207 }
208 *q++ = LF;
209 }
210 else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
211 // Entities handled by tinyXML2:
212 // - special entities in the entity table [in/out]
213 // - numeric character reference [in]
214 // &#20013; or &#x4e2d;
215
216 if ( *(p+1) == '#' ) {
217 char buf[10] = { 0 };
218 int len;
219 p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
220 for( int i=0; i<len; ++i ) {
221 *q++ = buf[i];
222 }
223 TIXMLASSERT( q <= p );
224 }
225 else {
226 int i=0;
227 for(; i<NUM_ENTITIES; ++i ) {
228 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
229 && *(p+entities[i].length+1) == ';' ) {
230 // Found an entity convert;
231 *q = entities[i].value;
232 ++q;
233 p += entities[i].length + 2;
234 break;
235 }
236 }
237 if ( i == NUM_ENTITIES ) {
238 // fixme: treat as error?
239 ++p;
240 ++q;
241 }
242 }
243 }
244 else {
245 *q = *p;
246 ++p;
247 ++q;
248 }
249 }
250 *q = 0;
251 }
252 // The loop below has plenty going on, and this
253 // is a less useful mode. Break it out.
254 if ( _flags & COLLAPSE_WHITESPACE ) {
255 CollapseWhitespace();
256 }
257 _flags = (_flags & NEEDS_DELETE);
258 }
259 return _start;
260}
261
262
263
264
265// --------- XMLUtil ----------- //
266
267const char* XMLUtil::ReadBOM( const char* p, bool* bom )
268{
269 *bom = false;
270 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
271 // Check for BOM:
272 if ( *(pu+0) == TIXML_UTF_LEAD_0
273 && *(pu+1) == TIXML_UTF_LEAD_1
274 && *(pu+2) == TIXML_UTF_LEAD_2 ) {
275 *bom = true;
276 p += 3;
277 }
278 return p;
279}
280
281
282void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
283{
284 const unsigned long BYTE_MASK = 0xBF;
285 const unsigned long BYTE_MARK = 0x80;
286 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
287
288 if (input < 0x80) {
289 *length = 1;
290 }
291 else if ( input < 0x800 ) {
292 *length = 2;
293 }
294 else if ( input < 0x10000 ) {
295 *length = 3;
296 }
297 else if ( input < 0x200000 ) {
298 *length = 4;
299 }
300 else {
301 *length = 0; // This code won't covert this correctly anyway.
302 return;
303 }
304
305 output += *length;
306
307 // Scary scary fall throughs.
308 switch (*length) {
309 case 4:
310 --output;
311 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
312 input >>= 6;
313 case 3:
314 --output;
315 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
316 input >>= 6;
317 case 2:
318 --output;
319 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
320 input >>= 6;
321 case 1:
322 --output;
323 *output = (char)(input | FIRST_BYTE_MARK[*length]);
324 default:
325 break;
326 }
327}
328
329
330const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
331{
332 // Presume an entity, and pull it out.
333 *length = 0;
334
335 if ( *(p+1) == '#' && *(p+2) ) {
336 unsigned long ucs = 0;
337 ptrdiff_t delta = 0;
338 unsigned mult = 1;
339
340 if ( *(p+2) == 'x' ) {
341 // Hexadecimal.
342 if ( !*(p+3) ) {
343 return 0;
344 }
345
346 const char* q = p+3;
347 q = strchr( q, ';' );
348
349 if ( !q || !*q ) {
350 return 0;
351 }
352
353 delta = q-p;
354 --q;
355
356 while ( *q != 'x' ) {
357 if ( *q >= '0' && *q <= '9' ) {
358 ucs += mult * (*q - '0');
359 }
360 else if ( *q >= 'a' && *q <= 'f' ) {
361 ucs += mult * (*q - 'a' + 10);
362 }
363 else if ( *q >= 'A' && *q <= 'F' ) {
364 ucs += mult * (*q - 'A' + 10 );
365 }
366 else {
367 return 0;
368 }
369 mult *= 16;
370 --q;
371 }
372 }
373 else {
374 // Decimal.
375 if ( !*(p+2) ) {
376 return 0;
377 }
378
379 const char* q = p+2;
380 q = strchr( q, ';' );
381
382 if ( !q || !*q ) {
383 return 0;
384 }
385
386 delta = q-p;
387 --q;
388
389 while ( *q != '#' ) {
390 if ( *q >= '0' && *q <= '9' ) {
391 ucs += mult * (*q - '0');
392 }
393 else {
394 return 0;
395 }
396 mult *= 10;
397 --q;
398 }
399 }
400 // convert the UCS to UTF-8
401 ConvertUTF32ToUTF8( ucs, value, length );
402 return p + delta + 1;
403 }
404 return p+1;
405}
406
407
408void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
409{
410 TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
411}
412
413
414void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
415{
416 TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
417}
418
419
420void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
421{
422 TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 );
423}
424
425/*
426 ToStr() of a number is a very tricky topic.
427 https://github.com/leethomason/tinyxml2/issues/106
428*/
429void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
430{
431 TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );
432}
433
434
435void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
436{
437 TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );
438}
439
440
441bool XMLUtil::ToInt( const char* str, int* value )
442{
443 if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
444 return true;
445 }
446 return false;
447}
448
449bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
450{
451 if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
452 return true;
453 }
454 return false;
455}
456
457bool XMLUtil::ToBool( const char* str, bool* value )
458{
459 int ival = 0;
460 if ( ToInt( str, &ival )) {
461 *value = (ival==0) ? false : true;
462 return true;
463 }
464 if ( StringEqual( str, "true" ) ) {
465 *value = true;
466 return true;
467 }
468 else if ( StringEqual( str, "false" ) ) {
469 *value = false;
470 return true;
471 }
472 return false;
473}
474
475
476bool XMLUtil::ToFloat( const char* str, float* value )
477{
478 if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
479 return true;
480 }
481 return false;
482}
483
484bool XMLUtil::ToDouble( const char* str, double* value )
485{
486 if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
487 return true;
488 }
489 return false;
490}
491
492
493char* XMLDocument::Identify( char* p, XMLNode** node )
494{
495 XMLNode* returnNode = 0;
496 char* start = p;
498 if( !p || !*p ) {
499 return p;
500 }
501
502 // What is this thing?
503 // These strings define the matching patters:
504 static const char* xmlHeader = { "<?" };
505 static const char* commentHeader = { "<!--" };
506 static const char* dtdHeader = { "<!" };
507 static const char* cdataHeader = { "<![CDATA[" };
508 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
509
510 static const int xmlHeaderLen = 2;
511 static const int commentHeaderLen = 4;
512 static const int dtdHeaderLen = 2;
513 static const int cdataHeaderLen = 9;
514 static const int elementHeaderLen = 1;
515
516#if defined(_MSC_VER)
517#pragma warning ( push )
518#pragma warning ( disable : 4127 )
519#endif
520 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
521 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
522#if defined(_MSC_VER)
523#pragma warning (pop)
524#endif
525 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
526 returnNode = new (_commentPool.Alloc()) XMLDeclaration( this );
527 returnNode->_memPool = &_commentPool;
528 p += xmlHeaderLen;
529 }
530 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
531 returnNode = new (_commentPool.Alloc()) XMLComment( this );
532 returnNode->_memPool = &_commentPool;
533 p += commentHeaderLen;
534 }
535 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
536 XMLText* text = new (_textPool.Alloc()) XMLText( this );
537 returnNode = text;
538 returnNode->_memPool = &_textPool;
539 p += cdataHeaderLen;
540 text->SetCData( true );
541 }
542 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
543 returnNode = new (_commentPool.Alloc()) XMLUnknown( this );
544 returnNode->_memPool = &_commentPool;
545 p += dtdHeaderLen;
546 }
547 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
548 returnNode = new (_elementPool.Alloc()) XMLElement( this );
549 returnNode->_memPool = &_elementPool;
550 p += elementHeaderLen;
551 }
552 else {
553 returnNode = new (_textPool.Alloc()) XMLText( this );
554 returnNode->_memPool = &_textPool;
555 p = start; // Back it up, all the text counts.
556 }
557
558 *node = returnNode;
559 return p;
560}
561
562
563bool XMLDocument::Accept( XMLVisitor* visitor ) const
564{
565 if ( visitor->VisitEnter( *this ) ) {
566 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
567 if ( !node->Accept( visitor ) ) {
568 break;
569 }
570 }
571 }
572 return visitor->VisitExit( *this );
573}
574
575
576// --------- XMLNode ----------- //
577
578XMLNode::XMLNode( XMLDocument* doc ) :
579 _document( doc ),
580 _parent( 0 ),
581 _firstChild( 0 ), _lastChild( 0 ),
582 _prev( 0 ), _next( 0 ),
583 _memPool( 0 )
584{
585}
586
587
588XMLNode::~XMLNode()
589{
590 DeleteChildren();
591 if ( _parent ) {
592 _parent->Unlink( this );
593 }
594}
595
596const char* XMLNode::Value() const
597{
598 return _value.GetStr();
599}
600
601void XMLNode::SetValue( const char* str, bool staticMem )
602{
603 if ( staticMem ) {
604 _value.SetInternedStr( str );
605 }
606 else {
607 _value.SetStr( str );
608 }
609}
610
611
612void XMLNode::DeleteChildren()
613{
614 while( _firstChild ) {
615 XMLNode* node = _firstChild;
616 Unlink( node );
617
618 DELETE_NODE( node );
619 }
620 _firstChild = _lastChild = 0;
621}
622
623
624void XMLNode::Unlink( XMLNode* child )
625{
626 if ( child == _firstChild ) {
627 _firstChild = _firstChild->_next;
628 }
629 if ( child == _lastChild ) {
630 _lastChild = _lastChild->_prev;
631 }
632
633 if ( child->_prev ) {
634 child->_prev->_next = child->_next;
635 }
636 if ( child->_next ) {
637 child->_next->_prev = child->_prev;
638 }
639 child->_parent = 0;
640}
641
642
643void XMLNode::DeleteChild( XMLNode* node )
644{
645 TIXMLASSERT( node->_parent == this );
646 DELETE_NODE( node );
647}
648
649
650XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
651{
652 if (addThis->_document != _document)
653 return 0;
654
655 if (addThis->_parent)
656 addThis->_parent->Unlink( addThis );
657 else
658 addThis->_memPool->SetTracked();
659
660 if ( _lastChild ) {
661 TIXMLASSERT( _firstChild );
662 TIXMLASSERT( _lastChild->_next == 0 );
663 _lastChild->_next = addThis;
664 addThis->_prev = _lastChild;
665 _lastChild = addThis;
666
667 addThis->_next = 0;
668 }
669 else {
670 TIXMLASSERT( _firstChild == 0 );
671 _firstChild = _lastChild = addThis;
672
673 addThis->_prev = 0;
674 addThis->_next = 0;
675 }
676 addThis->_parent = this;
677 return addThis;
678}
679
680
681XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
682{
683 if (addThis->_document != _document)
684 return 0;
685
686 if (addThis->_parent)
687 addThis->_parent->Unlink( addThis );
688 else
689 addThis->_memPool->SetTracked();
690
691 if ( _firstChild ) {
692 TIXMLASSERT( _lastChild );
693 TIXMLASSERT( _firstChild->_prev == 0 );
694
695 _firstChild->_prev = addThis;
696 addThis->_next = _firstChild;
697 _firstChild = addThis;
698
699 addThis->_prev = 0;
700 }
701 else {
702 TIXMLASSERT( _lastChild == 0 );
703 _firstChild = _lastChild = addThis;
704
705 addThis->_prev = 0;
706 addThis->_next = 0;
707 }
708 addThis->_parent = this;
709 return addThis;
710}
711
712
713XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
714{
715 if (addThis->_document != _document)
716 return 0;
717
718 TIXMLASSERT( afterThis->_parent == this );
719
720 if ( afterThis->_parent != this ) {
721 return 0;
722 }
723
724 if ( afterThis->_next == 0 ) {
725 // The last node or the only node.
726 return InsertEndChild( addThis );
727 }
728 if (addThis->_parent)
729 addThis->_parent->Unlink( addThis );
730 else
731 addThis->_memPool->SetTracked();
732 addThis->_prev = afterThis;
733 addThis->_next = afterThis->_next;
734 afterThis->_next->_prev = addThis;
735 afterThis->_next = addThis;
736 addThis->_parent = this;
737 return addThis;
738}
739
740
741
742
743const XMLElement* XMLNode::FirstChildElement( const char* value ) const
744{
745 for( XMLNode* node=_firstChild; node; node=node->_next ) {
746 XMLElement* element = node->ToElement();
747 if ( element ) {
748 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
749 return element;
750 }
751 }
752 }
753 return 0;
754}
755
756
757const XMLElement* XMLNode::LastChildElement( const char* value ) const
758{
759 for( XMLNode* node=_lastChild; node; node=node->_prev ) {
760 XMLElement* element = node->ToElement();
761 if ( element ) {
762 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
763 return element;
764 }
765 }
766 }
767 return 0;
768}
769
770
771const XMLElement* XMLNode::NextSiblingElement( const char* value ) const
772{
773 for( XMLNode* element=this->_next; element; element = element->_next ) {
774 if ( element->ToElement()
775 && (!value || XMLUtil::StringEqual( value, element->Value() ))) {
776 return element->ToElement();
777 }
778 }
779 return 0;
780}
781
782
783const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const
784{
785 for( XMLNode* element=_prev; element; element = element->_prev ) {
786 if ( element->ToElement()
787 && (!value || XMLUtil::StringEqual( value, element->Value() ))) {
788 return element->ToElement();
789 }
790 }
791 return 0;
792}
793
794
795char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
796{
797 // This is a recursive method, but thinking about it "at the current level"
798 // it is a pretty simple flat list:
799 // <foo/>
800 // <!-- comment -->
801 //
802 // With a special case:
803 // <foo>
804 // </foo>
805 // <!-- comment -->
806 //
807 // Where the closing element (/foo) *must* be the next thing after the opening
808 // element, and the names must match. BUT the tricky bit is that the closing
809 // element will be read by the child.
810 //
811 // 'endTag' is the end tag for this node, it is returned by a call to a child.
812 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
813
814 while( p && *p ) {
815 XMLNode* node = 0;
816
817 p = _document->Identify( p, &node );
818 if ( p == 0 || node == 0 ) {
819 break;
820 }
821
822 StrPair endTag;
823 p = node->ParseDeep( p, &endTag );
824 if ( !p ) {
825 DELETE_NODE( node );
826 node = 0;
827 if ( !_document->Error() ) {
828 _document->SetError( XML_ERROR_PARSING, 0, 0 );
829 }
830 break;
831 }
832
833 // We read the end tag. Return it to the parent.
834 if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) {
835 if ( parentEnd ) {
836 *parentEnd = static_cast<XMLElement*>(node)->_value;
837 }
838 node->_memPool->SetTracked(); // created and then immediately deleted.
839 DELETE_NODE( node );
840 return p;
841 }
842
843 // Handle an end tag returned to this level.
844 // And handle a bunch of annoying errors.
845 XMLElement* ele = node->ToElement();
846 if ( ele ) {
847 if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) {
848 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
849 p = 0;
850 }
851 else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) {
852 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
853 p = 0;
854 }
855 else if ( !endTag.Empty() ) {
856 if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) {
857 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
858 p = 0;
859 }
860 }
861 }
862 if ( p == 0 ) {
863 DELETE_NODE( node );
864 node = 0;
865 }
866 if ( node ) {
867 this->InsertEndChild( node );
868 }
869 }
870 return 0;
871}
872
873// --------- XMLText ---------- //
874char* XMLText::ParseDeep( char* p, StrPair* )
875{
876 const char* start = p;
877 if ( this->CData() ) {
878 p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
879 if ( !p ) {
880 _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
881 }
882 return p;
883 }
884 else {
885 int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
886 if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
887 flags |= StrPair::COLLAPSE_WHITESPACE;
888 }
889
890 p = _value.ParseText( p, "<", flags );
891 if ( !p ) {
892 _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
893 }
894 if ( p && *p ) {
895 return p-1;
896 }
897 }
898 return 0;
899}
900
901
902XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
903{
904 if ( !doc ) {
905 doc = _document;
906 }
907 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
908 text->SetCData( this->CData() );
909 return text;
910}
911
912
913bool XMLText::ShallowEqual( const XMLNode* compare ) const
914{
915 return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() ));
916}
917
918
919bool XMLText::Accept( XMLVisitor* visitor ) const
920{
921 return visitor->Visit( *this );
922}
923
924
925// --------- XMLComment ---------- //
926
927XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
928{
929}
930
931
932XMLComment::~XMLComment()
933{
934}
935
936
937char* XMLComment::ParseDeep( char* p, StrPair* )
938{
939 // Comment parses as text.
940 const char* start = p;
941 p = _value.ParseText( p, "-->", StrPair::COMMENT );
942 if ( p == 0 ) {
943 _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
944 }
945 return p;
946}
947
948
949XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
950{
951 if ( !doc ) {
952 doc = _document;
953 }
954 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
955 return comment;
956}
957
958
959bool XMLComment::ShallowEqual( const XMLNode* compare ) const
960{
961 return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() ));
962}
963
964
965bool XMLComment::Accept( XMLVisitor* visitor ) const
966{
967 return visitor->Visit( *this );
968}
969
970
971// --------- XMLDeclaration ---------- //
972
973XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
974{
975}
976
977
978XMLDeclaration::~XMLDeclaration()
979{
980 //printf( "~XMLDeclaration\n" );
981}
982
983
984char* XMLDeclaration::ParseDeep( char* p, StrPair* )
985{
986 // Declaration parses as text.
987 const char* start = p;
988 p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
989 if ( p == 0 ) {
990 _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
991 }
992 return p;
993}
994
995
996XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
997{
998 if ( !doc ) {
999 doc = _document;
1000 }
1001 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
1002 return dec;
1003}
1004
1005
1006bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
1007{
1008 return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() ));
1009}
1010
1011
1012
1013bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
1014{
1015 return visitor->Visit( *this );
1016}
1017
1018// --------- XMLUnknown ---------- //
1019
1020XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
1021{
1022}
1023
1024
1025XMLUnknown::~XMLUnknown()
1026{
1027}
1028
1029
1030char* XMLUnknown::ParseDeep( char* p, StrPair* )
1031{
1032 // Unknown parses as text.
1033 const char* start = p;
1034
1035 p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
1036 if ( !p ) {
1037 _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
1038 }
1039 return p;
1040}
1041
1042
1043XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
1044{
1045 if ( !doc ) {
1046 doc = _document;
1047 }
1048 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
1049 return text;
1050}
1051
1052
1053bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
1054{
1055 return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() ));
1056}
1057
1058
1059bool XMLUnknown::Accept( XMLVisitor* visitor ) const
1060{
1061 return visitor->Visit( *this );
1062}
1063
1064// --------- XMLAttribute ---------- //
1065
1066const char* XMLAttribute::Name() const
1067{
1068 return _name.GetStr();
1069}
1070
1071const char* XMLAttribute::Value() const
1072{
1073 return _value.GetStr();
1074}
1075
1076char* XMLAttribute::ParseDeep( char* p, bool processEntities )
1077{
1078 // Parse using the name rules: bug fix, was using ParseText before
1079 p = _name.ParseName( p );
1080 if ( !p || !*p ) {
1081 return 0;
1082 }
1083
1084 // Skip white space before =
1085 p = XMLUtil::SkipWhiteSpace( p );
1086 if ( !p || *p != '=' ) {
1087 return 0;
1088 }
1089
1090 ++p; // move up to opening quote
1091 p = XMLUtil::SkipWhiteSpace( p );
1092 if ( *p != '\"' && *p != '\'' ) {
1093 return 0;
1094 }
1095
1096 char endTag[2] = { *p, 0 };
1097 ++p; // move past opening quote
1098
1099 p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
1100 return p;
1101}
1102
1103
1104void XMLAttribute::SetName( const char* n )
1105{
1106 _name.SetStr( n );
1107}
1108
1109
1110XMLError XMLAttribute::QueryIntValue( int* value ) const
1111{
1112 if ( XMLUtil::ToInt( Value(), value )) {
1113 return XML_NO_ERROR;
1114 }
1116}
1117
1118
1119XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
1120{
1121 if ( XMLUtil::ToUnsigned( Value(), value )) {
1122 return XML_NO_ERROR;
1123 }
1125}
1126
1127
1128XMLError XMLAttribute::QueryBoolValue( bool* value ) const
1129{
1130 if ( XMLUtil::ToBool( Value(), value )) {
1131 return XML_NO_ERROR;
1132 }
1134}
1135
1136
1137XMLError XMLAttribute::QueryFloatValue( float* value ) const
1138{
1139 if ( XMLUtil::ToFloat( Value(), value )) {
1140 return XML_NO_ERROR;
1141 }
1143}
1144
1145
1146XMLError XMLAttribute::QueryDoubleValue( double* value ) const
1147{
1148 if ( XMLUtil::ToDouble( Value(), value )) {
1149 return XML_NO_ERROR;
1150 }
1152}
1153
1154
1155void XMLAttribute::SetAttribute( const char* v )
1156{
1157 _value.SetStr( v );
1158}
1159
1160
1161void XMLAttribute::SetAttribute( int v )
1162{
1163 char buf[BUF_SIZE];
1164 XMLUtil::ToStr( v, buf, BUF_SIZE );
1165 _value.SetStr( buf );
1166}
1167
1168
1169void XMLAttribute::SetAttribute( unsigned v )
1170{
1171 char buf[BUF_SIZE];
1172 XMLUtil::ToStr( v, buf, BUF_SIZE );
1173 _value.SetStr( buf );
1174}
1175
1176
1177void XMLAttribute::SetAttribute( bool v )
1178{
1179 char buf[BUF_SIZE];
1180 XMLUtil::ToStr( v, buf, BUF_SIZE );
1181 _value.SetStr( buf );
1182}
1183
1184void XMLAttribute::SetAttribute( double v )
1185{
1186 char buf[BUF_SIZE];
1187 XMLUtil::ToStr( v, buf, BUF_SIZE );
1188 _value.SetStr( buf );
1189}
1190
1191void XMLAttribute::SetAttribute( float v )
1192{
1193 char buf[BUF_SIZE];
1194 XMLUtil::ToStr( v, buf, BUF_SIZE );
1195 _value.SetStr( buf );
1196}
1197
1198
1199// --------- XMLElement ---------- //
1200XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
1201 _closingType( 0 ),
1202 _rootAttribute( 0 )
1203{
1204}
1205
1206
1207XMLElement::~XMLElement()
1208{
1209 while( _rootAttribute ) {
1210 XMLAttribute* next = _rootAttribute->_next;
1211 DELETE_ATTRIBUTE( _rootAttribute );
1212 _rootAttribute = next;
1213 }
1214}
1215
1216
1217XMLAttribute* XMLElement::FindAttribute( const char* name )
1218{
1219 XMLAttribute* a = 0;
1220 for( a=_rootAttribute; a; a = a->_next ) {
1221 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1222 return a;
1223 }
1224 }
1225 return 0;
1226}
1227
1228
1229const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1230{
1231 XMLAttribute* a = 0;
1232 for( a=_rootAttribute; a; a = a->_next ) {
1233 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1234 return a;
1235 }
1236 }
1237 return 0;
1238}
1239
1240
1241const char* XMLElement::Attribute( const char* name, const char* value ) const
1242{
1243 const XMLAttribute* a = FindAttribute( name );
1244 if ( !a ) {
1245 return 0;
1246 }
1247 if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
1248 return a->Value();
1249 }
1250 return 0;
1251}
1252
1253
1254const char* XMLElement::GetText() const
1255{
1256 if ( FirstChild() && FirstChild()->ToText() ) {
1257 return FirstChild()->ToText()->Value();
1258 }
1259 return 0;
1260}
1261
1262
1263void XMLElement::SetText( const char* inText )
1264{
1265 if ( FirstChild() && FirstChild()->ToText() )
1266 FirstChild()->SetValue( inText );
1267 else {
1268 XMLText* theText = GetDocument()->NewText( inText );
1269 InsertFirstChild( theText );
1270 }
1271}
1272
1273
1274void XMLElement::SetText( int v )
1275{
1276 char buf[BUF_SIZE];
1277 XMLUtil::ToStr( v, buf, BUF_SIZE );
1278 SetText( buf );
1279}
1280
1281
1282void XMLElement::SetText( unsigned v )
1283{
1284 char buf[BUF_SIZE];
1285 XMLUtil::ToStr( v, buf, BUF_SIZE );
1286 SetText( buf );
1287}
1288
1289
1290void XMLElement::SetText( bool v )
1291{
1292 char buf[BUF_SIZE];
1293 XMLUtil::ToStr( v, buf, BUF_SIZE );
1294 SetText( buf );
1295}
1296
1297
1298void XMLElement::SetText( float v )
1299{
1300 char buf[BUF_SIZE];
1301 XMLUtil::ToStr( v, buf, BUF_SIZE );
1302 SetText( buf );
1303}
1304
1305
1306void XMLElement::SetText( double v )
1307{
1308 char buf[BUF_SIZE];
1309 XMLUtil::ToStr( v, buf, BUF_SIZE );
1310 SetText( buf );
1311}
1312
1313
1314XMLError XMLElement::QueryIntText( int* ival ) const
1315{
1316 if ( FirstChild() && FirstChild()->ToText() ) {
1317 const char* t = FirstChild()->ToText()->Value();
1318 if ( XMLUtil::ToInt( t, ival ) ) {
1319 return XML_SUCCESS;
1320 }
1322 }
1323 return XML_NO_TEXT_NODE;
1324}
1325
1326
1327XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
1328{
1329 if ( FirstChild() && FirstChild()->ToText() ) {
1330 const char* t = FirstChild()->ToText()->Value();
1331 if ( XMLUtil::ToUnsigned( t, uval ) ) {
1332 return XML_SUCCESS;
1333 }
1335 }
1336 return XML_NO_TEXT_NODE;
1337}
1338
1339
1340XMLError XMLElement::QueryBoolText( bool* bval ) const
1341{
1342 if ( FirstChild() && FirstChild()->ToText() ) {
1343 const char* t = FirstChild()->ToText()->Value();
1344 if ( XMLUtil::ToBool( t, bval ) ) {
1345 return XML_SUCCESS;
1346 }
1348 }
1349 return XML_NO_TEXT_NODE;
1350}
1351
1352
1353XMLError XMLElement::QueryDoubleText( double* dval ) const
1354{
1355 if ( FirstChild() && FirstChild()->ToText() ) {
1356 const char* t = FirstChild()->ToText()->Value();
1357 if ( XMLUtil::ToDouble( t, dval ) ) {
1358 return XML_SUCCESS;
1359 }
1361 }
1362 return XML_NO_TEXT_NODE;
1363}
1364
1365
1366XMLError XMLElement::QueryFloatText( float* fval ) const
1367{
1368 if ( FirstChild() && FirstChild()->ToText() ) {
1369 const char* t = FirstChild()->ToText()->Value();
1370 if ( XMLUtil::ToFloat( t, fval ) ) {
1371 return XML_SUCCESS;
1372 }
1374 }
1375 return XML_NO_TEXT_NODE;
1376}
1377
1378
1379
1380XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1381{
1382 XMLAttribute* last = 0;
1383 XMLAttribute* attrib = 0;
1384 for( attrib = _rootAttribute;
1385 attrib;
1386 last = attrib, attrib = attrib->_next ) {
1387 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1388 break;
1389 }
1390 }
1391 if ( !attrib ) {
1392 attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1393 attrib->_memPool = &_document->_attributePool;
1394 if ( last ) {
1395 last->_next = attrib;
1396 }
1397 else {
1398 _rootAttribute = attrib;
1399 }
1400 attrib->SetName( name );
1401 attrib->_memPool->SetTracked(); // always created and linked.
1402 }
1403 return attrib;
1404}
1405
1406
1407void XMLElement::DeleteAttribute( const char* name )
1408{
1409 XMLAttribute* prev = 0;
1410 for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
1411 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1412 if ( prev ) {
1413 prev->_next = a->_next;
1414 }
1415 else {
1416 _rootAttribute = a->_next;
1417 }
1418 DELETE_ATTRIBUTE( a );
1419 break;
1420 }
1421 prev = a;
1422 }
1423}
1424
1425
1426char* XMLElement::ParseAttributes( char* p )
1427{
1428 const char* start = p;
1429 XMLAttribute* prevAttribute = 0;
1430
1431 // Read the attributes.
1432 while( p ) {
1433 p = XMLUtil::SkipWhiteSpace( p );
1434 if ( !p || !(*p) ) {
1435 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
1436 return 0;
1437 }
1438
1439 // attribute.
1440 if (XMLUtil::IsNameStartChar( *p ) ) {
1441 XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1442 attrib->_memPool = &_document->_attributePool;
1443 attrib->_memPool->SetTracked();
1444
1445 p = attrib->ParseDeep( p, _document->ProcessEntities() );
1446 if ( !p || Attribute( attrib->Name() ) ) {
1447 DELETE_ATTRIBUTE( attrib );
1448 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
1449 return 0;
1450 }
1451 // There is a minor bug here: if the attribute in the source xml
1452 // document is duplicated, it will not be detected and the
1453 // attribute will be doubly added. However, tracking the 'prevAttribute'
1454 // avoids re-scanning the attribute list. Preferring performance for
1455 // now, may reconsider in the future.
1456 if ( prevAttribute ) {
1457 prevAttribute->_next = attrib;
1458 }
1459 else {
1460 _rootAttribute = attrib;
1461 }
1462 prevAttribute = attrib;
1463 }
1464 // end of the tag
1465 else if ( *p == '/' && *(p+1) == '>' ) {
1466 _closingType = CLOSED;
1467 return p+2; // done; sealed element.
1468 }
1469 // end of the tag
1470 else if ( *p == '>' ) {
1471 ++p;
1472 break;
1473 }
1474 else {
1475 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
1476 return 0;
1477 }
1478 }
1479 return p;
1480}
1481
1482
1483//
1484// <ele></ele>
1485// <ele>foo<b>bar</b></ele>
1486//
1487char* XMLElement::ParseDeep( char* p, StrPair* strPair )
1488{
1489 // Read the element name.
1490 p = XMLUtil::SkipWhiteSpace( p );
1491 if ( !p ) {
1492 return 0;
1493 }
1494
1495 // The closing element is the </element> form. It is
1496 // parsed just like a regular element then deleted from
1497 // the DOM.
1498 if ( *p == '/' ) {
1499 _closingType = CLOSING;
1500 ++p;
1501 }
1502
1503 p = _value.ParseName( p );
1504 if ( _value.Empty() ) {
1505 return 0;
1506 }
1507
1508 p = ParseAttributes( p );
1509 if ( !p || !*p || _closingType ) {
1510 return p;
1511 }
1512
1513 p = XMLNode::ParseDeep( p, strPair );
1514 return p;
1515}
1516
1517
1518
1519XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1520{
1521 if ( !doc ) {
1522 doc = _document;
1523 }
1524 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1525 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1526 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1527 }
1528 return element;
1529}
1530
1531
1532bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1533{
1534 const XMLElement* other = compare->ToElement();
1535 if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
1536
1537 const XMLAttribute* a=FirstAttribute();
1538 const XMLAttribute* b=other->FirstAttribute();
1539
1540 while ( a && b ) {
1541 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1542 return false;
1543 }
1544 a = a->Next();
1545 b = b->Next();
1546 }
1547 if ( a || b ) {
1548 // different count
1549 return false;
1550 }
1551 return true;
1552 }
1553 return false;
1554}
1555
1556
1557bool XMLElement::Accept( XMLVisitor* visitor ) const
1558{
1559 if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
1560 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
1561 if ( !node->Accept( visitor ) ) {
1562 break;
1563 }
1564 }
1565 }
1566 return visitor->VisitExit( *this );
1567}
1568
1569
1570// --------- XMLDocument ----------- //
1571XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) :
1572 XMLNode( 0 ),
1573 _writeBOM( false ),
1574 _processEntities( processEntities ),
1575 _errorID( XML_NO_ERROR ),
1576 _whitespace( whitespace ),
1577 _errorStr1( 0 ),
1578 _errorStr2( 0 ),
1579 _charBuffer( 0 )
1580{
1581 _document = this; // avoid warning about 'this' in initializer list
1582}
1583
1584
1585XMLDocument::~XMLDocument()
1586{
1587 DeleteChildren();
1588 delete [] _charBuffer;
1589
1590#if 0
1591 _textPool.Trace( "text" );
1592 _elementPool.Trace( "element" );
1593 _commentPool.Trace( "comment" );
1594 _attributePool.Trace( "attribute" );
1595#endif
1596
1597#ifdef DEBUG
1598 if ( Error() == false ) {
1599 TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() );
1600 TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
1601 TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() );
1602 TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() );
1603 }
1604#endif
1605}
1606
1607
1608void XMLDocument::Clear()
1609{
1610 DeleteChildren();
1611
1612 _errorID = XML_NO_ERROR;
1613 _errorStr1 = 0;
1614 _errorStr2 = 0;
1615
1616 delete [] _charBuffer;
1617 _charBuffer = 0;
1618}
1619
1620
1621XMLElement* XMLDocument::NewElement( const char* name )
1622{
1623 XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this );
1624 ele->_memPool = &_elementPool;
1625 ele->SetName( name );
1626 return ele;
1627}
1628
1629
1630XMLComment* XMLDocument::NewComment( const char* str )
1631{
1632 XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this );
1633 comment->_memPool = &_commentPool;
1634 comment->SetValue( str );
1635 return comment;
1636}
1637
1638
1639XMLText* XMLDocument::NewText( const char* str )
1640{
1641 XMLText* text = new (_textPool.Alloc()) XMLText( this );
1642 text->_memPool = &_textPool;
1643 text->SetValue( str );
1644 return text;
1645}
1646
1647
1648XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1649{
1650 XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this );
1651 dec->_memPool = &_commentPool;
1652 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
1653 return dec;
1654}
1655
1656
1657XMLUnknown* XMLDocument::NewUnknown( const char* str )
1658{
1659 XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this );
1660 unk->_memPool = &_commentPool;
1661 unk->SetValue( str );
1662 return unk;
1663}
1664
1665
1666XMLError XMLDocument::LoadFile( const char* filename )
1667{
1668 Clear();
1669 FILE* fp = 0;
1670
1671#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
1672 errno_t err = fopen_s(&fp, filename, "rb" );
1673 if ( !fp || err) {
1674#else
1675 fp = fopen( filename, "rb" );
1676 if ( !fp) {
1677#endif
1678 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
1679 return _errorID;
1680 }
1681 LoadFile( fp );
1682 fclose( fp );
1683 return _errorID;
1684}
1685
1686
1687XMLError XMLDocument::LoadFile( FILE* fp )
1688{
1689 Clear();
1690
1691 fseek( fp, 0, SEEK_SET );
1692 fgetc( fp );
1693 if ( ferror( fp ) != 0 ) {
1694 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1695 return _errorID;
1696 }
1697
1698 fseek( fp, 0, SEEK_END );
1699 size_t size = ftell( fp );
1700 fseek( fp, 0, SEEK_SET );
1701
1702 if ( size == 0 ) {
1703 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1704 return _errorID;
1705 }
1706
1707 _charBuffer = new char[size+1];
1708 size_t read = fread( _charBuffer, 1, size, fp );
1709 if ( read != size ) {
1710 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1711 return _errorID;
1712 }
1713
1714 _charBuffer[size] = 0;
1715
1716 const char* p = _charBuffer;
1717 p = XMLUtil::SkipWhiteSpace( p );
1718 p = XMLUtil::ReadBOM( p, &_writeBOM );
1719 if ( !p || !*p ) {
1720 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1721 return _errorID;
1722 }
1723
1724 ParseDeep( _charBuffer + (p-_charBuffer), 0 );
1725 return _errorID;
1726}
1727
1728
1729XMLError XMLDocument::SaveFile( const char* filename, bool compact )
1730{
1731 FILE* fp = 0;
1732#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
1733 errno_t err = fopen_s(&fp, filename, "w" );
1734 if ( !fp || err) {
1735#else
1736 fp = fopen( filename, "w" );
1737 if ( !fp) {
1738#endif
1739 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
1740 return _errorID;
1741 }
1742 SaveFile(fp, compact);
1743 fclose( fp );
1744 return _errorID;
1745}
1746
1747
1748XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
1749{
1750 XMLPrinter stream( fp, compact );
1751 Print( &stream );
1752 return _errorID;
1753}
1754
1755
1756XMLError XMLDocument::Parse( const char* p, size_t len )
1757{
1758 const char* start = p;
1759 Clear();
1760
1761 if ( len == 0 || !p || !*p ) {
1762 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1763 return _errorID;
1764 }
1765 if ( len == (size_t)(-1) ) {
1766 len = strlen( p );
1767 }
1768 _charBuffer = new char[ len+1 ];
1769 memcpy( _charBuffer, p, len );
1770 _charBuffer[len] = 0;
1771
1772 p = XMLUtil::SkipWhiteSpace( p );
1773 p = XMLUtil::ReadBOM( p, &_writeBOM );
1774 if ( !p || !*p ) {
1775 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1776 return _errorID;
1777 }
1778
1779 ptrdiff_t delta = p - start; // skip initial whitespace, BOM, etc.
1780 ParseDeep( _charBuffer+delta, 0 );
1781 return _errorID;
1782}
1783
1784
1785void XMLDocument::Print( XMLPrinter* streamer ) const
1786{
1787 XMLPrinter stdStreamer( stdout );
1788 if ( !streamer ) {
1789 streamer = &stdStreamer;
1790 }
1791 Accept( streamer );
1792}
1793
1794
1795void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 )
1796{
1797 _errorID = error;
1798 _errorStr1 = str1;
1799 _errorStr2 = str2;
1800}
1801
1802
1803void XMLDocument::PrintError() const
1804{
1805 if ( _errorID ) {
1806 static const int LEN = 20;
1807 char buf1[LEN] = { 0 };
1808 char buf2[LEN] = { 0 };
1809
1810 if ( _errorStr1 ) {
1811 TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 );
1812 }
1813 if ( _errorStr2 ) {
1814 TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 );
1815 }
1816
1817 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1818 _errorID, buf1, buf2 );
1819 }
1820}
1821
1822
1823XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
1824 _elementJustOpened( false ),
1825 _firstElement( true ),
1826 _fp( file ),
1827 _depth( depth ),
1828 _textDepth( -1 ),
1829 _processEntities( true ),
1830 _compactMode( compact )
1831{
1832 for( int i=0; i<ENTITY_RANGE; ++i ) {
1833 _entityFlag[i] = false;
1834 _restrictedEntityFlag[i] = false;
1835 }
1836 for( int i=0; i<NUM_ENTITIES; ++i ) {
1837 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1838 if ( entities[i].value < ENTITY_RANGE ) {
1839 _entityFlag[ (int)entities[i].value ] = true;
1840 }
1841 }
1842 _restrictedEntityFlag[(int)'&'] = true;
1843 _restrictedEntityFlag[(int)'<'] = true;
1844 _restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
1845 _buffer.Push( 0 );
1846}
1847
1848
1849void XMLPrinter::Print( const char* format, ... )
1850{
1851 va_list va;
1852 va_start( va, format );
1853
1854 if ( _fp ) {
1855 vfprintf( _fp, format, va );
1856 }
1857 else {
1858#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
1859 int len = _vscprintf( format, va );
1860#else
1861 int len = vsnprintf( 0, 0, format, va );
1862#endif
1863 // Close out and re-start the va-args
1864 va_end( va );
1865 va_start( va, format );
1866 char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator.
1867#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
1868 vsnprintf_s( p, len+1, _TRUNCATE, format, va );
1869#else
1870 vsnprintf( p, len+1, format, va );
1871#endif
1872 }
1873 va_end( va );
1874}
1875
1876
1877void XMLPrinter::PrintSpace( int depth )
1878{
1879 for( int i=0; i<depth; ++i ) {
1880 Print( " " );
1881 }
1882}
1883
1884
1885void XMLPrinter::PrintString( const char* p, bool restricted )
1886{
1887 // Look for runs of bytes between entities to print.
1888 const char* q = p;
1889 const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
1890
1891 if ( _processEntities ) {
1892 while ( *q ) {
1893 // Remember, char is sometimes signed. (How many times has that bitten me?)
1894 if ( *q > 0 && *q < ENTITY_RANGE ) {
1895 // Check for entities. If one is found, flush
1896 // the stream up until the entity, write the
1897 // entity, and keep looking.
1898 if ( flag[(unsigned)(*q)] ) {
1899 while ( p < q ) {
1900 Print( "%c", *p );
1901 ++p;
1902 }
1903 for( int i=0; i<NUM_ENTITIES; ++i ) {
1904 if ( entities[i].value == *q ) {
1905 Print( "&%s;", entities[i].pattern );
1906 break;
1907 }
1908 }
1909 ++p;
1910 }
1911 }
1912 ++q;
1913 }
1914 }
1915 // Flush the remaining string. This will be the entire
1916 // string if an entity wasn't found.
1917 if ( !_processEntities || (q-p > 0) ) {
1918 Print( "%s", p );
1919 }
1920}
1921
1922
1923void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
1924{
1925 if ( writeBOM ) {
1926 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1927 Print( "%s", bom );
1928 }
1929 if ( writeDec ) {
1930 PushDeclaration( "xml version=\"1.0\"" );
1931 }
1932}
1933
1934
1935void XMLPrinter::OpenElement( const char* name, bool compactMode )
1936{
1937 if ( _elementJustOpened ) {
1938 SealElement();
1939 }
1940 _stack.Push( name );
1941
1942 if ( _textDepth < 0 && !_firstElement && !compactMode ) {
1943 Print( "\n" );
1944 }
1945 if ( !compactMode ) {
1946 PrintSpace( _depth );
1947 }
1948
1949 Print( "<%s", name );
1950 _elementJustOpened = true;
1951 _firstElement = false;
1952 ++_depth;
1953}
1954
1955
1956void XMLPrinter::PushAttribute( const char* name, const char* value )
1957{
1958 TIXMLASSERT( _elementJustOpened );
1959 Print( " %s=\"", name );
1960 PrintString( value, false );
1961 Print( "\"" );
1962}
1963
1964
1965void XMLPrinter::PushAttribute( const char* name, int v )
1966{
1967 char buf[BUF_SIZE];
1968 XMLUtil::ToStr( v, buf, BUF_SIZE );
1969 PushAttribute( name, buf );
1970}
1971
1972
1973void XMLPrinter::PushAttribute( const char* name, unsigned v )
1974{
1975 char buf[BUF_SIZE];
1976 XMLUtil::ToStr( v, buf, BUF_SIZE );
1977 PushAttribute( name, buf );
1978}
1979
1980
1981void XMLPrinter::PushAttribute( const char* name, bool v )
1982{
1983 char buf[BUF_SIZE];
1984 XMLUtil::ToStr( v, buf, BUF_SIZE );
1985 PushAttribute( name, buf );
1986}
1987
1988
1989void XMLPrinter::PushAttribute( const char* name, double v )
1990{
1991 char buf[BUF_SIZE];
1992 XMLUtil::ToStr( v, buf, BUF_SIZE );
1993 PushAttribute( name, buf );
1994}
1995
1996
1997void XMLPrinter::CloseElement( bool compactMode )
1998{
1999 --_depth;
2000 const char* name = _stack.Pop();
2001
2002 if ( _elementJustOpened ) {
2003 Print( "/>" );
2004 }
2005 else {
2006 if ( _textDepth < 0 && !compactMode) {
2007 Print( "\n" );
2008 PrintSpace( _depth );
2009 }
2010 Print( "</%s>", name );
2011 }
2012
2013 if ( _textDepth == _depth ) {
2014 _textDepth = -1;
2015 }
2016 if ( _depth == 0 && !compactMode) {
2017 Print( "\n" );
2018 }
2019 _elementJustOpened = false;
2020}
2021
2022
2023void XMLPrinter::SealElement()
2024{
2025 _elementJustOpened = false;
2026 Print( ">" );
2027}
2028
2029
2030void XMLPrinter::PushText( const char* text, bool cdata )
2031{
2032 _textDepth = _depth-1;
2033
2034 if ( _elementJustOpened ) {
2035 SealElement();
2036 }
2037 if ( cdata ) {
2038 Print( "<![CDATA[" );
2039 Print( "%s", text );
2040 Print( "]]>" );
2041 }
2042 else {
2043 PrintString( text, true );
2044 }
2045}
2046
2047void XMLPrinter::PushText( int value )
2048{
2049 char buf[BUF_SIZE];
2050 XMLUtil::ToStr( value, buf, BUF_SIZE );
2051 PushText( buf, false );
2052}
2053
2054
2055void XMLPrinter::PushText( unsigned value )
2056{
2057 char buf[BUF_SIZE];
2058 XMLUtil::ToStr( value, buf, BUF_SIZE );
2059 PushText( buf, false );
2060}
2061
2062
2063void XMLPrinter::PushText( bool value )
2064{
2065 char buf[BUF_SIZE];
2066 XMLUtil::ToStr( value, buf, BUF_SIZE );
2067 PushText( buf, false );
2068}
2069
2070
2071void XMLPrinter::PushText( float value )
2072{
2073 char buf[BUF_SIZE];
2074 XMLUtil::ToStr( value, buf, BUF_SIZE );
2075 PushText( buf, false );
2076}
2077
2078
2079void XMLPrinter::PushText( double value )
2080{
2081 char buf[BUF_SIZE];
2082 XMLUtil::ToStr( value, buf, BUF_SIZE );
2083 PushText( buf, false );
2084}
2085
2086
2087void XMLPrinter::PushComment( const char* comment )
2088{
2089 if ( _elementJustOpened ) {
2090 SealElement();
2091 }
2092 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2093 Print( "\n" );
2094 PrintSpace( _depth );
2095 }
2096 _firstElement = false;
2097 Print( "<!--%s-->", comment );
2098}
2099
2100
2101void XMLPrinter::PushDeclaration( const char* value )
2102{
2103 if ( _elementJustOpened ) {
2104 SealElement();
2105 }
2106 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2107 Print( "\n" );
2108 PrintSpace( _depth );
2109 }
2110 _firstElement = false;
2111 Print( "<?%s?>", value );
2112}
2113
2114
2115void XMLPrinter::PushUnknown( const char* value )
2116{
2117 if ( _elementJustOpened ) {
2118 SealElement();
2119 }
2120 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2121 Print( "\n" );
2122 PrintSpace( _depth );
2123 }
2124 _firstElement = false;
2125 Print( "<!%s>", value );
2126}
2127
2128
2129bool XMLPrinter::VisitEnter( const XMLDocument& doc )
2130{
2131 _processEntities = doc.ProcessEntities();
2132 if ( doc.HasBOM() ) {
2133 PushHeader( true, false );
2134 }
2135 return true;
2136}
2137
2138
2139bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
2140{
2141 const XMLElement* parentElem = element.Parent()->ToElement();
2142 bool compactMode = parentElem ? CompactMode(*parentElem) : _compactMode;
2143 OpenElement( element.Name(), compactMode );
2144 while ( attribute ) {
2145 PushAttribute( attribute->Name(), attribute->Value() );
2146 attribute = attribute->Next();
2147 }
2148 return true;
2149}
2150
2151
2152bool XMLPrinter::VisitExit( const XMLElement& element )
2153{
2154 CloseElement( CompactMode(element) );
2155 return true;
2156}
2157
2158
2159bool XMLPrinter::Visit( const XMLText& text )
2160{
2161 PushText( text.Value(), text.CData() );
2162 return true;
2163}
2164
2165
2166bool XMLPrinter::Visit( const XMLComment& comment )
2167{
2168 PushComment( comment.Value() );
2169 return true;
2170}
2171
2172bool XMLPrinter::Visit( const XMLDeclaration& declaration )
2173{
2174 PushDeclaration( declaration.Value() );
2175 return true;
2176}
2177
2178
2179bool XMLPrinter::Visit( const XMLUnknown& unknown )
2180{
2181 PushUnknown( unknown.Value() );
2182 return true;
2183}
2184
2185} // namespace tinyxml2
2186
void SetStr(const char *str, int flags=0)
void Set(char *start, char *end, int flags)
Definition tinyxml2.h:162
char * ParseName(char *in)
@ NEEDS_NEWLINE_NORMALIZATION
Definition tinyxml2.h:148
const char * GetStr()
char * ParseText(char *in, const char *endTag, int strFlags)
char * Identify(char *p, XMLNode **node)
virtual bool Accept(XMLVisitor *visitor) const
friend class XMLElement
Definition tinyxml2.h:1503
XMLNode(XMLDocument *)
const XMLNode * FirstChild() const
Get the first child node, or null if none exists.
Definition tinyxml2.h:675
const XMLNode * NextSibling() const
Get the next (right) sibling node of this node.
Definition tinyxml2.h:727
static const char * GetCharacterRef(const char *p, char *value, int *length)
static const char * SkipWhiteSpace(const char *p)
Definition tinyxml2.h:489
static bool IsWhiteSpace(char p)
Definition tinyxml2.h:501
static bool ToUnsigned(const char *str, unsigned *value)
static void ToStr(int v, char *buffer, int bufferSize)
static bool ToDouble(const char *str, double *value)
static void ConvertUTF32ToUTF8(unsigned long input, char *output, int *length)
static bool IsNameStartChar(unsigned char ch)
Definition tinyxml2.h:505
static bool ToFloat(const char *str, float *value)
static bool ToInt(const char *str, int *value)
static bool StringEqual(const char *p, const char *q, int nChar=INT_MAX)
Definition tinyxml2.h:518
static const char * ReadBOM(const char *p, bool *hasBOM)
static bool ToBool(const char *str, bool *value)
@ XML_SUCCESS
Definition tinyxml2.h:998
@ XML_NO_TEXT_NODE
Definition tinyxml2.h:1020
@ XML_NO_ERROR
Definition tinyxml2.h:997
@ XML_WRONG_ATTRIBUTE_TYPE
Definition tinyxml2.h:1001
@ XML_CAN_NOT_CONVERT_TEXT
Definition tinyxml2.h:1019
STBI_EXTERN unsigned long const char * str
Definition stb_image.h:1182
STBI_EXTERN unsigned long flags
Definition stb_image.h:1182
int TIXML_SNPRINTF(char *buffer, size_t size, const char *format,...)
Definition tinyxml2.h:103