blob: 4711b83de56a363c9306c276d42c52c8580f7c7a [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>
U-Lama\Lee560bd472011-12-28 19:42:49 -08007
8using namespace tinyxml2;
9
Lee Thomasone4422302012-01-20 17:59:50 -080010static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
Lee Thomasonfde6a752012-01-14 18:08:12 -080011static const char LF = LINE_FEED;
12static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
13static const char CR = CARRIAGE_RETURN;
Lee Thomasone4422302012-01-20 17:59:50 -080014static const char SINGLE_QUOTE = '\'';
15static const char DOUBLE_QUOTE = '\"';
Lee Thomasonfde6a752012-01-14 18:08:12 -080016
Lee Thomason8ee79892012-01-25 17:44:30 -080017struct Entity {
18 const char* pattern;
19 int length;
20 char value;
21};
22
23static const int NUM_ENTITIES = 5;
24static const Entity entities[NUM_ENTITIES] =
25{
Lee Thomason18d68bd2012-01-26 18:17:26 -080026 { "quot", 4, DOUBLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080027 { "amp", 3, '&' },
Lee Thomason18d68bd2012-01-26 18:17:26 -080028 { "apos", 4, SINGLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080029 { "lt", 2, '<' },
30 { "gt", 2, '>' }
31};
32
Lee Thomasonfde6a752012-01-14 18:08:12 -080033
Lee Thomasone4422302012-01-20 17:59:50 -080034const char* StrPair::GetStr()
35{
36 if ( flags & NEEDS_FLUSH ) {
37 *end = 0;
Lee Thomason8ee79892012-01-25 17:44:30 -080038 flags ^= NEEDS_FLUSH;
Lee Thomasone4422302012-01-20 17:59:50 -080039
Lee Thomason8ee79892012-01-25 17:44:30 -080040 if ( flags ) {
Lee Thomasone4422302012-01-20 17:59:50 -080041 char* p = start;
42 char* q = start;
43
44 while( p < end ) {
Lee Thomason8ee79892012-01-25 17:44:30 -080045 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Lee Thomasone4422302012-01-20 17:59:50 -080046 // CR-LF pair becomes LF
47 // CR alone becomes LF
48 // LF-CR becomes LF
49 if ( *(p+1) == LF ) {
50 p += 2;
51 }
52 else {
53 ++p;
54 }
55 *q = LF;
56 }
Lee Thomason8ee79892012-01-25 17:44:30 -080057 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Lee Thomasone4422302012-01-20 17:59:50 -080058 if ( *(p+1) == CR ) {
59 p += 2;
60 }
61 else {
62 ++p;
63 }
64 *q = LF;
65 }
Lee Thomason8ee79892012-01-25 17:44:30 -080066 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
67 int i=0;
68 for( i=0; i<NUM_ENTITIES; ++i ) {
69 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
70 && *(p+entities[i].length+1) == ';' )
71 {
72 // Found an entity convert;
73 *q = entities[i].value;
74 ++q;
75 p += entities[i].length + 2;
76 break;
77 }
78 }
79 if ( i == NUM_ENTITIES ) {
80 // fixme: treat as error?
81 ++p;
82 ++q;
83 }
84 }
Lee Thomasone4422302012-01-20 17:59:50 -080085 else {
86 *q = *p;
87 ++p;
Lee Thomasonec975ce2012-01-23 11:42:06 -080088 ++q;
Lee Thomasone4422302012-01-20 17:59:50 -080089 }
90 }
Lee Thomason8ee79892012-01-25 17:44:30 -080091 *q = 0;
Lee Thomasone4422302012-01-20 17:59:50 -080092 }
93 flags = 0;
94 }
95 return start;
96}
97
Lee Thomason2c85a712012-01-31 08:24:24 -080098/*
99const char* StringPool::Intern( const char* str )
100{
101 // Treat the array as a linear, inplace hash table.
102 // Nothing can get deleted, so that's handy.
103 if ( size > pool.Size()*3/4 ) {
104 DynArray< const char*, 20 > store;
105 for( int i=0; i<pool.Size(); ++i ) {
106 if ( pool[i] != 0 ) {
107 store.Push( pool[i] );
108 }
109 }
110 int newSize = pool.Size() * 2;
111 pool.PopArr( pool.Size() );
112
113 const char** mem = pool.PushArr( newSize );
114 memset( (void*)mem, 0, sizeof(char)*newSize );
115
116 while ( !store.Empty() ) {
117 Intern( store.Pop() );
118 }
119 }
120
121}
122*/
123
Lee Thomasone4422302012-01-20 17:59:50 -0800124
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800125// --------- XMLBase ----------- //
Lee Thomason18d68bd2012-01-26 18:17:26 -0800126// fixme: should take in the entity/newline flags as param
127char* XMLBase::ParseText( char* p, StrPair* pair, const char* endTag, int strFlags )
Lee Thomason3f57d272012-01-11 15:30:03 -0800128{
129 TIXMLASSERT( endTag && *endTag );
130
Lee Thomasonfde6a752012-01-14 18:08:12 -0800131 char* start = p;
Lee Thomasonfde6a752012-01-14 18:08:12 -0800132 char endChar = *endTag;
133 int length = strlen( endTag );
Lee Thomason3f57d272012-01-11 15:30:03 -0800134
Lee Thomasonfde6a752012-01-14 18:08:12 -0800135 // Inner loop of text parsing.
Lee Thomason3f57d272012-01-11 15:30:03 -0800136 while ( *p ) {
Lee Thomasonfde6a752012-01-14 18:08:12 -0800137 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
Lee Thomason18d68bd2012-01-26 18:17:26 -0800138 pair->Set( start, p, strFlags );
Lee Thomasonec975ce2012-01-23 11:42:06 -0800139 return p + length;
Lee Thomason3f57d272012-01-11 15:30:03 -0800140 }
Lee Thomasonec975ce2012-01-23 11:42:06 -0800141 ++p;
Lee Thomason3f57d272012-01-11 15:30:03 -0800142 }
Lee Thomasone4422302012-01-20 17:59:50 -0800143 return p;
Lee Thomason3f57d272012-01-11 15:30:03 -0800144}
145
146
Lee Thomasond34f52c2012-01-20 12:55:24 -0800147char* XMLBase::ParseName( char* p, StrPair* pair )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800148{
149 char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800150
151 start = p;
152 if ( !start || !(*start) ) {
153 return 0;
154 }
155
156 if ( !IsAlpha( *p ) ) {
157 return 0;
158 }
159
160 while( *p && (
161 IsAlphaNum( (unsigned char) *p )
162 || *p == '_'
163 || *p == '-'
164 || *p == '.'
165 || *p == ':' ))
166 {
167 ++p;
168 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800169
170 if ( p > start ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800171 pair->Set( start, p, 0 );
172 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800173 }
Lee Thomason39ede242012-01-20 11:27:56 -0800174 return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800175}
176
177
178char* XMLBase::Identify( XMLDocument* document, char* p, XMLNode** node )
179{
180 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800181 char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800182 p = XMLNode::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800183 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800184 {
185 return 0;
186 }
187
188 // What is this thing?
189 // - Elements start with a letter or underscore, but xml is reserved.
190 // - Comments: <!--
191 // - Decleration: <?xml
192 // - Everthing else is unknown to tinyxml.
193 //
194
195 static const char* xmlHeader = { "<?xml" };
196 static const char* commentHeader = { "<!--" };
197 static const char* dtdHeader = { "<!" };
198 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800199 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800200
201 static const int xmlHeaderLen = 5;
202 static const int commentHeaderLen = 4;
203 static const int dtdHeaderLen = 2;
204 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800205 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800206
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800207 if ( StringEqual( p, commentHeader, commentHeaderLen ) ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800208 returnNode = new XMLComment( document );
209 p += commentHeaderLen;
210 }
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800211 else if ( StringEqual( p, elementHeader, elementHeaderLen ) ) {
212 returnNode = new XMLElement( document );
213 p += elementHeaderLen;
214 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800215 // fixme: better text detection
216 else if ( (*p != '<') && IsAlphaNum( *p ) ) {
217 // fixme: this is filtering out empty text...should it?
218 returnNode = new XMLText( document );
219 p = start; // Back it up, all the text counts.
220 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800221 else {
222 TIXMLASSERT( 0 );
223 }
224
225 *node = returnNode;
226 return p;
227}
228
229
230// --------- XMLNode ----------- //
231
232XMLNode::XMLNode( XMLDocument* doc ) :
233 document( doc ),
234 parent( 0 ),
Lee Thomason67d61312012-01-24 16:01:51 -0800235 isTextParent( false ),
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800236 firstChild( 0 ), lastChild( 0 ),
237 prev( 0 ), next( 0 )
238{
239
240}
241
242
243XMLNode::~XMLNode()
244{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800245 ClearChildren();
Lee Thomason7c913cd2012-01-26 18:32:34 -0800246 if ( parent ) {
247 parent->Unlink( this );
248 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800249}
250
251
252void XMLNode::ClearChildren()
253{
Lee Thomasond923c672012-01-23 08:44:25 -0800254 while( firstChild ) {
255 XMLNode* node = firstChild;
256 Unlink( node );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800257 delete node;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800258 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800259 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800260}
261
262
263void XMLNode::Unlink( XMLNode* child )
264{
265 TIXMLASSERT( child->parent == this );
266 if ( child == firstChild )
267 firstChild = firstChild->next;
268 if ( child == lastChild )
269 lastChild = lastChild->prev;
270
271 if ( child->prev ) {
272 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800273 }
Lee Thomasond923c672012-01-23 08:44:25 -0800274 if ( child->next ) {
275 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800276 }
Lee Thomasond923c672012-01-23 08:44:25 -0800277 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800278}
279
280
281XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
282{
283 if ( lastChild ) {
284 TIXMLASSERT( firstChild );
285 TIXMLASSERT( lastChild->next == 0 );
286 lastChild->next = addThis;
287 addThis->prev = lastChild;
288 lastChild = addThis;
289
290 addThis->parent = this;
291 addThis->next = 0;
292 }
293 else {
294 TIXMLASSERT( firstChild == 0 );
295 firstChild = lastChild = addThis;
296
297 addThis->parent = this;
298 addThis->prev = 0;
299 addThis->next = 0;
300 }
Lee Thomason67d61312012-01-24 16:01:51 -0800301 if ( addThis->ToText() ) {
302 SetTextParent();
303 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800304 return addThis;
305}
306
307
Lee Thomason2c85a712012-01-31 08:24:24 -0800308XMLElement* XMLNode::FirstChildElement( const char* value )
309{
310 for( XMLNode* node=firstChild; node; node=node->next ) {
311 XMLElement* element = node->ToElement();
312 if ( element ) {
313 if ( !value || StringEqual( element->Name(), value ) ) {
314 return element;
315 }
316 }
317 }
318 return 0;
319}
320
321
Lee Thomason5cae8972012-01-24 18:03:07 -0800322void XMLNode::Print( XMLStreamer* streamer )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800323{
324 for( XMLNode* node = firstChild; node; node=node->next ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800325 node->Print( streamer );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800326 }
327}
328
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800329
Lee Thomason67d61312012-01-24 16:01:51 -0800330char* XMLNode::ParseDeep( char* p )
331{
332 while( p && *p ) {
333 XMLNode* node = 0;
334 p = Identify( document, p, &node );
335 if ( p && node ) {
336 p = node->ParseDeep( p );
337 // FIXME: is it the correct closing element?
338 if ( node->IsClosingElement() ) {
339 delete node;
340 return p;
341 }
342 this->InsertEndChild( node );
343 }
344 }
345 return 0;
346}
347
Lee Thomason5492a1c2012-01-23 15:32:10 -0800348// --------- XMLText ---------- //
349char* XMLText::ParseDeep( char* p )
350{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800351 p = ParseText( p, &value, "<", StrPair::TEXT_ELEMENT );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800352 // consumes the end tag.
353 if ( p && *p ) {
354 return p-1;
355 }
356 return 0;
357}
358
359
Lee Thomason5cae8972012-01-24 18:03:07 -0800360void XMLText::Print( XMLStreamer* streamer )
Lee Thomason5492a1c2012-01-23 15:32:10 -0800361{
Lee Thomason67d61312012-01-24 16:01:51 -0800362 const char* v = value.GetStr();
Lee Thomason5cae8972012-01-24 18:03:07 -0800363 streamer->PushText( v );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800364}
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800365
366
Lee Thomason3f57d272012-01-11 15:30:03 -0800367// --------- XMLComment ---------- //
368
Lee Thomasone4422302012-01-20 17:59:50 -0800369XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800370{
371}
372
373
Lee Thomasonce0763e2012-01-11 15:43:54 -0800374XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800375{
Lee Thomasond923c672012-01-23 08:44:25 -0800376 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800377}
378
379
Lee Thomason5cae8972012-01-24 18:03:07 -0800380void XMLComment::Print( XMLStreamer* streamer )
Lee Thomasonce0763e2012-01-11 15:43:54 -0800381{
Lee Thomason5cae8972012-01-24 18:03:07 -0800382// XMLNode::Print( fp, depth );
383// fprintf( fp, "<!--%s-->\n", value.GetStr() );
384 streamer->PushComment( value.GetStr() );
Lee Thomasonce0763e2012-01-11 15:43:54 -0800385}
386
387
388char* XMLComment::ParseDeep( char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800389{
390 // Comment parses as text.
Lee Thomason18d68bd2012-01-26 18:17:26 -0800391 return ParseText( p, &value, "-->", StrPair::COMMENT );
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800392}
393
394
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800395// --------- XMLAttribute ---------- //
396char* XMLAttribute::ParseDeep( char* p )
397{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800398 p = ParseText( p, &name, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800399 if ( !p || !*p ) return 0;
400
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800401 char endTag[2] = { *p, 0 };
402 ++p;
Lee Thomason18d68bd2012-01-26 18:17:26 -0800403 p = ParseText( p, &value, endTag, StrPair::ATTRIBUTE_VALUE );
Lee Thomasone4422302012-01-20 17:59:50 -0800404 if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800405 return p;
406}
407
408
Lee Thomason5cae8972012-01-24 18:03:07 -0800409void XMLAttribute::Print( XMLStreamer* streamer )
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800410{
Lee Thomason22aead12012-01-23 13:29:35 -0800411 // fixme: sort out single vs. double quote
Lee Thomason5cae8972012-01-24 18:03:07 -0800412 //fprintf( cfile, "%s=\"%s\"", name.GetStr(), value.GetStr() );
413 streamer->PushAttribute( name.GetStr(), value.GetStr() );
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800414}
415
416
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800417// --------- XMLElement ---------- //
418XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800419 closing( false ),
420 rootAttribute( 0 ),
421 lastAttribute( 0 )
422{
423}
424
425
426XMLElement::~XMLElement()
427{
Lee Thomasond923c672012-01-23 08:44:25 -0800428 //printf( "~XMLElemen %x\n",this );
429
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800430 XMLAttribute* attribute = rootAttribute;
431 while( attribute ) {
432 XMLAttribute* next = attribute->next;
433 delete attribute;
434 attribute = next;
435 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800436}
437
438
Lee Thomason67d61312012-01-24 16:01:51 -0800439char* XMLElement::ParseAttributes( char* p, bool* closedElement )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800440{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800441 const char* start = p;
Lee Thomason67d61312012-01-24 16:01:51 -0800442 *closedElement = false;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800443
444 // Read the attributes.
445 while( p ) {
446 p = SkipWhiteSpace( p );
447 if ( !p || !(*p) ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800448 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, name.GetStr() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800449 return 0;
450 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800451
452 // attribute.
Lee Thomason22aead12012-01-23 13:29:35 -0800453 if ( IsAlpha( *p ) ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800454 XMLAttribute* attrib = new XMLAttribute( this );
455 p = attrib->ParseDeep( p );
456 if ( !p ) {
457 delete attrib;
Lee Thomasone4422302012-01-20 17:59:50 -0800458 document->SetError( XMLDocument::ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800459 return 0;
460 }
461 if ( rootAttribute ) {
462 TIXMLASSERT( lastAttribute );
463 lastAttribute->next = attrib;
464 lastAttribute = attrib;
465 }
466 else {
467 rootAttribute = lastAttribute = attrib;
468 }
469 }
Lee Thomasone4422302012-01-20 17:59:50 -0800470 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800471 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800472 if ( closing ) {
473 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
474 return 0;
475 }
Lee Thomason67d61312012-01-24 16:01:51 -0800476 *closedElement = true;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800477 return p+2; // done; sealed element.
478 }
Lee Thomasone4422302012-01-20 17:59:50 -0800479 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800480 else if ( *p == '>' ) {
481 ++p;
482 break;
483 }
Lee Thomasone4422302012-01-20 17:59:50 -0800484 else {
485 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
486 return 0;
487 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800488 }
Lee Thomason67d61312012-01-24 16:01:51 -0800489 return p;
490}
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800491
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800492
Lee Thomason67d61312012-01-24 16:01:51 -0800493//
494// <ele></ele>
495// <ele>foo<b>bar</b></ele>
496//
497char* XMLElement::ParseDeep( char* p )
498{
499 // Read the element name.
500 p = SkipWhiteSpace( p );
501 if ( !p ) return 0;
502 const char* start = p;
503
504 // The closing element is the </element> form. It is
505 // parsed just like a regular element then deleted from
506 // the DOM.
507 if ( *p == '/' ) {
508 closing = true;
509 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800510 }
Lee Thomason67d61312012-01-24 16:01:51 -0800511
512 p = ParseName( p, &name );
513 if ( name.Empty() ) return 0;
514
515 bool elementClosed=false;
516 p = ParseAttributes( p, &elementClosed );
517 if ( !p || !*p || elementClosed || closing )
518 return p;
519
520 p = XMLNode::ParseDeep( p );
521 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800522}
523
524
Lee Thomason5cae8972012-01-24 18:03:07 -0800525void XMLElement::Print( XMLStreamer* streamer )
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800526{
Lee Thomason5cae8972012-01-24 18:03:07 -0800527 //if ( !parent || !parent->IsTextParent() ) {
528 // PrintSpace( cfile, depth );
529 //}
530 //fprintf( cfile, "<%s", Name() );
531 streamer->OpenElement( Name(), IsTextParent() );
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800532
533 for( XMLAttribute* attrib=rootAttribute; attrib; attrib=attrib->next ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800534 //fprintf( cfile, " " );
535 attrib->Print( streamer );
536
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800537 }
538
Lee Thomason5cae8972012-01-24 18:03:07 -0800539 for( XMLNode* node=firstChild; node; node=node->next ) {
540 node->Print( streamer );
541 }
542 streamer->CloseElement();
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800543}
544
545
Lee Thomason3f57d272012-01-11 15:30:03 -0800546// --------- XMLDocument ----------- //
Lee Thomason67d61312012-01-24 16:01:51 -0800547XMLDocument::XMLDocument() :
Lee Thomason18d68bd2012-01-26 18:17:26 -0800548 XMLNode( 0 ),
U-Lama\Lee560bd472011-12-28 19:42:49 -0800549 charBuffer( 0 )
550{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800551 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -0800552}
U-Lama\Leee13c3e62011-12-28 14:36:55 -0800553
554
Lee Thomason3f57d272012-01-11 15:30:03 -0800555XMLDocument::~XMLDocument()
556{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800557 delete [] charBuffer;
Lee Thomason3f57d272012-01-11 15:30:03 -0800558}
559
560
Lee Thomason18d68bd2012-01-26 18:17:26 -0800561void XMLDocument::InitDocument()
562{
563 errorID = NO_ERROR;
564 errorStr1 = 0;
565 errorStr2 = 0;
566
567 delete [] charBuffer;
568 charBuffer = 0;
569
570}
571
Lee Thomason3f57d272012-01-11 15:30:03 -0800572
Lee Thomason2c85a712012-01-31 08:24:24 -0800573XMLElement* XMLDocument::NewElement( const char* name )
574{
575 XMLElement* ele = new XMLElement( this );
576 ele->SetName( name );
577 return ele;
578}
579
580
Lee Thomason7c913cd2012-01-26 18:32:34 -0800581int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800582{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800583 ClearChildren();
584 InitDocument();
585
586 if ( !p || !*p ) {
587 return true; // correctly parse an empty string?
588 }
589 size_t len = strlen( p );
590 charBuffer = new char[ len+1 ];
591 memcpy( charBuffer, p, len+1 );
Lee Thomason3f57d272012-01-11 15:30:03 -0800592 XMLNode* node = 0;
Lee Thomason85403d82012-01-11 15:55:05 -0800593
Lee Thomason18d68bd2012-01-26 18:17:26 -0800594 char* q = ParseDeep( charBuffer );
Lee Thomason7c913cd2012-01-26 18:32:34 -0800595 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -0800596}
597
598
Lee Thomason5cae8972012-01-24 18:03:07 -0800599void XMLDocument::Print( XMLStreamer* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -0800600{
Lee Thomason5cae8972012-01-24 18:03:07 -0800601 XMLStreamer stdStreamer( stdout );
602 if ( !streamer )
603 streamer = &stdStreamer;
Lee Thomason67d61312012-01-24 16:01:51 -0800604 for( XMLNode* node = firstChild; node; node=node->next ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800605 node->Print( streamer );
Lee Thomasonce0763e2012-01-11 15:43:54 -0800606 }
Lee Thomason3f57d272012-01-11 15:30:03 -0800607}
608
609
Lee Thomason67d61312012-01-24 16:01:51 -0800610void XMLDocument::SetError( int error, const char* str1, const char* str2 )
611{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800612 errorID = error;
613 printf( "ERROR: id=%d '%s' '%s'\n", error, str1, str2 ); // fixme: remove
614 errorStr1 = str1;
615 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -0800616}
617
Lee Thomason5cae8972012-01-24 18:03:07 -0800618
Lee Thomason2c85a712012-01-31 08:24:24 -0800619/*
Lee Thomason5cae8972012-01-24 18:03:07 -0800620StringStack::StringStack()
621{
Lee Thomason5cae8972012-01-24 18:03:07 -0800622 nPositive = 0;
Lee Thomason1270ae52012-01-27 17:58:30 -0800623 mem.Push( 0 ); // start with null. makes later code simpler.
Lee Thomason5cae8972012-01-24 18:03:07 -0800624}
625
626
Lee Thomason24767b02012-01-25 17:16:23 -0800627StringStack::~StringStack()
628{
Lee Thomason24767b02012-01-25 17:16:23 -0800629}
630
631
Lee Thomason5cae8972012-01-24 18:03:07 -0800632void StringStack::Push( const char* str ) {
633 int needed = strlen( str ) + 1;
Lee Thomason1270ae52012-01-27 17:58:30 -0800634 char* p = mem.PushArr( needed );
635 strcpy( p, str );
636 if ( needed > 1 )
Lee Thomason5cae8972012-01-24 18:03:07 -0800637 nPositive++;
Lee Thomason5cae8972012-01-24 18:03:07 -0800638}
639
640
641const char* StringStack::Pop() {
Lee Thomason1270ae52012-01-27 17:58:30 -0800642 TIXMLASSERT( mem.Size() > 1 );
643 const char* p = mem.Mem() + mem.Size() - 2; // end of final string.
Lee Thomason5cae8972012-01-24 18:03:07 -0800644 if ( *p ) {
645 nPositive--;
646 }
647 while( *p ) { // stack starts with a null, don't need to check for 'mem'
Lee Thomason1270ae52012-01-27 17:58:30 -0800648 TIXMLASSERT( p > mem.Mem() );
Lee Thomason5cae8972012-01-24 18:03:07 -0800649 --p;
650 }
Lee Thomason1270ae52012-01-27 17:58:30 -0800651 mem.PopArr( strlen(p)+1 );
Lee Thomason5cae8972012-01-24 18:03:07 -0800652 return p+1;
653}
Lee Thomason2c85a712012-01-31 08:24:24 -0800654*/
Lee Thomason5cae8972012-01-24 18:03:07 -0800655
656
657XMLStreamer::XMLStreamer( FILE* file ) : fp( file ), depth( 0 ), elementJustOpened( false )
658{
Lee Thomason857b8682012-01-25 17:50:25 -0800659 for( int i=0; i<ENTITY_RANGE; ++i ) {
660 entityFlag[i] = false;
661 }
662 for( int i=0; i<NUM_ENTITIES; ++i ) {
663 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
664 if ( entities[i].value < ENTITY_RANGE ) {
665 entityFlag[ entities[i].value ] = true;
666 }
667 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800668}
669
670
671void XMLStreamer::PrintSpace( int depth )
672{
673 for( int i=0; i<depth; ++i ) {
674 fprintf( fp, " " );
675 }
676}
677
678
Lee Thomason951d8832012-01-26 08:47:06 -0800679void XMLStreamer::PrintString( const char* p )
Lee Thomason857b8682012-01-25 17:50:25 -0800680{
Lee Thomason951d8832012-01-26 08:47:06 -0800681 // Look for runs of bytes between entities to print.
682 const char* q = p;
Lee Thomason857b8682012-01-25 17:50:25 -0800683
Lee Thomason951d8832012-01-26 08:47:06 -0800684 while ( *q ) {
685 if ( *q < ENTITY_RANGE ) {
686 // Check for entities. If one is found, flush
687 // the stream up until the entity, write the
688 // entity, and keep looking.
689 if ( entityFlag[*q] ) {
690 while ( p < q ) {
691 fputc( *p, fp );
692 ++p;
693 }
694 for( int i=0; i<NUM_ENTITIES; ++i ) {
695 if ( entities[i].value == *q ) {
696 fprintf( fp, "&%s;", entities[i].pattern );
697 break;
698 }
699 }
700 ++p;
701 }
702 }
703 ++q;
704 }
705 // Flush the remaining string. This will be the entire
706 // string if an entity wasn't found.
707 if ( q-p > 0 ) {
708 fprintf( fp, "%s", p );
709 }
Lee Thomason857b8682012-01-25 17:50:25 -0800710}
711
Lee Thomason5cae8972012-01-24 18:03:07 -0800712void XMLStreamer::OpenElement( const char* name, bool textParent )
713{
714 if ( elementJustOpened ) {
715 SealElement();
716 }
Lee Thomason2c85a712012-01-31 08:24:24 -0800717 if ( !TextOnStack() ) {
Lee Thomason24767b02012-01-25 17:16:23 -0800718 PrintSpace( depth );
719 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800720 stack.Push( name );
Lee Thomason2c85a712012-01-31 08:24:24 -0800721 text.Push( textParent ? 'T' : 'e' );
Lee Thomason5cae8972012-01-24 18:03:07 -0800722
Lee Thomason951d8832012-01-26 08:47:06 -0800723 // fixme: can names have entities?
Lee Thomason5cae8972012-01-24 18:03:07 -0800724 fprintf( fp, "<%s", name );
725 elementJustOpened = true;
726 ++depth;
727}
728
729
730void XMLStreamer::PushAttribute( const char* name, const char* value )
731{
732 TIXMLASSERT( elementJustOpened );
Lee Thomason18d68bd2012-01-26 18:17:26 -0800733 fprintf( fp, " %s=\"", name );
734 PrintString( value );
735 fprintf( fp, "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -0800736}
737
738
739void XMLStreamer::CloseElement()
740{
741 --depth;
742 const char* name = stack.Pop();
Lee Thomason2c85a712012-01-31 08:24:24 -0800743 bool wasText = TextOnStack();
Lee Thomason5cae8972012-01-24 18:03:07 -0800744 text.Pop();
745
746 if ( elementJustOpened ) {
747 fprintf( fp, "/>" );
Lee Thomason2c85a712012-01-31 08:24:24 -0800748 if ( !wasText ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800749 fprintf( fp, "\n" );
750 }
751 }
752 else {
Lee Thomason2c85a712012-01-31 08:24:24 -0800753 if ( !wasText ) {
Lee Thomason24767b02012-01-25 17:16:23 -0800754 PrintSpace( depth );
755 }
Lee Thomason951d8832012-01-26 08:47:06 -0800756 // fixme can names have entities?
Lee Thomason5cae8972012-01-24 18:03:07 -0800757 fprintf( fp, "</%s>", name );
Lee Thomason2c85a712012-01-31 08:24:24 -0800758 if ( !TextOnStack() ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800759 fprintf( fp, "\n" );
760 }
761 }
762 elementJustOpened = false;
763}
764
765
766void XMLStreamer::SealElement()
767{
768 elementJustOpened = false;
769 fprintf( fp, ">" );
Lee Thomason2c85a712012-01-31 08:24:24 -0800770 if ( !TextOnStack() ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800771 fprintf( fp, "\n" );
772 }
773}
774
775
776void XMLStreamer::PushText( const char* text )
777{
778 if ( elementJustOpened ) {
779 SealElement();
780 }
Lee Thomason951d8832012-01-26 08:47:06 -0800781 PrintString( text );
Lee Thomason5cae8972012-01-24 18:03:07 -0800782}
783
784
785void XMLStreamer::PushComment( const char* comment )
786{
787 if ( elementJustOpened ) {
788 SealElement();
789 }
790 PrintSpace( depth );
791 fprintf( fp, "<!--%s-->\n", comment );
792}