blob: bb491de13442dcb291d519f9715b735afb6cf1bc [file] [log] [blame]
Lee Thomason (grinliz)28129862012-02-25 21:11:20 -08001/*
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*/
Lee Thomason (grinliz)9c38d132012-02-24 21:50:50 -080023
U-Lama\Lee560bd472011-12-28 19:42:49 -080024#include "tinyxml2.h"
25
26#include <string.h>
27#include <stdlib.h>
28#include <stdio.h>
U-Lama\Lee4cee6112011-12-31 14:58:18 -080029#include <ctype.h>
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -080030#include <new>
U-Stream\Leeae25a442012-02-17 17:48:16 -080031#include <stdarg.h>
Lee Thomasond1983222012-02-06 08:41:24 -080032
U-Lama\Lee560bd472011-12-28 19:42:49 -080033
34using namespace tinyxml2;
35
Lee Thomasone4422302012-01-20 17:59:50 -080036static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
Lee Thomasonfde6a752012-01-14 18:08:12 -080037static const char LF = LINE_FEED;
38static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
39static const char CR = CARRIAGE_RETURN;
Lee Thomasone4422302012-01-20 17:59:50 -080040static const char SINGLE_QUOTE = '\'';
41static const char DOUBLE_QUOTE = '\"';
Lee Thomasonfde6a752012-01-14 18:08:12 -080042
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -080043// Bunch of unicode info at:
44// http://www.unicode.org/faq/utf_bom.html
45// ef bb bf (Microsoft "lead bytes") - designates UTF-8
46
47static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
48static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
49static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -080050
51
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -080052#define DELETE_NODE( node ) { \
53 if ( node ) { \
54 MemPool* pool = node->memPool; \
55 node->~XMLNode(); \
56 pool->Free( node ); \
57 } \
58}
59#define DELETE_ATTRIBUTE( attrib ) { \
60 if ( attrib ) { \
61 MemPool* pool = attrib->memPool; \
62 attrib->~XMLAttribute(); \
63 pool->Free( attrib ); \
64 } \
65}
Lee Thomason43f59302012-02-06 18:18:11 -080066
Lee Thomason8ee79892012-01-25 17:44:30 -080067struct Entity {
68 const char* pattern;
69 int length;
70 char value;
71};
72
73static const int NUM_ENTITIES = 5;
74static const Entity entities[NUM_ENTITIES] =
75{
Lee Thomason18d68bd2012-01-26 18:17:26 -080076 { "quot", 4, DOUBLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080077 { "amp", 3, '&' },
Lee Thomason18d68bd2012-01-26 18:17:26 -080078 { "apos", 4, SINGLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080079 { "lt", 2, '<' },
80 { "gt", 2, '>' }
81};
82
Lee Thomasonfde6a752012-01-14 18:08:12 -080083
Lee Thomason1a1d4a72012-02-15 09:09:25 -080084StrPair::~StrPair()
85{
86 Reset();
87}
88
89
90void StrPair::Reset()
91{
92 if ( flags & NEEDS_DELETE ) {
93 delete [] start;
94 }
95 flags = 0;
96 start = 0;
97 end = 0;
98}
99
100
101void StrPair::SetStr( const char* str, int flags )
102{
103 Reset();
104 size_t len = strlen( str );
105 start = new char[ len+1 ];
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800106 memcpy( start, str, len+1 );
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800107 end = start + len;
108 this->flags = flags | NEEDS_DELETE;
109}
110
111
112char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
113{
114 TIXMLASSERT( endTag && *endTag );
115
116 char* start = p; // fixme: hides a member
117 char endChar = *endTag;
118 int length = strlen( endTag );
119
120 // Inner loop of text parsing.
121 while ( *p ) {
122 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
123 Set( start, p, strFlags );
124 return p + length;
125 }
126 ++p;
127 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800128 return 0;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800129}
130
131
132char* StrPair::ParseName( char* p )
133{
134 char* start = p;
135
136 start = p;
137 if ( !start || !(*start) ) {
138 return 0;
139 }
140
141 if ( !XMLUtil::IsAlpha( *p ) ) {
142 return 0;
143 }
144
145 while( *p && (
146 XMLUtil::IsAlphaNum( (unsigned char) *p )
147 || *p == '_'
148 || *p == '-'
149 || *p == '.'
150 || *p == ':' ))
151 {
152 ++p;
153 }
154
155 if ( p > start ) {
156 Set( start, p, 0 );
157 return p;
158 }
159 return 0;
160}
161
162
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800163
Lee Thomasone4422302012-01-20 17:59:50 -0800164const char* StrPair::GetStr()
165{
166 if ( flags & NEEDS_FLUSH ) {
167 *end = 0;
Lee Thomason8ee79892012-01-25 17:44:30 -0800168 flags ^= NEEDS_FLUSH;
Lee Thomasone4422302012-01-20 17:59:50 -0800169
Lee Thomason8ee79892012-01-25 17:44:30 -0800170 if ( flags ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800171 char* p = start; // the read pointer
172 char* q = start; // the write pointer
Lee Thomasone4422302012-01-20 17:59:50 -0800173
174 while( p < end ) {
Lee Thomason8ee79892012-01-25 17:44:30 -0800175 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800176 // CR-LF pair becomes LF
177 // CR alone becomes LF
178 // LF-CR becomes LF
179 if ( *(p+1) == LF ) {
180 p += 2;
181 }
182 else {
183 ++p;
184 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800185 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800186 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800187 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800188 if ( *(p+1) == CR ) {
189 p += 2;
190 }
191 else {
192 ++p;
193 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800194 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800195 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800196 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
197 int i=0;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800198
199 // Entities handled by tinyXML2:
200 // - special entities in the entity table [in/out]
201 // - numeric character reference [in]
202 // &#20013; or &#x4e2d;
203
204 if ( *(p+1) == '#' ) {
205 char buf[10] = { 0 };
206 int len;
207 p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
208 for( int i=0; i<len; ++i ) {
209 *q++ = buf[i];
Lee Thomason8ee79892012-01-25 17:44:30 -0800210 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800211 TIXMLASSERT( q <= p );
Lee Thomason8ee79892012-01-25 17:44:30 -0800212 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800213 else {
214 for( i=0; i<NUM_ENTITIES; ++i ) {
215 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
216 && *(p+entities[i].length+1) == ';' )
217 {
218 // Found an entity convert;
219 *q = entities[i].value;
220 ++q;
221 p += entities[i].length + 2;
222 break;
223 }
224 }
225 if ( i == NUM_ENTITIES ) {
226 // fixme: treat as error?
227 ++p;
228 ++q;
229 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800230 }
231 }
Lee Thomasone4422302012-01-20 17:59:50 -0800232 else {
233 *q = *p;
234 ++p;
Lee Thomasonec975ce2012-01-23 11:42:06 -0800235 ++q;
Lee Thomasone4422302012-01-20 17:59:50 -0800236 }
237 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800238 *q = 0;
Lee Thomasone4422302012-01-20 17:59:50 -0800239 }
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800240 flags = (flags & NEEDS_DELETE);
Lee Thomasone4422302012-01-20 17:59:50 -0800241 }
242 return start;
243}
244
Lee Thomason2c85a712012-01-31 08:24:24 -0800245
Lee Thomasone4422302012-01-20 17:59:50 -0800246
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800247
Lee Thomason56bdd022012-02-09 18:16:58 -0800248// --------- XMLUtil ----------- //
Lee Thomasond1983222012-02-06 08:41:24 -0800249
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800250const char* XMLUtil::ReadBOM( const char* p, bool* bom )
251{
252 *bom = false;
253 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
254 // Check for BOM:
255 if ( *(pu+0) == TIXML_UTF_LEAD_0
256 && *(pu+1) == TIXML_UTF_LEAD_1
257 && *(pu+2) == TIXML_UTF_LEAD_2 )
258 {
259 *bom = true;
260 p += 3;
261 }
262 return p;
263}
264
265
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800266void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
267{
268 const unsigned long BYTE_MASK = 0xBF;
269 const unsigned long BYTE_MARK = 0x80;
270 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
271
272 if (input < 0x80)
273 *length = 1;
274 else if ( input < 0x800 )
275 *length = 2;
276 else if ( input < 0x10000 )
277 *length = 3;
278 else if ( input < 0x200000 )
279 *length = 4;
280 else
281 { *length = 0; return; } // This code won't covert this correctly anyway.
282
283 output += *length;
284
285 // Scary scary fall throughs.
286 switch (*length)
287 {
288 case 4:
289 --output;
290 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
291 input >>= 6;
292 case 3:
293 --output;
294 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
295 input >>= 6;
296 case 2:
297 --output;
298 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
299 input >>= 6;
300 case 1:
301 --output;
302 *output = (char)(input | FIRST_BYTE_MARK[*length]);
303 }
304}
305
306
307const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
308{
309 // Presume an entity, and pull it out.
310 *length = 0;
311
312 if ( *(p+1) == '#' && *(p+2) )
313 {
314 unsigned long ucs = 0;
315 ptrdiff_t delta = 0;
316 unsigned mult = 1;
317
318 if ( *(p+2) == 'x' )
319 {
320 // Hexadecimal.
321 if ( !*(p+3) ) return 0;
322
323 const char* q = p+3;
324 q = strchr( q, ';' );
325
326 if ( !q || !*q ) return 0;
327
328 delta = q-p;
329 --q;
330
331 while ( *q != 'x' )
332 {
333 if ( *q >= '0' && *q <= '9' )
334 ucs += mult * (*q - '0');
335 else if ( *q >= 'a' && *q <= 'f' )
336 ucs += mult * (*q - 'a' + 10);
337 else if ( *q >= 'A' && *q <= 'F' )
338 ucs += mult * (*q - 'A' + 10 );
339 else
340 return 0;
341 mult *= 16;
342 --q;
343 }
344 }
345 else
346 {
347 // Decimal.
348 if ( !*(p+2) ) return 0;
349
350 const char* q = p+2;
351 q = strchr( q, ';' );
352
353 if ( !q || !*q ) return 0;
354
355 delta = q-p;
356 --q;
357
358 while ( *q != '#' )
359 {
360 if ( *q >= '0' && *q <= '9' )
361 ucs += mult * (*q - '0');
362 else
363 return 0;
364 mult *= 10;
365 --q;
366 }
367 }
368 // convert the UCS to UTF-8
369 ConvertUTF32ToUTF8( ucs, value, length );
370 return p + delta + 1;
371 }
372 return p+1;
373}
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800374
375
Lee Thomasond1983222012-02-06 08:41:24 -0800376char* XMLDocument::Identify( char* p, XMLNode** node )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800377{
378 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800379 char* start = p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800380 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800381 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800382 {
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800383 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800384 }
385
386 // What is this thing?
387 // - Elements start with a letter or underscore, but xml is reserved.
388 // - Comments: <!--
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800389 // - Decleration: <?
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800390 // - Everthing else is unknown to tinyxml.
391 //
392
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800393 static const char* xmlHeader = { "<?" };
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800394 static const char* commentHeader = { "<!--" };
395 static const char* dtdHeader = { "<!" };
396 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800397 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800398
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800399 static const int xmlHeaderLen = 2;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800400 static const int commentHeaderLen = 4;
401 static const int dtdHeaderLen = 2;
402 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800403 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800404
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800405#if defined(_MSC_VER)
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -0800406#pragma warning ( push )
407#pragma warning ( disable : 4127 )
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800408#endif
Lee Thomason50f97b22012-02-11 16:33:40 -0800409 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
410 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800411#if defined(_MSC_VER)
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -0800412#pragma warning (pop)
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800413#endif
Lee Thomason50f97b22012-02-11 16:33:40 -0800414 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
415 returnNode = new (commentPool.Alloc()) XMLDeclaration( this );
416 returnNode->memPool = &commentPool;
417 p += xmlHeaderLen;
418 }
419 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800420 returnNode = new (commentPool.Alloc()) XMLComment( this );
421 returnNode->memPool = &commentPool;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800422 p += commentHeaderLen;
423 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800424 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
425 XMLText* text = new (textPool.Alloc()) XMLText( this );
426 returnNode = text;
427 returnNode->memPool = &textPool;
428 p += cdataHeaderLen;
429 text->SetCData( true );
430 }
431 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
432 returnNode = new (commentPool.Alloc()) XMLUnknown( this );
433 returnNode->memPool = &commentPool;
434 p += dtdHeaderLen;
435 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800436 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800437 returnNode = new (elementPool.Alloc()) XMLElement( this );
438 returnNode->memPool = &elementPool;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800439 p += elementHeaderLen;
440 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800441 else {
Lee Thomasond1983222012-02-06 08:41:24 -0800442 returnNode = new (textPool.Alloc()) XMLText( this );
443 returnNode->memPool = &textPool;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800444 p = start; // Back it up, all the text counts.
445 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800446
447 *node = returnNode;
448 return p;
449}
450
451
Lee Thomason751da522012-02-10 08:50:51 -0800452bool XMLDocument::Accept( XMLVisitor* visitor ) const
453{
454 if ( visitor->VisitEnter( *this ) )
455 {
456 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
457 {
458 if ( !node->Accept( visitor ) )
459 break;
460 }
461 }
462 return visitor->VisitExit( *this );
463}
Lee Thomason56bdd022012-02-09 18:16:58 -0800464
465
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800466// --------- XMLNode ----------- //
467
468XMLNode::XMLNode( XMLDocument* doc ) :
469 document( doc ),
470 parent( 0 ),
471 firstChild( 0 ), lastChild( 0 ),
472 prev( 0 ), next( 0 )
473{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800474}
475
476
477XMLNode::~XMLNode()
478{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -0800479 DeleteChildren();
Lee Thomason7c913cd2012-01-26 18:32:34 -0800480 if ( parent ) {
481 parent->Unlink( this );
482 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800483}
484
485
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800486void XMLNode::SetValue( const char* str, bool staticMem )
487{
488 if ( staticMem )
489 value.SetInternedStr( str );
490 else
491 value.SetStr( str );
492}
493
494
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -0800495void XMLNode::DeleteChildren()
Lee Thomason18d68bd2012-01-26 18:17:26 -0800496{
Lee Thomasond923c672012-01-23 08:44:25 -0800497 while( firstChild ) {
498 XMLNode* node = firstChild;
499 Unlink( node );
Lee Thomasond1983222012-02-06 08:41:24 -0800500
Lee Thomason43f59302012-02-06 18:18:11 -0800501 DELETE_NODE( node );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800502 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800503 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800504}
505
506
507void XMLNode::Unlink( XMLNode* child )
508{
509 TIXMLASSERT( child->parent == this );
510 if ( child == firstChild )
511 firstChild = firstChild->next;
512 if ( child == lastChild )
513 lastChild = lastChild->prev;
514
515 if ( child->prev ) {
516 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800517 }
Lee Thomasond923c672012-01-23 08:44:25 -0800518 if ( child->next ) {
519 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800520 }
Lee Thomasond923c672012-01-23 08:44:25 -0800521 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800522}
523
524
U-Stream\Leeae25a442012-02-17 17:48:16 -0800525void XMLNode::DeleteChild( XMLNode* node )
526{
527 TIXMLASSERT( node->parent == this );
528 DELETE_NODE( node );
529}
530
531
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800532XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
533{
534 if ( lastChild ) {
535 TIXMLASSERT( firstChild );
536 TIXMLASSERT( lastChild->next == 0 );
537 lastChild->next = addThis;
538 addThis->prev = lastChild;
539 lastChild = addThis;
540
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800541 addThis->next = 0;
542 }
543 else {
544 TIXMLASSERT( firstChild == 0 );
545 firstChild = lastChild = addThis;
546
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800547 addThis->prev = 0;
548 addThis->next = 0;
549 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800550 addThis->parent = this;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800551 return addThis;
552}
553
554
Lee Thomason1ff38e02012-02-14 18:18:16 -0800555XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
556{
557 if ( firstChild ) {
558 TIXMLASSERT( lastChild );
559 TIXMLASSERT( firstChild->prev == 0 );
560
561 firstChild->prev = addThis;
562 addThis->next = firstChild;
563 firstChild = addThis;
564
Lee Thomason1ff38e02012-02-14 18:18:16 -0800565 addThis->prev = 0;
566 }
567 else {
568 TIXMLASSERT( lastChild == 0 );
569 firstChild = lastChild = addThis;
570
Lee Thomason1ff38e02012-02-14 18:18:16 -0800571 addThis->prev = 0;
572 addThis->next = 0;
573 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800574 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800575 return addThis;
576}
577
578
579XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
580{
581 TIXMLASSERT( afterThis->parent == this );
582 if ( afterThis->parent != this )
583 return 0;
584
585 if ( afterThis->next == 0 ) {
586 // The last node or the only node.
587 return InsertEndChild( addThis );
588 }
589 addThis->prev = afterThis;
590 addThis->next = afterThis->next;
591 afterThis->next->prev = addThis;
592 afterThis->next = addThis;
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800593 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800594 return addThis;
595}
596
597
598
599
Lee Thomason56bdd022012-02-09 18:16:58 -0800600const XMLElement* XMLNode::FirstChildElement( const char* value ) const
Lee Thomason2c85a712012-01-31 08:24:24 -0800601{
602 for( XMLNode* node=firstChild; node; node=node->next ) {
603 XMLElement* element = node->ToElement();
604 if ( element ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800605 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
Lee Thomason2c85a712012-01-31 08:24:24 -0800606 return element;
607 }
608 }
609 }
610 return 0;
611}
612
613
Lee Thomason56bdd022012-02-09 18:16:58 -0800614const XMLElement* XMLNode::LastChildElement( const char* value ) const
615{
616 for( XMLNode* node=lastChild; node; node=node->prev ) {
617 XMLElement* element = node->ToElement();
618 if ( element ) {
619 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
620 return element;
621 }
622 }
623 }
624 return 0;
625}
626
627
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800628const XMLElement* XMLNode::NextSiblingElement( const char* value ) const
629{
630 for( XMLNode* element=this->next; element; element = element->next ) {
631 if ( element->ToElement()
632 && (!value || XMLUtil::StringEqual( value, element->Value() )))
633 {
634 return element->ToElement();
635 }
636 }
637 return 0;
638}
639
640
641const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const
642{
643 for( XMLNode* element=this->prev; element; element = element->prev ) {
644 if ( element->ToElement()
645 && (!value || XMLUtil::StringEqual( value, element->Value() )))
646 {
647 return element->ToElement();
648 }
649 }
650 return 0;
651}
652
653
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800654char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
Lee Thomason67d61312012-01-24 16:01:51 -0800655{
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800656 // This is a recursive method, but thinking about it "at the current level"
657 // it is a pretty simple flat list:
658 // <foo/>
659 // <!-- comment -->
660 //
661 // With a special case:
662 // <foo>
663 // </foo>
664 // <!-- comment -->
665 //
666 // Where the closing element (/foo) *must* be the next thing after the opening
667 // element, and the names must match. BUT the tricky bit is that the closing
668 // element will be read by the child.
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800669 //
670 // 'endTag' is the end tag for this node, it is returned by a call to a child.
671 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800672
Lee Thomason67d61312012-01-24 16:01:51 -0800673 while( p && *p ) {
674 XMLNode* node = 0;
Lee Thomasond6277762012-02-22 16:00:12 -0800675
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800676 p = document->Identify( p, &node );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800677 if ( p == 0 || node == 0 ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800678 break;
679 }
680
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800681 StrPair endTag;
682 p = node->ParseDeep( p, &endTag );
683 if ( !p ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800684 DELETE_NODE( node );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800685 node = 0;
Lee Thomason (grinliz)784607f2012-02-24 16:23:40 -0800686 if ( !document->Error() ) {
687 document->SetError( ERROR_PARSING, 0, 0 );
688 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800689 break;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800690 }
691
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800692 // We read the end tag. Return it to the parent.
693 if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) {
694 if ( parentEnd ) {
695 *parentEnd = ((XMLElement*)node)->value;
Lee Thomason67d61312012-01-24 16:01:51 -0800696 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800697 DELETE_NODE( node );
698 return p;
699 }
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800700
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800701 // Handle an end tag returned to this level.
702 // And handle a bunch of annoying errors.
703 XMLElement* ele = node->ToElement();
704 if ( ele ) {
705 if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) {
706 document->SetError( ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
707 p = 0;
708 }
709 else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) {
710 document->SetError( ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
711 p = 0;
712 }
713 else if ( !endTag.Empty() ) {
714 if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800715 document->SetError( ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
716 p = 0;
717 }
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800718 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800719 }
720 if ( p == 0 ) {
721 DELETE_NODE( node );
722 node = 0;
723 }
724 if ( node ) {
725 this->InsertEndChild( node );
Lee Thomason67d61312012-01-24 16:01:51 -0800726 }
727 }
728 return 0;
729}
730
Lee Thomason5492a1c2012-01-23 15:32:10 -0800731// --------- XMLText ---------- //
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800732char* XMLText::ParseDeep( char* p, StrPair* )
Lee Thomason5492a1c2012-01-23 15:32:10 -0800733{
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800734 const char* start = p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800735 if ( this->CData() ) {
736 p = value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800737 if ( !p ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800738 document->SetError( ERROR_PARSING_CDATA, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800739 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800740 return p;
741 }
742 else {
743 p = value.ParseText( p, "<", StrPair::TEXT_ELEMENT );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800744 if ( !p ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800745 document->SetError( ERROR_PARSING_TEXT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800746 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800747 if ( p && *p ) {
748 return p-1;
749 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800750 }
751 return 0;
752}
753
754
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800755XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
756{
757 if ( !doc ) {
758 doc = document;
759 }
760 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
761 text->SetCData( this->CData() );
762 return text;
763}
764
765
766bool XMLText::ShallowEqual( const XMLNode* compare ) const
767{
768 return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() ));
769}
770
771
Lee Thomason56bdd022012-02-09 18:16:58 -0800772bool XMLText::Accept( XMLVisitor* visitor ) const
773{
774 return visitor->Visit( *this );
775}
776
777
Lee Thomason3f57d272012-01-11 15:30:03 -0800778// --------- XMLComment ---------- //
779
Lee Thomasone4422302012-01-20 17:59:50 -0800780XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800781{
782}
783
784
Lee Thomasonce0763e2012-01-11 15:43:54 -0800785XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800786{
Lee Thomasond923c672012-01-23 08:44:25 -0800787 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800788}
789
790
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800791char* XMLComment::ParseDeep( char* p, StrPair* )
Lee Thomason3f57d272012-01-11 15:30:03 -0800792{
793 // Comment parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800794 const char* start = p;
795 p = value.ParseText( p, "-->", StrPair::COMMENT );
796 if ( p == 0 ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800797 document->SetError( ERROR_PARSING_COMMENT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800798 }
799 return p;
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800800}
801
802
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800803XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
804{
805 if ( !doc ) {
806 doc = document;
807 }
808 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
809 return comment;
810}
811
812
813bool XMLComment::ShallowEqual( const XMLNode* compare ) const
814{
815 return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() ));
816}
817
818
Lee Thomason751da522012-02-10 08:50:51 -0800819bool XMLComment::Accept( XMLVisitor* visitor ) const
820{
821 return visitor->Visit( *this );
822}
Lee Thomason56bdd022012-02-09 18:16:58 -0800823
824
Lee Thomason50f97b22012-02-11 16:33:40 -0800825// --------- XMLDeclaration ---------- //
826
827XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
828{
829}
830
831
832XMLDeclaration::~XMLDeclaration()
833{
834 //printf( "~XMLDeclaration\n" );
835}
836
837
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800838char* XMLDeclaration::ParseDeep( char* p, StrPair* )
Lee Thomason50f97b22012-02-11 16:33:40 -0800839{
840 // Declaration parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800841 const char* start = p;
842 p = value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
843 if ( p == 0 ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800844 document->SetError( ERROR_PARSING_DECLARATION, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800845 }
846 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800847}
848
849
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800850XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
851{
852 if ( !doc ) {
853 doc = document;
854 }
855 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
856 return dec;
857}
858
859
860bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
861{
862 return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() ));
863}
864
865
866
Lee Thomason50f97b22012-02-11 16:33:40 -0800867bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
868{
869 return visitor->Visit( *this );
870}
871
872// --------- XMLUnknown ---------- //
873
874XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
875{
876}
877
878
879XMLUnknown::~XMLUnknown()
880{
881}
882
883
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800884char* XMLUnknown::ParseDeep( char* p, StrPair* )
Lee Thomason50f97b22012-02-11 16:33:40 -0800885{
886 // Unknown parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800887 const char* start = p;
888
889 p = value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
890 if ( !p ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800891 document->SetError( ERROR_PARSING_UNKNOWN, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800892 }
893 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800894}
895
896
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800897XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
898{
899 if ( !doc ) {
900 doc = document;
901 }
902 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
903 return text;
904}
905
906
907bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
908{
909 return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() ));
910}
911
912
Lee Thomason50f97b22012-02-11 16:33:40 -0800913bool XMLUnknown::Accept( XMLVisitor* visitor ) const
914{
915 return visitor->Visit( *this );
916}
917
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800918// --------- XMLAttribute ---------- //
919char* XMLAttribute::ParseDeep( char* p )
920{
Lee Thomason56bdd022012-02-09 18:16:58 -0800921 p = name.ParseText( p, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800922 if ( !p || !*p ) return 0;
923
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800924 char endTag[2] = { *p, 0 };
925 ++p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800926 p = value.ParseText( p, endTag, StrPair::ATTRIBUTE_VALUE );
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800927 //if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800928 return p;
929}
930
931
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800932void XMLAttribute::SetName( const char* n )
933{
934 name.SetStr( n );
935}
936
937
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800938int XMLAttribute::QueryIntValue( int* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800939{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800940 if ( TIXML_SSCANF( Value(), "%d", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800941 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800942 return WRONG_ATTRIBUTE_TYPE;
943}
944
945
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800946int XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800947{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800948 if ( TIXML_SSCANF( Value(), "%u", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800949 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800950 return WRONG_ATTRIBUTE_TYPE;
951}
952
953
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800954int XMLAttribute::QueryBoolValue( bool* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800955{
956 int ival = -1;
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800957 QueryIntValue( &ival );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800958
959 if ( ival > 0 || XMLUtil::StringEqual( Value(), "true" ) ) {
960 *value = true;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800961 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800962 }
963 else if ( ival == 0 || XMLUtil::StringEqual( Value(), "false" ) ) {
964 *value = false;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800965 return XML_NO_ERROR;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800966 }
Lee Thomason1ff38e02012-02-14 18:18:16 -0800967 return WRONG_ATTRIBUTE_TYPE;
968}
969
970
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800971int XMLAttribute::QueryDoubleValue( double* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800972{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800973 if ( TIXML_SSCANF( Value(), "%lf", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800974 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800975 return WRONG_ATTRIBUTE_TYPE;
976}
977
978
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800979int XMLAttribute::QueryFloatValue( float* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800980{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800981 if ( TIXML_SSCANF( Value(), "%f", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800982 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800983 return WRONG_ATTRIBUTE_TYPE;
984}
985
986
987void XMLAttribute::SetAttribute( const char* v )
988{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800989 value.SetStr( v );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800990}
991
992
Lee Thomason1ff38e02012-02-14 18:18:16 -0800993void XMLAttribute::SetAttribute( int v )
994{
995 char buf[BUF_SIZE];
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800996 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v );
997 value.SetStr( buf );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800998}
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800999
1000
1001void XMLAttribute::SetAttribute( unsigned v )
1002{
1003 char buf[BUF_SIZE];
1004 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%u", v );
1005 value.SetStr( buf );
1006}
1007
1008
1009void XMLAttribute::SetAttribute( bool v )
1010{
1011 char buf[BUF_SIZE];
1012 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v ? 1 : 0 );
1013 value.SetStr( buf );
1014}
1015
1016void XMLAttribute::SetAttribute( double v )
1017{
1018 char buf[BUF_SIZE];
1019 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
1020 value.SetStr( buf );
1021}
1022
1023void XMLAttribute::SetAttribute( float v )
1024{
1025 char buf[BUF_SIZE];
1026 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
1027 value.SetStr( buf );
1028}
1029
Lee Thomasondadcdfa2012-01-18 17:55:48 -08001030
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001031// --------- XMLElement ---------- //
1032XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001033 closingType( 0 ),
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001034 rootAttribute( 0 )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001035{
1036}
1037
1038
1039XMLElement::~XMLElement()
1040{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001041 while( rootAttribute ) {
1042 XMLAttribute* next = rootAttribute->next;
1043 DELETE_ATTRIBUTE( rootAttribute );
1044 rootAttribute = next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001045 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001046}
1047
1048
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001049XMLAttribute* XMLElement::FindAttribute( const char* name )
1050{
1051 XMLAttribute* a = 0;
1052 for( a=rootAttribute; a; a = a->next ) {
1053 if ( XMLUtil::StringEqual( a->Name(), name ) )
1054 return a;
1055 }
1056 return 0;
1057}
1058
1059
1060const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1061{
1062 XMLAttribute* a = 0;
1063 for( a=rootAttribute; a; a = a->next ) {
1064 if ( XMLUtil::StringEqual( a->Name(), name ) )
1065 return a;
1066 }
1067 return 0;
1068}
1069
1070
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001071const char* XMLElement::GetText() const
1072{
1073 if ( FirstChild() && FirstChild()->ToText() ) {
1074 return FirstChild()->ToText()->Value();
1075 }
1076 return 0;
1077}
1078
1079
1080
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001081XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1082{
1083 XMLAttribute* attrib = FindAttribute( name );
1084 if ( !attrib ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001085 attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001086 attrib->memPool = &document->attributePool;
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001087 LinkAttribute( attrib );
1088 attrib->SetName( name );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001089 }
1090 return attrib;
1091}
1092
1093
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001094void XMLElement::LinkAttribute( XMLAttribute* attrib )
1095{
1096 if ( rootAttribute ) {
1097 XMLAttribute* end = rootAttribute;
1098 while ( end->next )
1099 end = end->next;
1100 end->next = attrib;
1101 }
1102 else {
1103 rootAttribute = attrib;
1104 }
1105}
1106
1107
U-Stream\Leeae25a442012-02-17 17:48:16 -08001108void XMLElement::DeleteAttribute( const char* name )
1109{
1110 XMLAttribute* prev = 0;
1111 for( XMLAttribute* a=rootAttribute; a; a=a->next ) {
1112 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1113 if ( prev ) {
1114 prev->next = a->next;
1115 }
1116 else {
1117 rootAttribute = a->next;
1118 }
1119 DELETE_ATTRIBUTE( a );
1120 break;
1121 }
1122 prev = a;
1123 }
1124}
1125
1126
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001127char* XMLElement::ParseAttributes( char* p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001128{
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001129 const char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001130
1131 // Read the attributes.
1132 while( p ) {
Lee Thomason56bdd022012-02-09 18:16:58 -08001133 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001134 if ( !p || !(*p) ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001135 document->SetError( ERROR_PARSING_ELEMENT, start, Name() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001136 return 0;
1137 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001138
1139 // attribute.
Lee Thomason56bdd022012-02-09 18:16:58 -08001140 if ( XMLUtil::IsAlpha( *p ) ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001141 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason43f59302012-02-06 18:18:11 -08001142 attrib->memPool = &document->attributePool;
Lee Thomasond1983222012-02-06 08:41:24 -08001143
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001144 p = attrib->ParseDeep( p );
Lee Thomasond6277762012-02-22 16:00:12 -08001145 if ( !p || Attribute( attrib->Name() ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -08001146 DELETE_ATTRIBUTE( attrib );
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001147 document->SetError( ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001148 return 0;
1149 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001150 LinkAttribute( attrib );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001151 }
Lee Thomasone4422302012-01-20 17:59:50 -08001152 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001153 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001154 closingType = CLOSED;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001155 return p+2; // done; sealed element.
1156 }
Lee Thomasone4422302012-01-20 17:59:50 -08001157 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001158 else if ( *p == '>' ) {
1159 ++p;
1160 break;
1161 }
Lee Thomasone4422302012-01-20 17:59:50 -08001162 else {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001163 document->SetError( ERROR_PARSING_ELEMENT, start, p );
Lee Thomasone4422302012-01-20 17:59:50 -08001164 return 0;
1165 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001166 }
Lee Thomason67d61312012-01-24 16:01:51 -08001167 return p;
1168}
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001169
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001170
Lee Thomason67d61312012-01-24 16:01:51 -08001171//
1172// <ele></ele>
1173// <ele>foo<b>bar</b></ele>
1174//
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001175char* XMLElement::ParseDeep( char* p, StrPair* strPair )
Lee Thomason67d61312012-01-24 16:01:51 -08001176{
1177 // Read the element name.
Lee Thomason56bdd022012-02-09 18:16:58 -08001178 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason67d61312012-01-24 16:01:51 -08001179 if ( !p ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001180
1181 // The closing element is the </element> form. It is
1182 // parsed just like a regular element then deleted from
1183 // the DOM.
1184 if ( *p == '/' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001185 closingType = CLOSING;
Lee Thomason67d61312012-01-24 16:01:51 -08001186 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001187 }
Lee Thomason67d61312012-01-24 16:01:51 -08001188
Lee Thomason56bdd022012-02-09 18:16:58 -08001189 p = value.ParseName( p );
Lee Thomasond1983222012-02-06 08:41:24 -08001190 if ( value.Empty() ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001191
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001192 p = ParseAttributes( p );
1193 if ( !p || !*p || closingType )
Lee Thomason67d61312012-01-24 16:01:51 -08001194 return p;
1195
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001196 p = XMLNode::ParseDeep( p, strPair );
Lee Thomason67d61312012-01-24 16:01:51 -08001197 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001198}
1199
1200
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001201
1202XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1203{
1204 if ( !doc ) {
1205 doc = document;
1206 }
1207 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1208 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1209 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1210 }
1211 return element;
1212}
1213
1214
1215bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1216{
1217 const XMLElement* other = compare->ToElement();
1218 if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
1219
1220 const XMLAttribute* a=FirstAttribute();
1221 const XMLAttribute* b=other->FirstAttribute();
1222
1223 while ( a && b ) {
1224 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1225 return false;
1226 }
1227 }
1228 if ( a || b ) {
1229 // different count
1230 return false;
1231 }
1232 return true;
1233 }
1234 return false;
1235}
1236
1237
Lee Thomason751da522012-02-10 08:50:51 -08001238bool XMLElement::Accept( XMLVisitor* visitor ) const
1239{
1240 if ( visitor->VisitEnter( *this, rootAttribute ) )
1241 {
1242 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
1243 {
1244 if ( !node->Accept( visitor ) )
1245 break;
1246 }
1247 }
1248 return visitor->VisitExit( *this );
Lee Thomason751da522012-02-10 08:50:51 -08001249}
Lee Thomason56bdd022012-02-09 18:16:58 -08001250
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001251
Lee Thomason3f57d272012-01-11 15:30:03 -08001252// --------- XMLDocument ----------- //
Lee Thomason67d61312012-01-24 16:01:51 -08001253XMLDocument::XMLDocument() :
Lee Thomason18d68bd2012-01-26 18:17:26 -08001254 XMLNode( 0 ),
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001255 writeBOM( false ),
U-Lama\Lee560bd472011-12-28 19:42:49 -08001256 charBuffer( 0 )
1257{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001258 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -08001259}
U-Lama\Leee13c3e62011-12-28 14:36:55 -08001260
1261
Lee Thomason3f57d272012-01-11 15:30:03 -08001262XMLDocument::~XMLDocument()
1263{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001264 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001265 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -08001266
Lee Thomasonec5a7b42012-02-13 18:16:52 -08001267#if 0
Lee Thomason455c9d42012-02-06 09:14:14 -08001268 textPool.Trace( "text" );
1269 elementPool.Trace( "element" );
1270 commentPool.Trace( "comment" );
1271 attributePool.Trace( "attribute" );
Lee Thomasone9ecdab2012-02-13 18:11:20 -08001272#endif
1273
Lee Thomason455c9d42012-02-06 09:14:14 -08001274 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
1275 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
1276 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
1277 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -08001278}
1279
1280
Lee Thomason18d68bd2012-01-26 18:17:26 -08001281void XMLDocument::InitDocument()
1282{
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001283 errorID = XML_NO_ERROR;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001284 errorStr1 = 0;
1285 errorStr2 = 0;
1286
1287 delete [] charBuffer;
1288 charBuffer = 0;
1289
1290}
1291
Lee Thomason3f57d272012-01-11 15:30:03 -08001292
Lee Thomason2c85a712012-01-31 08:24:24 -08001293XMLElement* XMLDocument::NewElement( const char* name )
1294{
Lee Thomasond1983222012-02-06 08:41:24 -08001295 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
1296 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -08001297 ele->SetName( name );
1298 return ele;
1299}
1300
1301
Lee Thomason1ff38e02012-02-14 18:18:16 -08001302XMLComment* XMLDocument::NewComment( const char* str )
1303{
1304 XMLComment* comment = new (commentPool.Alloc()) XMLComment( this );
1305 comment->memPool = &commentPool;
1306 comment->SetValue( str );
1307 return comment;
1308}
1309
1310
1311XMLText* XMLDocument::NewText( const char* str )
1312{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001313 XMLText* text = new (textPool.Alloc()) XMLText( this );
1314 text->memPool = &textPool;
1315 text->SetValue( str );
1316 return text;
Lee Thomason1ff38e02012-02-14 18:18:16 -08001317}
1318
1319
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001320XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1321{
1322 XMLDeclaration* dec = new (commentPool.Alloc()) XMLDeclaration( this );
1323 dec->memPool = &commentPool;
1324 dec->SetValue( str );
1325 return dec;
1326}
1327
1328
1329XMLUnknown* XMLDocument::NewUnknown( const char* str )
1330{
1331 XMLUnknown* unk = new (commentPool.Alloc()) XMLUnknown( this );
1332 unk->memPool = &commentPool;
1333 unk->SetValue( str );
1334 return unk;
1335}
1336
1337
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001338int XMLDocument::LoadFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001339{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001340 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001341 InitDocument();
1342
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001343#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001344#pragma warning ( push )
1345#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001346#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001347 FILE* fp = fopen( filename, "rb" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001348#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001349#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001350#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001351 if ( !fp ) {
1352 SetError( ERROR_FILE_NOT_FOUND, filename, 0 );
1353 return errorID;
1354 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001355 LoadFile( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001356 fclose( fp );
1357 return errorID;
1358}
1359
1360
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001361int XMLDocument::LoadFile( FILE* fp )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001362{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001363 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001364 InitDocument();
1365
1366 fseek( fp, 0, SEEK_END );
1367 unsigned size = ftell( fp );
1368 fseek( fp, 0, SEEK_SET );
1369
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001370 if ( size == 0 ) {
1371 return errorID;
1372 }
1373
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001374 charBuffer = new char[size+1];
1375 fread( charBuffer, size, 1, fp );
1376 charBuffer[size] = 0;
1377
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001378 const char* p = charBuffer;
1379 p = XMLUtil::SkipWhiteSpace( p );
1380 p = XMLUtil::ReadBOM( p, &writeBOM );
1381 if ( !p || !*p ) {
Lee Thomasond6277762012-02-22 16:00:12 -08001382 SetError( ERROR_EMPTY_DOCUMENT, 0, 0 );
1383 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001384 }
1385
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001386 ParseDeep( charBuffer + (p-charBuffer), 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001387 return errorID;
1388}
1389
1390
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001391void XMLDocument::SaveFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001392{
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001393#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001394#pragma warning ( push )
1395#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001396#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001397 FILE* fp = fopen( filename, "w" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001398#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001399#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001400#endif
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001401 XMLPrinter stream( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001402 Print( &stream );
1403 fclose( fp );
1404}
1405
Lee Thomason1ff38e02012-02-14 18:18:16 -08001406
Lee Thomason7c913cd2012-01-26 18:32:34 -08001407int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -08001408{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001409 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001410 InitDocument();
1411
1412 if ( !p || !*p ) {
Lee Thomasond6277762012-02-22 16:00:12 -08001413 SetError( ERROR_EMPTY_DOCUMENT, 0, 0 );
1414 return errorID;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001415 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001416 p = XMLUtil::SkipWhiteSpace( p );
1417 p = XMLUtil::ReadBOM( p, &writeBOM );
1418 if ( !p || !*p ) {
Lee Thomasond6277762012-02-22 16:00:12 -08001419 SetError( ERROR_EMPTY_DOCUMENT, 0, 0 );
1420 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001421 }
1422
Lee Thomason18d68bd2012-01-26 18:17:26 -08001423 size_t len = strlen( p );
1424 charBuffer = new char[ len+1 ];
1425 memcpy( charBuffer, p, len+1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001426
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001427
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001428 ParseDeep( charBuffer, 0 );
Lee Thomason7c913cd2012-01-26 18:32:34 -08001429 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -08001430}
1431
1432
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001433void XMLDocument::Print( XMLPrinter* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -08001434{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001435 XMLPrinter stdStreamer( stdout );
Lee Thomason5cae8972012-01-24 18:03:07 -08001436 if ( !streamer )
1437 streamer = &stdStreamer;
Lee Thomason751da522012-02-10 08:50:51 -08001438 Accept( streamer );
Lee Thomason3f57d272012-01-11 15:30:03 -08001439}
1440
1441
Lee Thomason67d61312012-01-24 16:01:51 -08001442void XMLDocument::SetError( int error, const char* str1, const char* str2 )
1443{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001444 errorID = error;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001445 errorStr1 = str1;
1446 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -08001447}
1448
Lee Thomason5cae8972012-01-24 18:03:07 -08001449
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001450void XMLDocument::PrintError() const
1451{
1452 if ( errorID ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001453 static const int LEN = 20;
1454 char buf1[LEN] = { 0 };
1455 char buf2[LEN] = { 0 };
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001456
1457 if ( errorStr1 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001458 TIXML_SNPRINTF( buf1, LEN, "%s", errorStr1 );
1459 buf1[LEN-1] = 0;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001460 }
1461 if ( errorStr2 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001462 TIXML_SNPRINTF( buf2, LEN, "%s", errorStr2 );
1463 buf2[LEN-1] = 0;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001464 }
1465
1466 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1467 errorID, buf1, buf2 );
1468 }
1469}
1470
1471
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001472XMLPrinter::XMLPrinter( FILE* file ) :
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001473 elementJustOpened( false ),
1474 firstElement( true ),
1475 fp( file ),
1476 depth( 0 ),
1477 textDepth( -1 )
Lee Thomason5cae8972012-01-24 18:03:07 -08001478{
Lee Thomason857b8682012-01-25 17:50:25 -08001479 for( int i=0; i<ENTITY_RANGE; ++i ) {
1480 entityFlag[i] = false;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001481 restrictedEntityFlag[i] = false;
Lee Thomason857b8682012-01-25 17:50:25 -08001482 }
1483 for( int i=0; i<NUM_ENTITIES; ++i ) {
1484 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1485 if ( entities[i].value < ENTITY_RANGE ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001486 entityFlag[ (int)entities[i].value ] = true;
Lee Thomason857b8682012-01-25 17:50:25 -08001487 }
1488 }
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001489 restrictedEntityFlag[(int)'&'] = true;
1490 restrictedEntityFlag[(int)'<'] = true;
1491 restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
U-Stream\Leeae25a442012-02-17 17:48:16 -08001492 buffer.Push( 0 );
1493}
1494
1495
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001496void XMLPrinter::Print( const char* format, ... )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001497{
1498 va_list va;
1499 va_start( va, format );
1500
1501 if ( fp ) {
1502 vfprintf( fp, format, va );
1503 }
1504 else {
1505 // This seems brutally complex. Haven't figured out a better
1506 // way on windows.
1507 #ifdef _MSC_VER
1508 int len = -1;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001509 int expand = 1000;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001510 while ( len < 0 ) {
1511 len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), accumulator.Capacity()-1, format, va );
1512 if ( len < 0 ) {
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001513 accumulator.PushArr( expand );
1514 expand *= 3/2;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001515 }
1516 }
1517 char* p = buffer.PushArr( len ) - 1;
1518 memcpy( p, accumulator.Mem(), len+1 );
1519 #else
1520 int len = vsnprintf( 0, 0, format, va );
1521 char* p = buffer.PushArr( len ) - 1;
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001522 vsnprintf( p, len+1, format, va );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001523 #endif
1524 }
1525 va_end( va );
Lee Thomason5cae8972012-01-24 18:03:07 -08001526}
1527
1528
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001529void XMLPrinter::PrintSpace( int depth )
Lee Thomason5cae8972012-01-24 18:03:07 -08001530{
1531 for( int i=0; i<depth; ++i ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001532 Print( " " );
Lee Thomason5cae8972012-01-24 18:03:07 -08001533 }
1534}
1535
1536
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001537void XMLPrinter::PrintString( const char* p, bool restricted )
Lee Thomason857b8682012-01-25 17:50:25 -08001538{
Lee Thomason951d8832012-01-26 08:47:06 -08001539 // Look for runs of bytes between entities to print.
1540 const char* q = p;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001541 const bool* flag = restricted ? restrictedEntityFlag : entityFlag;
Lee Thomason857b8682012-01-25 17:50:25 -08001542
Lee Thomason951d8832012-01-26 08:47:06 -08001543 while ( *q ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001544 // Remember, char is sometimes signed. (How many times has that bitten me?)
1545 if ( *q > 0 && *q < ENTITY_RANGE ) {
Lee Thomason951d8832012-01-26 08:47:06 -08001546 // Check for entities. If one is found, flush
1547 // the stream up until the entity, write the
1548 // entity, and keep looking.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001549 if ( flag[*q] ) {
Lee Thomason951d8832012-01-26 08:47:06 -08001550 while ( p < q ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001551 Print( "%c", *p );
Lee Thomason951d8832012-01-26 08:47:06 -08001552 ++p;
1553 }
1554 for( int i=0; i<NUM_ENTITIES; ++i ) {
1555 if ( entities[i].value == *q ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001556 Print( "&%s;", entities[i].pattern );
Lee Thomason951d8832012-01-26 08:47:06 -08001557 break;
1558 }
1559 }
1560 ++p;
1561 }
1562 }
1563 ++q;
1564 }
1565 // Flush the remaining string. This will be the entire
1566 // string if an entity wasn't found.
1567 if ( q-p > 0 ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001568 Print( "%s", p );
Lee Thomason951d8832012-01-26 08:47:06 -08001569 }
Lee Thomason857b8682012-01-25 17:50:25 -08001570}
1571
U-Stream\Leeae25a442012-02-17 17:48:16 -08001572
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001573void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001574{
1575 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1576 if ( writeBOM ) {
1577 Print( "%s", bom );
1578 }
1579 if ( writeDec ) {
1580 PushDeclaration( "xml version=\"1.0\"" );
1581 }
1582}
1583
1584
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001585void XMLPrinter::OpenElement( const char* name )
Lee Thomason5cae8972012-01-24 18:03:07 -08001586{
1587 if ( elementJustOpened ) {
1588 SealElement();
1589 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001590 stack.Push( name );
1591
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001592 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001593 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001594 PrintSpace( depth );
1595 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001596
U-Stream\Leeae25a442012-02-17 17:48:16 -08001597 Print( "<%s", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001598 elementJustOpened = true;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001599 firstElement = false;
Lee Thomason5cae8972012-01-24 18:03:07 -08001600 ++depth;
1601}
1602
1603
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001604void XMLPrinter::PushAttribute( const char* name, const char* value )
Lee Thomason5cae8972012-01-24 18:03:07 -08001605{
1606 TIXMLASSERT( elementJustOpened );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001607 Print( " %s=\"", name );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001608 PrintString( value, false );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001609 Print( "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001610}
1611
1612
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001613void XMLPrinter::PushAttribute( const char* name, int v )
1614{
1615 char buf[BUF_SIZE];
1616 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v );
1617 PushAttribute( name, buf );
1618}
1619
1620
1621void XMLPrinter::PushAttribute( const char* name, unsigned v )
1622{
1623 char buf[BUF_SIZE];
1624 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%u", v );
1625 PushAttribute( name, buf );
1626}
1627
1628
1629void XMLPrinter::PushAttribute( const char* name, bool v )
1630{
1631 char buf[BUF_SIZE];
1632 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v ? 1 : 0 );
1633 PushAttribute( name, buf );
1634}
1635
1636
1637void XMLPrinter::PushAttribute( const char* name, double v )
1638{
1639 char buf[BUF_SIZE];
1640 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
1641 PushAttribute( name, buf );
1642}
1643
1644
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001645void XMLPrinter::CloseElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001646{
1647 --depth;
1648 const char* name = stack.Pop();
Lee Thomason5cae8972012-01-24 18:03:07 -08001649
1650 if ( elementJustOpened ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001651 Print( "/>" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001652 }
1653 else {
Lee Thomason56bdd022012-02-09 18:16:58 -08001654 if ( textDepth < 0 ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001655 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001656 PrintSpace( depth );
1657 }
U-Stream\Leeae25a442012-02-17 17:48:16 -08001658 Print( "</%s>", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001659 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001660
1661 if ( textDepth == depth )
1662 textDepth = -1;
1663 if ( depth == 0 )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001664 Print( "\n" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001665 elementJustOpened = false;
1666}
1667
1668
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001669void XMLPrinter::SealElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001670{
1671 elementJustOpened = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001672 Print( ">" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001673}
1674
1675
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001676void XMLPrinter::PushText( const char* text, bool cdata )
Lee Thomason5cae8972012-01-24 18:03:07 -08001677{
Lee Thomason56bdd022012-02-09 18:16:58 -08001678 textDepth = depth-1;
1679
Lee Thomason5cae8972012-01-24 18:03:07 -08001680 if ( elementJustOpened ) {
1681 SealElement();
1682 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001683 if ( cdata ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001684 Print( "<![CDATA[" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001685 Print( "%s", text );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001686 Print( "]]>" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001687 }
1688 else {
1689 PrintString( text, true );
1690 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001691}
1692
1693
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001694void XMLPrinter::PushComment( const char* comment )
Lee Thomason5cae8972012-01-24 18:03:07 -08001695{
1696 if ( elementJustOpened ) {
1697 SealElement();
1698 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001699 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001700 Print( "\n" );
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001701 PrintSpace( depth );
1702 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001703 firstElement = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001704 Print( "<!--%s-->", comment );
Lee Thomason5cae8972012-01-24 18:03:07 -08001705}
Lee Thomason751da522012-02-10 08:50:51 -08001706
1707
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001708void XMLPrinter::PushDeclaration( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001709{
1710 if ( elementJustOpened ) {
1711 SealElement();
1712 }
1713 if ( textDepth < 0 && !firstElement) {
1714 Print( "\n" );
1715 PrintSpace( depth );
1716 }
1717 firstElement = false;
1718 Print( "<?%s?>", value );
1719}
1720
1721
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001722void XMLPrinter::PushUnknown( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001723{
1724 if ( elementJustOpened ) {
1725 SealElement();
1726 }
1727 if ( textDepth < 0 && !firstElement ) {
1728 Print( "\n" );
1729 PrintSpace( depth );
1730 }
1731 firstElement = false;
1732 Print( "<!%s>", value );
1733}
1734
1735
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001736bool XMLPrinter::VisitEnter( const XMLDocument& doc )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001737{
1738 if ( doc.HasBOM() ) {
1739 PushHeader( true, false );
1740 }
1741 return true;
1742}
1743
1744
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001745bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
Lee Thomason751da522012-02-10 08:50:51 -08001746{
1747 OpenElement( element.Name() );
1748 while ( attribute ) {
1749 PushAttribute( attribute->Name(), attribute->Value() );
1750 attribute = attribute->Next();
1751 }
1752 return true;
1753}
1754
1755
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001756bool XMLPrinter::VisitExit( const XMLElement& )
Lee Thomason751da522012-02-10 08:50:51 -08001757{
1758 CloseElement();
1759 return true;
1760}
1761
1762
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001763bool XMLPrinter::Visit( const XMLText& text )
Lee Thomason751da522012-02-10 08:50:51 -08001764{
Lee Thomasond6277762012-02-22 16:00:12 -08001765 PushText( text.Value(), text.CData() );
Lee Thomason751da522012-02-10 08:50:51 -08001766 return true;
1767}
1768
1769
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001770bool XMLPrinter::Visit( const XMLComment& comment )
Lee Thomason751da522012-02-10 08:50:51 -08001771{
1772 PushComment( comment.Value() );
1773 return true;
1774}
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001775
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001776bool XMLPrinter::Visit( const XMLDeclaration& declaration )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001777{
1778 PushDeclaration( declaration.Value() );
1779 return true;
1780}
1781
1782
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001783bool XMLPrinter::Visit( const XMLUnknown& unknown )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001784{
1785 PushUnknown( unknown.Value() );
1786 return true;
1787}
1788
1789