blob: 65f26895cfd9fd75eccd40da1d3774018e221d9b [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 Thomason8ee79892012-01-25 17:44:30 -080020struct Entity {
21 const char* pattern;
22 int length;
23 char value;
24};
25
26static const int NUM_ENTITIES = 5;
27static const Entity entities[NUM_ENTITIES] =
28{
Lee Thomason18d68bd2012-01-26 18:17:26 -080029 { "quot", 4, DOUBLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080030 { "amp", 3, '&' },
Lee Thomason18d68bd2012-01-26 18:17:26 -080031 { "apos", 4, SINGLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080032 { "lt", 2, '<' },
33 { "gt", 2, '>' }
34};
35
Lee Thomasonfde6a752012-01-14 18:08:12 -080036
Lee Thomasone4422302012-01-20 17:59:50 -080037const char* StrPair::GetStr()
38{
39 if ( flags & NEEDS_FLUSH ) {
40 *end = 0;
Lee Thomason8ee79892012-01-25 17:44:30 -080041 flags ^= NEEDS_FLUSH;
Lee Thomasone4422302012-01-20 17:59:50 -080042
Lee Thomason8ee79892012-01-25 17:44:30 -080043 if ( flags ) {
Lee Thomasone4422302012-01-20 17:59:50 -080044 char* p = start;
45 char* q = start;
46
47 while( p < end ) {
Lee Thomason8ee79892012-01-25 17:44:30 -080048 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Lee Thomasone4422302012-01-20 17:59:50 -080049 // CR-LF pair becomes LF
50 // CR alone becomes LF
51 // LF-CR becomes LF
52 if ( *(p+1) == LF ) {
53 p += 2;
54 }
55 else {
56 ++p;
57 }
58 *q = LF;
59 }
Lee Thomason8ee79892012-01-25 17:44:30 -080060 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Lee Thomasone4422302012-01-20 17:59:50 -080061 if ( *(p+1) == CR ) {
62 p += 2;
63 }
64 else {
65 ++p;
66 }
67 *q = LF;
68 }
Lee Thomason8ee79892012-01-25 17:44:30 -080069 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
70 int i=0;
71 for( i=0; i<NUM_ENTITIES; ++i ) {
72 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
73 && *(p+entities[i].length+1) == ';' )
74 {
75 // Found an entity convert;
76 *q = entities[i].value;
77 ++q;
78 p += entities[i].length + 2;
79 break;
80 }
81 }
82 if ( i == NUM_ENTITIES ) {
83 // fixme: treat as error?
84 ++p;
85 ++q;
86 }
87 }
Lee Thomasone4422302012-01-20 17:59:50 -080088 else {
89 *q = *p;
90 ++p;
Lee Thomasonec975ce2012-01-23 11:42:06 -080091 ++q;
Lee Thomasone4422302012-01-20 17:59:50 -080092 }
93 }
Lee Thomason8ee79892012-01-25 17:44:30 -080094 *q = 0;
Lee Thomasone4422302012-01-20 17:59:50 -080095 }
96 flags = 0;
97 }
98 return start;
99}
100
Lee Thomason2c85a712012-01-31 08:24:24 -0800101/*
102const char* StringPool::Intern( const char* str )
103{
104 // Treat the array as a linear, inplace hash table.
105 // Nothing can get deleted, so that's handy.
106 if ( size > pool.Size()*3/4 ) {
107 DynArray< const char*, 20 > store;
108 for( int i=0; i<pool.Size(); ++i ) {
109 if ( pool[i] != 0 ) {
110 store.Push( pool[i] );
111 }
112 }
113 int newSize = pool.Size() * 2;
114 pool.PopArr( pool.Size() );
115
116 const char** mem = pool.PushArr( newSize );
117 memset( (void*)mem, 0, sizeof(char)*newSize );
118
119 while ( !store.Empty() ) {
120 Intern( store.Pop() );
121 }
122 }
123
124}
125*/
126
Lee Thomasone4422302012-01-20 17:59:50 -0800127
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800128// --------- XMLBase ----------- //
Lee Thomasond1983222012-02-06 08:41:24 -0800129
Lee Thomason18d68bd2012-01-26 18:17:26 -0800130char* XMLBase::ParseText( char* p, StrPair* pair, const char* endTag, int strFlags )
Lee Thomason3f57d272012-01-11 15:30:03 -0800131{
132 TIXMLASSERT( endTag && *endTag );
133
Lee Thomasonfde6a752012-01-14 18:08:12 -0800134 char* start = p;
Lee Thomasonfde6a752012-01-14 18:08:12 -0800135 char endChar = *endTag;
136 int length = strlen( endTag );
Lee Thomason3f57d272012-01-11 15:30:03 -0800137
Lee Thomasonfde6a752012-01-14 18:08:12 -0800138 // Inner loop of text parsing.
Lee Thomason3f57d272012-01-11 15:30:03 -0800139 while ( *p ) {
Lee Thomasonfde6a752012-01-14 18:08:12 -0800140 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
Lee Thomason18d68bd2012-01-26 18:17:26 -0800141 pair->Set( start, p, strFlags );
Lee Thomasonec975ce2012-01-23 11:42:06 -0800142 return p + length;
Lee Thomason3f57d272012-01-11 15:30:03 -0800143 }
Lee Thomasonec975ce2012-01-23 11:42:06 -0800144 ++p;
Lee Thomason3f57d272012-01-11 15:30:03 -0800145 }
Lee Thomasone4422302012-01-20 17:59:50 -0800146 return p;
Lee Thomason3f57d272012-01-11 15:30:03 -0800147}
148
149
Lee Thomasond34f52c2012-01-20 12:55:24 -0800150char* XMLBase::ParseName( char* p, StrPair* pair )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800151{
152 char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800153
154 start = p;
155 if ( !start || !(*start) ) {
156 return 0;
157 }
158
159 if ( !IsAlpha( *p ) ) {
160 return 0;
161 }
162
163 while( *p && (
164 IsAlphaNum( (unsigned char) *p )
165 || *p == '_'
166 || *p == '-'
167 || *p == '.'
168 || *p == ':' ))
169 {
170 ++p;
171 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800172
173 if ( p > start ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800174 pair->Set( start, p, 0 );
175 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800176 }
Lee Thomason39ede242012-01-20 11:27:56 -0800177 return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800178}
179
180
Lee Thomasond1983222012-02-06 08:41:24 -0800181char* XMLDocument::Identify( char* p, XMLNode** node )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800182{
183 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800184 char* start = p;
Lee Thomasond1983222012-02-06 08:41:24 -0800185 p = XMLBase::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800186 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800187 {
188 return 0;
189 }
190
191 // What is this thing?
192 // - Elements start with a letter or underscore, but xml is reserved.
193 // - Comments: <!--
194 // - Decleration: <?xml
195 // - Everthing else is unknown to tinyxml.
196 //
197
198 static const char* xmlHeader = { "<?xml" };
199 static const char* commentHeader = { "<!--" };
200 static const char* dtdHeader = { "<!" };
201 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800202 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800203
204 static const int xmlHeaderLen = 5;
205 static const int commentHeaderLen = 4;
206 static const int dtdHeaderLen = 2;
207 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800208 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800209
Lee Thomasond1983222012-02-06 08:41:24 -0800210 if ( XMLBase::StringEqual( p, commentHeader, commentHeaderLen ) ) {
211 returnNode = new (commentPool.Alloc()) XMLComment( this );
212 returnNode->memPool = &commentPool;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800213 p += commentHeaderLen;
214 }
Lee Thomasond1983222012-02-06 08:41:24 -0800215 else if ( XMLBase::StringEqual( p, elementHeader, elementHeaderLen ) ) {
216 returnNode = new (elementPool.Alloc()) XMLElement( this );
217 returnNode->memPool = &elementPool;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800218 p += elementHeaderLen;
219 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800220 // fixme: better text detection
Lee Thomasond1983222012-02-06 08:41:24 -0800221 else if ( (*p != '<') && XMLBase::IsAlphaNum( *p ) ) {
222 returnNode = new (textPool.Alloc()) XMLText( this );
223 returnNode->memPool = &textPool;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800224 p = start; // Back it up, all the text counts.
225 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800226 else {
227 TIXMLASSERT( 0 );
228 }
229
230 *node = returnNode;
231 return p;
232}
233
234
235// --------- XMLNode ----------- //
236
237XMLNode::XMLNode( XMLDocument* doc ) :
238 document( doc ),
239 parent( 0 ),
Lee Thomason67d61312012-01-24 16:01:51 -0800240 isTextParent( false ),
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800241 firstChild( 0 ), lastChild( 0 ),
242 prev( 0 ), next( 0 )
243{
244
245}
246
247
248XMLNode::~XMLNode()
249{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800250 ClearChildren();
Lee Thomason7c913cd2012-01-26 18:32:34 -0800251 if ( parent ) {
252 parent->Unlink( this );
253 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800254}
255
256
257void XMLNode::ClearChildren()
258{
Lee Thomasond923c672012-01-23 08:44:25 -0800259 while( firstChild ) {
260 XMLNode* node = firstChild;
261 Unlink( node );
Lee Thomasond1983222012-02-06 08:41:24 -0800262
263 //delete node;
264 // placement new!
265 MemPool* pool = node->memPool;
266 node->~XMLNode();
267 pool->Free( node );
268 // fixme: memory never free'd.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800269 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800270 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800271}
272
273
274void XMLNode::Unlink( XMLNode* child )
275{
276 TIXMLASSERT( child->parent == this );
277 if ( child == firstChild )
278 firstChild = firstChild->next;
279 if ( child == lastChild )
280 lastChild = lastChild->prev;
281
282 if ( child->prev ) {
283 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800284 }
Lee Thomasond923c672012-01-23 08:44:25 -0800285 if ( child->next ) {
286 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800287 }
Lee Thomasond923c672012-01-23 08:44:25 -0800288 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800289}
290
291
292XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
293{
294 if ( lastChild ) {
295 TIXMLASSERT( firstChild );
296 TIXMLASSERT( lastChild->next == 0 );
297 lastChild->next = addThis;
298 addThis->prev = lastChild;
299 lastChild = addThis;
300
301 addThis->parent = this;
302 addThis->next = 0;
303 }
304 else {
305 TIXMLASSERT( firstChild == 0 );
306 firstChild = lastChild = addThis;
307
308 addThis->parent = this;
309 addThis->prev = 0;
310 addThis->next = 0;
311 }
Lee Thomason67d61312012-01-24 16:01:51 -0800312 if ( addThis->ToText() ) {
313 SetTextParent();
314 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800315 return addThis;
316}
317
318
Lee Thomason2c85a712012-01-31 08:24:24 -0800319XMLElement* XMLNode::FirstChildElement( const char* value )
320{
321 for( XMLNode* node=firstChild; node; node=node->next ) {
322 XMLElement* element = node->ToElement();
323 if ( element ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800324 if ( !value || XMLBase::StringEqual( element->Name(), value ) ) {
Lee Thomason2c85a712012-01-31 08:24:24 -0800325 return element;
326 }
327 }
328 }
329 return 0;
330}
331
332
Lee Thomason5cae8972012-01-24 18:03:07 -0800333void XMLNode::Print( XMLStreamer* streamer )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800334{
335 for( XMLNode* node = firstChild; node; node=node->next ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800336 node->Print( streamer );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800337 }
338}
339
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800340
Lee Thomason67d61312012-01-24 16:01:51 -0800341char* XMLNode::ParseDeep( char* p )
342{
343 while( p && *p ) {
344 XMLNode* node = 0;
Lee Thomasond1983222012-02-06 08:41:24 -0800345 p = document->Identify( p, &node );
Lee Thomason67d61312012-01-24 16:01:51 -0800346 if ( p && node ) {
347 p = node->ParseDeep( p );
348 // FIXME: is it the correct closing element?
349 if ( node->IsClosingElement() ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800350 //delete node;
351 MemPool* pool = node->memPool;
352 node->~XMLNode(); // fixme linked list memory not free
353 pool->Free( node );
Lee Thomason67d61312012-01-24 16:01:51 -0800354 return p;
355 }
356 this->InsertEndChild( node );
357 }
358 }
359 return 0;
360}
361
Lee Thomason5492a1c2012-01-23 15:32:10 -0800362// --------- XMLText ---------- //
363char* XMLText::ParseDeep( char* p )
364{
Lee Thomasond1983222012-02-06 08:41:24 -0800365 p = XMLBase::ParseText( p, &value, "<", StrPair::TEXT_ELEMENT );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800366 // consumes the end tag.
367 if ( p && *p ) {
368 return p-1;
369 }
370 return 0;
371}
372
373
Lee Thomason5cae8972012-01-24 18:03:07 -0800374void XMLText::Print( XMLStreamer* streamer )
Lee Thomason5492a1c2012-01-23 15:32:10 -0800375{
Lee Thomason67d61312012-01-24 16:01:51 -0800376 const char* v = value.GetStr();
Lee Thomason5cae8972012-01-24 18:03:07 -0800377 streamer->PushText( v );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800378}
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800379
380
Lee Thomason3f57d272012-01-11 15:30:03 -0800381// --------- XMLComment ---------- //
382
Lee Thomasone4422302012-01-20 17:59:50 -0800383XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800384{
385}
386
387
Lee Thomasonce0763e2012-01-11 15:43:54 -0800388XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800389{
Lee Thomasond923c672012-01-23 08:44:25 -0800390 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800391}
392
393
Lee Thomason5cae8972012-01-24 18:03:07 -0800394void XMLComment::Print( XMLStreamer* streamer )
Lee Thomasonce0763e2012-01-11 15:43:54 -0800395{
Lee Thomason5cae8972012-01-24 18:03:07 -0800396// XMLNode::Print( fp, depth );
397// fprintf( fp, "<!--%s-->\n", value.GetStr() );
398 streamer->PushComment( value.GetStr() );
Lee Thomasonce0763e2012-01-11 15:43:54 -0800399}
400
401
402char* XMLComment::ParseDeep( char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800403{
404 // Comment parses as text.
Lee Thomasond1983222012-02-06 08:41:24 -0800405 return XMLBase::ParseText( p, &value, "-->", StrPair::COMMENT );
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800406}
407
408
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800409// --------- XMLAttribute ---------- //
410char* XMLAttribute::ParseDeep( char* p )
411{
Lee Thomasond1983222012-02-06 08:41:24 -0800412 p = XMLBase::ParseText( p, &name, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800413 if ( !p || !*p ) return 0;
414
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800415 char endTag[2] = { *p, 0 };
416 ++p;
Lee Thomasond1983222012-02-06 08:41:24 -0800417 p = XMLBase::ParseText( p, &value, endTag, StrPair::ATTRIBUTE_VALUE );
Lee Thomasone4422302012-01-20 17:59:50 -0800418 if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800419 return p;
420}
421
422
Lee Thomason5cae8972012-01-24 18:03:07 -0800423void XMLAttribute::Print( XMLStreamer* streamer )
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800424{
Lee Thomason22aead12012-01-23 13:29:35 -0800425 // fixme: sort out single vs. double quote
Lee Thomason5cae8972012-01-24 18:03:07 -0800426 //fprintf( cfile, "%s=\"%s\"", name.GetStr(), value.GetStr() );
427 streamer->PushAttribute( name.GetStr(), value.GetStr() );
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800428}
429
430
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800431// --------- XMLElement ---------- //
432XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800433 closing( false ),
434 rootAttribute( 0 ),
435 lastAttribute( 0 )
436{
437}
438
439
440XMLElement::~XMLElement()
441{
Lee Thomasond923c672012-01-23 08:44:25 -0800442 //printf( "~XMLElemen %x\n",this );
443
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800444 XMLAttribute* attribute = rootAttribute;
445 while( attribute ) {
446 XMLAttribute* next = attribute->next;
447 delete attribute;
448 attribute = next;
449 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800450}
451
452
Lee Thomason67d61312012-01-24 16:01:51 -0800453char* XMLElement::ParseAttributes( char* p, bool* closedElement )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800454{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800455 const char* start = p;
Lee Thomason67d61312012-01-24 16:01:51 -0800456 *closedElement = false;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800457
458 // Read the attributes.
459 while( p ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800460 p = XMLBase::SkipWhiteSpace( p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800461 if ( !p || !(*p) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800462 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, Name() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800463 return 0;
464 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800465
466 // attribute.
Lee Thomasond1983222012-02-06 08:41:24 -0800467 if ( XMLBase::IsAlpha( *p ) ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800468 XMLAttribute* attrib = new XMLAttribute( this );
Lee Thomasond1983222012-02-06 08:41:24 -0800469
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800470 p = attrib->ParseDeep( p );
471 if ( !p ) {
472 delete attrib;
Lee Thomasone4422302012-01-20 17:59:50 -0800473 document->SetError( XMLDocument::ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800474 return 0;
475 }
476 if ( rootAttribute ) {
477 TIXMLASSERT( lastAttribute );
478 lastAttribute->next = attrib;
479 lastAttribute = attrib;
480 }
481 else {
482 rootAttribute = lastAttribute = attrib;
483 }
484 }
Lee Thomasone4422302012-01-20 17:59:50 -0800485 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800486 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800487 if ( closing ) {
488 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
489 return 0;
490 }
Lee Thomason67d61312012-01-24 16:01:51 -0800491 *closedElement = true;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800492 return p+2; // done; sealed element.
493 }
Lee Thomasone4422302012-01-20 17:59:50 -0800494 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800495 else if ( *p == '>' ) {
496 ++p;
497 break;
498 }
Lee Thomasone4422302012-01-20 17:59:50 -0800499 else {
500 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
501 return 0;
502 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800503 }
Lee Thomason67d61312012-01-24 16:01:51 -0800504 return p;
505}
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800506
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800507
Lee Thomason67d61312012-01-24 16:01:51 -0800508//
509// <ele></ele>
510// <ele>foo<b>bar</b></ele>
511//
512char* XMLElement::ParseDeep( char* p )
513{
514 // Read the element name.
Lee Thomasond1983222012-02-06 08:41:24 -0800515 p = XMLBase::SkipWhiteSpace( p );
Lee Thomason67d61312012-01-24 16:01:51 -0800516 if ( !p ) return 0;
517 const char* start = p;
518
519 // The closing element is the </element> form. It is
520 // parsed just like a regular element then deleted from
521 // the DOM.
522 if ( *p == '/' ) {
523 closing = true;
524 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800525 }
Lee Thomason67d61312012-01-24 16:01:51 -0800526
Lee Thomasond1983222012-02-06 08:41:24 -0800527 p = XMLBase::ParseName( p, &value );
528 if ( value.Empty() ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -0800529
530 bool elementClosed=false;
531 p = ParseAttributes( p, &elementClosed );
532 if ( !p || !*p || elementClosed || closing )
533 return p;
534
535 p = XMLNode::ParseDeep( p );
536 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800537}
538
539
Lee Thomason5cae8972012-01-24 18:03:07 -0800540void XMLElement::Print( XMLStreamer* streamer )
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800541{
Lee Thomason5cae8972012-01-24 18:03:07 -0800542 //if ( !parent || !parent->IsTextParent() ) {
543 // PrintSpace( cfile, depth );
544 //}
545 //fprintf( cfile, "<%s", Name() );
546 streamer->OpenElement( Name(), IsTextParent() );
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800547
548 for( XMLAttribute* attrib=rootAttribute; attrib; attrib=attrib->next ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800549 //fprintf( cfile, " " );
550 attrib->Print( streamer );
551
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800552 }
553
Lee Thomason5cae8972012-01-24 18:03:07 -0800554 for( XMLNode* node=firstChild; node; node=node->next ) {
555 node->Print( streamer );
556 }
557 streamer->CloseElement();
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800558}
559
560
Lee Thomason3f57d272012-01-11 15:30:03 -0800561// --------- XMLDocument ----------- //
Lee Thomason67d61312012-01-24 16:01:51 -0800562XMLDocument::XMLDocument() :
Lee Thomason18d68bd2012-01-26 18:17:26 -0800563 XMLNode( 0 ),
U-Lama\Lee560bd472011-12-28 19:42:49 -0800564 charBuffer( 0 )
565{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800566 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -0800567}
U-Lama\Leee13c3e62011-12-28 14:36:55 -0800568
569
Lee Thomason3f57d272012-01-11 15:30:03 -0800570XMLDocument::~XMLDocument()
571{
Lee Thomasond1983222012-02-06 08:41:24 -0800572 ClearChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -0800573 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -0800574
575 TIXMLASSERT( textPool.NAlloc() == 0 );
576 TIXMLASSERT( elementPool.NAlloc() == 0 );
577 TIXMLASSERT( commentPool.NAlloc() == 0 );
578 TIXMLASSERT( attributePool.NAlloc() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -0800579}
580
581
Lee Thomason18d68bd2012-01-26 18:17:26 -0800582void XMLDocument::InitDocument()
583{
584 errorID = NO_ERROR;
585 errorStr1 = 0;
586 errorStr2 = 0;
587
588 delete [] charBuffer;
589 charBuffer = 0;
590
591}
592
Lee Thomason3f57d272012-01-11 15:30:03 -0800593
Lee Thomason2c85a712012-01-31 08:24:24 -0800594XMLElement* XMLDocument::NewElement( const char* name )
595{
Lee Thomasond1983222012-02-06 08:41:24 -0800596 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
597 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -0800598 ele->SetName( name );
599 return ele;
600}
601
602
Lee Thomason7c913cd2012-01-26 18:32:34 -0800603int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800604{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800605 ClearChildren();
606 InitDocument();
607
608 if ( !p || !*p ) {
609 return true; // correctly parse an empty string?
610 }
611 size_t len = strlen( p );
612 charBuffer = new char[ len+1 ];
613 memcpy( charBuffer, p, len+1 );
Lee Thomason3f57d272012-01-11 15:30:03 -0800614 XMLNode* node = 0;
Lee Thomason85403d82012-01-11 15:55:05 -0800615
Lee Thomason18d68bd2012-01-26 18:17:26 -0800616 char* q = ParseDeep( charBuffer );
Lee Thomason7c913cd2012-01-26 18:32:34 -0800617 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -0800618}
619
620
Lee Thomason5cae8972012-01-24 18:03:07 -0800621void XMLDocument::Print( XMLStreamer* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -0800622{
Lee Thomason5cae8972012-01-24 18:03:07 -0800623 XMLStreamer stdStreamer( stdout );
624 if ( !streamer )
625 streamer = &stdStreamer;
Lee Thomason67d61312012-01-24 16:01:51 -0800626 for( XMLNode* node = firstChild; node; node=node->next ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800627 node->Print( streamer );
Lee Thomasonce0763e2012-01-11 15:43:54 -0800628 }
Lee Thomason3f57d272012-01-11 15:30:03 -0800629}
630
631
Lee Thomason67d61312012-01-24 16:01:51 -0800632void XMLDocument::SetError( int error, const char* str1, const char* str2 )
633{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800634 errorID = error;
635 printf( "ERROR: id=%d '%s' '%s'\n", error, str1, str2 ); // fixme: remove
636 errorStr1 = str1;
637 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -0800638}
639
Lee Thomason5cae8972012-01-24 18:03:07 -0800640
Lee Thomason2c85a712012-01-31 08:24:24 -0800641/*
Lee Thomason5cae8972012-01-24 18:03:07 -0800642StringStack::StringStack()
643{
Lee Thomason5cae8972012-01-24 18:03:07 -0800644 nPositive = 0;
Lee Thomason1270ae52012-01-27 17:58:30 -0800645 mem.Push( 0 ); // start with null. makes later code simpler.
Lee Thomason5cae8972012-01-24 18:03:07 -0800646}
647
648
Lee Thomason24767b02012-01-25 17:16:23 -0800649StringStack::~StringStack()
650{
Lee Thomason24767b02012-01-25 17:16:23 -0800651}
652
653
Lee Thomason5cae8972012-01-24 18:03:07 -0800654void StringStack::Push( const char* str ) {
655 int needed = strlen( str ) + 1;
Lee Thomason1270ae52012-01-27 17:58:30 -0800656 char* p = mem.PushArr( needed );
657 strcpy( p, str );
658 if ( needed > 1 )
Lee Thomason5cae8972012-01-24 18:03:07 -0800659 nPositive++;
Lee Thomason5cae8972012-01-24 18:03:07 -0800660}
661
662
663const char* StringStack::Pop() {
Lee Thomason1270ae52012-01-27 17:58:30 -0800664 TIXMLASSERT( mem.Size() > 1 );
665 const char* p = mem.Mem() + mem.Size() - 2; // end of final string.
Lee Thomason5cae8972012-01-24 18:03:07 -0800666 if ( *p ) {
667 nPositive--;
668 }
669 while( *p ) { // stack starts with a null, don't need to check for 'mem'
Lee Thomason1270ae52012-01-27 17:58:30 -0800670 TIXMLASSERT( p > mem.Mem() );
Lee Thomason5cae8972012-01-24 18:03:07 -0800671 --p;
672 }
Lee Thomason1270ae52012-01-27 17:58:30 -0800673 mem.PopArr( strlen(p)+1 );
Lee Thomason5cae8972012-01-24 18:03:07 -0800674 return p+1;
675}
Lee Thomason2c85a712012-01-31 08:24:24 -0800676*/
Lee Thomason5cae8972012-01-24 18:03:07 -0800677
678
679XMLStreamer::XMLStreamer( FILE* file ) : fp( file ), depth( 0 ), elementJustOpened( false )
680{
Lee Thomason857b8682012-01-25 17:50:25 -0800681 for( int i=0; i<ENTITY_RANGE; ++i ) {
682 entityFlag[i] = false;
683 }
684 for( int i=0; i<NUM_ENTITIES; ++i ) {
685 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
686 if ( entities[i].value < ENTITY_RANGE ) {
687 entityFlag[ entities[i].value ] = true;
688 }
689 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800690}
691
692
693void XMLStreamer::PrintSpace( int depth )
694{
695 for( int i=0; i<depth; ++i ) {
696 fprintf( fp, " " );
697 }
698}
699
700
Lee Thomason951d8832012-01-26 08:47:06 -0800701void XMLStreamer::PrintString( const char* p )
Lee Thomason857b8682012-01-25 17:50:25 -0800702{
Lee Thomason951d8832012-01-26 08:47:06 -0800703 // Look for runs of bytes between entities to print.
704 const char* q = p;
Lee Thomason857b8682012-01-25 17:50:25 -0800705
Lee Thomason951d8832012-01-26 08:47:06 -0800706 while ( *q ) {
707 if ( *q < ENTITY_RANGE ) {
708 // Check for entities. If one is found, flush
709 // the stream up until the entity, write the
710 // entity, and keep looking.
711 if ( entityFlag[*q] ) {
712 while ( p < q ) {
713 fputc( *p, fp );
714 ++p;
715 }
716 for( int i=0; i<NUM_ENTITIES; ++i ) {
717 if ( entities[i].value == *q ) {
718 fprintf( fp, "&%s;", entities[i].pattern );
719 break;
720 }
721 }
722 ++p;
723 }
724 }
725 ++q;
726 }
727 // Flush the remaining string. This will be the entire
728 // string if an entity wasn't found.
729 if ( q-p > 0 ) {
730 fprintf( fp, "%s", p );
731 }
Lee Thomason857b8682012-01-25 17:50:25 -0800732}
733
Lee Thomason5cae8972012-01-24 18:03:07 -0800734void XMLStreamer::OpenElement( const char* name, bool textParent )
735{
736 if ( elementJustOpened ) {
737 SealElement();
738 }
Lee Thomason2c85a712012-01-31 08:24:24 -0800739 if ( !TextOnStack() ) {
Lee Thomason24767b02012-01-25 17:16:23 -0800740 PrintSpace( depth );
741 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800742 stack.Push( name );
Lee Thomason2c85a712012-01-31 08:24:24 -0800743 text.Push( textParent ? 'T' : 'e' );
Lee Thomason5cae8972012-01-24 18:03:07 -0800744
Lee Thomason951d8832012-01-26 08:47:06 -0800745 // fixme: can names have entities?
Lee Thomason5cae8972012-01-24 18:03:07 -0800746 fprintf( fp, "<%s", name );
747 elementJustOpened = true;
748 ++depth;
749}
750
751
752void XMLStreamer::PushAttribute( const char* name, const char* value )
753{
754 TIXMLASSERT( elementJustOpened );
Lee Thomason18d68bd2012-01-26 18:17:26 -0800755 fprintf( fp, " %s=\"", name );
756 PrintString( value );
757 fprintf( fp, "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -0800758}
759
760
761void XMLStreamer::CloseElement()
762{
763 --depth;
764 const char* name = stack.Pop();
Lee Thomason2c85a712012-01-31 08:24:24 -0800765 bool wasText = TextOnStack();
Lee Thomason5cae8972012-01-24 18:03:07 -0800766 text.Pop();
767
768 if ( elementJustOpened ) {
769 fprintf( fp, "/>" );
Lee Thomason2c85a712012-01-31 08:24:24 -0800770 if ( !wasText ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800771 fprintf( fp, "\n" );
772 }
773 }
774 else {
Lee Thomason2c85a712012-01-31 08:24:24 -0800775 if ( !wasText ) {
Lee Thomason24767b02012-01-25 17:16:23 -0800776 PrintSpace( depth );
777 }
Lee Thomason951d8832012-01-26 08:47:06 -0800778 // fixme can names have entities?
Lee Thomason5cae8972012-01-24 18:03:07 -0800779 fprintf( fp, "</%s>", name );
Lee Thomason2c85a712012-01-31 08:24:24 -0800780 if ( !TextOnStack() ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800781 fprintf( fp, "\n" );
782 }
783 }
784 elementJustOpened = false;
785}
786
787
788void XMLStreamer::SealElement()
789{
790 elementJustOpened = false;
791 fprintf( fp, ">" );
Lee Thomason2c85a712012-01-31 08:24:24 -0800792 if ( !TextOnStack() ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800793 fprintf( fp, "\n" );
794 }
795}
796
797
798void XMLStreamer::PushText( const char* text )
799{
800 if ( elementJustOpened ) {
801 SealElement();
802 }
Lee Thomason951d8832012-01-26 08:47:06 -0800803 PrintString( text );
Lee Thomason5cae8972012-01-24 18:03:07 -0800804}
805
806
807void XMLStreamer::PushComment( const char* comment )
808{
809 if ( elementJustOpened ) {
810 SealElement();
811 }
812 PrintSpace( depth );
813 fprintf( fp, "<!--%s-->\n", comment );
814}