ovr_sdk

view 3rdParty/TinyXml/tinyxml2.cpp @ 0:1b39a1b46319

initial 0.4.4
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 14 Jan 2015 06:51:16 +0200
parents
children
line source
1 /*
2 Original code by Lee Thomason (www.grinninglizard.com)
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any
6 damages arising from the use of this software.
8 Permission is granted to anyone to use this software for any
9 purpose, including commercial applications, and to alter it and
10 redistribute it freely, subject to the following restrictions:
12 1. The origin of this software must not be misrepresented; you must
13 not claim that you wrote the original software. If you use this
14 software in a product, an acknowledgment in the product documentation
15 would be appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and
18 must not be misrepresented as being the original software.
20 3. This notice may not be removed or altered from any source
21 distribution.
22 */
24 #include "tinyxml2.h"
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
33 static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
34 static const char LF = LINE_FEED;
35 static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
36 static const char CR = CARRIAGE_RETURN;
37 static const char SINGLE_QUOTE = '\'';
38 static const char DOUBLE_QUOTE = '\"';
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
44 static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
45 static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
46 static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
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 }
64 namespace tinyxml2
65 {
67 struct Entity {
68 const char* pattern;
69 int length;
70 char value;
71 };
73 static const int NUM_ENTITIES = 5;
74 static 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 };
83 StrPair::~StrPair()
84 {
85 Reset();
86 }
89 void StrPair::Reset()
90 {
91 if ( _flags & NEEDS_DELETE ) {
92 delete [] _start;
93 }
94 _flags = 0;
95 _start = 0;
96 _end = 0;
97 }
100 void 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 }
111 char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
112 {
113 TIXMLASSERT( endTag && *endTag );
115 char* start = p; // fixme: hides a member
116 char endChar = *endTag;
117 size_t length = strlen( endTag );
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 }
131 char* StrPair::ParseName( char* p )
132 {
133 char* start = p;
135 if ( !start || !(*start) ) {
136 return 0;
137 }
139 while( *p && (
140 XMLUtil::IsAlphaNum( (unsigned char) *p )
141 || *p == '_'
142 || *p == ':'
143 || (*p == '-' && p>start ) // can be in a name, but not lead it.
144 || (*p == '.' && p>start ) )) { // can be in a name, but not lead it.
145 ++p;
146 }
148 if ( p > start ) {
149 Set( start, p, 0 );
150 return p;
151 }
152 return 0;
153 }
156 void StrPair::CollapseWhitespace()
157 {
158 // Trim leading space.
159 _start = XMLUtil::SkipWhiteSpace( _start );
161 if ( _start && *_start ) {
162 char* p = _start; // the read pointer
163 char* q = _start; // the write pointer
165 while( *p ) {
166 if ( XMLUtil::IsWhiteSpace( *p )) {
167 p = XMLUtil::SkipWhiteSpace( p );
168 if ( *p == 0 ) {
169 break; // don't write to q; this trims the trailing space.
170 }
171 *q = ' ';
172 ++q;
173 }
174 *q = *p;
175 ++q;
176 ++p;
177 }
178 *q = 0;
179 }
180 }
183 const char* StrPair::GetStr()
184 {
185 if ( _flags & NEEDS_FLUSH ) {
186 *_end = 0;
187 _flags ^= NEEDS_FLUSH;
189 if ( _flags ) {
190 char* p = _start; // the read pointer
191 char* q = _start; // the write pointer
193 while( p < _end ) {
194 if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
195 // CR-LF pair becomes LF
196 // CR alone becomes LF
197 // LF-CR becomes LF
198 if ( *(p+1) == LF ) {
199 p += 2;
200 }
201 else {
202 ++p;
203 }
204 *q++ = LF;
205 }
206 else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
207 if ( *(p+1) == CR ) {
208 p += 2;
209 }
210 else {
211 ++p;
212 }
213 *q++ = LF;
214 }
215 else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
216 // Entities handled by tinyXML2:
217 // - special entities in the entity table [in/out]
218 // - numeric character reference [in]
219 // &#20013; or &#x4e2d;
221 if ( *(p+1) == '#' ) {
222 char buf[10] = { 0 };
223 int len;
224 p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
225 for( int i=0; i<len; ++i ) {
226 *q++ = buf[i];
227 }
228 TIXMLASSERT( q <= p );
229 }
230 else {
231 int i=0;
232 for(; i<NUM_ENTITIES; ++i ) {
233 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
234 && *(p+entities[i].length+1) == ';' ) {
235 // Found an entity convert;
236 *q = entities[i].value;
237 ++q;
238 p += entities[i].length + 2;
239 break;
240 }
241 }
242 if ( i == NUM_ENTITIES ) {
243 // fixme: treat as error?
244 ++p;
245 ++q;
246 }
247 }
248 }
249 else {
250 *q = *p;
251 ++p;
252 ++q;
253 }
254 }
255 *q = 0;
256 }
257 // The loop below has plenty going on, and this
258 // is a less useful mode. Break it out.
259 if ( _flags & COLLAPSE_WHITESPACE ) {
260 CollapseWhitespace();
261 }
262 _flags = (_flags & NEEDS_DELETE);
263 }
264 return _start;
265 }
270 // --------- XMLUtil ----------- //
272 const char* XMLUtil::ReadBOM( const char* p, bool* bom )
273 {
274 *bom = false;
275 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
276 // Check for BOM:
277 if ( *(pu+0) == TIXML_UTF_LEAD_0
278 && *(pu+1) == TIXML_UTF_LEAD_1
279 && *(pu+2) == TIXML_UTF_LEAD_2 ) {
280 *bom = true;
281 p += 3;
282 }
283 return p;
284 }
287 void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
288 {
289 const unsigned long BYTE_MASK = 0xBF;
290 const unsigned long BYTE_MARK = 0x80;
291 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
293 if (input < 0x80) {
294 *length = 1;
295 }
296 else if ( input < 0x800 ) {
297 *length = 2;
298 }
299 else if ( input < 0x10000 ) {
300 *length = 3;
301 }
302 else if ( input < 0x200000 ) {
303 *length = 4;
304 }
305 else {
306 *length = 0; // This code won't covert this correctly anyway.
307 return;
308 }
310 output += *length;
312 // Scary scary fall throughs.
313 switch (*length) {
314 case 4:
315 --output;
316 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
317 input >>= 6;
318 case 3:
319 --output;
320 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
321 input >>= 6;
322 case 2:
323 --output;
324 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
325 input >>= 6;
326 case 1:
327 --output;
328 *output = (char)(input | FIRST_BYTE_MARK[*length]);
329 default:
330 break;
331 }
332 }
335 const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
336 {
337 // Presume an entity, and pull it out.
338 *length = 0;
340 if ( *(p+1) == '#' && *(p+2) ) {
341 unsigned long ucs = 0;
342 ptrdiff_t delta = 0;
343 unsigned mult = 1;
345 if ( *(p+2) == 'x' ) {
346 // Hexadecimal.
347 if ( !*(p+3) ) {
348 return 0;
349 }
351 const char* q = p+3;
352 q = strchr( q, ';' );
354 if ( !q || !*q ) {
355 return 0;
356 }
358 delta = q-p;
359 --q;
361 while ( *q != 'x' ) {
362 if ( *q >= '0' && *q <= '9' ) {
363 ucs += mult * (*q - '0');
364 }
365 else if ( *q >= 'a' && *q <= 'f' ) {
366 ucs += mult * (*q - 'a' + 10);
367 }
368 else if ( *q >= 'A' && *q <= 'F' ) {
369 ucs += mult * (*q - 'A' + 10 );
370 }
371 else {
372 return 0;
373 }
374 mult *= 16;
375 --q;
376 }
377 }
378 else {
379 // Decimal.
380 if ( !*(p+2) ) {
381 return 0;
382 }
384 const char* q = p+2;
385 q = strchr( q, ';' );
387 if ( !q || !*q ) {
388 return 0;
389 }
391 delta = q-p;
392 --q;
394 while ( *q != '#' ) {
395 if ( *q >= '0' && *q <= '9' ) {
396 ucs += mult * (*q - '0');
397 }
398 else {
399 return 0;
400 }
401 mult *= 10;
402 --q;
403 }
404 }
405 // convert the UCS to UTF-8
406 ConvertUTF32ToUTF8( ucs, value, length );
407 return p + delta + 1;
408 }
409 return p+1;
410 }
413 void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
414 {
415 TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
416 }
419 void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
420 {
421 TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
422 }
425 void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
426 {
427 TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 );
428 }
431 void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
432 {
433 TIXML_SNPRINTF( buffer, bufferSize, "%g", v );
434 }
437 void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
438 {
439 TIXML_SNPRINTF( buffer, bufferSize, "%g", v );
440 }
443 bool XMLUtil::ToInt( const char* str, int* value )
444 {
445 if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
446 return true;
447 }
448 return false;
449 }
451 bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
452 {
453 if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
454 return true;
455 }
456 return false;
457 }
459 bool XMLUtil::ToBool( const char* str, bool* value )
460 {
461 int ival = 0;
462 if ( ToInt( str, &ival )) {
463 *value = (ival==0) ? false : true;
464 return true;
465 }
466 if ( StringEqual( str, "true" ) ) {
467 *value = true;
468 return true;
469 }
470 else if ( StringEqual( str, "false" ) ) {
471 *value = false;
472 return true;
473 }
474 return false;
475 }
478 bool XMLUtil::ToFloat( const char* str, float* value )
479 {
480 if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
481 return true;
482 }
483 return false;
484 }
486 bool XMLUtil::ToDouble( const char* str, double* value )
487 {
488 if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
489 return true;
490 }
491 return false;
492 }
495 char* XMLDocument::Identify( char* p, XMLNode** node )
496 {
497 XMLNode* returnNode = 0;
498 char* start = p;
499 p = XMLUtil::SkipWhiteSpace( p );
500 if( !p || !*p ) {
501 return p;
502 }
504 // What is this thing?
505 // - Elements start with a letter or underscore, but xml is reserved.
506 // - Comments: <!--
507 // - Decleration: <?
508 // - Everthing else is unknown to tinyxml.
509 //
511 static const char* xmlHeader = { "<?" };
512 static const char* commentHeader = { "<!--" };
513 static const char* dtdHeader = { "<!" };
514 static const char* cdataHeader = { "<![CDATA[" };
515 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
517 static const int xmlHeaderLen = 2;
518 static const int commentHeaderLen = 4;
519 static const int dtdHeaderLen = 2;
520 static const int cdataHeaderLen = 9;
521 static const int elementHeaderLen = 1;
523 #if defined(_MSC_VER)
524 #pragma warning ( push )
525 #pragma warning ( disable : 4127 )
526 #endif
527 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
528 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
529 #if defined(_MSC_VER)
530 #pragma warning (pop)
531 #endif
532 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
533 returnNode = new (_commentPool.Alloc()) XMLDeclaration( this );
534 returnNode->_memPool = &_commentPool;
535 p += xmlHeaderLen;
536 }
537 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
538 returnNode = new (_commentPool.Alloc()) XMLComment( this );
539 returnNode->_memPool = &_commentPool;
540 p += commentHeaderLen;
541 }
542 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
543 XMLText* text = new (_textPool.Alloc()) XMLText( this );
544 returnNode = text;
545 returnNode->_memPool = &_textPool;
546 p += cdataHeaderLen;
547 text->SetCData( true );
548 }
549 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
550 returnNode = new (_commentPool.Alloc()) XMLUnknown( this );
551 returnNode->_memPool = &_commentPool;
552 p += dtdHeaderLen;
553 }
554 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
555 returnNode = new (_elementPool.Alloc()) XMLElement( this );
556 returnNode->_memPool = &_elementPool;
557 p += elementHeaderLen;
558 }
559 else {
560 returnNode = new (_textPool.Alloc()) XMLText( this );
561 returnNode->_memPool = &_textPool;
562 p = start; // Back it up, all the text counts.
563 }
565 *node = returnNode;
566 return p;
567 }
570 bool XMLDocument::Accept( XMLVisitor* visitor ) const
571 {
572 if ( visitor->VisitEnter( *this ) ) {
573 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
574 if ( !node->Accept( visitor ) ) {
575 break;
576 }
577 }
578 }
579 return visitor->VisitExit( *this );
580 }
583 // --------- XMLNode ----------- //
585 XMLNode::XMLNode( XMLDocument* doc ) :
586 _document( doc ),
587 _parent( 0 ),
588 _firstChild( 0 ), _lastChild( 0 ),
589 _prev( 0 ), _next( 0 )
590 {
591 }
594 XMLNode::~XMLNode()
595 {
596 DeleteChildren();
597 if ( _parent ) {
598 _parent->Unlink( this );
599 }
600 }
603 void XMLNode::SetValue( const char* str, bool staticMem )
604 {
605 if ( staticMem ) {
606 _value.SetInternedStr( str );
607 }
608 else {
609 _value.SetStr( str );
610 }
611 }
614 void XMLNode::DeleteChildren()
615 {
616 while( _firstChild ) {
617 XMLNode* node = _firstChild;
618 Unlink( node );
620 DELETE_NODE( node );
621 }
622 _firstChild = _lastChild = 0;
623 }
626 void XMLNode::Unlink( XMLNode* child )
627 {
628 TIXMLASSERT( child->_parent == this );
629 if ( child == _firstChild ) {
630 _firstChild = _firstChild->_next;
631 }
632 if ( child == _lastChild ) {
633 _lastChild = _lastChild->_prev;
634 }
636 if ( child->_prev ) {
637 child->_prev->_next = child->_next;
638 }
639 if ( child->_next ) {
640 child->_next->_prev = child->_prev;
641 }
642 child->_parent = 0;
643 }
646 void XMLNode::DeleteChild( XMLNode* node )
647 {
648 TIXMLASSERT( node->_parent == this );
649 DELETE_NODE( node );
650 }
653 XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
654 {
655 if ( _lastChild ) {
656 TIXMLASSERT( _firstChild );
657 TIXMLASSERT( _lastChild->_next == 0 );
658 _lastChild->_next = addThis;
659 addThis->_prev = _lastChild;
660 _lastChild = addThis;
662 addThis->_next = 0;
663 }
664 else {
665 TIXMLASSERT( _firstChild == 0 );
666 _firstChild = _lastChild = addThis;
668 addThis->_prev = 0;
669 addThis->_next = 0;
670 }
671 addThis->_parent = this;
672 addThis->_memPool->SetTracked();
673 return addThis;
674 }
677 XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
678 {
679 if ( _firstChild ) {
680 TIXMLASSERT( _lastChild );
681 TIXMLASSERT( _firstChild->_prev == 0 );
683 _firstChild->_prev = addThis;
684 addThis->_next = _firstChild;
685 _firstChild = addThis;
687 addThis->_prev = 0;
688 }
689 else {
690 TIXMLASSERT( _lastChild == 0 );
691 _firstChild = _lastChild = addThis;
693 addThis->_prev = 0;
694 addThis->_next = 0;
695 }
696 addThis->_parent = this;
697 addThis->_memPool->SetTracked();
698 return addThis;
699 }
702 XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
703 {
704 TIXMLASSERT( afterThis->_parent == this );
705 if ( afterThis->_parent != this ) {
706 return 0;
707 }
709 if ( afterThis->_next == 0 ) {
710 // The last node or the only node.
711 return InsertEndChild( addThis );
712 }
713 addThis->_prev = afterThis;
714 addThis->_next = afterThis->_next;
715 afterThis->_next->_prev = addThis;
716 afterThis->_next = addThis;
717 addThis->_parent = this;
718 addThis->_memPool->SetTracked();
719 return addThis;
720 }
725 const XMLElement* XMLNode::FirstChildElement( const char* value ) const
726 {
727 for( XMLNode* node=_firstChild; node; node=node->_next ) {
728 XMLElement* element = node->ToElement();
729 if ( element ) {
730 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
731 return element;
732 }
733 }
734 }
735 return 0;
736 }
739 const XMLElement* XMLNode::LastChildElement( const char* value ) const
740 {
741 for( XMLNode* node=_lastChild; node; node=node->_prev ) {
742 XMLElement* element = node->ToElement();
743 if ( element ) {
744 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
745 return element;
746 }
747 }
748 }
749 return 0;
750 }
753 const XMLElement* XMLNode::NextSiblingElement( const char* value ) const
754 {
755 for( XMLNode* element=this->_next; element; element = element->_next ) {
756 if ( element->ToElement()
757 && (!value || XMLUtil::StringEqual( value, element->Value() ))) {
758 return element->ToElement();
759 }
760 }
761 return 0;
762 }
765 const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const
766 {
767 for( XMLNode* element=_prev; element; element = element->_prev ) {
768 if ( element->ToElement()
769 && (!value || XMLUtil::StringEqual( value, element->Value() ))) {
770 return element->ToElement();
771 }
772 }
773 return 0;
774 }
777 char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
778 {
779 // This is a recursive method, but thinking about it "at the current level"
780 // it is a pretty simple flat list:
781 // <foo/>
782 // <!-- comment -->
783 //
784 // With a special case:
785 // <foo>
786 // </foo>
787 // <!-- comment -->
788 //
789 // Where the closing element (/foo) *must* be the next thing after the opening
790 // element, and the names must match. BUT the tricky bit is that the closing
791 // element will be read by the child.
792 //
793 // 'endTag' is the end tag for this node, it is returned by a call to a child.
794 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
796 while( p && *p ) {
797 XMLNode* node = 0;
799 p = _document->Identify( p, &node );
800 if ( p == 0 || node == 0 ) {
801 break;
802 }
804 StrPair endTag;
805 p = node->ParseDeep( p, &endTag );
806 if ( !p ) {
807 DELETE_NODE( node );
808 node = 0;
809 if ( !_document->Error() ) {
810 _document->SetError( XML_ERROR_PARSING, 0, 0 );
811 }
812 break;
813 }
815 // We read the end tag. Return it to the parent.
816 if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) {
817 if ( parentEnd ) {
818 *parentEnd = static_cast<XMLElement*>(node)->_value;
819 }
820 node->_memPool->SetTracked(); // created and then immediately deleted.
821 DELETE_NODE( node );
822 return p;
823 }
825 // Handle an end tag returned to this level.
826 // And handle a bunch of annoying errors.
827 XMLElement* ele = node->ToElement();
828 if ( ele ) {
829 if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) {
830 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
831 p = 0;
832 }
833 else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) {
834 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
835 p = 0;
836 }
837 else if ( !endTag.Empty() ) {
838 if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) {
839 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
840 p = 0;
841 }
842 }
843 }
844 if ( p == 0 ) {
845 DELETE_NODE( node );
846 node = 0;
847 }
848 if ( node ) {
849 this->InsertEndChild( node );
850 }
851 }
852 return 0;
853 }
855 // --------- XMLText ---------- //
856 char* XMLText::ParseDeep( char* p, StrPair* )
857 {
858 const char* start = p;
859 if ( this->CData() ) {
860 p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
861 if ( !p ) {
862 _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
863 }
864 return p;
865 }
866 else {
867 int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
868 if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
869 flags |= StrPair::COLLAPSE_WHITESPACE;
870 }
872 p = _value.ParseText( p, "<", flags );
873 if ( !p ) {
874 _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
875 }
876 if ( p && *p ) {
877 return p-1;
878 }
879 }
880 return 0;
881 }
884 XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
885 {
886 if ( !doc ) {
887 doc = _document;
888 }
889 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
890 text->SetCData( this->CData() );
891 return text;
892 }
895 bool XMLText::ShallowEqual( const XMLNode* compare ) const
896 {
897 return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() ));
898 }
901 bool XMLText::Accept( XMLVisitor* visitor ) const
902 {
903 return visitor->Visit( *this );
904 }
907 // --------- XMLComment ---------- //
909 XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
910 {
911 }
914 XMLComment::~XMLComment()
915 {
916 }
919 char* XMLComment::ParseDeep( char* p, StrPair* )
920 {
921 // Comment parses as text.
922 const char* start = p;
923 p = _value.ParseText( p, "-->", StrPair::COMMENT );
924 if ( p == 0 ) {
925 _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
926 }
927 return p;
928 }
931 XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
932 {
933 if ( !doc ) {
934 doc = _document;
935 }
936 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
937 return comment;
938 }
941 bool XMLComment::ShallowEqual( const XMLNode* compare ) const
942 {
943 return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() ));
944 }
947 bool XMLComment::Accept( XMLVisitor* visitor ) const
948 {
949 return visitor->Visit( *this );
950 }
953 // --------- XMLDeclaration ---------- //
955 XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
956 {
957 }
960 XMLDeclaration::~XMLDeclaration()
961 {
962 //printf( "~XMLDeclaration\n" );
963 }
966 char* XMLDeclaration::ParseDeep( char* p, StrPair* )
967 {
968 // Declaration parses as text.
969 const char* start = p;
970 p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
971 if ( p == 0 ) {
972 _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
973 }
974 return p;
975 }
978 XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
979 {
980 if ( !doc ) {
981 doc = _document;
982 }
983 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
984 return dec;
985 }
988 bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
989 {
990 return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() ));
991 }
995 bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
996 {
997 return visitor->Visit( *this );
998 }
1000 // --------- XMLUnknown ---------- //
1002 XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
1007 XMLUnknown::~XMLUnknown()
1012 char* XMLUnknown::ParseDeep( char* p, StrPair* )
1014 // Unknown parses as text.
1015 const char* start = p;
1017 p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
1018 if ( !p ) {
1019 _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
1021 return p;
1025 XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
1027 if ( !doc ) {
1028 doc = _document;
1030 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
1031 return text;
1035 bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
1037 return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() ));
1041 bool XMLUnknown::Accept( XMLVisitor* visitor ) const
1043 return visitor->Visit( *this );
1046 // --------- XMLAttribute ---------- //
1047 char* XMLAttribute::ParseDeep( char* p, bool processEntities )
1049 // Parse using the name rules: bug fix, was using ParseText before
1050 p = _name.ParseName( p );
1051 if ( !p || !*p ) {
1052 return 0;
1055 // Skip white space before =
1056 p = XMLUtil::SkipWhiteSpace( p );
1057 if ( !p || *p != '=' ) {
1058 return 0;
1061 ++p; // move up to opening quote
1062 p = XMLUtil::SkipWhiteSpace( p );
1063 if ( *p != '\"' && *p != '\'' ) {
1064 return 0;
1067 char endTag[2] = { *p, 0 };
1068 ++p; // move past opening quote
1070 p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
1071 return p;
1075 void XMLAttribute::SetName( const char* n )
1077 _name.SetStr( n );
1081 XMLError XMLAttribute::QueryIntValue( int* value ) const
1083 if ( XMLUtil::ToInt( Value(), value )) {
1084 return XML_NO_ERROR;
1086 return XML_WRONG_ATTRIBUTE_TYPE;
1090 XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
1092 if ( XMLUtil::ToUnsigned( Value(), value )) {
1093 return XML_NO_ERROR;
1095 return XML_WRONG_ATTRIBUTE_TYPE;
1099 XMLError XMLAttribute::QueryBoolValue( bool* value ) const
1101 if ( XMLUtil::ToBool( Value(), value )) {
1102 return XML_NO_ERROR;
1104 return XML_WRONG_ATTRIBUTE_TYPE;
1108 XMLError XMLAttribute::QueryFloatValue( float* value ) const
1110 if ( XMLUtil::ToFloat( Value(), value )) {
1111 return XML_NO_ERROR;
1113 return XML_WRONG_ATTRIBUTE_TYPE;
1117 XMLError XMLAttribute::QueryDoubleValue( double* value ) const
1119 if ( XMLUtil::ToDouble( Value(), value )) {
1120 return XML_NO_ERROR;
1122 return XML_WRONG_ATTRIBUTE_TYPE;
1126 void XMLAttribute::SetAttribute( const char* v )
1128 _value.SetStr( v );
1132 void XMLAttribute::SetAttribute( int v )
1134 char buf[BUF_SIZE];
1135 XMLUtil::ToStr( v, buf, BUF_SIZE );
1136 _value.SetStr( buf );
1140 void XMLAttribute::SetAttribute( unsigned v )
1142 char buf[BUF_SIZE];
1143 XMLUtil::ToStr( v, buf, BUF_SIZE );
1144 _value.SetStr( buf );
1148 void XMLAttribute::SetAttribute( bool v )
1150 char buf[BUF_SIZE];
1151 XMLUtil::ToStr( v, buf, BUF_SIZE );
1152 _value.SetStr( buf );
1155 void XMLAttribute::SetAttribute( double v )
1157 char buf[BUF_SIZE];
1158 XMLUtil::ToStr( v, buf, BUF_SIZE );
1159 _value.SetStr( buf );
1162 void XMLAttribute::SetAttribute( float v )
1164 char buf[BUF_SIZE];
1165 XMLUtil::ToStr( v, buf, BUF_SIZE );
1166 _value.SetStr( buf );
1170 // --------- XMLElement ---------- //
1171 XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
1172 _closingType( 0 ),
1173 _rootAttribute( 0 )
1178 XMLElement::~XMLElement()
1180 while( _rootAttribute ) {
1181 XMLAttribute* next = _rootAttribute->_next;
1182 DELETE_ATTRIBUTE( _rootAttribute );
1183 _rootAttribute = next;
1188 XMLAttribute* XMLElement::FindAttribute( const char* name )
1190 XMLAttribute* a = 0;
1191 for( a=_rootAttribute; a; a = a->_next ) {
1192 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1193 return a;
1196 return 0;
1200 const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1202 XMLAttribute* a = 0;
1203 for( a=_rootAttribute; a; a = a->_next ) {
1204 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1205 return a;
1208 return 0;
1212 const char* XMLElement::Attribute( const char* name, const char* value ) const
1214 const XMLAttribute* a = FindAttribute( name );
1215 if ( !a ) {
1216 return 0;
1218 if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
1219 return a->Value();
1221 return 0;
1225 const char* XMLElement::GetText() const
1227 if ( FirstChild() && FirstChild()->ToText() ) {
1228 return FirstChild()->ToText()->Value();
1230 return 0;
1234 XMLError XMLElement::QueryIntText( int* ival ) const
1236 if ( FirstChild() && FirstChild()->ToText() ) {
1237 const char* t = FirstChild()->ToText()->Value();
1238 if ( XMLUtil::ToInt( t, ival ) ) {
1239 return XML_SUCCESS;
1241 return XML_CAN_NOT_CONVERT_TEXT;
1243 return XML_NO_TEXT_NODE;
1247 XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
1249 if ( FirstChild() && FirstChild()->ToText() ) {
1250 const char* t = FirstChild()->ToText()->Value();
1251 if ( XMLUtil::ToUnsigned( t, uval ) ) {
1252 return XML_SUCCESS;
1254 return XML_CAN_NOT_CONVERT_TEXT;
1256 return XML_NO_TEXT_NODE;
1260 XMLError XMLElement::QueryBoolText( bool* bval ) const
1262 if ( FirstChild() && FirstChild()->ToText() ) {
1263 const char* t = FirstChild()->ToText()->Value();
1264 if ( XMLUtil::ToBool( t, bval ) ) {
1265 return XML_SUCCESS;
1267 return XML_CAN_NOT_CONVERT_TEXT;
1269 return XML_NO_TEXT_NODE;
1273 XMLError XMLElement::QueryDoubleText( double* dval ) const
1275 if ( FirstChild() && FirstChild()->ToText() ) {
1276 const char* t = FirstChild()->ToText()->Value();
1277 if ( XMLUtil::ToDouble( t, dval ) ) {
1278 return XML_SUCCESS;
1280 return XML_CAN_NOT_CONVERT_TEXT;
1282 return XML_NO_TEXT_NODE;
1286 XMLError XMLElement::QueryFloatText( float* fval ) const
1288 if ( FirstChild() && FirstChild()->ToText() ) {
1289 const char* t = FirstChild()->ToText()->Value();
1290 if ( XMLUtil::ToFloat( t, fval ) ) {
1291 return XML_SUCCESS;
1293 return XML_CAN_NOT_CONVERT_TEXT;
1295 return XML_NO_TEXT_NODE;
1300 XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1302 XMLAttribute* last = 0;
1303 XMLAttribute* attrib = 0;
1304 for( attrib = _rootAttribute;
1305 attrib;
1306 last = attrib, attrib = attrib->_next ) {
1307 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1308 break;
1311 if ( !attrib ) {
1312 attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1313 attrib->_memPool = &_document->_attributePool;
1314 if ( last ) {
1315 last->_next = attrib;
1317 else {
1318 _rootAttribute = attrib;
1320 attrib->SetName( name );
1321 attrib->_memPool->SetTracked(); // always created and linked.
1323 return attrib;
1327 void XMLElement::DeleteAttribute( const char* name )
1329 XMLAttribute* prev = 0;
1330 for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
1331 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1332 if ( prev ) {
1333 prev->_next = a->_next;
1335 else {
1336 _rootAttribute = a->_next;
1338 DELETE_ATTRIBUTE( a );
1339 break;
1341 prev = a;
1346 char* XMLElement::ParseAttributes( char* p )
1348 const char* start = p;
1349 XMLAttribute* prevAttribute = 0;
1351 // Read the attributes.
1352 while( p ) {
1353 p = XMLUtil::SkipWhiteSpace( p );
1354 if ( !p || !(*p) ) {
1355 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
1356 return 0;
1359 // attribute.
1360 if ( XMLUtil::IsAlpha( *p ) ) {
1361 XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1362 attrib->_memPool = &_document->_attributePool;
1363 attrib->_memPool->SetTracked();
1365 p = attrib->ParseDeep( p, _document->ProcessEntities() );
1366 if ( !p || Attribute( attrib->Name() ) ) {
1367 DELETE_ATTRIBUTE( attrib );
1368 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
1369 return 0;
1371 // There is a minor bug here: if the attribute in the source xml
1372 // document is duplicated, it will not be detected and the
1373 // attribute will be doubly added. However, tracking the 'prevAttribute'
1374 // avoids re-scanning the attribute list. Preferring performance for
1375 // now, may reconsider in the future.
1376 if ( prevAttribute ) {
1377 prevAttribute->_next = attrib;
1379 else {
1380 _rootAttribute = attrib;
1382 prevAttribute = attrib;
1384 // end of the tag
1385 else if ( *p == '/' && *(p+1) == '>' ) {
1386 _closingType = CLOSED;
1387 return p+2; // done; sealed element.
1389 // end of the tag
1390 else if ( *p == '>' ) {
1391 ++p;
1392 break;
1394 else {
1395 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
1396 return 0;
1399 return p;
1403 //
1404 // <ele></ele>
1405 // <ele>foo<b>bar</b></ele>
1406 //
1407 char* XMLElement::ParseDeep( char* p, StrPair* strPair )
1409 // Read the element name.
1410 p = XMLUtil::SkipWhiteSpace( p );
1411 if ( !p ) {
1412 return 0;
1415 // The closing element is the </element> form. It is
1416 // parsed just like a regular element then deleted from
1417 // the DOM.
1418 if ( *p == '/' ) {
1419 _closingType = CLOSING;
1420 ++p;
1423 p = _value.ParseName( p );
1424 if ( _value.Empty() ) {
1425 return 0;
1428 p = ParseAttributes( p );
1429 if ( !p || !*p || _closingType ) {
1430 return p;
1433 p = XMLNode::ParseDeep( p, strPair );
1434 return p;
1439 XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1441 if ( !doc ) {
1442 doc = _document;
1444 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1445 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1446 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1448 return element;
1452 bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1454 const XMLElement* other = compare->ToElement();
1455 if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
1457 const XMLAttribute* a=FirstAttribute();
1458 const XMLAttribute* b=other->FirstAttribute();
1460 while ( a && b ) {
1461 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1462 return false;
1464 a = a->Next();
1465 b = b->Next();
1467 if ( a || b ) {
1468 // different count
1469 return false;
1471 return true;
1473 return false;
1477 bool XMLElement::Accept( XMLVisitor* visitor ) const
1479 if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
1480 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
1481 if ( !node->Accept( visitor ) ) {
1482 break;
1486 return visitor->VisitExit( *this );
1490 // --------- XMLDocument ----------- //
1491 XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) :
1492 XMLNode( 0 ),
1493 _writeBOM( false ),
1494 _processEntities( processEntities ),
1495 _errorID( XML_NO_ERROR ),
1496 _whitespace( whitespace ),
1497 _errorStr1( 0 ),
1498 _errorStr2( 0 ),
1499 _charBuffer( 0 )
1501 _document = this; // avoid warning about 'this' in initializer list
1505 XMLDocument::~XMLDocument()
1507 DeleteChildren();
1508 delete [] _charBuffer;
1510 #if 0
1511 textPool.Trace( "text" );
1512 elementPool.Trace( "element" );
1513 commentPool.Trace( "comment" );
1514 attributePool.Trace( "attribute" );
1515 #endif
1517 #ifdef DEBUG
1518 if ( Error() == false ) {
1519 TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() );
1520 TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
1521 TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() );
1522 TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() );
1524 #endif
1528 void XMLDocument::InitDocument()
1530 _errorID = XML_NO_ERROR;
1531 _errorStr1 = 0;
1532 _errorStr2 = 0;
1534 delete [] _charBuffer;
1535 _charBuffer = 0;
1539 XMLElement* XMLDocument::NewElement( const char* name )
1541 XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this );
1542 ele->_memPool = &_elementPool;
1543 ele->SetName( name );
1544 return ele;
1548 XMLComment* XMLDocument::NewComment( const char* str )
1550 XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this );
1551 comment->_memPool = &_commentPool;
1552 comment->SetValue( str );
1553 return comment;
1557 XMLText* XMLDocument::NewText( const char* str )
1559 XMLText* text = new (_textPool.Alloc()) XMLText( this );
1560 text->_memPool = &_textPool;
1561 text->SetValue( str );
1562 return text;
1566 XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1568 XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this );
1569 dec->_memPool = &_commentPool;
1570 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
1571 return dec;
1575 XMLUnknown* XMLDocument::NewUnknown( const char* str )
1577 XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this );
1578 unk->_memPool = &_commentPool;
1579 unk->SetValue( str );
1580 return unk;
1584 XMLError XMLDocument::LoadFile( const char* filename )
1586 DeleteChildren();
1587 InitDocument();
1588 FILE* fp = 0;
1590 #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
1591 errno_t err = fopen_s(&fp, filename, "rb" );
1592 if ( !fp || err) {
1593 #else
1594 fp = fopen( filename, "rb" );
1595 if ( !fp) {
1596 #endif
1597 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
1598 return _errorID;
1600 LoadFile( fp );
1601 fclose( fp );
1602 return _errorID;
1606 XMLError XMLDocument::LoadFile( FILE* fp )
1608 DeleteChildren();
1609 InitDocument();
1611 fseek( fp, 0, SEEK_END );
1612 size_t size = ftell( fp );
1613 fseek( fp, 0, SEEK_SET );
1615 if ( size == 0 ) {
1616 return _errorID;
1619 _charBuffer = new char[size+1];
1620 size_t read = fread( _charBuffer, 1, size, fp );
1621 if ( read != size ) {
1622 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1623 return _errorID;
1626 _charBuffer[size] = 0;
1628 const char* p = _charBuffer;
1629 p = XMLUtil::SkipWhiteSpace( p );
1630 p = XMLUtil::ReadBOM( p, &_writeBOM );
1631 if ( !p || !*p ) {
1632 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1633 return _errorID;
1636 ParseDeep( _charBuffer + (p-_charBuffer), 0 );
1637 return _errorID;
1641 XMLError XMLDocument::SaveFile( const char* filename, bool compact )
1643 FILE* fp = 0;
1644 #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
1645 errno_t err = fopen_s(&fp, filename, "w" );
1646 if ( !fp || err) {
1647 #else
1648 fp = fopen( filename, "w" );
1649 if ( !fp) {
1650 #endif
1651 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
1652 return _errorID;
1654 SaveFile(fp, compact);
1655 fclose( fp );
1656 return _errorID;
1660 XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
1662 XMLPrinter stream( fp, compact );
1663 Print( &stream );
1664 return _errorID;
1668 XMLError XMLDocument::Parse( const char* p, size_t len )
1670 DeleteChildren();
1671 InitDocument();
1673 if ( !p || !*p ) {
1674 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1675 return _errorID;
1677 if ( len == (size_t)(-1) ) {
1678 len = strlen( p );
1680 _charBuffer = new char[ len+1 ];
1681 memcpy( _charBuffer, p, len );
1682 _charBuffer[len] = 0;
1684 p = XMLUtil::SkipWhiteSpace( p );
1685 p = XMLUtil::ReadBOM( p, &_writeBOM );
1686 if ( !p || !*p ) {
1687 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1688 return _errorID;
1691 ParseDeep( _charBuffer, 0 );
1692 return _errorID;
1696 void XMLDocument::Print( XMLPrinter* streamer )
1698 XMLPrinter stdStreamer( stdout );
1699 if ( !streamer ) {
1700 streamer = &stdStreamer;
1702 Accept( streamer );
1706 void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 )
1708 _errorID = error;
1709 _errorStr1 = str1;
1710 _errorStr2 = str2;
1714 void XMLDocument::PrintError() const
1716 if ( _errorID ) {
1717 static const int LEN = 20;
1718 char buf1[LEN] = { 0 };
1719 char buf2[LEN] = { 0 };
1721 if ( _errorStr1 ) {
1722 TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 );
1724 if ( _errorStr2 ) {
1725 TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 );
1728 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1729 _errorID, buf1, buf2 );
1734 XMLPrinter::XMLPrinter( FILE* file, bool compact ) :
1735 _elementJustOpened( false ),
1736 _firstElement( true ),
1737 _fp( file ),
1738 _depth( 0 ),
1739 _textDepth( -1 ),
1740 _processEntities( true ),
1741 _compactMode( compact )
1743 for( int i=0; i<ENTITY_RANGE; ++i ) {
1744 _entityFlag[i] = false;
1745 _restrictedEntityFlag[i] = false;
1747 for( int i=0; i<NUM_ENTITIES; ++i ) {
1748 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1749 if ( entities[i].value < ENTITY_RANGE ) {
1750 _entityFlag[ (int)entities[i].value ] = true;
1753 _restrictedEntityFlag[(int)'&'] = true;
1754 _restrictedEntityFlag[(int)'<'] = true;
1755 _restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
1756 _buffer.Push( 0 );
1760 void XMLPrinter::Print( const char* format, ... )
1762 va_list va;
1763 va_start( va, format );
1765 if ( _fp ) {
1766 vfprintf( _fp, format, va );
1768 else {
1769 // This seems brutally complex. Haven't figured out a better
1770 // way on windows.
1771 #ifdef _MSC_VER
1772 int len = -1;
1773 int expand = 1000;
1774 while ( len < 0 ) {
1775 len = vsnprintf_s( _accumulator.Mem(), _accumulator.Capacity(), _TRUNCATE, format, va );
1776 if ( len < 0 ) {
1777 expand *= 3/2;
1778 _accumulator.PushArr( expand );
1781 char* p = _buffer.PushArr( len ) - 1;
1782 memcpy( p, _accumulator.Mem(), len+1 );
1783 #else
1784 int len = vsnprintf( 0, 0, format, va );
1785 // Close out and re-start the va-args
1786 va_end( va );
1787 va_start( va, format );
1788 char* p = _buffer.PushArr( len ) - 1;
1789 vsnprintf( p, len+1, format, va );
1790 #endif
1792 va_end( va );
1796 void XMLPrinter::PrintSpace( int depth )
1798 for( int i=0; i<depth; ++i ) {
1799 Print( " " );
1804 void XMLPrinter::PrintString( const char* p, bool restricted )
1806 // Look for runs of bytes between entities to print.
1807 const char* q = p;
1808 const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
1810 if ( _processEntities ) {
1811 while ( *q ) {
1812 // Remember, char is sometimes signed. (How many times has that bitten me?)
1813 if ( *q > 0 && *q < ENTITY_RANGE ) {
1814 // Check for entities. If one is found, flush
1815 // the stream up until the entity, write the
1816 // entity, and keep looking.
1817 if ( flag[(unsigned)(*q)] ) {
1818 while ( p < q ) {
1819 Print( "%c", *p );
1820 ++p;
1822 for( int i=0; i<NUM_ENTITIES; ++i ) {
1823 if ( entities[i].value == *q ) {
1824 Print( "&%s;", entities[i].pattern );
1825 break;
1828 ++p;
1831 ++q;
1834 // Flush the remaining string. This will be the entire
1835 // string if an entity wasn't found.
1836 if ( !_processEntities || (q-p > 0) ) {
1837 Print( "%s", p );
1842 void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
1844 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1845 if ( writeBOM ) {
1846 Print( "%s", bom );
1848 if ( writeDec ) {
1849 PushDeclaration( "xml version=\"1.0\"" );
1854 void XMLPrinter::OpenElement( const char* name )
1856 if ( _elementJustOpened ) {
1857 SealElement();
1859 _stack.Push( name );
1861 if ( _textDepth < 0 && !_firstElement && !_compactMode ) {
1862 Print( "\n" );
1863 PrintSpace( _depth );
1866 Print( "<%s", name );
1867 _elementJustOpened = true;
1868 _firstElement = false;
1869 ++_depth;
1873 void XMLPrinter::PushAttribute( const char* name, const char* value )
1875 TIXMLASSERT( _elementJustOpened );
1876 Print( " %s=\"", name );
1877 PrintString( value, false );
1878 Print( "\"" );
1882 void XMLPrinter::PushAttribute( const char* name, int v )
1884 char buf[BUF_SIZE];
1885 XMLUtil::ToStr( v, buf, BUF_SIZE );
1886 PushAttribute( name, buf );
1890 void XMLPrinter::PushAttribute( const char* name, unsigned v )
1892 char buf[BUF_SIZE];
1893 XMLUtil::ToStr( v, buf, BUF_SIZE );
1894 PushAttribute( name, buf );
1898 void XMLPrinter::PushAttribute( const char* name, bool v )
1900 char buf[BUF_SIZE];
1901 XMLUtil::ToStr( v, buf, BUF_SIZE );
1902 PushAttribute( name, buf );
1906 void XMLPrinter::PushAttribute( const char* name, double v )
1908 char buf[BUF_SIZE];
1909 XMLUtil::ToStr( v, buf, BUF_SIZE );
1910 PushAttribute( name, buf );
1914 void XMLPrinter::CloseElement()
1916 --_depth;
1917 const char* name = _stack.Pop();
1919 if ( _elementJustOpened ) {
1920 Print( "/>" );
1922 else {
1923 if ( _textDepth < 0 && !_compactMode) {
1924 Print( "\n" );
1925 PrintSpace( _depth );
1927 Print( "</%s>", name );
1930 if ( _textDepth == _depth ) {
1931 _textDepth = -1;
1933 if ( _depth == 0 && !_compactMode) {
1934 Print( "\n" );
1936 _elementJustOpened = false;
1940 void XMLPrinter::SealElement()
1942 _elementJustOpened = false;
1943 Print( ">" );
1947 void XMLPrinter::PushText( const char* text, bool cdata )
1949 _textDepth = _depth-1;
1951 if ( _elementJustOpened ) {
1952 SealElement();
1954 if ( cdata ) {
1955 Print( "<![CDATA[" );
1956 Print( "%s", text );
1957 Print( "]]>" );
1959 else {
1960 PrintString( text, true );
1964 void XMLPrinter::PushText( int value )
1966 char buf[BUF_SIZE];
1967 XMLUtil::ToStr( value, buf, BUF_SIZE );
1968 PushText( buf, false );
1972 void XMLPrinter::PushText( unsigned value )
1974 char buf[BUF_SIZE];
1975 XMLUtil::ToStr( value, buf, BUF_SIZE );
1976 PushText( buf, false );
1980 void XMLPrinter::PushText( bool value )
1982 char buf[BUF_SIZE];
1983 XMLUtil::ToStr( value, buf, BUF_SIZE );
1984 PushText( buf, false );
1988 void XMLPrinter::PushText( float value )
1990 char buf[BUF_SIZE];
1991 XMLUtil::ToStr( value, buf, BUF_SIZE );
1992 PushText( buf, false );
1996 void XMLPrinter::PushText( double value )
1998 char buf[BUF_SIZE];
1999 XMLUtil::ToStr( value, buf, BUF_SIZE );
2000 PushText( buf, false );
2004 void XMLPrinter::PushComment( const char* comment )
2006 if ( _elementJustOpened ) {
2007 SealElement();
2009 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2010 Print( "\n" );
2011 PrintSpace( _depth );
2013 _firstElement = false;
2014 Print( "<!--%s-->", comment );
2018 void XMLPrinter::PushDeclaration( const char* value )
2020 if ( _elementJustOpened ) {
2021 SealElement();
2023 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2024 Print( "\n" );
2025 PrintSpace( _depth );
2027 _firstElement = false;
2028 Print( "<?%s?>", value );
2032 void XMLPrinter::PushUnknown( const char* value )
2034 if ( _elementJustOpened ) {
2035 SealElement();
2037 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2038 Print( "\n" );
2039 PrintSpace( _depth );
2041 _firstElement = false;
2042 Print( "<!%s>", value );
2046 bool XMLPrinter::VisitEnter( const XMLDocument& doc )
2048 _processEntities = doc.ProcessEntities();
2049 if ( doc.HasBOM() ) {
2050 PushHeader( true, false );
2052 return true;
2056 bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
2058 OpenElement( element.Name() );
2059 while ( attribute ) {
2060 PushAttribute( attribute->Name(), attribute->Value() );
2061 attribute = attribute->Next();
2063 return true;
2067 bool XMLPrinter::VisitExit( const XMLElement& )
2069 CloseElement();
2070 return true;
2074 bool XMLPrinter::Visit( const XMLText& text )
2076 PushText( text.Value(), text.CData() );
2077 return true;
2081 bool XMLPrinter::Visit( const XMLComment& comment )
2083 PushComment( comment.Value() );
2084 return true;
2087 bool XMLPrinter::Visit( const XMLDeclaration& declaration )
2089 PushDeclaration( declaration.Value() );
2090 return true;
2094 bool XMLPrinter::Visit( const XMLUnknown& unknown )
2096 PushUnknown( unknown.Value() );
2097 return true;
2100 } // namespace tinyxml2