blob: 8a4fa9521608b6d155fdcdc6b21792d5d77fad67 [file] [log] [blame]
U-Lama\Lee560bd472011-12-28 19:42:49 -08001#include "tinyxml2.h"
2
3#include <string.h>
4#include <stdlib.h>
5#include <stdio.h>
U-Lama\Lee4cee6112011-12-31 14:58:18 -08006#include <ctype.h>
Lee Thomasond1983222012-02-06 08:41:24 -08007#include <new.h>
U-Stream\Leeae25a442012-02-17 17:48:16 -08008#include <stdarg.h>
Lee Thomasond1983222012-02-06 08:41:24 -08009
10//#pragma warning ( disable : 4291 )
U-Lama\Lee560bd472011-12-28 19:42:49 -080011
12using namespace tinyxml2;
13
Lee Thomasone4422302012-01-20 17:59:50 -080014static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
Lee Thomasonfde6a752012-01-14 18:08:12 -080015static const char LF = LINE_FEED;
16static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
17static const char CR = CARRIAGE_RETURN;
Lee Thomasone4422302012-01-20 17:59:50 -080018static const char SINGLE_QUOTE = '\'';
19static const char DOUBLE_QUOTE = '\"';
Lee Thomasonfde6a752012-01-14 18:08:12 -080020
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -080021// Bunch of unicode info at:
22// http://www.unicode.org/faq/utf_bom.html
23// ef bb bf (Microsoft "lead bytes") - designates UTF-8
24
25static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
26static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
27static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
28
29
Lee Thomason43f59302012-02-06 18:18:11 -080030#define DELETE_NODE( node ) { MemPool* pool = node->memPool; node->~XMLNode(); pool->Free( node ); }
31#define DELETE_ATTRIBUTE( attrib ) { MemPool* pool = attrib->memPool; attrib->~XMLAttribute(); pool->Free( attrib ); }
32
Lee Thomason8ee79892012-01-25 17:44:30 -080033struct Entity {
34 const char* pattern;
35 int length;
36 char value;
37};
38
39static const int NUM_ENTITIES = 5;
40static const Entity entities[NUM_ENTITIES] =
41{
Lee Thomason18d68bd2012-01-26 18:17:26 -080042 { "quot", 4, DOUBLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080043 { "amp", 3, '&' },
Lee Thomason18d68bd2012-01-26 18:17:26 -080044 { "apos", 4, SINGLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080045 { "lt", 2, '<' },
46 { "gt", 2, '>' }
47};
48
Lee Thomasonfde6a752012-01-14 18:08:12 -080049
Lee Thomason1a1d4a72012-02-15 09:09:25 -080050StrPair::~StrPair()
51{
52 Reset();
53}
54
55
56void StrPair::Reset()
57{
58 if ( flags & NEEDS_DELETE ) {
59 delete [] start;
60 }
61 flags = 0;
62 start = 0;
63 end = 0;
64}
65
66
67void StrPair::SetStr( const char* str, int flags )
68{
69 Reset();
70 size_t len = strlen( str );
71 start = new char[ len+1 ];
U-Stream\Lee09a11c52012-02-17 08:31:16 -080072 memcpy( start, str, len+1 );
Lee Thomason1a1d4a72012-02-15 09:09:25 -080073 end = start + len;
74 this->flags = flags | NEEDS_DELETE;
75}
76
77
78char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
79{
80 TIXMLASSERT( endTag && *endTag );
81
82 char* start = p; // fixme: hides a member
83 char endChar = *endTag;
84 int length = strlen( endTag );
85
86 // Inner loop of text parsing.
87 while ( *p ) {
88 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
89 Set( start, p, strFlags );
90 return p + length;
91 }
92 ++p;
93 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -080094 return 0;
Lee Thomason1a1d4a72012-02-15 09:09:25 -080095}
96
97
98char* StrPair::ParseName( char* p )
99{
100 char* start = p;
101
102 start = p;
103 if ( !start || !(*start) ) {
104 return 0;
105 }
106
107 if ( !XMLUtil::IsAlpha( *p ) ) {
108 return 0;
109 }
110
111 while( *p && (
112 XMLUtil::IsAlphaNum( (unsigned char) *p )
113 || *p == '_'
114 || *p == '-'
115 || *p == '.'
116 || *p == ':' ))
117 {
118 ++p;
119 }
120
121 if ( p > start ) {
122 Set( start, p, 0 );
123 return p;
124 }
125 return 0;
126}
127
128
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800129
Lee Thomasone4422302012-01-20 17:59:50 -0800130const char* StrPair::GetStr()
131{
132 if ( flags & NEEDS_FLUSH ) {
133 *end = 0;
Lee Thomason8ee79892012-01-25 17:44:30 -0800134 flags ^= NEEDS_FLUSH;
Lee Thomasone4422302012-01-20 17:59:50 -0800135
Lee Thomason8ee79892012-01-25 17:44:30 -0800136 if ( flags ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800137 char* p = start; // the read pointer
138 char* q = start; // the write pointer
Lee Thomasone4422302012-01-20 17:59:50 -0800139
140 while( p < end ) {
Lee Thomason8ee79892012-01-25 17:44:30 -0800141 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800142 // CR-LF pair becomes LF
143 // CR alone becomes LF
144 // LF-CR becomes LF
145 if ( *(p+1) == LF ) {
146 p += 2;
147 }
148 else {
149 ++p;
150 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800151 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800152 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800153 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800154 if ( *(p+1) == CR ) {
155 p += 2;
156 }
157 else {
158 ++p;
159 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800160 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800161 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800162 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
163 int i=0;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800164
165 // Entities handled by tinyXML2:
166 // - special entities in the entity table [in/out]
167 // - numeric character reference [in]
168 // &#20013; or &#x4e2d;
169
170 if ( *(p+1) == '#' ) {
171 char buf[10] = { 0 };
172 int len;
173 p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
174 for( int i=0; i<len; ++i ) {
175 *q++ = buf[i];
Lee Thomason8ee79892012-01-25 17:44:30 -0800176 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800177 TIXMLASSERT( q <= p );
Lee Thomason8ee79892012-01-25 17:44:30 -0800178 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800179 else {
180 for( i=0; i<NUM_ENTITIES; ++i ) {
181 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
182 && *(p+entities[i].length+1) == ';' )
183 {
184 // Found an entity convert;
185 *q = entities[i].value;
186 ++q;
187 p += entities[i].length + 2;
188 break;
189 }
190 }
191 if ( i == NUM_ENTITIES ) {
192 // fixme: treat as error?
193 ++p;
194 ++q;
195 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800196 }
197 }
Lee Thomasone4422302012-01-20 17:59:50 -0800198 else {
199 *q = *p;
200 ++p;
Lee Thomasonec975ce2012-01-23 11:42:06 -0800201 ++q;
Lee Thomasone4422302012-01-20 17:59:50 -0800202 }
203 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800204 *q = 0;
Lee Thomasone4422302012-01-20 17:59:50 -0800205 }
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800206 flags = (flags & NEEDS_DELETE);
Lee Thomasone4422302012-01-20 17:59:50 -0800207 }
208 return start;
209}
210
Lee Thomason2c85a712012-01-31 08:24:24 -0800211
Lee Thomasone4422302012-01-20 17:59:50 -0800212
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800213
Lee Thomason56bdd022012-02-09 18:16:58 -0800214// --------- XMLUtil ----------- //
Lee Thomasond1983222012-02-06 08:41:24 -0800215
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800216const char* XMLUtil::ReadBOM( const char* p, bool* bom )
217{
218 *bom = false;
219 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
220 // Check for BOM:
221 if ( *(pu+0) == TIXML_UTF_LEAD_0
222 && *(pu+1) == TIXML_UTF_LEAD_1
223 && *(pu+2) == TIXML_UTF_LEAD_2 )
224 {
225 *bom = true;
226 p += 3;
227 }
228 return p;
229}
230
231
232void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
233{
234 const unsigned long BYTE_MASK = 0xBF;
235 const unsigned long BYTE_MARK = 0x80;
236 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
237
238 if (input < 0x80)
239 *length = 1;
240 else if ( input < 0x800 )
241 *length = 2;
242 else if ( input < 0x10000 )
243 *length = 3;
244 else if ( input < 0x200000 )
245 *length = 4;
246 else
247 { *length = 0; return; } // This code won't covert this correctly anyway.
248
249 output += *length;
250
251 // Scary scary fall throughs.
252 switch (*length)
253 {
254 case 4:
255 --output;
256 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
257 input >>= 6;
258 case 3:
259 --output;
260 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
261 input >>= 6;
262 case 2:
263 --output;
264 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
265 input >>= 6;
266 case 1:
267 --output;
268 *output = (char)(input | FIRST_BYTE_MARK[*length]);
269 }
270}
271
272
273const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
274{
275 // Presume an entity, and pull it out.
276 *length = 0;
277
278 if ( *(p+1) == '#' && *(p+2) )
279 {
280 unsigned long ucs = 0;
281 ptrdiff_t delta = 0;
282 unsigned mult = 1;
283
284 if ( *(p+2) == 'x' )
285 {
286 // Hexadecimal.
287 if ( !*(p+3) ) return 0;
288
289 const char* q = p+3;
290 q = strchr( q, ';' );
291
292 if ( !q || !*q ) return 0;
293
294 delta = q-p;
295 --q;
296
297 while ( *q != 'x' )
298 {
299 if ( *q >= '0' && *q <= '9' )
300 ucs += mult * (*q - '0');
301 else if ( *q >= 'a' && *q <= 'f' )
302 ucs += mult * (*q - 'a' + 10);
303 else if ( *q >= 'A' && *q <= 'F' )
304 ucs += mult * (*q - 'A' + 10 );
305 else
306 return 0;
307 mult *= 16;
308 --q;
309 }
310 }
311 else
312 {
313 // Decimal.
314 if ( !*(p+2) ) return 0;
315
316 const char* q = p+2;
317 q = strchr( q, ';' );
318
319 if ( !q || !*q ) return 0;
320
321 delta = q-p;
322 --q;
323
324 while ( *q != '#' )
325 {
326 if ( *q >= '0' && *q <= '9' )
327 ucs += mult * (*q - '0');
328 else
329 return 0;
330 mult *= 10;
331 --q;
332 }
333 }
334 // convert the UCS to UTF-8
335 ConvertUTF32ToUTF8( ucs, value, length );
336 return p + delta + 1;
337 }
338 return p+1;
339}
340
341
Lee Thomasond1983222012-02-06 08:41:24 -0800342char* XMLDocument::Identify( char* p, XMLNode** node )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800343{
344 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800345 char* start = p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800346 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800347 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800348 {
349 return 0;
350 }
351
352 // What is this thing?
353 // - Elements start with a letter or underscore, but xml is reserved.
354 // - Comments: <!--
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800355 // - Decleration: <?
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800356 // - Everthing else is unknown to tinyxml.
357 //
358
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800359 static const char* xmlHeader = { "<?" };
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800360 static const char* commentHeader = { "<!--" };
361 static const char* dtdHeader = { "<!" };
362 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800363 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800364
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800365 static const int xmlHeaderLen = 2;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800366 static const int commentHeaderLen = 4;
367 static const int dtdHeaderLen = 2;
368 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800369 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800370
Lee Thomason50f97b22012-02-11 16:33:40 -0800371 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
372 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
373
374 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
375 returnNode = new (commentPool.Alloc()) XMLDeclaration( this );
376 returnNode->memPool = &commentPool;
377 p += xmlHeaderLen;
378 }
379 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800380 returnNode = new (commentPool.Alloc()) XMLComment( this );
381 returnNode->memPool = &commentPool;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800382 p += commentHeaderLen;
383 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800384 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
385 XMLText* text = new (textPool.Alloc()) XMLText( this );
386 returnNode = text;
387 returnNode->memPool = &textPool;
388 p += cdataHeaderLen;
389 text->SetCData( true );
390 }
391 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
392 returnNode = new (commentPool.Alloc()) XMLUnknown( this );
393 returnNode->memPool = &commentPool;
394 p += dtdHeaderLen;
395 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800396 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800397 returnNode = new (elementPool.Alloc()) XMLElement( this );
398 returnNode->memPool = &elementPool;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800399 p += elementHeaderLen;
400 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800401 else {
Lee Thomasond1983222012-02-06 08:41:24 -0800402 returnNode = new (textPool.Alloc()) XMLText( this );
403 returnNode->memPool = &textPool;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800404 p = start; // Back it up, all the text counts.
405 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800406
407 *node = returnNode;
408 return p;
409}
410
411
Lee Thomason751da522012-02-10 08:50:51 -0800412bool XMLDocument::Accept( XMLVisitor* visitor ) const
413{
414 if ( visitor->VisitEnter( *this ) )
415 {
416 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
417 {
418 if ( !node->Accept( visitor ) )
419 break;
420 }
421 }
422 return visitor->VisitExit( *this );
423}
Lee Thomason56bdd022012-02-09 18:16:58 -0800424
425
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800426// --------- XMLNode ----------- //
427
428XMLNode::XMLNode( XMLDocument* doc ) :
429 document( doc ),
430 parent( 0 ),
431 firstChild( 0 ), lastChild( 0 ),
432 prev( 0 ), next( 0 )
433{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800434}
435
436
437XMLNode::~XMLNode()
438{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800439 ClearChildren();
Lee Thomason7c913cd2012-01-26 18:32:34 -0800440 if ( parent ) {
441 parent->Unlink( this );
442 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800443}
444
445
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800446void XMLNode::SetValue( const char* str, bool staticMem )
447{
448 if ( staticMem )
449 value.SetInternedStr( str );
450 else
451 value.SetStr( str );
452}
453
454
Lee Thomason18d68bd2012-01-26 18:17:26 -0800455void XMLNode::ClearChildren()
456{
Lee Thomasond923c672012-01-23 08:44:25 -0800457 while( firstChild ) {
458 XMLNode* node = firstChild;
459 Unlink( node );
Lee Thomasond1983222012-02-06 08:41:24 -0800460
Lee Thomason43f59302012-02-06 18:18:11 -0800461 DELETE_NODE( node );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800462 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800463 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800464}
465
466
467void XMLNode::Unlink( XMLNode* child )
468{
469 TIXMLASSERT( child->parent == this );
470 if ( child == firstChild )
471 firstChild = firstChild->next;
472 if ( child == lastChild )
473 lastChild = lastChild->prev;
474
475 if ( child->prev ) {
476 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800477 }
Lee Thomasond923c672012-01-23 08:44:25 -0800478 if ( child->next ) {
479 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800480 }
Lee Thomasond923c672012-01-23 08:44:25 -0800481 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800482}
483
484
U-Stream\Leeae25a442012-02-17 17:48:16 -0800485void XMLNode::DeleteChild( XMLNode* node )
486{
487 TIXMLASSERT( node->parent == this );
488 DELETE_NODE( node );
489}
490
491
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800492XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
493{
494 if ( lastChild ) {
495 TIXMLASSERT( firstChild );
496 TIXMLASSERT( lastChild->next == 0 );
497 lastChild->next = addThis;
498 addThis->prev = lastChild;
499 lastChild = addThis;
500
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800501 addThis->next = 0;
502 }
503 else {
504 TIXMLASSERT( firstChild == 0 );
505 firstChild = lastChild = addThis;
506
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800507 addThis->prev = 0;
508 addThis->next = 0;
509 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800510 addThis->parent = this;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800511 return addThis;
512}
513
514
Lee Thomason1ff38e02012-02-14 18:18:16 -0800515XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
516{
517 if ( firstChild ) {
518 TIXMLASSERT( lastChild );
519 TIXMLASSERT( firstChild->prev == 0 );
520
521 firstChild->prev = addThis;
522 addThis->next = firstChild;
523 firstChild = addThis;
524
Lee Thomason1ff38e02012-02-14 18:18:16 -0800525 addThis->prev = 0;
526 }
527 else {
528 TIXMLASSERT( lastChild == 0 );
529 firstChild = lastChild = addThis;
530
Lee Thomason1ff38e02012-02-14 18:18:16 -0800531 addThis->prev = 0;
532 addThis->next = 0;
533 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800534 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800535 return addThis;
536}
537
538
539XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
540{
541 TIXMLASSERT( afterThis->parent == this );
542 if ( afterThis->parent != this )
543 return 0;
544
545 if ( afterThis->next == 0 ) {
546 // The last node or the only node.
547 return InsertEndChild( addThis );
548 }
549 addThis->prev = afterThis;
550 addThis->next = afterThis->next;
551 afterThis->next->prev = addThis;
552 afterThis->next = addThis;
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800553 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800554 return addThis;
555}
556
557
558
559
Lee Thomason56bdd022012-02-09 18:16:58 -0800560const XMLElement* XMLNode::FirstChildElement( const char* value ) const
Lee Thomason2c85a712012-01-31 08:24:24 -0800561{
562 for( XMLNode* node=firstChild; node; node=node->next ) {
563 XMLElement* element = node->ToElement();
564 if ( element ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800565 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
Lee Thomason2c85a712012-01-31 08:24:24 -0800566 return element;
567 }
568 }
569 }
570 return 0;
571}
572
573
Lee Thomason56bdd022012-02-09 18:16:58 -0800574const XMLElement* XMLNode::LastChildElement( const char* value ) const
575{
576 for( XMLNode* node=lastChild; node; node=node->prev ) {
577 XMLElement* element = node->ToElement();
578 if ( element ) {
579 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
580 return element;
581 }
582 }
583 }
584 return 0;
585}
586
587
Lee Thomason67d61312012-01-24 16:01:51 -0800588char* XMLNode::ParseDeep( char* p )
589{
590 while( p && *p ) {
591 XMLNode* node = 0;
Lee Thomasond1983222012-02-06 08:41:24 -0800592 p = document->Identify( p, &node );
Lee Thomason67d61312012-01-24 16:01:51 -0800593 if ( p && node ) {
594 p = node->ParseDeep( p );
595 // FIXME: is it the correct closing element?
596 if ( node->IsClosingElement() ) {
Lee Thomason43f59302012-02-06 18:18:11 -0800597 DELETE_NODE( node );
Lee Thomason67d61312012-01-24 16:01:51 -0800598 return p;
599 }
600 this->InsertEndChild( node );
601 }
602 }
603 return 0;
604}
605
Lee Thomason5492a1c2012-01-23 15:32:10 -0800606// --------- XMLText ---------- //
607char* XMLText::ParseDeep( char* p )
608{
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800609 const char* start = p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800610 if ( this->CData() ) {
611 p = value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800612 if ( !p ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800613 document->SetError( ERROR_PARSING_CDATA, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800614 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800615 return p;
616 }
617 else {
618 p = value.ParseText( p, "<", StrPair::TEXT_ELEMENT );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800619 if ( !p ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800620 document->SetError( ERROR_PARSING_TEXT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800621 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800622 if ( p && *p ) {
623 return p-1;
624 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800625 }
626 return 0;
627}
628
629
Lee Thomason56bdd022012-02-09 18:16:58 -0800630bool XMLText::Accept( XMLVisitor* visitor ) const
631{
632 return visitor->Visit( *this );
633}
634
635
Lee Thomason3f57d272012-01-11 15:30:03 -0800636// --------- XMLComment ---------- //
637
Lee Thomasone4422302012-01-20 17:59:50 -0800638XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800639{
640}
641
642
Lee Thomasonce0763e2012-01-11 15:43:54 -0800643XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800644{
Lee Thomasond923c672012-01-23 08:44:25 -0800645 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800646}
647
648
Lee Thomasonce0763e2012-01-11 15:43:54 -0800649char* XMLComment::ParseDeep( char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800650{
651 // Comment parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800652 const char* start = p;
653 p = value.ParseText( p, "-->", StrPair::COMMENT );
654 if ( p == 0 ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800655 document->SetError( ERROR_PARSING_COMMENT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800656 }
657 return p;
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800658}
659
660
Lee Thomason751da522012-02-10 08:50:51 -0800661bool XMLComment::Accept( XMLVisitor* visitor ) const
662{
663 return visitor->Visit( *this );
664}
Lee Thomason56bdd022012-02-09 18:16:58 -0800665
666
Lee Thomason50f97b22012-02-11 16:33:40 -0800667// --------- XMLDeclaration ---------- //
668
669XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
670{
671}
672
673
674XMLDeclaration::~XMLDeclaration()
675{
676 //printf( "~XMLDeclaration\n" );
677}
678
679
680char* XMLDeclaration::ParseDeep( char* p )
681{
682 // Declaration parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800683 const char* start = p;
684 p = value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
685 if ( p == 0 ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800686 document->SetError( ERROR_PARSING_DECLARATION, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800687 }
688 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800689}
690
691
692bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
693{
694 return visitor->Visit( *this );
695}
696
697// --------- XMLUnknown ---------- //
698
699XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
700{
701}
702
703
704XMLUnknown::~XMLUnknown()
705{
706}
707
708
709char* XMLUnknown::ParseDeep( char* p )
710{
711 // Unknown parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800712 const char* start = p;
713
714 p = value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
715 if ( !p ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800716 document->SetError( ERROR_PARSING_UNKNOWN, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800717 }
718 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800719}
720
721
722bool XMLUnknown::Accept( XMLVisitor* visitor ) const
723{
724 return visitor->Visit( *this );
725}
726
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800727// --------- XMLAttribute ---------- //
728char* XMLAttribute::ParseDeep( char* p )
729{
Lee Thomason56bdd022012-02-09 18:16:58 -0800730 p = name.ParseText( p, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800731 if ( !p || !*p ) return 0;
732
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800733 char endTag[2] = { *p, 0 };
734 ++p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800735 p = value.ParseText( p, endTag, StrPair::ATTRIBUTE_VALUE );
Lee Thomasone4422302012-01-20 17:59:50 -0800736 if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800737 return p;
738}
739
740
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800741void XMLAttribute::SetName( const char* n )
742{
743 name.SetStr( n );
744}
745
746
Lee Thomason1ff38e02012-02-14 18:18:16 -0800747int XMLAttribute::QueryIntAttribute( int* value ) const
748{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800749 if ( TIXML_SSCANF( Value(), "%d", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800750 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800751 return WRONG_ATTRIBUTE_TYPE;
752}
753
754
755int XMLAttribute::QueryUnsignedAttribute( unsigned int* value ) const
756{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800757 if ( TIXML_SSCANF( Value(), "%u", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800758 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800759 return WRONG_ATTRIBUTE_TYPE;
760}
761
762
763int XMLAttribute::QueryBoolAttribute( bool* value ) const
764{
765 int ival = -1;
766 QueryIntAttribute( &ival );
767
768 if ( ival > 0 || XMLUtil::StringEqual( Value(), "true" ) ) {
769 *value = true;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800770 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800771 }
772 else if ( ival == 0 || XMLUtil::StringEqual( Value(), "false" ) ) {
773 *value = false;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800774 return XML_NO_ERROR;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800775 }
Lee Thomason1ff38e02012-02-14 18:18:16 -0800776 return WRONG_ATTRIBUTE_TYPE;
777}
778
779
780int XMLAttribute::QueryDoubleAttribute( double* value ) const
781{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800782 if ( TIXML_SSCANF( Value(), "%lf", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800783 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800784 return WRONG_ATTRIBUTE_TYPE;
785}
786
787
788int XMLAttribute::QueryFloatAttribute( float* value ) const
789{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800790 if ( TIXML_SSCANF( Value(), "%f", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800791 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800792 return WRONG_ATTRIBUTE_TYPE;
793}
794
795
796void XMLAttribute::SetAttribute( const char* v )
797{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800798 value.SetStr( v );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800799}
800
801
Lee Thomason1ff38e02012-02-14 18:18:16 -0800802void XMLAttribute::SetAttribute( int v )
803{
804 char buf[BUF_SIZE];
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800805 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v );
806 value.SetStr( buf );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800807}
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800808
809
810void XMLAttribute::SetAttribute( unsigned v )
811{
812 char buf[BUF_SIZE];
813 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%u", v );
814 value.SetStr( buf );
815}
816
817
818void XMLAttribute::SetAttribute( bool v )
819{
820 char buf[BUF_SIZE];
821 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v ? 1 : 0 );
822 value.SetStr( buf );
823}
824
825void XMLAttribute::SetAttribute( double v )
826{
827 char buf[BUF_SIZE];
828 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
829 value.SetStr( buf );
830}
831
832void XMLAttribute::SetAttribute( float v )
833{
834 char buf[BUF_SIZE];
835 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
836 value.SetStr( buf );
837}
838
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800839
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800840// --------- XMLElement ---------- //
841XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800842 closing( false ),
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800843 rootAttribute( 0 )
844 //lastAttribute( 0 )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800845{
846}
847
848
849XMLElement::~XMLElement()
850{
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800851 while( rootAttribute ) {
852 XMLAttribute* next = rootAttribute->next;
853 DELETE_ATTRIBUTE( rootAttribute );
854 rootAttribute = next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800855 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800856}
857
858
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800859XMLAttribute* XMLElement::FindAttribute( const char* name )
860{
861 XMLAttribute* a = 0;
862 for( a=rootAttribute; a; a = a->next ) {
863 if ( XMLUtil::StringEqual( a->Name(), name ) )
864 return a;
865 }
866 return 0;
867}
868
869
870const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
871{
872 XMLAttribute* a = 0;
873 for( a=rootAttribute; a; a = a->next ) {
874 if ( XMLUtil::StringEqual( a->Name(), name ) )
875 return a;
876 }
877 return 0;
878}
879
880
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800881const char* XMLElement::GetText() const
882{
883 if ( FirstChild() && FirstChild()->ToText() ) {
884 return FirstChild()->ToText()->Value();
885 }
886 return 0;
887}
888
889
890
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800891XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
892{
893 XMLAttribute* attrib = FindAttribute( name );
894 if ( !attrib ) {
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800895 attrib = new (document->attributePool.Alloc() ) XMLAttribute( this );
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800896 attrib->memPool = &document->attributePool;
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800897 LinkAttribute( attrib );
898 attrib->SetName( name );
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800899 }
900 return attrib;
901}
902
903
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800904void XMLElement::LinkAttribute( XMLAttribute* attrib )
905{
906 if ( rootAttribute ) {
907 XMLAttribute* end = rootAttribute;
908 while ( end->next )
909 end = end->next;
910 end->next = attrib;
911 }
912 else {
913 rootAttribute = attrib;
914 }
915}
916
917
U-Stream\Leeae25a442012-02-17 17:48:16 -0800918void XMLElement::DeleteAttribute( const char* name )
919{
920 XMLAttribute* prev = 0;
921 for( XMLAttribute* a=rootAttribute; a; a=a->next ) {
922 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
923 if ( prev ) {
924 prev->next = a->next;
925 }
926 else {
927 rootAttribute = a->next;
928 }
929 DELETE_ATTRIBUTE( a );
930 break;
931 }
932 prev = a;
933 }
934}
935
936
Lee Thomason67d61312012-01-24 16:01:51 -0800937char* XMLElement::ParseAttributes( char* p, bool* closedElement )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800938{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800939 const char* start = p;
Lee Thomason67d61312012-01-24 16:01:51 -0800940 *closedElement = false;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800941
942 // Read the attributes.
943 while( p ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800944 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800945 if ( !p || !(*p) ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800946 document->SetError( ERROR_PARSING_ELEMENT, start, Name() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800947 return 0;
948 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800949
950 // attribute.
Lee Thomason56bdd022012-02-09 18:16:58 -0800951 if ( XMLUtil::IsAlpha( *p ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -0800952 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute( this );
953 attrib->memPool = &document->attributePool;
Lee Thomasond1983222012-02-06 08:41:24 -0800954
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800955 p = attrib->ParseDeep( p );
956 if ( !p ) {
Lee Thomason43f59302012-02-06 18:18:11 -0800957 DELETE_ATTRIBUTE( attrib );
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800958 document->SetError( ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800959 return 0;
960 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800961 LinkAttribute( attrib );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800962 }
Lee Thomasone4422302012-01-20 17:59:50 -0800963 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800964 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800965 if ( closing ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800966 document->SetError( ERROR_PARSING_ELEMENT, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800967 return 0;
968 }
Lee Thomason67d61312012-01-24 16:01:51 -0800969 *closedElement = true;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800970 return p+2; // done; sealed element.
971 }
Lee Thomasone4422302012-01-20 17:59:50 -0800972 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800973 else if ( *p == '>' ) {
974 ++p;
975 break;
976 }
Lee Thomasone4422302012-01-20 17:59:50 -0800977 else {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800978 document->SetError( ERROR_PARSING_ELEMENT, start, p );
Lee Thomasone4422302012-01-20 17:59:50 -0800979 return 0;
980 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800981 }
Lee Thomason67d61312012-01-24 16:01:51 -0800982 return p;
983}
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800984
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800985
Lee Thomason67d61312012-01-24 16:01:51 -0800986//
987// <ele></ele>
988// <ele>foo<b>bar</b></ele>
989//
990char* XMLElement::ParseDeep( char* p )
991{
992 // Read the element name.
Lee Thomason56bdd022012-02-09 18:16:58 -0800993 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason67d61312012-01-24 16:01:51 -0800994 if ( !p ) return 0;
995 const char* start = p;
996
997 // The closing element is the </element> form. It is
998 // parsed just like a regular element then deleted from
999 // the DOM.
1000 if ( *p == '/' ) {
1001 closing = true;
1002 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001003 }
Lee Thomason67d61312012-01-24 16:01:51 -08001004
Lee Thomason56bdd022012-02-09 18:16:58 -08001005 p = value.ParseName( p );
Lee Thomasond1983222012-02-06 08:41:24 -08001006 if ( value.Empty() ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001007
1008 bool elementClosed=false;
1009 p = ParseAttributes( p, &elementClosed );
1010 if ( !p || !*p || elementClosed || closing )
1011 return p;
1012
1013 p = XMLNode::ParseDeep( p );
1014 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001015}
1016
1017
Lee Thomason751da522012-02-10 08:50:51 -08001018bool XMLElement::Accept( XMLVisitor* visitor ) const
1019{
1020 if ( visitor->VisitEnter( *this, rootAttribute ) )
1021 {
1022 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
1023 {
1024 if ( !node->Accept( visitor ) )
1025 break;
1026 }
1027 }
1028 return visitor->VisitExit( *this );
1029
1030}
Lee Thomason56bdd022012-02-09 18:16:58 -08001031
Lee Thomason3f57d272012-01-11 15:30:03 -08001032// --------- XMLDocument ----------- //
Lee Thomason67d61312012-01-24 16:01:51 -08001033XMLDocument::XMLDocument() :
Lee Thomason18d68bd2012-01-26 18:17:26 -08001034 XMLNode( 0 ),
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001035 writeBOM( false ),
U-Lama\Lee560bd472011-12-28 19:42:49 -08001036 charBuffer( 0 )
1037{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001038 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -08001039}
U-Lama\Leee13c3e62011-12-28 14:36:55 -08001040
1041
Lee Thomason3f57d272012-01-11 15:30:03 -08001042XMLDocument::~XMLDocument()
1043{
Lee Thomasond1983222012-02-06 08:41:24 -08001044 ClearChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001045 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -08001046
Lee Thomasonec5a7b42012-02-13 18:16:52 -08001047#if 0
Lee Thomason455c9d42012-02-06 09:14:14 -08001048 textPool.Trace( "text" );
1049 elementPool.Trace( "element" );
1050 commentPool.Trace( "comment" );
1051 attributePool.Trace( "attribute" );
Lee Thomasone9ecdab2012-02-13 18:11:20 -08001052#endif
1053
Lee Thomason455c9d42012-02-06 09:14:14 -08001054 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
1055 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
1056 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
1057 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -08001058}
1059
1060
Lee Thomason18d68bd2012-01-26 18:17:26 -08001061void XMLDocument::InitDocument()
1062{
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001063 errorID = XML_NO_ERROR;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001064 errorStr1 = 0;
1065 errorStr2 = 0;
1066
1067 delete [] charBuffer;
1068 charBuffer = 0;
1069
1070}
1071
Lee Thomason3f57d272012-01-11 15:30:03 -08001072
Lee Thomason2c85a712012-01-31 08:24:24 -08001073XMLElement* XMLDocument::NewElement( const char* name )
1074{
Lee Thomasond1983222012-02-06 08:41:24 -08001075 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
1076 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -08001077 ele->SetName( name );
1078 return ele;
1079}
1080
1081
Lee Thomason1ff38e02012-02-14 18:18:16 -08001082XMLComment* XMLDocument::NewComment( const char* str )
1083{
1084 XMLComment* comment = new (commentPool.Alloc()) XMLComment( this );
1085 comment->memPool = &commentPool;
1086 comment->SetValue( str );
1087 return comment;
1088}
1089
1090
1091XMLText* XMLDocument::NewText( const char* str )
1092{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001093 XMLText* text = new (textPool.Alloc()) XMLText( this );
1094 text->memPool = &textPool;
1095 text->SetValue( str );
1096 return text;
Lee Thomason1ff38e02012-02-14 18:18:16 -08001097}
1098
1099
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001100int XMLDocument::LoadFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001101{
1102 ClearChildren();
1103 InitDocument();
1104
1105 FILE* fp = fopen( filename, "rb" );
1106 if ( !fp ) {
1107 SetError( ERROR_FILE_NOT_FOUND, filename, 0 );
1108 return errorID;
1109 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001110 LoadFile( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001111 fclose( fp );
1112 return errorID;
1113}
1114
1115
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001116int XMLDocument::LoadFile( FILE* fp )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001117{
1118 ClearChildren();
1119 InitDocument();
1120
1121 fseek( fp, 0, SEEK_END );
1122 unsigned size = ftell( fp );
1123 fseek( fp, 0, SEEK_SET );
1124
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001125 if ( size == 0 ) {
1126 return errorID;
1127 }
1128
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001129 charBuffer = new char[size+1];
1130 fread( charBuffer, size, 1, fp );
1131 charBuffer[size] = 0;
1132
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001133 const char* p = charBuffer;
1134 p = XMLUtil::SkipWhiteSpace( p );
1135 p = XMLUtil::ReadBOM( p, &writeBOM );
1136 if ( !p || !*p ) {
1137 return 0; // correctly parse an empty string?
1138 }
1139
1140 ParseDeep( charBuffer + (p-charBuffer) );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001141 return errorID;
1142}
1143
1144
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001145void XMLDocument::SaveFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001146{
1147 FILE* fp = fopen( filename, "w" );
1148 XMLStreamer stream( fp );
1149 Print( &stream );
1150 fclose( fp );
1151}
1152
Lee Thomason1ff38e02012-02-14 18:18:16 -08001153
Lee Thomason7c913cd2012-01-26 18:32:34 -08001154int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -08001155{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001156 ClearChildren();
1157 InitDocument();
1158
1159 if ( !p || !*p ) {
1160 return true; // correctly parse an empty string?
1161 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001162 p = XMLUtil::SkipWhiteSpace( p );
1163 p = XMLUtil::ReadBOM( p, &writeBOM );
1164 if ( !p || !*p ) {
1165 return true; // correctly parse an empty string?
1166 }
1167
Lee Thomason18d68bd2012-01-26 18:17:26 -08001168 size_t len = strlen( p );
1169 charBuffer = new char[ len+1 ];
1170 memcpy( charBuffer, p, len+1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001171
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001172
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001173 ParseDeep( charBuffer );
Lee Thomason7c913cd2012-01-26 18:32:34 -08001174 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -08001175}
1176
1177
Lee Thomason5cae8972012-01-24 18:03:07 -08001178void XMLDocument::Print( XMLStreamer* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -08001179{
Lee Thomason5cae8972012-01-24 18:03:07 -08001180 XMLStreamer stdStreamer( stdout );
1181 if ( !streamer )
1182 streamer = &stdStreamer;
Lee Thomason751da522012-02-10 08:50:51 -08001183 //for( XMLNode* node = firstChild; node; node=node->next ) {
1184 // node->Print( streamer );
1185 //}
1186 Accept( streamer );
Lee Thomason3f57d272012-01-11 15:30:03 -08001187}
1188
1189
Lee Thomason67d61312012-01-24 16:01:51 -08001190void XMLDocument::SetError( int error, const char* str1, const char* str2 )
1191{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001192 errorID = error;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001193 errorStr1 = str1;
1194 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -08001195}
1196
Lee Thomason5cae8972012-01-24 18:03:07 -08001197
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001198void XMLDocument::PrintError() const
1199{
1200 if ( errorID ) {
1201 char buf1[20] = { 0 };
1202 char buf2[20] = { 0 };
1203
1204 if ( errorStr1 ) {
1205 strncpy( buf1, errorStr1, 20 );
1206 buf1[19] = 0;
1207 }
1208 if ( errorStr2 ) {
1209 strncpy( buf2, errorStr2, 20 );
1210 buf2[19] = 0;
1211 }
1212
1213 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1214 errorID, buf1, buf2 );
1215 }
1216}
1217
1218
1219XMLStreamer::XMLStreamer( FILE* file ) :
1220 elementJustOpened( false ),
1221 firstElement( true ),
1222 fp( file ),
1223 depth( 0 ),
1224 textDepth( -1 )
Lee Thomason5cae8972012-01-24 18:03:07 -08001225{
Lee Thomason857b8682012-01-25 17:50:25 -08001226 for( int i=0; i<ENTITY_RANGE; ++i ) {
1227 entityFlag[i] = false;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001228 restrictedEntityFlag[i] = false;
Lee Thomason857b8682012-01-25 17:50:25 -08001229 }
1230 for( int i=0; i<NUM_ENTITIES; ++i ) {
1231 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1232 if ( entities[i].value < ENTITY_RANGE ) {
1233 entityFlag[ entities[i].value ] = true;
1234 }
1235 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001236 restrictedEntityFlag['&'] = true;
1237 restrictedEntityFlag['<'] = true;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001238 restrictedEntityFlag['>'] = true; // not required, but consistency is nice
U-Stream\Leeae25a442012-02-17 17:48:16 -08001239 buffer.Push( 0 );
1240}
1241
1242
1243void XMLStreamer::Print( const char* format, ... )
1244{
1245 va_list va;
1246 va_start( va, format );
1247
1248 if ( fp ) {
1249 vfprintf( fp, format, va );
1250 }
1251 else {
1252 // This seems brutally complex. Haven't figured out a better
1253 // way on windows.
1254 #ifdef _MSC_VER
1255 int len = -1;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001256 int expand = 1000;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001257 while ( len < 0 ) {
1258 len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), accumulator.Capacity()-1, format, va );
1259 if ( len < 0 ) {
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001260 accumulator.PushArr( expand );
1261 expand *= 3/2;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001262 }
1263 }
1264 char* p = buffer.PushArr( len ) - 1;
1265 memcpy( p, accumulator.Mem(), len+1 );
1266 #else
1267 int len = vsnprintf( 0, 0, format, va );
1268 char* p = buffer.PushArr( len ) - 1;
1269 vsprintf_s( p, len+1, format, va );
1270 #endif
1271 }
1272 va_end( va );
Lee Thomason5cae8972012-01-24 18:03:07 -08001273}
1274
1275
1276void XMLStreamer::PrintSpace( int depth )
1277{
1278 for( int i=0; i<depth; ++i ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001279 Print( " " );
Lee Thomason5cae8972012-01-24 18:03:07 -08001280 }
1281}
1282
1283
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001284void XMLStreamer::PrintString( const char* p, bool restricted )
Lee Thomason857b8682012-01-25 17:50:25 -08001285{
Lee Thomason951d8832012-01-26 08:47:06 -08001286 // Look for runs of bytes between entities to print.
1287 const char* q = p;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001288 const bool* flag = restricted ? restrictedEntityFlag : entityFlag;
Lee Thomason857b8682012-01-25 17:50:25 -08001289
Lee Thomason951d8832012-01-26 08:47:06 -08001290 while ( *q ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001291 // Remember, char is sometimes signed. (How many times has that bitten me?)
1292 if ( *q > 0 && *q < ENTITY_RANGE ) {
Lee Thomason951d8832012-01-26 08:47:06 -08001293 // Check for entities. If one is found, flush
1294 // the stream up until the entity, write the
1295 // entity, and keep looking.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001296 if ( flag[*q] ) {
Lee Thomason951d8832012-01-26 08:47:06 -08001297 while ( p < q ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001298 Print( "%c", *p );
Lee Thomason951d8832012-01-26 08:47:06 -08001299 ++p;
1300 }
1301 for( int i=0; i<NUM_ENTITIES; ++i ) {
1302 if ( entities[i].value == *q ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001303 Print( "&%s;", entities[i].pattern );
Lee Thomason951d8832012-01-26 08:47:06 -08001304 break;
1305 }
1306 }
1307 ++p;
1308 }
1309 }
1310 ++q;
1311 }
1312 // Flush the remaining string. This will be the entire
1313 // string if an entity wasn't found.
1314 if ( q-p > 0 ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001315 Print( "%s", p );
Lee Thomason951d8832012-01-26 08:47:06 -08001316 }
Lee Thomason857b8682012-01-25 17:50:25 -08001317}
1318
U-Stream\Leeae25a442012-02-17 17:48:16 -08001319
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001320void XMLStreamer::PushHeader( bool writeBOM, bool writeDec )
1321{
1322 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1323 if ( writeBOM ) {
1324 Print( "%s", bom );
1325 }
1326 if ( writeDec ) {
1327 PushDeclaration( "xml version=\"1.0\"" );
1328 }
1329}
1330
1331
Lee Thomason56bdd022012-02-09 18:16:58 -08001332void XMLStreamer::OpenElement( const char* name )
Lee Thomason5cae8972012-01-24 18:03:07 -08001333{
1334 if ( elementJustOpened ) {
1335 SealElement();
1336 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001337 stack.Push( name );
1338
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001339 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001340 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001341 PrintSpace( depth );
1342 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001343
U-Stream\Leeae25a442012-02-17 17:48:16 -08001344 Print( "<%s", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001345 elementJustOpened = true;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001346 firstElement = false;
Lee Thomason5cae8972012-01-24 18:03:07 -08001347 ++depth;
1348}
1349
1350
1351void XMLStreamer::PushAttribute( const char* name, const char* value )
1352{
1353 TIXMLASSERT( elementJustOpened );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001354 Print( " %s=\"", name );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001355 PrintString( value, false );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001356 Print( "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001357}
1358
1359
1360void XMLStreamer::CloseElement()
1361{
1362 --depth;
1363 const char* name = stack.Pop();
Lee Thomason5cae8972012-01-24 18:03:07 -08001364
1365 if ( elementJustOpened ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001366 Print( "/>" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001367 }
1368 else {
Lee Thomason56bdd022012-02-09 18:16:58 -08001369 if ( textDepth < 0 ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001370 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001371 PrintSpace( depth );
1372 }
U-Stream\Leeae25a442012-02-17 17:48:16 -08001373 Print( "</%s>", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001374 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001375
1376 if ( textDepth == depth )
1377 textDepth = -1;
1378 if ( depth == 0 )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001379 Print( "\n" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001380 elementJustOpened = false;
1381}
1382
1383
1384void XMLStreamer::SealElement()
1385{
1386 elementJustOpened = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001387 Print( ">" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001388}
1389
1390
Lee Thomason50f97b22012-02-11 16:33:40 -08001391void XMLStreamer::PushText( const char* text, bool cdata )
Lee Thomason5cae8972012-01-24 18:03:07 -08001392{
Lee Thomason56bdd022012-02-09 18:16:58 -08001393 textDepth = depth-1;
1394
Lee Thomason5cae8972012-01-24 18:03:07 -08001395 if ( elementJustOpened ) {
1396 SealElement();
1397 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001398 if ( cdata ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001399 Print( "<![CDATA[" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001400 Print( "%s", text );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001401 Print( "]]>" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001402 }
1403 else {
1404 PrintString( text, true );
1405 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001406}
1407
1408
1409void XMLStreamer::PushComment( const char* comment )
1410{
1411 if ( elementJustOpened ) {
1412 SealElement();
1413 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001414 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001415 Print( "\n" );
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001416 PrintSpace( depth );
1417 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001418 firstElement = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001419 Print( "<!--%s-->", comment );
Lee Thomason5cae8972012-01-24 18:03:07 -08001420}
Lee Thomason751da522012-02-10 08:50:51 -08001421
1422
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001423void XMLStreamer::PushDeclaration( const char* value )
1424{
1425 if ( elementJustOpened ) {
1426 SealElement();
1427 }
1428 if ( textDepth < 0 && !firstElement) {
1429 Print( "\n" );
1430 PrintSpace( depth );
1431 }
1432 firstElement = false;
1433 Print( "<?%s?>", value );
1434}
1435
1436
1437void XMLStreamer::PushUnknown( const char* value )
1438{
1439 if ( elementJustOpened ) {
1440 SealElement();
1441 }
1442 if ( textDepth < 0 && !firstElement ) {
1443 Print( "\n" );
1444 PrintSpace( depth );
1445 }
1446 firstElement = false;
1447 Print( "<!%s>", value );
1448}
1449
1450
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001451bool XMLStreamer::VisitEnter( const XMLDocument& doc )
1452{
1453 if ( doc.HasBOM() ) {
1454 PushHeader( true, false );
1455 }
1456 return true;
1457}
1458
1459
Lee Thomason751da522012-02-10 08:50:51 -08001460bool XMLStreamer::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
1461{
1462 OpenElement( element.Name() );
1463 while ( attribute ) {
1464 PushAttribute( attribute->Name(), attribute->Value() );
1465 attribute = attribute->Next();
1466 }
1467 return true;
1468}
1469
1470
1471bool XMLStreamer::VisitExit( const XMLElement& element )
1472{
1473 CloseElement();
1474 return true;
1475}
1476
1477
1478bool XMLStreamer::Visit( const XMLText& text )
1479{
1480 PushText( text.Value() );
1481 return true;
1482}
1483
1484
1485bool XMLStreamer::Visit( const XMLComment& comment )
1486{
1487 PushComment( comment.Value() );
1488 return true;
1489}
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001490
1491bool XMLStreamer::Visit( const XMLDeclaration& declaration )
1492{
1493 PushDeclaration( declaration.Value() );
1494 return true;
1495}
1496
1497
1498bool XMLStreamer::Visit( const XMLUnknown& unknown )
1499{
1500 PushUnknown( unknown.Value() );
1501 return true;
1502}
1503
1504