1 /*
2 Original code by Lee Thomason (www.grinninglizard.com)
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any
6 damages arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any
9 purpose, including commercial applications, and to alter it and
10 redistribute it freely, subject to the following restrictions:
11
12 1. The origin of this software must not be misrepresented; you must
13 not claim that you wrote the original software. If you use this
14 software in a product, an acknowledgment in the product documentation
15 would be appreciated but is not required.
16
17 2. Altered source versions must be plainly marked as such, and
18 must not be misrepresented as being the original software.
19
20 3. This notice may not be removed or altered from any source
21 distribution.
22 */
23 #include "tinyxml2.h"
24
25 #include <cstdio>
26 #include <cstdlib>
27 #include <new>
28 #include <cstddef>
29
30 #include <fcntl.h>
31 using namespace tinyxml2;
32
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 = '\"';
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
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;
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
64 struct Entity {
65 const char* pattern;
66 int length;
67 char value;
68 };
69
70 static const int NUM_ENTITIES = 5;
71 static 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
~StrPair()81 StrPair::~StrPair()
82 {
83 Reset();
84 }
85
86
Reset()87 void StrPair::Reset()
88 {
89 if ( flags & NEEDS_DELETE ) {
90 delete [] start;
91 }
92 flags = 0;
93 start = 0;
94 end = 0;
95 }
96
97
SetStr(const char * str,int flags)98 void 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
ParseText(char * p,const char * endTag,int strFlags)109 char* 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
ParseName(char * p)129 char* 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
GetStr()160 const 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 // 中 or 中
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
ReadBOM(const char * p,bool * bom)245 const 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
ConvertUTF32ToUTF8(unsigned long input,char * output,int * length)261 void 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
GetCharacterRef(const char * p,char * value,int * length)302 const 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
ToStr(int v,char * buffer,int bufferSize)371 void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
372 {
373 TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
374 }
375
376
ToStr(unsigned v,char * buffer,int bufferSize)377 void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
378 {
379 TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
380 }
381
382
ToStr(bool v,char * buffer,int bufferSize)383 void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
384 {
385 TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 );
386 }
387
388
ToStr(float v,char * buffer,int bufferSize)389 void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
390 {
391 TIXML_SNPRINTF( buffer, bufferSize, "%f", v );
392 }
393
394
ToStr(double v,char * buffer,int bufferSize)395 void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
396 {
397 TIXML_SNPRINTF( buffer, bufferSize, "%f", v );
398 }
399
400
ToInt(const char * str,int * value)401 bool XMLUtil::ToInt( const char* str, int* value )
402 {
403 if ( TIXML_SSCANF( str, "%d", value ) == 1 )
404 return true;
405 return false;
406 }
407
ToUnsigned(const char * str,unsigned * value)408 bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
409 {
410 if ( TIXML_SSCANF( str, "%u", value ) == 1 )
411 return true;
412 return false;
413 }
414
ToBool(const char * str,bool * value)415 bool 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
ToFloat(const char * str,float * value)434 bool 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
ToDouble(const char * str,double * value)442 bool 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
Identify(char * p,XMLNode ** node)451 char* 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
Accept(XMLVisitor * visitor) const527 bool 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
XMLNode(XMLDocument * doc)543 XMLNode::XMLNode( XMLDocument* doc ) :
544 document( doc ),
545 parent( 0 ),
546 firstChild( 0 ), lastChild( 0 ),
547 prev( 0 ), next( 0 )
548 {
549 }
550
551
~XMLNode()552 XMLNode::~XMLNode()
553 {
554 DeleteChildren();
555 if ( parent ) {
556 parent->Unlink( this );
557 }
558 }
559
560
SetValue(const char * str,bool staticMem)561 void XMLNode::SetValue( const char* str, bool staticMem )
562 {
563 if ( staticMem )
564 value.SetInternedStr( str );
565 else
566 value.SetStr( str );
567 }
568
569
DeleteChildren()570 void 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
Unlink(XMLNode * child)582 void 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
DeleteChild(XMLNode * node)600 void XMLNode::DeleteChild( XMLNode* node )
601 {
602 TIXMLASSERT( node->parent == this );
603 DELETE_NODE( node );
604 }
605
606
InsertEndChild(XMLNode * addThis)607 XMLNode* 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
InsertFirstChild(XMLNode * addThis)630 XMLNode* 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
InsertAfterChild(XMLNode * afterThis,XMLNode * addThis)654 XMLNode* 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
FirstChildElement(const char * value) const675 const 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
LastChildElement(const char * value) const689 const 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
NextSiblingElement(const char * value) const703 const 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
PreviousSiblingElement(const char * value) const716 const 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
ParseDeep(char * p,StrPair * parentEnd)729 char* 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 ---------- //
ParseDeep(char * p,StrPair *)807 char* 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
ShallowClone(XMLDocument * doc) const830 XMLNode* 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
ShallowEqual(const XMLNode * compare) const841 bool XMLText::ShallowEqual( const XMLNode* compare ) const
842 {
843 return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() ));
844 }
845
846
Accept(XMLVisitor * visitor) const847 bool XMLText::Accept( XMLVisitor* visitor ) const
848 {
849 return visitor->Visit( *this );
850 }
851
852
853 // --------- XMLComment ---------- //
854
XMLComment(XMLDocument * doc)855 XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
856 {
857 }
858
859
~XMLComment()860 XMLComment::~XMLComment()
861 {
862 //printf( "~XMLComment\n" );
863 }
864
865
ParseDeep(char * p,StrPair *)866 char* 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
ShallowClone(XMLDocument * doc) const878 XMLNode* 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
ShallowEqual(const XMLNode * compare) const888 bool XMLComment::ShallowEqual( const XMLNode* compare ) const
889 {
890 return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() ));
891 }
892
893
Accept(XMLVisitor * visitor) const894 bool XMLComment::Accept( XMLVisitor* visitor ) const
895 {
896 return visitor->Visit( *this );
897 }
898
899
900 // --------- XMLDeclaration ---------- //
901
XMLDeclaration(XMLDocument * doc)902 XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
903 {
904 }
905
906
~XMLDeclaration()907 XMLDeclaration::~XMLDeclaration()
908 {
909 //printf( "~XMLDeclaration\n" );
910 }
911
912
ParseDeep(char * p,StrPair *)913 char* 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
ShallowClone(XMLDocument * doc) const925 XMLNode* 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
ShallowEqual(const XMLNode * compare) const935 bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
936 {
937 return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() ));
938 }
939
940
941
Accept(XMLVisitor * visitor) const942 bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
943 {
944 return visitor->Visit( *this );
945 }
946
947 // --------- XMLUnknown ---------- //
948
XMLUnknown(XMLDocument * doc)949 XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
950 {
951 }
952
953
~XMLUnknown()954 XMLUnknown::~XMLUnknown()
955 {
956 }
957
958
ParseDeep(char * p,StrPair *)959 char* 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
ShallowClone(XMLDocument * doc) const972 XMLNode* 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
ShallowEqual(const XMLNode * compare) const982 bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
983 {
984 return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() ));
985 }
986
987
Accept(XMLVisitor * visitor) const988 bool XMLUnknown::Accept( XMLVisitor* visitor ) const
989 {
990 return visitor->Visit( *this );
991 }
992
993 // --------- XMLAttribute ---------- //
ParseDeep(char * p,bool processEntities)994 char* 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
SetName(const char * n)1016 void XMLAttribute::SetName( const char* n )
1017 {
1018 name.SetStr( n );
1019 }
1020
1021
QueryIntValue(int * value) const1022 int 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
QueryUnsignedValue(unsigned int * value) const1030 int 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
QueryBoolValue(bool * value) const1038 int 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
QueryFloatValue(float * value) const1047 int 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
QueryDoubleValue(double * value) const1055 int 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
SetAttribute(const char * v)1063 void XMLAttribute::SetAttribute( const char* v )
1064 {
1065 value.SetStr( v );
1066 }
1067
1068
SetAttribute(int v)1069 void XMLAttribute::SetAttribute( int v )
1070 {
1071 char buf[BUF_SIZE];
1072 XMLUtil::ToStr( v, buf, BUF_SIZE );
1073 value.SetStr( buf );
1074 }
1075
1076
SetAttribute(unsigned v)1077 void XMLAttribute::SetAttribute( unsigned v )
1078 {
1079 char buf[BUF_SIZE];
1080 XMLUtil::ToStr( v, buf, BUF_SIZE );
1081 value.SetStr( buf );
1082 }
1083
1084
SetAttribute(bool v)1085 void XMLAttribute::SetAttribute( bool v )
1086 {
1087 char buf[BUF_SIZE];
1088 XMLUtil::ToStr( v, buf, BUF_SIZE );
1089 value.SetStr( buf );
1090 }
1091
SetAttribute(double v)1092 void XMLAttribute::SetAttribute( double v )
1093 {
1094 char buf[BUF_SIZE];
1095 XMLUtil::ToStr( v, buf, BUF_SIZE );
1096 value.SetStr( buf );
1097 }
1098
SetAttribute(float v)1099 void 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 ---------- //
XMLElement(XMLDocument * doc)1108 XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
1109 closingType( 0 ),
1110 rootAttribute( 0 )
1111 {
1112 }
1113
1114
~XMLElement()1115 XMLElement::~XMLElement()
1116 {
1117 while( rootAttribute ) {
1118 XMLAttribute* next = rootAttribute->next;
1119 DELETE_ATTRIBUTE( rootAttribute );
1120 rootAttribute = next;
1121 }
1122 }
1123
1124
FindAttribute(const char * name)1125 XMLAttribute* 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
FindAttribute(const char * name) const1136 const 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
Attribute(const char * name,const char * value) const1147 const 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
GetText() const1158 const char* XMLElement::GetText() const
1159 {
1160 if ( FirstChild() && FirstChild()->ToText() ) {
1161 return FirstChild()->ToText()->Value();
1162 }
1163 return 0;
1164 }
1165
1166
QueryIntText(int * _value) const1167 int 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
QueryUnsignedText(unsigned * _value) const1180 int 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
QueryBoolText(bool * _value) const1193 int 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
QueryDoubleText(double * _value) const1206 int 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
QueryFloatText(float * _value) const1219 int 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
FindOrCreateAttribute(const char * name)1233 XMLAttribute* 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
DeleteAttribute(const char * name)1260 void 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
ParseAttributes(char * p)1279 char* 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 //
ParseDeep(char * p,StrPair * strPair)1339 char* 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
ShallowClone(XMLDocument * doc) const1366 XMLNode* 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
ShallowEqual(const XMLNode * compare) const1379 bool 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
Accept(XMLVisitor * visitor) const1404 bool 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 ----------- //
XMLDocument(bool _processEntities)1419 XMLDocument::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
~XMLDocument()1432 XMLDocument::~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
InitDocument()1451 void 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
NewElement(const char * name)1463 XMLElement* 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
NewComment(const char * str)1472 XMLComment* 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
NewText(const char * str)1481 XMLText* 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
NewDeclaration(const char * str)1490 XMLDeclaration* 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
NewUnknown(const char * str)1499 XMLUnknown* 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
LoadFile(const char * filename)1508 int 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
LoadFile(FILE * fp)1531 int 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
SaveFile(const char * filename)1566 int 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
SaveFile(FILE * fp)1588 int XMLDocument::SaveFile( FILE* fp )
1589 {
1590 XMLPrinter stream( fp );
1591 Print( &stream );
1592 return errorID;
1593 }
1594
1595
Parse(const char * p)1596 int 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
Print(XMLPrinter * streamer)1622 void XMLDocument::Print( XMLPrinter* streamer )
1623 {
1624 XMLPrinter stdStreamer( stdout );
1625 if ( !streamer )
1626 streamer = &stdStreamer;
1627 Accept( streamer );
1628 }
1629
1630
SetError(int error,const char * str1,const char * str2)1631 void XMLDocument::SetError( int error, const char* str1, const char* str2 )
1632 {
1633 errorID = error;
1634 errorStr1 = str1;
1635 errorStr2 = str2;
1636 }
1637
1638
PrintError() const1639 void 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
XMLPrinter(FILE * file,bool compact)1659 XMLPrinter::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
Print(const char * format,...)1685 void 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
PrintSpace(int depth)1721 void XMLPrinter::PrintSpace( int depth )
1722 {
1723 for( int i=0; i<depth; ++i ) {
1724 Print( " " );
1725 }
1726 }
1727
1728
PrintString(const char * p,bool restricted)1729 void 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
PushHeader(bool writeBOM,bool writeDec)1767 void 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
OpenElement(const char * name)1779 void 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
PushAttribute(const char * name,const char * value)1798 void 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
PushAttribute(const char * name,int v)1807 void 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
PushAttribute(const char * name,unsigned v)1815 void 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
PushAttribute(const char * name,bool v)1823 void 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
PushAttribute(const char * name,double v)1831 void 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
CloseElement()1839 void 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
SealElement()1863 void XMLPrinter::SealElement()
1864 {
1865 elementJustOpened = false;
1866 Print( ">" );
1867 }
1868
1869
PushText(const char * text,bool cdata)1870 void 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
PushText(int value)1887 void XMLPrinter::PushText( int value )
1888 {
1889 char buf[BUF_SIZE];
1890 XMLUtil::ToStr( value, buf, BUF_SIZE );
1891 PushText( buf, false );
1892 }
1893
1894
PushText(unsigned value)1895 void XMLPrinter::PushText( unsigned value )
1896 {
1897 char buf[BUF_SIZE];
1898 XMLUtil::ToStr( value, buf, BUF_SIZE );
1899 PushText( buf, false );
1900 }
1901
1902
PushText(bool value)1903 void XMLPrinter::PushText( bool value )
1904 {
1905 char buf[BUF_SIZE];
1906 XMLUtil::ToStr( value, buf, BUF_SIZE );
1907 PushText( buf, false );
1908 }
1909
1910
PushText(float value)1911 void XMLPrinter::PushText( float value )
1912 {
1913 char buf[BUF_SIZE];
1914 XMLUtil::ToStr( value, buf, BUF_SIZE );
1915 PushText( buf, false );
1916 }
1917
1918
PushText(double value)1919 void XMLPrinter::PushText( double value )
1920 {
1921 char buf[BUF_SIZE];
1922 XMLUtil::ToStr( value, buf, BUF_SIZE );
1923 PushText( buf, false );
1924 }
1925
1926
PushComment(const char * comment)1927 void 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
PushDeclaration(const char * value)1941 void 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
PushUnknown(const char * value)1955 void 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
VisitEnter(const XMLDocument & doc)1969 bool 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
VisitEnter(const XMLElement & element,const XMLAttribute * attribute)1979 bool 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
VisitExit(const XMLElement &)1990 bool XMLPrinter::VisitExit( const XMLElement& )
1991 {
1992 CloseElement();
1993 return true;
1994 }
1995
1996
Visit(const XMLText & text)1997 bool XMLPrinter::Visit( const XMLText& text )
1998 {
1999 PushText( text.Value(), text.CData() );
2000 return true;
2001 }
2002
2003
Visit(const XMLComment & comment)2004 bool XMLPrinter::Visit( const XMLComment& comment )
2005 {
2006 PushComment( comment.Value() );
2007 return true;
2008 }
2009
Visit(const XMLDeclaration & declaration)2010 bool XMLPrinter::Visit( const XMLDeclaration& declaration )
2011 {
2012 PushDeclaration( declaration.Value() );
2013 return true;
2014 }
2015
2016
Visit(const XMLUnknown & unknown)2017 bool XMLPrinter::Visit( const XMLUnknown& unknown )
2018 {
2019 PushUnknown( unknown.Value() );
2020 return true;
2021 }
2022