1/*
2Original code by Lee Thomason (www.grinninglizard.com)
3
4This software is provided 'as-is', without any express or implied
5warranty. In no event will the authors be held liable for any
6damages arising from the use of this software.
7
8Permission is granted to anyone to use this software for any
9purpose, including commercial applications, and to alter it and
10redistribute it freely, subject to the following restrictions:
11
121. The origin of this software must not be misrepresented; you must
13not claim that you wrote the original software. If you use this
14software in a product, an acknowledgment in the product documentation
15would be appreciated but is not required.
16
172. Altered source versions must be plainly marked as such, and
18must not be misrepresented as being the original software.
19
203. This notice may not be removed or altered from any source
21distribution.
22*/
23#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}
2022