blob: fd11c27a334c88a2806a887ec90c550512e021c7 [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>
8
9//#pragma warning ( disable : 4291 )
U-Lama\Lee560bd472011-12-28 19:42:49 -080010
11using namespace tinyxml2;
12
Lee Thomasone4422302012-01-20 17:59:50 -080013static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
Lee Thomasonfde6a752012-01-14 18:08:12 -080014static const char LF = LINE_FEED;
15static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
16static const char CR = CARRIAGE_RETURN;
Lee Thomasone4422302012-01-20 17:59:50 -080017static const char SINGLE_QUOTE = '\'';
18static const char DOUBLE_QUOTE = '\"';
Lee Thomasonfde6a752012-01-14 18:08:12 -080019
Lee Thomason43f59302012-02-06 18:18:11 -080020#define DELETE_NODE( node ) { MemPool* pool = node->memPool; node->~XMLNode(); pool->Free( node ); }
21#define DELETE_ATTRIBUTE( attrib ) { MemPool* pool = attrib->memPool; attrib->~XMLAttribute(); pool->Free( attrib ); }
22
Lee Thomason8ee79892012-01-25 17:44:30 -080023struct Entity {
24 const char* pattern;
25 int length;
26 char value;
27};
28
29static const int NUM_ENTITIES = 5;
30static const Entity entities[NUM_ENTITIES] =
31{
Lee Thomason18d68bd2012-01-26 18:17:26 -080032 { "quot", 4, DOUBLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080033 { "amp", 3, '&' },
Lee Thomason18d68bd2012-01-26 18:17:26 -080034 { "apos", 4, SINGLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080035 { "lt", 2, '<' },
36 { "gt", 2, '>' }
37};
38
Lee Thomasonfde6a752012-01-14 18:08:12 -080039
Lee Thomasone4422302012-01-20 17:59:50 -080040const char* StrPair::GetStr()
41{
42 if ( flags & NEEDS_FLUSH ) {
43 *end = 0;
Lee Thomason8ee79892012-01-25 17:44:30 -080044 flags ^= NEEDS_FLUSH;
Lee Thomasone4422302012-01-20 17:59:50 -080045
Lee Thomason8ee79892012-01-25 17:44:30 -080046 if ( flags ) {
Lee Thomasone4422302012-01-20 17:59:50 -080047 char* p = start;
48 char* q = start;
49
50 while( p < end ) {
Lee Thomason8ee79892012-01-25 17:44:30 -080051 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Lee Thomasone4422302012-01-20 17:59:50 -080052 // CR-LF pair becomes LF
53 // CR alone becomes LF
54 // LF-CR becomes LF
55 if ( *(p+1) == LF ) {
56 p += 2;
57 }
58 else {
59 ++p;
60 }
61 *q = LF;
62 }
Lee Thomason8ee79892012-01-25 17:44:30 -080063 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Lee Thomasone4422302012-01-20 17:59:50 -080064 if ( *(p+1) == CR ) {
65 p += 2;
66 }
67 else {
68 ++p;
69 }
70 *q = LF;
71 }
Lee Thomason8ee79892012-01-25 17:44:30 -080072 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
73 int i=0;
74 for( i=0; i<NUM_ENTITIES; ++i ) {
75 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
76 && *(p+entities[i].length+1) == ';' )
77 {
78 // Found an entity convert;
79 *q = entities[i].value;
80 ++q;
81 p += entities[i].length + 2;
82 break;
83 }
84 }
85 if ( i == NUM_ENTITIES ) {
86 // fixme: treat as error?
87 ++p;
88 ++q;
89 }
90 }
Lee Thomasone4422302012-01-20 17:59:50 -080091 else {
92 *q = *p;
93 ++p;
Lee Thomasonec975ce2012-01-23 11:42:06 -080094 ++q;
Lee Thomasone4422302012-01-20 17:59:50 -080095 }
96 }
Lee Thomason8ee79892012-01-25 17:44:30 -080097 *q = 0;
Lee Thomasone4422302012-01-20 17:59:50 -080098 }
99 flags = 0;
100 }
101 return start;
102}
103
Lee Thomason2c85a712012-01-31 08:24:24 -0800104/*
105const char* StringPool::Intern( const char* str )
106{
107 // Treat the array as a linear, inplace hash table.
108 // Nothing can get deleted, so that's handy.
109 if ( size > pool.Size()*3/4 ) {
110 DynArray< const char*, 20 > store;
111 for( int i=0; i<pool.Size(); ++i ) {
112 if ( pool[i] != 0 ) {
113 store.Push( pool[i] );
114 }
115 }
116 int newSize = pool.Size() * 2;
117 pool.PopArr( pool.Size() );
118
119 const char** mem = pool.PushArr( newSize );
120 memset( (void*)mem, 0, sizeof(char)*newSize );
121
122 while ( !store.Empty() ) {
123 Intern( store.Pop() );
124 }
125 }
126
127}
128*/
129
Lee Thomasone4422302012-01-20 17:59:50 -0800130
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800131// --------- XMLBase ----------- //
Lee Thomasond1983222012-02-06 08:41:24 -0800132
Lee Thomason18d68bd2012-01-26 18:17:26 -0800133char* XMLBase::ParseText( char* p, StrPair* pair, const char* endTag, int strFlags )
Lee Thomason3f57d272012-01-11 15:30:03 -0800134{
135 TIXMLASSERT( endTag && *endTag );
136
Lee Thomasonfde6a752012-01-14 18:08:12 -0800137 char* start = p;
Lee Thomasonfde6a752012-01-14 18:08:12 -0800138 char endChar = *endTag;
139 int length = strlen( endTag );
Lee Thomason3f57d272012-01-11 15:30:03 -0800140
Lee Thomasonfde6a752012-01-14 18:08:12 -0800141 // Inner loop of text parsing.
Lee Thomason3f57d272012-01-11 15:30:03 -0800142 while ( *p ) {
Lee Thomasonfde6a752012-01-14 18:08:12 -0800143 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
Lee Thomason18d68bd2012-01-26 18:17:26 -0800144 pair->Set( start, p, strFlags );
Lee Thomasonec975ce2012-01-23 11:42:06 -0800145 return p + length;
Lee Thomason3f57d272012-01-11 15:30:03 -0800146 }
Lee Thomasonec975ce2012-01-23 11:42:06 -0800147 ++p;
Lee Thomason3f57d272012-01-11 15:30:03 -0800148 }
Lee Thomasone4422302012-01-20 17:59:50 -0800149 return p;
Lee Thomason3f57d272012-01-11 15:30:03 -0800150}
151
152
Lee Thomasond34f52c2012-01-20 12:55:24 -0800153char* XMLBase::ParseName( char* p, StrPair* pair )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800154{
155 char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800156
157 start = p;
158 if ( !start || !(*start) ) {
159 return 0;
160 }
161
162 if ( !IsAlpha( *p ) ) {
163 return 0;
164 }
165
166 while( *p && (
167 IsAlphaNum( (unsigned char) *p )
168 || *p == '_'
169 || *p == '-'
170 || *p == '.'
171 || *p == ':' ))
172 {
173 ++p;
174 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800175
176 if ( p > start ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800177 pair->Set( start, p, 0 );
178 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800179 }
Lee Thomason39ede242012-01-20 11:27:56 -0800180 return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800181}
182
183
Lee Thomasond1983222012-02-06 08:41:24 -0800184char* XMLDocument::Identify( char* p, XMLNode** node )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800185{
186 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800187 char* start = p;
Lee Thomasond1983222012-02-06 08:41:24 -0800188 p = XMLBase::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800189 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800190 {
191 return 0;
192 }
193
194 // What is this thing?
195 // - Elements start with a letter or underscore, but xml is reserved.
196 // - Comments: <!--
197 // - Decleration: <?xml
198 // - Everthing else is unknown to tinyxml.
199 //
200
201 static const char* xmlHeader = { "<?xml" };
202 static const char* commentHeader = { "<!--" };
203 static const char* dtdHeader = { "<!" };
204 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800205 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800206
207 static const int xmlHeaderLen = 5;
208 static const int commentHeaderLen = 4;
209 static const int dtdHeaderLen = 2;
210 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800211 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800212
Lee Thomasond1983222012-02-06 08:41:24 -0800213 if ( XMLBase::StringEqual( p, commentHeader, commentHeaderLen ) ) {
214 returnNode = new (commentPool.Alloc()) XMLComment( this );
215 returnNode->memPool = &commentPool;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800216 p += commentHeaderLen;
217 }
Lee Thomasond1983222012-02-06 08:41:24 -0800218 else if ( XMLBase::StringEqual( p, elementHeader, elementHeaderLen ) ) {
219 returnNode = new (elementPool.Alloc()) XMLElement( this );
220 returnNode->memPool = &elementPool;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800221 p += elementHeaderLen;
222 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800223 // fixme: better text detection
Lee Thomasond1983222012-02-06 08:41:24 -0800224 else if ( (*p != '<') && XMLBase::IsAlphaNum( *p ) ) {
225 returnNode = new (textPool.Alloc()) XMLText( this );
226 returnNode->memPool = &textPool;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800227 p = start; // Back it up, all the text counts.
228 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800229 else {
230 TIXMLASSERT( 0 );
231 }
232
233 *node = returnNode;
234 return p;
235}
236
237
238// --------- XMLNode ----------- //
239
240XMLNode::XMLNode( XMLDocument* doc ) :
241 document( doc ),
242 parent( 0 ),
Lee Thomason67d61312012-01-24 16:01:51 -0800243 isTextParent( false ),
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800244 firstChild( 0 ), lastChild( 0 ),
245 prev( 0 ), next( 0 )
246{
247
248}
249
250
251XMLNode::~XMLNode()
252{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800253 ClearChildren();
Lee Thomason7c913cd2012-01-26 18:32:34 -0800254 if ( parent ) {
255 parent->Unlink( this );
256 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800257}
258
259
260void XMLNode::ClearChildren()
261{
Lee Thomasond923c672012-01-23 08:44:25 -0800262 while( firstChild ) {
263 XMLNode* node = firstChild;
264 Unlink( node );
Lee Thomasond1983222012-02-06 08:41:24 -0800265
Lee Thomason43f59302012-02-06 18:18:11 -0800266 DELETE_NODE( node );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800267 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800268 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800269}
270
271
272void XMLNode::Unlink( XMLNode* child )
273{
274 TIXMLASSERT( child->parent == this );
275 if ( child == firstChild )
276 firstChild = firstChild->next;
277 if ( child == lastChild )
278 lastChild = lastChild->prev;
279
280 if ( child->prev ) {
281 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800282 }
Lee Thomasond923c672012-01-23 08:44:25 -0800283 if ( child->next ) {
284 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800285 }
Lee Thomasond923c672012-01-23 08:44:25 -0800286 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800287}
288
289
290XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
291{
292 if ( lastChild ) {
293 TIXMLASSERT( firstChild );
294 TIXMLASSERT( lastChild->next == 0 );
295 lastChild->next = addThis;
296 addThis->prev = lastChild;
297 lastChild = addThis;
298
299 addThis->parent = this;
300 addThis->next = 0;
301 }
302 else {
303 TIXMLASSERT( firstChild == 0 );
304 firstChild = lastChild = addThis;
305
306 addThis->parent = this;
307 addThis->prev = 0;
308 addThis->next = 0;
309 }
Lee Thomason67d61312012-01-24 16:01:51 -0800310 if ( addThis->ToText() ) {
311 SetTextParent();
312 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800313 return addThis;
314}
315
316
Lee Thomason2c85a712012-01-31 08:24:24 -0800317XMLElement* XMLNode::FirstChildElement( const char* value )
318{
319 for( XMLNode* node=firstChild; node; node=node->next ) {
320 XMLElement* element = node->ToElement();
321 if ( element ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800322 if ( !value || XMLBase::StringEqual( element->Name(), value ) ) {
Lee Thomason2c85a712012-01-31 08:24:24 -0800323 return element;
324 }
325 }
326 }
327 return 0;
328}
329
330
Lee Thomason5cae8972012-01-24 18:03:07 -0800331void XMLNode::Print( XMLStreamer* streamer )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800332{
333 for( XMLNode* node = firstChild; node; node=node->next ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800334 node->Print( streamer );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800335 }
336}
337
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800338
Lee Thomason67d61312012-01-24 16:01:51 -0800339char* XMLNode::ParseDeep( char* p )
340{
341 while( p && *p ) {
342 XMLNode* node = 0;
Lee Thomasond1983222012-02-06 08:41:24 -0800343 p = document->Identify( p, &node );
Lee Thomason67d61312012-01-24 16:01:51 -0800344 if ( p && node ) {
345 p = node->ParseDeep( p );
346 // FIXME: is it the correct closing element?
347 if ( node->IsClosingElement() ) {
Lee Thomason43f59302012-02-06 18:18:11 -0800348 DELETE_NODE( node );
Lee Thomason67d61312012-01-24 16:01:51 -0800349 return p;
350 }
351 this->InsertEndChild( node );
352 }
353 }
354 return 0;
355}
356
Lee Thomason5492a1c2012-01-23 15:32:10 -0800357// --------- XMLText ---------- //
358char* XMLText::ParseDeep( char* p )
359{
Lee Thomasond1983222012-02-06 08:41:24 -0800360 p = XMLBase::ParseText( p, &value, "<", StrPair::TEXT_ELEMENT );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800361 // consumes the end tag.
362 if ( p && *p ) {
363 return p-1;
364 }
365 return 0;
366}
367
368
Lee Thomason5cae8972012-01-24 18:03:07 -0800369void XMLText::Print( XMLStreamer* streamer )
Lee Thomason5492a1c2012-01-23 15:32:10 -0800370{
Lee Thomason67d61312012-01-24 16:01:51 -0800371 const char* v = value.GetStr();
Lee Thomason5cae8972012-01-24 18:03:07 -0800372 streamer->PushText( v );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800373}
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800374
375
Lee Thomason3f57d272012-01-11 15:30:03 -0800376// --------- XMLComment ---------- //
377
Lee Thomasone4422302012-01-20 17:59:50 -0800378XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800379{
380}
381
382
Lee Thomasonce0763e2012-01-11 15:43:54 -0800383XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800384{
Lee Thomasond923c672012-01-23 08:44:25 -0800385 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800386}
387
388
Lee Thomason5cae8972012-01-24 18:03:07 -0800389void XMLComment::Print( XMLStreamer* streamer )
Lee Thomasonce0763e2012-01-11 15:43:54 -0800390{
Lee Thomason5cae8972012-01-24 18:03:07 -0800391// XMLNode::Print( fp, depth );
392// fprintf( fp, "<!--%s-->\n", value.GetStr() );
393 streamer->PushComment( value.GetStr() );
Lee Thomasonce0763e2012-01-11 15:43:54 -0800394}
395
396
397char* XMLComment::ParseDeep( char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800398{
399 // Comment parses as text.
Lee Thomasond1983222012-02-06 08:41:24 -0800400 return XMLBase::ParseText( p, &value, "-->", StrPair::COMMENT );
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800401}
402
403
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800404// --------- XMLAttribute ---------- //
405char* XMLAttribute::ParseDeep( char* p )
406{
Lee Thomasond1983222012-02-06 08:41:24 -0800407 p = XMLBase::ParseText( p, &name, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800408 if ( !p || !*p ) return 0;
409
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800410 char endTag[2] = { *p, 0 };
411 ++p;
Lee Thomasond1983222012-02-06 08:41:24 -0800412 p = XMLBase::ParseText( p, &value, endTag, StrPair::ATTRIBUTE_VALUE );
Lee Thomasone4422302012-01-20 17:59:50 -0800413 if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800414 return p;
415}
416
417
Lee Thomason5cae8972012-01-24 18:03:07 -0800418void XMLAttribute::Print( XMLStreamer* streamer )
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800419{
Lee Thomason22aead12012-01-23 13:29:35 -0800420 // fixme: sort out single vs. double quote
Lee Thomason5cae8972012-01-24 18:03:07 -0800421 //fprintf( cfile, "%s=\"%s\"", name.GetStr(), value.GetStr() );
422 streamer->PushAttribute( name.GetStr(), value.GetStr() );
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800423}
424
425
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800426// --------- XMLElement ---------- //
427XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800428 closing( false ),
429 rootAttribute( 0 ),
430 lastAttribute( 0 )
431{
432}
433
434
435XMLElement::~XMLElement()
436{
Lee Thomasond923c672012-01-23 08:44:25 -0800437 //printf( "~XMLElemen %x\n",this );
438
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800439 XMLAttribute* attribute = rootAttribute;
440 while( attribute ) {
441 XMLAttribute* next = attribute->next;
Lee Thomason43f59302012-02-06 18:18:11 -0800442 DELETE_ATTRIBUTE( attribute );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800443 attribute = next;
444 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800445}
446
447
Lee Thomason67d61312012-01-24 16:01:51 -0800448char* XMLElement::ParseAttributes( char* p, bool* closedElement )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800449{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800450 const char* start = p;
Lee Thomason67d61312012-01-24 16:01:51 -0800451 *closedElement = false;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800452
453 // Read the attributes.
454 while( p ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800455 p = XMLBase::SkipWhiteSpace( p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800456 if ( !p || !(*p) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800457 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, Name() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800458 return 0;
459 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800460
461 // attribute.
Lee Thomasond1983222012-02-06 08:41:24 -0800462 if ( XMLBase::IsAlpha( *p ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -0800463 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute( this );
464 attrib->memPool = &document->attributePool;
Lee Thomasond1983222012-02-06 08:41:24 -0800465
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800466 p = attrib->ParseDeep( p );
467 if ( !p ) {
Lee Thomason43f59302012-02-06 18:18:11 -0800468 DELETE_ATTRIBUTE( attrib );
Lee Thomasone4422302012-01-20 17:59:50 -0800469 document->SetError( XMLDocument::ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800470 return 0;
471 }
472 if ( rootAttribute ) {
473 TIXMLASSERT( lastAttribute );
474 lastAttribute->next = attrib;
475 lastAttribute = attrib;
476 }
477 else {
478 rootAttribute = lastAttribute = attrib;
479 }
480 }
Lee Thomasone4422302012-01-20 17:59:50 -0800481 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800482 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800483 if ( closing ) {
484 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
485 return 0;
486 }
Lee Thomason67d61312012-01-24 16:01:51 -0800487 *closedElement = true;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800488 return p+2; // done; sealed element.
489 }
Lee Thomasone4422302012-01-20 17:59:50 -0800490 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800491 else if ( *p == '>' ) {
492 ++p;
493 break;
494 }
Lee Thomasone4422302012-01-20 17:59:50 -0800495 else {
496 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
497 return 0;
498 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800499 }
Lee Thomason67d61312012-01-24 16:01:51 -0800500 return p;
501}
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800502
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800503
Lee Thomason67d61312012-01-24 16:01:51 -0800504//
505// <ele></ele>
506// <ele>foo<b>bar</b></ele>
507//
508char* XMLElement::ParseDeep( char* p )
509{
510 // Read the element name.
Lee Thomasond1983222012-02-06 08:41:24 -0800511 p = XMLBase::SkipWhiteSpace( p );
Lee Thomason67d61312012-01-24 16:01:51 -0800512 if ( !p ) return 0;
513 const char* start = p;
514
515 // The closing element is the </element> form. It is
516 // parsed just like a regular element then deleted from
517 // the DOM.
518 if ( *p == '/' ) {
519 closing = true;
520 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800521 }
Lee Thomason67d61312012-01-24 16:01:51 -0800522
Lee Thomasond1983222012-02-06 08:41:24 -0800523 p = XMLBase::ParseName( p, &value );
524 if ( value.Empty() ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -0800525
526 bool elementClosed=false;
527 p = ParseAttributes( p, &elementClosed );
528 if ( !p || !*p || elementClosed || closing )
529 return p;
530
531 p = XMLNode::ParseDeep( p );
532 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800533}
534
535
Lee Thomason5cae8972012-01-24 18:03:07 -0800536void XMLElement::Print( XMLStreamer* streamer )
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800537{
Lee Thomason5cae8972012-01-24 18:03:07 -0800538 //if ( !parent || !parent->IsTextParent() ) {
539 // PrintSpace( cfile, depth );
540 //}
541 //fprintf( cfile, "<%s", Name() );
542 streamer->OpenElement( Name(), IsTextParent() );
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800543
544 for( XMLAttribute* attrib=rootAttribute; attrib; attrib=attrib->next ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800545 //fprintf( cfile, " " );
546 attrib->Print( streamer );
547
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800548 }
549
Lee Thomason5cae8972012-01-24 18:03:07 -0800550 for( XMLNode* node=firstChild; node; node=node->next ) {
551 node->Print( streamer );
552 }
553 streamer->CloseElement();
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800554}
555
556
Lee Thomason3f57d272012-01-11 15:30:03 -0800557// --------- XMLDocument ----------- //
Lee Thomason67d61312012-01-24 16:01:51 -0800558XMLDocument::XMLDocument() :
Lee Thomason18d68bd2012-01-26 18:17:26 -0800559 XMLNode( 0 ),
U-Lama\Lee560bd472011-12-28 19:42:49 -0800560 charBuffer( 0 )
561{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800562 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -0800563}
U-Lama\Leee13c3e62011-12-28 14:36:55 -0800564
565
Lee Thomason3f57d272012-01-11 15:30:03 -0800566XMLDocument::~XMLDocument()
567{
Lee Thomasond1983222012-02-06 08:41:24 -0800568 ClearChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -0800569 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -0800570
Lee Thomason455c9d42012-02-06 09:14:14 -0800571 /*
572 textPool.Trace( "text" );
573 elementPool.Trace( "element" );
574 commentPool.Trace( "comment" );
575 attributePool.Trace( "attribute" );
576 */
577 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
578 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
579 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
580 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -0800581}
582
583
Lee Thomason18d68bd2012-01-26 18:17:26 -0800584void XMLDocument::InitDocument()
585{
586 errorID = NO_ERROR;
587 errorStr1 = 0;
588 errorStr2 = 0;
589
590 delete [] charBuffer;
591 charBuffer = 0;
592
593}
594
Lee Thomason3f57d272012-01-11 15:30:03 -0800595
Lee Thomason2c85a712012-01-31 08:24:24 -0800596XMLElement* XMLDocument::NewElement( const char* name )
597{
Lee Thomasond1983222012-02-06 08:41:24 -0800598 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
599 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -0800600 ele->SetName( name );
601 return ele;
602}
603
604
Lee Thomason7c913cd2012-01-26 18:32:34 -0800605int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800606{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800607 ClearChildren();
608 InitDocument();
609
610 if ( !p || !*p ) {
611 return true; // correctly parse an empty string?
612 }
613 size_t len = strlen( p );
614 charBuffer = new char[ len+1 ];
615 memcpy( charBuffer, p, len+1 );
Lee Thomason3f57d272012-01-11 15:30:03 -0800616 XMLNode* node = 0;
Lee Thomason85403d82012-01-11 15:55:05 -0800617
Lee Thomason18d68bd2012-01-26 18:17:26 -0800618 char* q = ParseDeep( charBuffer );
Lee Thomason7c913cd2012-01-26 18:32:34 -0800619 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -0800620}
621
622
Lee Thomason5cae8972012-01-24 18:03:07 -0800623void XMLDocument::Print( XMLStreamer* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -0800624{
Lee Thomason5cae8972012-01-24 18:03:07 -0800625 XMLStreamer stdStreamer( stdout );
626 if ( !streamer )
627 streamer = &stdStreamer;
Lee Thomason67d61312012-01-24 16:01:51 -0800628 for( XMLNode* node = firstChild; node; node=node->next ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800629 node->Print( streamer );
Lee Thomasonce0763e2012-01-11 15:43:54 -0800630 }
Lee Thomason3f57d272012-01-11 15:30:03 -0800631}
632
633
Lee Thomason67d61312012-01-24 16:01:51 -0800634void XMLDocument::SetError( int error, const char* str1, const char* str2 )
635{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800636 errorID = error;
637 printf( "ERROR: id=%d '%s' '%s'\n", error, str1, str2 ); // fixme: remove
638 errorStr1 = str1;
639 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -0800640}
641
Lee Thomason5cae8972012-01-24 18:03:07 -0800642
Lee Thomason2c85a712012-01-31 08:24:24 -0800643/*
Lee Thomason5cae8972012-01-24 18:03:07 -0800644StringStack::StringStack()
645{
Lee Thomason5cae8972012-01-24 18:03:07 -0800646 nPositive = 0;
Lee Thomason1270ae52012-01-27 17:58:30 -0800647 mem.Push( 0 ); // start with null. makes later code simpler.
Lee Thomason5cae8972012-01-24 18:03:07 -0800648}
649
650
Lee Thomason24767b02012-01-25 17:16:23 -0800651StringStack::~StringStack()
652{
Lee Thomason24767b02012-01-25 17:16:23 -0800653}
654
655
Lee Thomason5cae8972012-01-24 18:03:07 -0800656void StringStack::Push( const char* str ) {
657 int needed = strlen( str ) + 1;
Lee Thomason1270ae52012-01-27 17:58:30 -0800658 char* p = mem.PushArr( needed );
659 strcpy( p, str );
660 if ( needed > 1 )
Lee Thomason5cae8972012-01-24 18:03:07 -0800661 nPositive++;
Lee Thomason5cae8972012-01-24 18:03:07 -0800662}
663
664
665const char* StringStack::Pop() {
Lee Thomason1270ae52012-01-27 17:58:30 -0800666 TIXMLASSERT( mem.Size() > 1 );
667 const char* p = mem.Mem() + mem.Size() - 2; // end of final string.
Lee Thomason5cae8972012-01-24 18:03:07 -0800668 if ( *p ) {
669 nPositive--;
670 }
671 while( *p ) { // stack starts with a null, don't need to check for 'mem'
Lee Thomason1270ae52012-01-27 17:58:30 -0800672 TIXMLASSERT( p > mem.Mem() );
Lee Thomason5cae8972012-01-24 18:03:07 -0800673 --p;
674 }
Lee Thomason1270ae52012-01-27 17:58:30 -0800675 mem.PopArr( strlen(p)+1 );
Lee Thomason5cae8972012-01-24 18:03:07 -0800676 return p+1;
677}
Lee Thomason2c85a712012-01-31 08:24:24 -0800678*/
Lee Thomason5cae8972012-01-24 18:03:07 -0800679
680
681XMLStreamer::XMLStreamer( FILE* file ) : fp( file ), depth( 0 ), elementJustOpened( false )
682{
Lee Thomason857b8682012-01-25 17:50:25 -0800683 for( int i=0; i<ENTITY_RANGE; ++i ) {
684 entityFlag[i] = false;
685 }
686 for( int i=0; i<NUM_ENTITIES; ++i ) {
687 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
688 if ( entities[i].value < ENTITY_RANGE ) {
689 entityFlag[ entities[i].value ] = true;
690 }
691 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800692}
693
694
695void XMLStreamer::PrintSpace( int depth )
696{
697 for( int i=0; i<depth; ++i ) {
698 fprintf( fp, " " );
699 }
700}
701
702
Lee Thomason951d8832012-01-26 08:47:06 -0800703void XMLStreamer::PrintString( const char* p )
Lee Thomason857b8682012-01-25 17:50:25 -0800704{
Lee Thomason951d8832012-01-26 08:47:06 -0800705 // Look for runs of bytes between entities to print.
706 const char* q = p;
Lee Thomason857b8682012-01-25 17:50:25 -0800707
Lee Thomason951d8832012-01-26 08:47:06 -0800708 while ( *q ) {
709 if ( *q < ENTITY_RANGE ) {
710 // Check for entities. If one is found, flush
711 // the stream up until the entity, write the
712 // entity, and keep looking.
713 if ( entityFlag[*q] ) {
714 while ( p < q ) {
715 fputc( *p, fp );
716 ++p;
717 }
718 for( int i=0; i<NUM_ENTITIES; ++i ) {
719 if ( entities[i].value == *q ) {
720 fprintf( fp, "&%s;", entities[i].pattern );
721 break;
722 }
723 }
724 ++p;
725 }
726 }
727 ++q;
728 }
729 // Flush the remaining string. This will be the entire
730 // string if an entity wasn't found.
731 if ( q-p > 0 ) {
732 fprintf( fp, "%s", p );
733 }
Lee Thomason857b8682012-01-25 17:50:25 -0800734}
735
Lee Thomason5cae8972012-01-24 18:03:07 -0800736void XMLStreamer::OpenElement( const char* name, bool textParent )
737{
738 if ( elementJustOpened ) {
739 SealElement();
740 }
Lee Thomason2c85a712012-01-31 08:24:24 -0800741 if ( !TextOnStack() ) {
Lee Thomason24767b02012-01-25 17:16:23 -0800742 PrintSpace( depth );
743 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800744 stack.Push( name );
Lee Thomason2c85a712012-01-31 08:24:24 -0800745 text.Push( textParent ? 'T' : 'e' );
Lee Thomason5cae8972012-01-24 18:03:07 -0800746
Lee Thomason951d8832012-01-26 08:47:06 -0800747 // fixme: can names have entities?
Lee Thomason5cae8972012-01-24 18:03:07 -0800748 fprintf( fp, "<%s", name );
749 elementJustOpened = true;
750 ++depth;
751}
752
753
754void XMLStreamer::PushAttribute( const char* name, const char* value )
755{
756 TIXMLASSERT( elementJustOpened );
Lee Thomason18d68bd2012-01-26 18:17:26 -0800757 fprintf( fp, " %s=\"", name );
758 PrintString( value );
759 fprintf( fp, "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -0800760}
761
762
763void XMLStreamer::CloseElement()
764{
765 --depth;
766 const char* name = stack.Pop();
Lee Thomason2c85a712012-01-31 08:24:24 -0800767 bool wasText = TextOnStack();
Lee Thomason5cae8972012-01-24 18:03:07 -0800768 text.Pop();
769
770 if ( elementJustOpened ) {
771 fprintf( fp, "/>" );
Lee Thomason2c85a712012-01-31 08:24:24 -0800772 if ( !wasText ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800773 fprintf( fp, "\n" );
774 }
775 }
776 else {
Lee Thomason2c85a712012-01-31 08:24:24 -0800777 if ( !wasText ) {
Lee Thomason24767b02012-01-25 17:16:23 -0800778 PrintSpace( depth );
779 }
Lee Thomason951d8832012-01-26 08:47:06 -0800780 // fixme can names have entities?
Lee Thomason5cae8972012-01-24 18:03:07 -0800781 fprintf( fp, "</%s>", name );
Lee Thomason2c85a712012-01-31 08:24:24 -0800782 if ( !TextOnStack() ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800783 fprintf( fp, "\n" );
784 }
785 }
786 elementJustOpened = false;
787}
788
789
790void XMLStreamer::SealElement()
791{
792 elementJustOpened = false;
793 fprintf( fp, ">" );
Lee Thomason2c85a712012-01-31 08:24:24 -0800794 if ( !TextOnStack() ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800795 fprintf( fp, "\n" );
796 }
797}
798
799
800void XMLStreamer::PushText( const char* text )
801{
802 if ( elementJustOpened ) {
803 SealElement();
804 }
Lee Thomason951d8832012-01-26 08:47:06 -0800805 PrintString( text );
Lee Thomason5cae8972012-01-24 18:03:07 -0800806}
807
808
809void XMLStreamer::PushComment( const char* comment )
810{
811 if ( elementJustOpened ) {
812 SealElement();
813 }
814 PrintSpace( depth );
815 fprintf( fp, "<!--%s-->\n", comment );
816}