blob: 1d68cb0e3fad8fb1f5e7113b72c5234f21e6c0b4 [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 );
Lee Thomasond6277762012-02-22 16:00:12 -0800595
Lee Thomason67d61312012-01-24 16:01:51 -0800596 if ( node->IsClosingElement() ) {
Lee Thomasond6277762012-02-22 16:00:12 -0800597 if ( !XMLUtil::StringEqual( Value(), node->Value() )) {
598 document->SetError( ERROR_MISMATCHED_ELEMENT, Value(), 0 );
599 }
Lee Thomason43f59302012-02-06 18:18:11 -0800600 DELETE_NODE( node );
Lee Thomason67d61312012-01-24 16:01:51 -0800601 return p;
602 }
603 this->InsertEndChild( node );
604 }
605 }
606 return 0;
607}
608
Lee Thomason5492a1c2012-01-23 15:32:10 -0800609// --------- XMLText ---------- //
610char* XMLText::ParseDeep( char* p )
611{
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800612 const char* start = p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800613 if ( this->CData() ) {
614 p = value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800615 if ( !p ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800616 document->SetError( ERROR_PARSING_CDATA, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800617 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800618 return p;
619 }
620 else {
621 p = value.ParseText( p, "<", StrPair::TEXT_ELEMENT );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800622 if ( !p ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800623 document->SetError( ERROR_PARSING_TEXT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800624 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800625 if ( p && *p ) {
626 return p-1;
627 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800628 }
629 return 0;
630}
631
632
Lee Thomason56bdd022012-02-09 18:16:58 -0800633bool XMLText::Accept( XMLVisitor* visitor ) const
634{
635 return visitor->Visit( *this );
636}
637
638
Lee Thomason3f57d272012-01-11 15:30:03 -0800639// --------- XMLComment ---------- //
640
Lee Thomasone4422302012-01-20 17:59:50 -0800641XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800642{
643}
644
645
Lee Thomasonce0763e2012-01-11 15:43:54 -0800646XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800647{
Lee Thomasond923c672012-01-23 08:44:25 -0800648 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800649}
650
651
Lee Thomasonce0763e2012-01-11 15:43:54 -0800652char* XMLComment::ParseDeep( char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800653{
654 // Comment parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800655 const char* start = p;
656 p = value.ParseText( p, "-->", StrPair::COMMENT );
657 if ( p == 0 ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800658 document->SetError( ERROR_PARSING_COMMENT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800659 }
660 return p;
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800661}
662
663
Lee Thomason751da522012-02-10 08:50:51 -0800664bool XMLComment::Accept( XMLVisitor* visitor ) const
665{
666 return visitor->Visit( *this );
667}
Lee Thomason56bdd022012-02-09 18:16:58 -0800668
669
Lee Thomason50f97b22012-02-11 16:33:40 -0800670// --------- XMLDeclaration ---------- //
671
672XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
673{
674}
675
676
677XMLDeclaration::~XMLDeclaration()
678{
679 //printf( "~XMLDeclaration\n" );
680}
681
682
683char* XMLDeclaration::ParseDeep( char* p )
684{
685 // Declaration parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800686 const char* start = p;
687 p = value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
688 if ( p == 0 ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800689 document->SetError( ERROR_PARSING_DECLARATION, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800690 }
691 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800692}
693
694
695bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
696{
697 return visitor->Visit( *this );
698}
699
700// --------- XMLUnknown ---------- //
701
702XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
703{
704}
705
706
707XMLUnknown::~XMLUnknown()
708{
709}
710
711
712char* XMLUnknown::ParseDeep( char* p )
713{
714 // Unknown parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800715 const char* start = p;
716
717 p = value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
718 if ( !p ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800719 document->SetError( ERROR_PARSING_UNKNOWN, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800720 }
721 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800722}
723
724
725bool XMLUnknown::Accept( XMLVisitor* visitor ) const
726{
727 return visitor->Visit( *this );
728}
729
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800730// --------- XMLAttribute ---------- //
731char* XMLAttribute::ParseDeep( char* p )
732{
Lee Thomason56bdd022012-02-09 18:16:58 -0800733 p = name.ParseText( p, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800734 if ( !p || !*p ) return 0;
735
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800736 char endTag[2] = { *p, 0 };
737 ++p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800738 p = value.ParseText( p, endTag, StrPair::ATTRIBUTE_VALUE );
Lee Thomasone4422302012-01-20 17:59:50 -0800739 if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800740 return p;
741}
742
743
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800744void XMLAttribute::SetName( const char* n )
745{
746 name.SetStr( n );
747}
748
749
Lee Thomason1ff38e02012-02-14 18:18:16 -0800750int XMLAttribute::QueryIntAttribute( int* value ) const
751{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800752 if ( TIXML_SSCANF( Value(), "%d", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800753 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800754 return WRONG_ATTRIBUTE_TYPE;
755}
756
757
758int XMLAttribute::QueryUnsignedAttribute( unsigned int* value ) const
759{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800760 if ( TIXML_SSCANF( Value(), "%u", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800761 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800762 return WRONG_ATTRIBUTE_TYPE;
763}
764
765
766int XMLAttribute::QueryBoolAttribute( bool* value ) const
767{
768 int ival = -1;
769 QueryIntAttribute( &ival );
770
771 if ( ival > 0 || XMLUtil::StringEqual( Value(), "true" ) ) {
772 *value = true;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800773 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800774 }
775 else if ( ival == 0 || XMLUtil::StringEqual( Value(), "false" ) ) {
776 *value = false;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800777 return XML_NO_ERROR;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800778 }
Lee Thomason1ff38e02012-02-14 18:18:16 -0800779 return WRONG_ATTRIBUTE_TYPE;
780}
781
782
783int XMLAttribute::QueryDoubleAttribute( double* value ) const
784{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800785 if ( TIXML_SSCANF( Value(), "%lf", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800786 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800787 return WRONG_ATTRIBUTE_TYPE;
788}
789
790
791int XMLAttribute::QueryFloatAttribute( float* value ) const
792{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800793 if ( TIXML_SSCANF( Value(), "%f", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800794 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800795 return WRONG_ATTRIBUTE_TYPE;
796}
797
798
799void XMLAttribute::SetAttribute( const char* v )
800{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800801 value.SetStr( v );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800802}
803
804
Lee Thomason1ff38e02012-02-14 18:18:16 -0800805void XMLAttribute::SetAttribute( int v )
806{
807 char buf[BUF_SIZE];
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800808 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v );
809 value.SetStr( buf );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800810}
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800811
812
813void XMLAttribute::SetAttribute( unsigned v )
814{
815 char buf[BUF_SIZE];
816 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%u", v );
817 value.SetStr( buf );
818}
819
820
821void XMLAttribute::SetAttribute( bool v )
822{
823 char buf[BUF_SIZE];
824 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v ? 1 : 0 );
825 value.SetStr( buf );
826}
827
828void XMLAttribute::SetAttribute( double v )
829{
830 char buf[BUF_SIZE];
831 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
832 value.SetStr( buf );
833}
834
835void XMLAttribute::SetAttribute( float v )
836{
837 char buf[BUF_SIZE];
838 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
839 value.SetStr( buf );
840}
841
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800842
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800843// --------- XMLElement ---------- //
844XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800845 closing( false ),
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800846 rootAttribute( 0 )
847 //lastAttribute( 0 )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800848{
849}
850
851
852XMLElement::~XMLElement()
853{
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800854 while( rootAttribute ) {
855 XMLAttribute* next = rootAttribute->next;
856 DELETE_ATTRIBUTE( rootAttribute );
857 rootAttribute = next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800858 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800859}
860
861
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800862XMLAttribute* XMLElement::FindAttribute( const char* name )
863{
864 XMLAttribute* a = 0;
865 for( a=rootAttribute; a; a = a->next ) {
866 if ( XMLUtil::StringEqual( a->Name(), name ) )
867 return a;
868 }
869 return 0;
870}
871
872
873const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
874{
875 XMLAttribute* a = 0;
876 for( a=rootAttribute; a; a = a->next ) {
877 if ( XMLUtil::StringEqual( a->Name(), name ) )
878 return a;
879 }
880 return 0;
881}
882
883
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800884const char* XMLElement::GetText() const
885{
886 if ( FirstChild() && FirstChild()->ToText() ) {
887 return FirstChild()->ToText()->Value();
888 }
889 return 0;
890}
891
892
893
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800894XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
895{
896 XMLAttribute* attrib = FindAttribute( name );
897 if ( !attrib ) {
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800898 attrib = new (document->attributePool.Alloc() ) XMLAttribute( this );
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800899 attrib->memPool = &document->attributePool;
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800900 LinkAttribute( attrib );
901 attrib->SetName( name );
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800902 }
903 return attrib;
904}
905
906
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800907void XMLElement::LinkAttribute( XMLAttribute* attrib )
908{
909 if ( rootAttribute ) {
910 XMLAttribute* end = rootAttribute;
911 while ( end->next )
912 end = end->next;
913 end->next = attrib;
914 }
915 else {
916 rootAttribute = attrib;
917 }
918}
919
920
U-Stream\Leeae25a442012-02-17 17:48:16 -0800921void XMLElement::DeleteAttribute( const char* name )
922{
923 XMLAttribute* prev = 0;
924 for( XMLAttribute* a=rootAttribute; a; a=a->next ) {
925 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
926 if ( prev ) {
927 prev->next = a->next;
928 }
929 else {
930 rootAttribute = a->next;
931 }
932 DELETE_ATTRIBUTE( a );
933 break;
934 }
935 prev = a;
936 }
937}
938
939
Lee Thomason67d61312012-01-24 16:01:51 -0800940char* XMLElement::ParseAttributes( char* p, bool* closedElement )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800941{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800942 const char* start = p;
Lee Thomason67d61312012-01-24 16:01:51 -0800943 *closedElement = false;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800944
945 // Read the attributes.
946 while( p ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800947 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800948 if ( !p || !(*p) ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800949 document->SetError( ERROR_PARSING_ELEMENT, start, Name() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800950 return 0;
951 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800952
953 // attribute.
Lee Thomason56bdd022012-02-09 18:16:58 -0800954 if ( XMLUtil::IsAlpha( *p ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -0800955 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute( this );
956 attrib->memPool = &document->attributePool;
Lee Thomasond1983222012-02-06 08:41:24 -0800957
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800958 p = attrib->ParseDeep( p );
Lee Thomasond6277762012-02-22 16:00:12 -0800959 if ( !p || Attribute( attrib->Name() ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -0800960 DELETE_ATTRIBUTE( attrib );
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800961 document->SetError( ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800962 return 0;
963 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800964 LinkAttribute( attrib );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800965 }
Lee Thomasone4422302012-01-20 17:59:50 -0800966 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800967 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800968 if ( closing ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800969 document->SetError( ERROR_PARSING_ELEMENT, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800970 return 0;
971 }
Lee Thomason67d61312012-01-24 16:01:51 -0800972 *closedElement = true;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800973 return p+2; // done; sealed element.
974 }
Lee Thomasone4422302012-01-20 17:59:50 -0800975 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800976 else if ( *p == '>' ) {
977 ++p;
978 break;
979 }
Lee Thomasone4422302012-01-20 17:59:50 -0800980 else {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800981 document->SetError( ERROR_PARSING_ELEMENT, start, p );
Lee Thomasone4422302012-01-20 17:59:50 -0800982 return 0;
983 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800984 }
Lee Thomason67d61312012-01-24 16:01:51 -0800985 return p;
986}
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800987
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800988
Lee Thomason67d61312012-01-24 16:01:51 -0800989//
990// <ele></ele>
991// <ele>foo<b>bar</b></ele>
992//
993char* XMLElement::ParseDeep( char* p )
994{
995 // Read the element name.
Lee Thomason56bdd022012-02-09 18:16:58 -0800996 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason67d61312012-01-24 16:01:51 -0800997 if ( !p ) return 0;
998 const char* start = p;
999
1000 // The closing element is the </element> form. It is
1001 // parsed just like a regular element then deleted from
1002 // the DOM.
1003 if ( *p == '/' ) {
1004 closing = true;
1005 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001006 }
Lee Thomason67d61312012-01-24 16:01:51 -08001007
Lee Thomason56bdd022012-02-09 18:16:58 -08001008 p = value.ParseName( p );
Lee Thomasond1983222012-02-06 08:41:24 -08001009 if ( value.Empty() ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001010
1011 bool elementClosed=false;
1012 p = ParseAttributes( p, &elementClosed );
1013 if ( !p || !*p || elementClosed || closing )
1014 return p;
1015
1016 p = XMLNode::ParseDeep( p );
1017 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001018}
1019
1020
Lee Thomason751da522012-02-10 08:50:51 -08001021bool XMLElement::Accept( XMLVisitor* visitor ) const
1022{
1023 if ( visitor->VisitEnter( *this, rootAttribute ) )
1024 {
1025 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
1026 {
1027 if ( !node->Accept( visitor ) )
1028 break;
1029 }
1030 }
1031 return visitor->VisitExit( *this );
1032
1033}
Lee Thomason56bdd022012-02-09 18:16:58 -08001034
Lee Thomason3f57d272012-01-11 15:30:03 -08001035// --------- XMLDocument ----------- //
Lee Thomason67d61312012-01-24 16:01:51 -08001036XMLDocument::XMLDocument() :
Lee Thomason18d68bd2012-01-26 18:17:26 -08001037 XMLNode( 0 ),
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001038 writeBOM( false ),
U-Lama\Lee560bd472011-12-28 19:42:49 -08001039 charBuffer( 0 )
1040{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001041 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -08001042}
U-Lama\Leee13c3e62011-12-28 14:36:55 -08001043
1044
Lee Thomason3f57d272012-01-11 15:30:03 -08001045XMLDocument::~XMLDocument()
1046{
Lee Thomasond1983222012-02-06 08:41:24 -08001047 ClearChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001048 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -08001049
Lee Thomasonec5a7b42012-02-13 18:16:52 -08001050#if 0
Lee Thomason455c9d42012-02-06 09:14:14 -08001051 textPool.Trace( "text" );
1052 elementPool.Trace( "element" );
1053 commentPool.Trace( "comment" );
1054 attributePool.Trace( "attribute" );
Lee Thomasone9ecdab2012-02-13 18:11:20 -08001055#endif
1056
Lee Thomason455c9d42012-02-06 09:14:14 -08001057 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
1058 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
1059 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
1060 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -08001061}
1062
1063
Lee Thomason18d68bd2012-01-26 18:17:26 -08001064void XMLDocument::InitDocument()
1065{
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001066 errorID = XML_NO_ERROR;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001067 errorStr1 = 0;
1068 errorStr2 = 0;
1069
1070 delete [] charBuffer;
1071 charBuffer = 0;
1072
1073}
1074
Lee Thomason3f57d272012-01-11 15:30:03 -08001075
Lee Thomason2c85a712012-01-31 08:24:24 -08001076XMLElement* XMLDocument::NewElement( const char* name )
1077{
Lee Thomasond1983222012-02-06 08:41:24 -08001078 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
1079 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -08001080 ele->SetName( name );
1081 return ele;
1082}
1083
1084
Lee Thomason1ff38e02012-02-14 18:18:16 -08001085XMLComment* XMLDocument::NewComment( const char* str )
1086{
1087 XMLComment* comment = new (commentPool.Alloc()) XMLComment( this );
1088 comment->memPool = &commentPool;
1089 comment->SetValue( str );
1090 return comment;
1091}
1092
1093
1094XMLText* XMLDocument::NewText( const char* str )
1095{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001096 XMLText* text = new (textPool.Alloc()) XMLText( this );
1097 text->memPool = &textPool;
1098 text->SetValue( str );
1099 return text;
Lee Thomason1ff38e02012-02-14 18:18:16 -08001100}
1101
1102
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001103int XMLDocument::LoadFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001104{
1105 ClearChildren();
1106 InitDocument();
1107
1108 FILE* fp = fopen( filename, "rb" );
1109 if ( !fp ) {
1110 SetError( ERROR_FILE_NOT_FOUND, filename, 0 );
1111 return errorID;
1112 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001113 LoadFile( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001114 fclose( fp );
1115 return errorID;
1116}
1117
1118
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001119int XMLDocument::LoadFile( FILE* fp )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001120{
1121 ClearChildren();
1122 InitDocument();
1123
1124 fseek( fp, 0, SEEK_END );
1125 unsigned size = ftell( fp );
1126 fseek( fp, 0, SEEK_SET );
1127
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001128 if ( size == 0 ) {
1129 return errorID;
1130 }
1131
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001132 charBuffer = new char[size+1];
1133 fread( charBuffer, size, 1, fp );
1134 charBuffer[size] = 0;
1135
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001136 const char* p = charBuffer;
1137 p = XMLUtil::SkipWhiteSpace( p );
1138 p = XMLUtil::ReadBOM( p, &writeBOM );
1139 if ( !p || !*p ) {
Lee Thomasond6277762012-02-22 16:00:12 -08001140 SetError( ERROR_EMPTY_DOCUMENT, 0, 0 );
1141 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001142 }
1143
1144 ParseDeep( charBuffer + (p-charBuffer) );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001145 return errorID;
1146}
1147
1148
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001149void XMLDocument::SaveFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001150{
1151 FILE* fp = fopen( filename, "w" );
1152 XMLStreamer stream( fp );
1153 Print( &stream );
1154 fclose( fp );
1155}
1156
Lee Thomason1ff38e02012-02-14 18:18:16 -08001157
Lee Thomason7c913cd2012-01-26 18:32:34 -08001158int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -08001159{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001160 ClearChildren();
1161 InitDocument();
1162
1163 if ( !p || !*p ) {
Lee Thomasond6277762012-02-22 16:00:12 -08001164 SetError( ERROR_EMPTY_DOCUMENT, 0, 0 );
1165 return errorID;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001166 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001167 p = XMLUtil::SkipWhiteSpace( p );
1168 p = XMLUtil::ReadBOM( p, &writeBOM );
1169 if ( !p || !*p ) {
Lee Thomasond6277762012-02-22 16:00:12 -08001170 SetError( ERROR_EMPTY_DOCUMENT, 0, 0 );
1171 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001172 }
1173
Lee Thomason18d68bd2012-01-26 18:17:26 -08001174 size_t len = strlen( p );
1175 charBuffer = new char[ len+1 ];
1176 memcpy( charBuffer, p, len+1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001177
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001178
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001179 ParseDeep( charBuffer );
Lee Thomason7c913cd2012-01-26 18:32:34 -08001180 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -08001181}
1182
1183
Lee Thomason5cae8972012-01-24 18:03:07 -08001184void XMLDocument::Print( XMLStreamer* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -08001185{
Lee Thomason5cae8972012-01-24 18:03:07 -08001186 XMLStreamer stdStreamer( stdout );
1187 if ( !streamer )
1188 streamer = &stdStreamer;
Lee Thomason751da522012-02-10 08:50:51 -08001189 Accept( streamer );
Lee Thomason3f57d272012-01-11 15:30:03 -08001190}
1191
1192
Lee Thomason67d61312012-01-24 16:01:51 -08001193void XMLDocument::SetError( int error, const char* str1, const char* str2 )
1194{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001195 errorID = error;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001196 errorStr1 = str1;
1197 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -08001198}
1199
Lee Thomason5cae8972012-01-24 18:03:07 -08001200
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001201void XMLDocument::PrintError() const
1202{
1203 if ( errorID ) {
1204 char buf1[20] = { 0 };
1205 char buf2[20] = { 0 };
1206
1207 if ( errorStr1 ) {
1208 strncpy( buf1, errorStr1, 20 );
1209 buf1[19] = 0;
1210 }
1211 if ( errorStr2 ) {
1212 strncpy( buf2, errorStr2, 20 );
1213 buf2[19] = 0;
1214 }
1215
1216 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1217 errorID, buf1, buf2 );
1218 }
1219}
1220
1221
1222XMLStreamer::XMLStreamer( FILE* file ) :
1223 elementJustOpened( false ),
1224 firstElement( true ),
1225 fp( file ),
1226 depth( 0 ),
1227 textDepth( -1 )
Lee Thomason5cae8972012-01-24 18:03:07 -08001228{
Lee Thomason857b8682012-01-25 17:50:25 -08001229 for( int i=0; i<ENTITY_RANGE; ++i ) {
1230 entityFlag[i] = false;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001231 restrictedEntityFlag[i] = false;
Lee Thomason857b8682012-01-25 17:50:25 -08001232 }
1233 for( int i=0; i<NUM_ENTITIES; ++i ) {
1234 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1235 if ( entities[i].value < ENTITY_RANGE ) {
1236 entityFlag[ entities[i].value ] = true;
1237 }
1238 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001239 restrictedEntityFlag['&'] = true;
1240 restrictedEntityFlag['<'] = true;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001241 restrictedEntityFlag['>'] = true; // not required, but consistency is nice
U-Stream\Leeae25a442012-02-17 17:48:16 -08001242 buffer.Push( 0 );
1243}
1244
1245
1246void XMLStreamer::Print( const char* format, ... )
1247{
1248 va_list va;
1249 va_start( va, format );
1250
1251 if ( fp ) {
1252 vfprintf( fp, format, va );
1253 }
1254 else {
1255 // This seems brutally complex. Haven't figured out a better
1256 // way on windows.
1257 #ifdef _MSC_VER
1258 int len = -1;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001259 int expand = 1000;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001260 while ( len < 0 ) {
1261 len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), accumulator.Capacity()-1, format, va );
1262 if ( len < 0 ) {
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001263 accumulator.PushArr( expand );
1264 expand *= 3/2;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001265 }
1266 }
1267 char* p = buffer.PushArr( len ) - 1;
1268 memcpy( p, accumulator.Mem(), len+1 );
1269 #else
1270 int len = vsnprintf( 0, 0, format, va );
1271 char* p = buffer.PushArr( len ) - 1;
1272 vsprintf_s( p, len+1, format, va );
1273 #endif
1274 }
1275 va_end( va );
Lee Thomason5cae8972012-01-24 18:03:07 -08001276}
1277
1278
1279void XMLStreamer::PrintSpace( int depth )
1280{
1281 for( int i=0; i<depth; ++i ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001282 Print( " " );
Lee Thomason5cae8972012-01-24 18:03:07 -08001283 }
1284}
1285
1286
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001287void XMLStreamer::PrintString( const char* p, bool restricted )
Lee Thomason857b8682012-01-25 17:50:25 -08001288{
Lee Thomason951d8832012-01-26 08:47:06 -08001289 // Look for runs of bytes between entities to print.
1290 const char* q = p;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001291 const bool* flag = restricted ? restrictedEntityFlag : entityFlag;
Lee Thomason857b8682012-01-25 17:50:25 -08001292
Lee Thomason951d8832012-01-26 08:47:06 -08001293 while ( *q ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001294 // Remember, char is sometimes signed. (How many times has that bitten me?)
1295 if ( *q > 0 && *q < ENTITY_RANGE ) {
Lee Thomason951d8832012-01-26 08:47:06 -08001296 // Check for entities. If one is found, flush
1297 // the stream up until the entity, write the
1298 // entity, and keep looking.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001299 if ( flag[*q] ) {
Lee Thomason951d8832012-01-26 08:47:06 -08001300 while ( p < q ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001301 Print( "%c", *p );
Lee Thomason951d8832012-01-26 08:47:06 -08001302 ++p;
1303 }
1304 for( int i=0; i<NUM_ENTITIES; ++i ) {
1305 if ( entities[i].value == *q ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001306 Print( "&%s;", entities[i].pattern );
Lee Thomason951d8832012-01-26 08:47:06 -08001307 break;
1308 }
1309 }
1310 ++p;
1311 }
1312 }
1313 ++q;
1314 }
1315 // Flush the remaining string. This will be the entire
1316 // string if an entity wasn't found.
1317 if ( q-p > 0 ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001318 Print( "%s", p );
Lee Thomason951d8832012-01-26 08:47:06 -08001319 }
Lee Thomason857b8682012-01-25 17:50:25 -08001320}
1321
U-Stream\Leeae25a442012-02-17 17:48:16 -08001322
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001323void XMLStreamer::PushHeader( bool writeBOM, bool writeDec )
1324{
1325 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1326 if ( writeBOM ) {
1327 Print( "%s", bom );
1328 }
1329 if ( writeDec ) {
1330 PushDeclaration( "xml version=\"1.0\"" );
1331 }
1332}
1333
1334
Lee Thomason56bdd022012-02-09 18:16:58 -08001335void XMLStreamer::OpenElement( const char* name )
Lee Thomason5cae8972012-01-24 18:03:07 -08001336{
1337 if ( elementJustOpened ) {
1338 SealElement();
1339 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001340 stack.Push( name );
1341
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001342 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001343 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001344 PrintSpace( depth );
1345 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001346
U-Stream\Leeae25a442012-02-17 17:48:16 -08001347 Print( "<%s", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001348 elementJustOpened = true;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001349 firstElement = false;
Lee Thomason5cae8972012-01-24 18:03:07 -08001350 ++depth;
1351}
1352
1353
1354void XMLStreamer::PushAttribute( const char* name, const char* value )
1355{
1356 TIXMLASSERT( elementJustOpened );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001357 Print( " %s=\"", name );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001358 PrintString( value, false );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001359 Print( "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001360}
1361
1362
1363void XMLStreamer::CloseElement()
1364{
1365 --depth;
1366 const char* name = stack.Pop();
Lee Thomason5cae8972012-01-24 18:03:07 -08001367
1368 if ( elementJustOpened ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001369 Print( "/>" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001370 }
1371 else {
Lee Thomason56bdd022012-02-09 18:16:58 -08001372 if ( textDepth < 0 ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001373 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001374 PrintSpace( depth );
1375 }
U-Stream\Leeae25a442012-02-17 17:48:16 -08001376 Print( "</%s>", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001377 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001378
1379 if ( textDepth == depth )
1380 textDepth = -1;
1381 if ( depth == 0 )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001382 Print( "\n" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001383 elementJustOpened = false;
1384}
1385
1386
1387void XMLStreamer::SealElement()
1388{
1389 elementJustOpened = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001390 Print( ">" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001391}
1392
1393
Lee Thomason50f97b22012-02-11 16:33:40 -08001394void XMLStreamer::PushText( const char* text, bool cdata )
Lee Thomason5cae8972012-01-24 18:03:07 -08001395{
Lee Thomason56bdd022012-02-09 18:16:58 -08001396 textDepth = depth-1;
1397
Lee Thomason5cae8972012-01-24 18:03:07 -08001398 if ( elementJustOpened ) {
1399 SealElement();
1400 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001401 if ( cdata ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001402 Print( "<![CDATA[" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001403 Print( "%s", text );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001404 Print( "]]>" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001405 }
1406 else {
1407 PrintString( text, true );
1408 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001409}
1410
1411
1412void XMLStreamer::PushComment( const char* comment )
1413{
1414 if ( elementJustOpened ) {
1415 SealElement();
1416 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001417 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001418 Print( "\n" );
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001419 PrintSpace( depth );
1420 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001421 firstElement = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001422 Print( "<!--%s-->", comment );
Lee Thomason5cae8972012-01-24 18:03:07 -08001423}
Lee Thomason751da522012-02-10 08:50:51 -08001424
1425
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001426void XMLStreamer::PushDeclaration( const char* value )
1427{
1428 if ( elementJustOpened ) {
1429 SealElement();
1430 }
1431 if ( textDepth < 0 && !firstElement) {
1432 Print( "\n" );
1433 PrintSpace( depth );
1434 }
1435 firstElement = false;
1436 Print( "<?%s?>", value );
1437}
1438
1439
1440void XMLStreamer::PushUnknown( const char* value )
1441{
1442 if ( elementJustOpened ) {
1443 SealElement();
1444 }
1445 if ( textDepth < 0 && !firstElement ) {
1446 Print( "\n" );
1447 PrintSpace( depth );
1448 }
1449 firstElement = false;
1450 Print( "<!%s>", value );
1451}
1452
1453
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001454bool XMLStreamer::VisitEnter( const XMLDocument& doc )
1455{
1456 if ( doc.HasBOM() ) {
1457 PushHeader( true, false );
1458 }
1459 return true;
1460}
1461
1462
Lee Thomason751da522012-02-10 08:50:51 -08001463bool XMLStreamer::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
1464{
1465 OpenElement( element.Name() );
1466 while ( attribute ) {
1467 PushAttribute( attribute->Name(), attribute->Value() );
1468 attribute = attribute->Next();
1469 }
1470 return true;
1471}
1472
1473
1474bool XMLStreamer::VisitExit( const XMLElement& element )
1475{
1476 CloseElement();
1477 return true;
1478}
1479
1480
1481bool XMLStreamer::Visit( const XMLText& text )
1482{
Lee Thomasond6277762012-02-22 16:00:12 -08001483 PushText( text.Value(), text.CData() );
Lee Thomason751da522012-02-10 08:50:51 -08001484 return true;
1485}
1486
1487
1488bool XMLStreamer::Visit( const XMLComment& comment )
1489{
1490 PushComment( comment.Value() );
1491 return true;
1492}
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001493
1494bool XMLStreamer::Visit( const XMLDeclaration& declaration )
1495{
1496 PushDeclaration( declaration.Value() );
1497 return true;
1498}
1499
1500
1501bool XMLStreamer::Visit( const XMLUnknown& unknown )
1502{
1503 PushUnknown( unknown.Value() );
1504 return true;
1505}
1506
1507