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