blob: f868c91ad2b51b89273ec4cc94f367c5e770633c [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 Thomason18d68bd2012-01-26 18:17:26 -080034#if 0
Lee Thomason3f57d272012-01-11 15:30:03 -080035// --------- CharBuffer ----------- //
U-Lama\Lee560bd472011-12-28 19:42:49 -080036/*static*/ CharBuffer* CharBuffer::Construct( const char* in )
37{
38 size_t len = strlen( in );
39 size_t size = len + sizeof( CharBuffer );
40 CharBuffer* cb = (CharBuffer*) malloc( size );
41 cb->length = len;
42 strcpy( cb->mem, in );
43 return cb;
44}
45
46
47/*static*/ void CharBuffer::Free( CharBuffer* cb )
48{
49 free( cb );
50}
Lee Thomason18d68bd2012-01-26 18:17:26 -080051#endif
U-Lama\Lee560bd472011-12-28 19:42:49 -080052
53
Lee Thomasone4422302012-01-20 17:59:50 -080054const char* StrPair::GetStr()
55{
56 if ( flags & NEEDS_FLUSH ) {
57 *end = 0;
Lee Thomason8ee79892012-01-25 17:44:30 -080058 flags ^= NEEDS_FLUSH;
Lee Thomasone4422302012-01-20 17:59:50 -080059
Lee Thomason8ee79892012-01-25 17:44:30 -080060 if ( flags ) {
Lee Thomasone4422302012-01-20 17:59:50 -080061 char* p = start;
62 char* q = start;
63
64 while( p < end ) {
Lee Thomason8ee79892012-01-25 17:44:30 -080065 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Lee Thomasone4422302012-01-20 17:59:50 -080066 // CR-LF pair becomes LF
67 // CR alone becomes LF
68 // LF-CR becomes LF
69 if ( *(p+1) == LF ) {
70 p += 2;
71 }
72 else {
73 ++p;
74 }
75 *q = LF;
76 }
Lee Thomason8ee79892012-01-25 17:44:30 -080077 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Lee Thomasone4422302012-01-20 17:59:50 -080078 if ( *(p+1) == CR ) {
79 p += 2;
80 }
81 else {
82 ++p;
83 }
84 *q = LF;
85 }
Lee Thomason8ee79892012-01-25 17:44:30 -080086 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
87 int i=0;
88 for( i=0; i<NUM_ENTITIES; ++i ) {
89 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
90 && *(p+entities[i].length+1) == ';' )
91 {
92 // Found an entity convert;
93 *q = entities[i].value;
94 ++q;
95 p += entities[i].length + 2;
96 break;
97 }
98 }
99 if ( i == NUM_ENTITIES ) {
100 // fixme: treat as error?
101 ++p;
102 ++q;
103 }
104 }
Lee Thomasone4422302012-01-20 17:59:50 -0800105 else {
106 *q = *p;
107 ++p;
Lee Thomasonec975ce2012-01-23 11:42:06 -0800108 ++q;
Lee Thomasone4422302012-01-20 17:59:50 -0800109 }
110 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800111 *q = 0;
Lee Thomasone4422302012-01-20 17:59:50 -0800112 }
113 flags = 0;
114 }
115 return start;
116}
117
118
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800119// --------- XMLBase ----------- //
Lee Thomason18d68bd2012-01-26 18:17:26 -0800120// fixme: should take in the entity/newline flags as param
121char* XMLBase::ParseText( char* p, StrPair* pair, const char* endTag, int strFlags )
Lee Thomason3f57d272012-01-11 15:30:03 -0800122{
123 TIXMLASSERT( endTag && *endTag );
124
Lee Thomasonfde6a752012-01-14 18:08:12 -0800125 char* start = p;
Lee Thomasonfde6a752012-01-14 18:08:12 -0800126 char endChar = *endTag;
127 int length = strlen( endTag );
Lee Thomason3f57d272012-01-11 15:30:03 -0800128
Lee Thomasonfde6a752012-01-14 18:08:12 -0800129 // Inner loop of text parsing.
Lee Thomason3f57d272012-01-11 15:30:03 -0800130 while ( *p ) {
Lee Thomasonfde6a752012-01-14 18:08:12 -0800131 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
Lee Thomason18d68bd2012-01-26 18:17:26 -0800132 pair->Set( start, p, strFlags );
Lee Thomasonec975ce2012-01-23 11:42:06 -0800133 return p + length;
Lee Thomason3f57d272012-01-11 15:30:03 -0800134 }
Lee Thomasonec975ce2012-01-23 11:42:06 -0800135 ++p;
Lee Thomason3f57d272012-01-11 15:30:03 -0800136 }
Lee Thomasone4422302012-01-20 17:59:50 -0800137 return p;
Lee Thomason3f57d272012-01-11 15:30:03 -0800138}
139
140
Lee Thomasond34f52c2012-01-20 12:55:24 -0800141char* XMLBase::ParseName( char* p, StrPair* pair )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800142{
143 char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800144
145 start = p;
146 if ( !start || !(*start) ) {
147 return 0;
148 }
149
150 if ( !IsAlpha( *p ) ) {
151 return 0;
152 }
153
154 while( *p && (
155 IsAlphaNum( (unsigned char) *p )
156 || *p == '_'
157 || *p == '-'
158 || *p == '.'
159 || *p == ':' ))
160 {
161 ++p;
162 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800163
164 if ( p > start ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800165 pair->Set( start, p, 0 );
166 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800167 }
Lee Thomason39ede242012-01-20 11:27:56 -0800168 return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800169}
170
171
172char* XMLBase::Identify( XMLDocument* document, char* p, XMLNode** node )
173{
174 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800175 char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800176 p = XMLNode::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800177 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800178 {
179 return 0;
180 }
181
182 // What is this thing?
183 // - Elements start with a letter or underscore, but xml is reserved.
184 // - Comments: <!--
185 // - Decleration: <?xml
186 // - Everthing else is unknown to tinyxml.
187 //
188
189 static const char* xmlHeader = { "<?xml" };
190 static const char* commentHeader = { "<!--" };
191 static const char* dtdHeader = { "<!" };
192 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800193 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800194
195 static const int xmlHeaderLen = 5;
196 static const int commentHeaderLen = 4;
197 static const int dtdHeaderLen = 2;
198 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800199 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800200
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800201 if ( StringEqual( p, commentHeader, commentHeaderLen ) ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800202 returnNode = new XMLComment( document );
203 p += commentHeaderLen;
204 }
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800205 else if ( StringEqual( p, elementHeader, elementHeaderLen ) ) {
206 returnNode = new XMLElement( document );
207 p += elementHeaderLen;
208 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800209 // fixme: better text detection
210 else if ( (*p != '<') && IsAlphaNum( *p ) ) {
211 // fixme: this is filtering out empty text...should it?
212 returnNode = new XMLText( document );
213 p = start; // Back it up, all the text counts.
214 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800215 else {
216 TIXMLASSERT( 0 );
217 }
218
219 *node = returnNode;
220 return p;
221}
222
223
224// --------- XMLNode ----------- //
225
226XMLNode::XMLNode( XMLDocument* doc ) :
227 document( doc ),
228 parent( 0 ),
Lee Thomason67d61312012-01-24 16:01:51 -0800229 isTextParent( false ),
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800230 firstChild( 0 ), lastChild( 0 ),
231 prev( 0 ), next( 0 )
232{
233
234}
235
236
237XMLNode::~XMLNode()
238{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800239 ClearChildren();
240}
241
242
243void XMLNode::ClearChildren()
244{
Lee Thomasond923c672012-01-23 08:44:25 -0800245 while( firstChild ) {
246 XMLNode* node = firstChild;
247 Unlink( node );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800248 delete node;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800249 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800250 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800251}
252
253
254void XMLNode::Unlink( XMLNode* child )
255{
256 TIXMLASSERT( child->parent == this );
257 if ( child == firstChild )
258 firstChild = firstChild->next;
259 if ( child == lastChild )
260 lastChild = lastChild->prev;
261
262 if ( child->prev ) {
263 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800264 }
Lee Thomasond923c672012-01-23 08:44:25 -0800265 if ( child->next ) {
266 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800267 }
Lee Thomasond923c672012-01-23 08:44:25 -0800268 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800269}
270
271
272XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
273{
274 if ( lastChild ) {
275 TIXMLASSERT( firstChild );
276 TIXMLASSERT( lastChild->next == 0 );
277 lastChild->next = addThis;
278 addThis->prev = lastChild;
279 lastChild = addThis;
280
281 addThis->parent = this;
282 addThis->next = 0;
283 }
284 else {
285 TIXMLASSERT( firstChild == 0 );
286 firstChild = lastChild = addThis;
287
288 addThis->parent = this;
289 addThis->prev = 0;
290 addThis->next = 0;
291 }
Lee Thomason67d61312012-01-24 16:01:51 -0800292 if ( addThis->ToText() ) {
293 SetTextParent();
294 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800295 return addThis;
296}
297
298
Lee Thomason5cae8972012-01-24 18:03:07 -0800299void XMLNode::Print( XMLStreamer* streamer )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800300{
301 for( XMLNode* node = firstChild; node; node=node->next ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800302 node->Print( streamer );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800303 }
304}
305
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800306
Lee Thomason67d61312012-01-24 16:01:51 -0800307char* XMLNode::ParseDeep( char* p )
308{
309 while( p && *p ) {
310 XMLNode* node = 0;
311 p = Identify( document, p, &node );
312 if ( p && node ) {
313 p = node->ParseDeep( p );
314 // FIXME: is it the correct closing element?
315 if ( node->IsClosingElement() ) {
316 delete node;
317 return p;
318 }
319 this->InsertEndChild( node );
320 }
321 }
322 return 0;
323}
324
Lee Thomason5492a1c2012-01-23 15:32:10 -0800325// --------- XMLText ---------- //
326char* XMLText::ParseDeep( char* p )
327{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800328 p = ParseText( p, &value, "<", StrPair::TEXT_ELEMENT );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800329 // consumes the end tag.
330 if ( p && *p ) {
331 return p-1;
332 }
333 return 0;
334}
335
336
Lee Thomason5cae8972012-01-24 18:03:07 -0800337void XMLText::Print( XMLStreamer* streamer )
Lee Thomason5492a1c2012-01-23 15:32:10 -0800338{
Lee Thomason67d61312012-01-24 16:01:51 -0800339 const char* v = value.GetStr();
Lee Thomason5cae8972012-01-24 18:03:07 -0800340 streamer->PushText( v );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800341}
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800342
343
Lee Thomason3f57d272012-01-11 15:30:03 -0800344// --------- XMLComment ---------- //
345
Lee Thomasone4422302012-01-20 17:59:50 -0800346XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800347{
348}
349
350
Lee Thomasonce0763e2012-01-11 15:43:54 -0800351XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800352{
Lee Thomasond923c672012-01-23 08:44:25 -0800353 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800354}
355
356
Lee Thomason5cae8972012-01-24 18:03:07 -0800357void XMLComment::Print( XMLStreamer* streamer )
Lee Thomasonce0763e2012-01-11 15:43:54 -0800358{
Lee Thomason5cae8972012-01-24 18:03:07 -0800359// XMLNode::Print( fp, depth );
360// fprintf( fp, "<!--%s-->\n", value.GetStr() );
361 streamer->PushComment( value.GetStr() );
Lee Thomasonce0763e2012-01-11 15:43:54 -0800362}
363
364
365char* XMLComment::ParseDeep( char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800366{
367 // Comment parses as text.
Lee Thomason18d68bd2012-01-26 18:17:26 -0800368 return ParseText( p, &value, "-->", StrPair::COMMENT );
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800369}
370
371
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800372// --------- XMLAttribute ---------- //
373char* XMLAttribute::ParseDeep( char* p )
374{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800375 p = ParseText( p, &name, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800376 if ( !p || !*p ) return 0;
377
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800378 char endTag[2] = { *p, 0 };
379 ++p;
Lee Thomason18d68bd2012-01-26 18:17:26 -0800380 p = ParseText( p, &value, endTag, StrPair::ATTRIBUTE_VALUE );
Lee Thomasone4422302012-01-20 17:59:50 -0800381 if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800382 return p;
383}
384
385
Lee Thomason5cae8972012-01-24 18:03:07 -0800386void XMLAttribute::Print( XMLStreamer* streamer )
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800387{
Lee Thomason22aead12012-01-23 13:29:35 -0800388 // fixme: sort out single vs. double quote
Lee Thomason5cae8972012-01-24 18:03:07 -0800389 //fprintf( cfile, "%s=\"%s\"", name.GetStr(), value.GetStr() );
390 streamer->PushAttribute( name.GetStr(), value.GetStr() );
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800391}
392
393
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800394// --------- XMLElement ---------- //
395XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800396 closing( false ),
397 rootAttribute( 0 ),
398 lastAttribute( 0 )
399{
400}
401
402
403XMLElement::~XMLElement()
404{
Lee Thomasond923c672012-01-23 08:44:25 -0800405 //printf( "~XMLElemen %x\n",this );
406
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800407 XMLAttribute* attribute = rootAttribute;
408 while( attribute ) {
409 XMLAttribute* next = attribute->next;
410 delete attribute;
411 attribute = next;
412 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800413}
414
415
Lee Thomason67d61312012-01-24 16:01:51 -0800416char* XMLElement::ParseAttributes( char* p, bool* closedElement )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800417{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800418 const char* start = p;
Lee Thomason67d61312012-01-24 16:01:51 -0800419 *closedElement = false;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800420
421 // Read the attributes.
422 while( p ) {
423 p = SkipWhiteSpace( p );
424 if ( !p || !(*p) ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800425 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, name.GetStr() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800426 return 0;
427 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800428
429 // attribute.
Lee Thomason22aead12012-01-23 13:29:35 -0800430 if ( IsAlpha( *p ) ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800431 XMLAttribute* attrib = new XMLAttribute( this );
432 p = attrib->ParseDeep( p );
433 if ( !p ) {
434 delete attrib;
Lee Thomasone4422302012-01-20 17:59:50 -0800435 document->SetError( XMLDocument::ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800436 return 0;
437 }
438 if ( rootAttribute ) {
439 TIXMLASSERT( lastAttribute );
440 lastAttribute->next = attrib;
441 lastAttribute = attrib;
442 }
443 else {
444 rootAttribute = lastAttribute = attrib;
445 }
446 }
Lee Thomasone4422302012-01-20 17:59:50 -0800447 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800448 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800449 if ( closing ) {
450 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
451 return 0;
452 }
Lee Thomason67d61312012-01-24 16:01:51 -0800453 *closedElement = true;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800454 return p+2; // done; sealed element.
455 }
Lee Thomasone4422302012-01-20 17:59:50 -0800456 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800457 else if ( *p == '>' ) {
458 ++p;
459 break;
460 }
Lee Thomasone4422302012-01-20 17:59:50 -0800461 else {
462 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
463 return 0;
464 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800465 }
Lee Thomason67d61312012-01-24 16:01:51 -0800466 return p;
467}
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800468
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800469
Lee Thomason67d61312012-01-24 16:01:51 -0800470//
471// <ele></ele>
472// <ele>foo<b>bar</b></ele>
473//
474char* XMLElement::ParseDeep( char* p )
475{
476 // Read the element name.
477 p = SkipWhiteSpace( p );
478 if ( !p ) return 0;
479 const char* start = p;
480
481 // The closing element is the </element> form. It is
482 // parsed just like a regular element then deleted from
483 // the DOM.
484 if ( *p == '/' ) {
485 closing = true;
486 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800487 }
Lee Thomason67d61312012-01-24 16:01:51 -0800488
489 p = ParseName( p, &name );
490 if ( name.Empty() ) return 0;
491
492 bool elementClosed=false;
493 p = ParseAttributes( p, &elementClosed );
494 if ( !p || !*p || elementClosed || closing )
495 return p;
496
497 p = XMLNode::ParseDeep( p );
498 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800499}
500
501
Lee Thomason5cae8972012-01-24 18:03:07 -0800502void XMLElement::Print( XMLStreamer* streamer )
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800503{
Lee Thomason5cae8972012-01-24 18:03:07 -0800504 //if ( !parent || !parent->IsTextParent() ) {
505 // PrintSpace( cfile, depth );
506 //}
507 //fprintf( cfile, "<%s", Name() );
508 streamer->OpenElement( Name(), IsTextParent() );
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800509
510 for( XMLAttribute* attrib=rootAttribute; attrib; attrib=attrib->next ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800511 //fprintf( cfile, " " );
512 attrib->Print( streamer );
513
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800514 }
515
Lee Thomason5cae8972012-01-24 18:03:07 -0800516 for( XMLNode* node=firstChild; node; node=node->next ) {
517 node->Print( streamer );
518 }
519 streamer->CloseElement();
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800520}
521
522
Lee Thomason3f57d272012-01-11 15:30:03 -0800523// --------- XMLDocument ----------- //
Lee Thomason67d61312012-01-24 16:01:51 -0800524XMLDocument::XMLDocument() :
Lee Thomason18d68bd2012-01-26 18:17:26 -0800525 XMLNode( 0 ),
U-Lama\Lee560bd472011-12-28 19:42:49 -0800526 charBuffer( 0 )
527{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800528 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -0800529}
U-Lama\Leee13c3e62011-12-28 14:36:55 -0800530
531
Lee Thomason3f57d272012-01-11 15:30:03 -0800532XMLDocument::~XMLDocument()
533{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800534 delete [] charBuffer;
Lee Thomason3f57d272012-01-11 15:30:03 -0800535}
536
537
Lee Thomason18d68bd2012-01-26 18:17:26 -0800538void XMLDocument::InitDocument()
539{
540 errorID = NO_ERROR;
541 errorStr1 = 0;
542 errorStr2 = 0;
543
544 delete [] charBuffer;
545 charBuffer = 0;
546
547}
548
Lee Thomason3f57d272012-01-11 15:30:03 -0800549
550bool XMLDocument::Parse( const char* p )
551{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800552 ClearChildren();
553 InitDocument();
554
555 if ( !p || !*p ) {
556 return true; // correctly parse an empty string?
557 }
558 size_t len = strlen( p );
559 charBuffer = new char[ len+1 ];
560 memcpy( charBuffer, p, len+1 );
Lee Thomason3f57d272012-01-11 15:30:03 -0800561 XMLNode* node = 0;
Lee Thomason85403d82012-01-11 15:55:05 -0800562
Lee Thomason18d68bd2012-01-26 18:17:26 -0800563 char* q = ParseDeep( charBuffer );
Lee Thomason67d61312012-01-24 16:01:51 -0800564 return true;
Lee Thomason3f57d272012-01-11 15:30:03 -0800565}
566
567
Lee Thomason5cae8972012-01-24 18:03:07 -0800568void XMLDocument::Print( XMLStreamer* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -0800569{
Lee Thomason5cae8972012-01-24 18:03:07 -0800570 XMLStreamer stdStreamer( stdout );
571 if ( !streamer )
572 streamer = &stdStreamer;
Lee Thomason67d61312012-01-24 16:01:51 -0800573 for( XMLNode* node = firstChild; node; node=node->next ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800574 node->Print( streamer );
Lee Thomasonce0763e2012-01-11 15:43:54 -0800575 }
Lee Thomason3f57d272012-01-11 15:30:03 -0800576}
577
578
Lee Thomason67d61312012-01-24 16:01:51 -0800579void XMLDocument::SetError( int error, const char* str1, const char* str2 )
580{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800581 errorID = error;
582 printf( "ERROR: id=%d '%s' '%s'\n", error, str1, str2 ); // fixme: remove
583 errorStr1 = str1;
584 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -0800585}
586
Lee Thomason5cae8972012-01-24 18:03:07 -0800587
588StringStack::StringStack()
589{
Lee Thomason24767b02012-01-25 17:16:23 -0800590 *pool = 0;
591 mem = pool;
Lee Thomason5cae8972012-01-24 18:03:07 -0800592 inUse = 1; // always has a null
593 allocated = INIT;
594 nPositive = 0;
595}
596
597
Lee Thomason24767b02012-01-25 17:16:23 -0800598StringStack::~StringStack()
599{
600 if ( mem != pool ) {
601 delete [] mem;
602 }
603}
604
605
Lee Thomason5cae8972012-01-24 18:03:07 -0800606void StringStack::Push( const char* str ) {
607 int needed = strlen( str ) + 1;
608 if ( needed > 1 )
609 nPositive++;
610 if ( inUse+needed > allocated ) {
611 // fixme: power of 2
612 // less stupid allocation
613 int more = inUse+needed + 1000;
614
615 char* newMem = new char[more];
616 memcpy( newMem, mem, inUse );
Lee Thomason24767b02012-01-25 17:16:23 -0800617 if ( mem != pool ) {
618 delete [] mem;
619 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800620 mem = newMem;
621 }
622 strcpy( mem+inUse, str );
623 inUse += needed;
624}
625
626
627const char* StringStack::Pop() {
628 TIXMLASSERT( inUse > 1 );
629 const char* p = mem+inUse-2;
630 if ( *p ) {
631 nPositive--;
632 }
633 while( *p ) { // stack starts with a null, don't need to check for 'mem'
634 TIXMLASSERT( p > mem );
635 --p;
636 }
637 inUse = p-mem+1;
638 return p+1;
639}
640
641
642XMLStreamer::XMLStreamer( FILE* file ) : fp( file ), depth( 0 ), elementJustOpened( false )
643{
Lee Thomason857b8682012-01-25 17:50:25 -0800644 for( int i=0; i<ENTITY_RANGE; ++i ) {
645 entityFlag[i] = false;
646 }
647 for( int i=0; i<NUM_ENTITIES; ++i ) {
648 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
649 if ( entities[i].value < ENTITY_RANGE ) {
650 entityFlag[ entities[i].value ] = true;
651 }
652 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800653}
654
655
656void XMLStreamer::PrintSpace( int depth )
657{
658 for( int i=0; i<depth; ++i ) {
659 fprintf( fp, " " );
660 }
661}
662
663
Lee Thomason951d8832012-01-26 08:47:06 -0800664void XMLStreamer::PrintString( const char* p )
Lee Thomason857b8682012-01-25 17:50:25 -0800665{
Lee Thomason951d8832012-01-26 08:47:06 -0800666 // Look for runs of bytes between entities to print.
667 const char* q = p;
Lee Thomason857b8682012-01-25 17:50:25 -0800668
Lee Thomason951d8832012-01-26 08:47:06 -0800669 while ( *q ) {
670 if ( *q < ENTITY_RANGE ) {
671 // Check for entities. If one is found, flush
672 // the stream up until the entity, write the
673 // entity, and keep looking.
674 if ( entityFlag[*q] ) {
675 while ( p < q ) {
676 fputc( *p, fp );
677 ++p;
678 }
679 for( int i=0; i<NUM_ENTITIES; ++i ) {
680 if ( entities[i].value == *q ) {
681 fprintf( fp, "&%s;", entities[i].pattern );
682 break;
683 }
684 }
685 ++p;
686 }
687 }
688 ++q;
689 }
690 // Flush the remaining string. This will be the entire
691 // string if an entity wasn't found.
692 if ( q-p > 0 ) {
693 fprintf( fp, "%s", p );
694 }
Lee Thomason857b8682012-01-25 17:50:25 -0800695}
696
Lee Thomason5cae8972012-01-24 18:03:07 -0800697void XMLStreamer::OpenElement( const char* name, bool textParent )
698{
699 if ( elementJustOpened ) {
700 SealElement();
701 }
Lee Thomason24767b02012-01-25 17:16:23 -0800702 if ( text.NumPositive() == 0 ) {
703 PrintSpace( depth );
704 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800705 stack.Push( name );
706 text.Push( textParent ? "T" : "" );
707
Lee Thomason951d8832012-01-26 08:47:06 -0800708 // fixme: can names have entities?
Lee Thomason5cae8972012-01-24 18:03:07 -0800709 fprintf( fp, "<%s", name );
710 elementJustOpened = true;
711 ++depth;
712}
713
714
715void XMLStreamer::PushAttribute( const char* name, const char* value )
716{
717 TIXMLASSERT( elementJustOpened );
Lee Thomason18d68bd2012-01-26 18:17:26 -0800718 fprintf( fp, " %s=\"", name );
719 PrintString( value );
720 fprintf( fp, "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -0800721}
722
723
724void XMLStreamer::CloseElement()
725{
726 --depth;
727 const char* name = stack.Pop();
Lee Thomason24767b02012-01-25 17:16:23 -0800728 int wasPositive = text.NumPositive();
Lee Thomason5cae8972012-01-24 18:03:07 -0800729 text.Pop();
730
731 if ( elementJustOpened ) {
732 fprintf( fp, "/>" );
733 if ( text.NumPositive() == 0 ) {
734 fprintf( fp, "\n" );
735 }
736 }
737 else {
Lee Thomason24767b02012-01-25 17:16:23 -0800738 if ( wasPositive == 0 ) {
739 PrintSpace( depth );
740 }
Lee Thomason951d8832012-01-26 08:47:06 -0800741 // fixme can names have entities?
Lee Thomason5cae8972012-01-24 18:03:07 -0800742 fprintf( fp, "</%s>", name );
743 if ( text.NumPositive() == 0 ) {
744 fprintf( fp, "\n" );
745 }
746 }
747 elementJustOpened = false;
748}
749
750
751void XMLStreamer::SealElement()
752{
753 elementJustOpened = false;
754 fprintf( fp, ">" );
755 if ( text.NumPositive() == 0 ) {
756 fprintf( fp, "\n" );
757 }
758}
759
760
761void XMLStreamer::PushText( const char* text )
762{
763 if ( elementJustOpened ) {
764 SealElement();
765 }
Lee Thomason951d8832012-01-26 08:47:06 -0800766 PrintString( text );
Lee Thomason5cae8972012-01-24 18:03:07 -0800767}
768
769
770void XMLStreamer::PushComment( const char* comment )
771{
772 if ( elementJustOpened ) {
773 SealElement();
774 }
775 PrintSpace( depth );
776 fprintf( fp, "<!--%s-->\n", comment );
777}