blob: b50a2c3e41eb0ff1c88b2192348eceaddc0c617b [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
98
Lee Thomason8a5dfee2012-01-18 17:43:40 -080099// --------- XMLBase ----------- //
Lee Thomason18d68bd2012-01-26 18:17:26 -0800100// fixme: should take in the entity/newline flags as param
101char* XMLBase::ParseText( char* p, StrPair* pair, const char* endTag, int strFlags )
Lee Thomason3f57d272012-01-11 15:30:03 -0800102{
103 TIXMLASSERT( endTag && *endTag );
104
Lee Thomasonfde6a752012-01-14 18:08:12 -0800105 char* start = p;
Lee Thomasonfde6a752012-01-14 18:08:12 -0800106 char endChar = *endTag;
107 int length = strlen( endTag );
Lee Thomason3f57d272012-01-11 15:30:03 -0800108
Lee Thomasonfde6a752012-01-14 18:08:12 -0800109 // Inner loop of text parsing.
Lee Thomason3f57d272012-01-11 15:30:03 -0800110 while ( *p ) {
Lee Thomasonfde6a752012-01-14 18:08:12 -0800111 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
Lee Thomason18d68bd2012-01-26 18:17:26 -0800112 pair->Set( start, p, strFlags );
Lee Thomasonec975ce2012-01-23 11:42:06 -0800113 return p + length;
Lee Thomason3f57d272012-01-11 15:30:03 -0800114 }
Lee Thomasonec975ce2012-01-23 11:42:06 -0800115 ++p;
Lee Thomason3f57d272012-01-11 15:30:03 -0800116 }
Lee Thomasone4422302012-01-20 17:59:50 -0800117 return p;
Lee Thomason3f57d272012-01-11 15:30:03 -0800118}
119
120
Lee Thomasond34f52c2012-01-20 12:55:24 -0800121char* XMLBase::ParseName( char* p, StrPair* pair )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800122{
123 char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800124
125 start = p;
126 if ( !start || !(*start) ) {
127 return 0;
128 }
129
130 if ( !IsAlpha( *p ) ) {
131 return 0;
132 }
133
134 while( *p && (
135 IsAlphaNum( (unsigned char) *p )
136 || *p == '_'
137 || *p == '-'
138 || *p == '.'
139 || *p == ':' ))
140 {
141 ++p;
142 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800143
144 if ( p > start ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800145 pair->Set( start, p, 0 );
146 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800147 }
Lee Thomason39ede242012-01-20 11:27:56 -0800148 return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800149}
150
151
152char* XMLBase::Identify( XMLDocument* document, char* p, XMLNode** node )
153{
154 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800155 char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800156 p = XMLNode::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800157 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800158 {
159 return 0;
160 }
161
162 // What is this thing?
163 // - Elements start with a letter or underscore, but xml is reserved.
164 // - Comments: <!--
165 // - Decleration: <?xml
166 // - Everthing else is unknown to tinyxml.
167 //
168
169 static const char* xmlHeader = { "<?xml" };
170 static const char* commentHeader = { "<!--" };
171 static const char* dtdHeader = { "<!" };
172 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800173 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800174
175 static const int xmlHeaderLen = 5;
176 static const int commentHeaderLen = 4;
177 static const int dtdHeaderLen = 2;
178 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800179 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800180
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800181 if ( StringEqual( p, commentHeader, commentHeaderLen ) ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800182 returnNode = new XMLComment( document );
183 p += commentHeaderLen;
184 }
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800185 else if ( StringEqual( p, elementHeader, elementHeaderLen ) ) {
186 returnNode = new XMLElement( document );
187 p += elementHeaderLen;
188 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800189 // fixme: better text detection
190 else if ( (*p != '<') && IsAlphaNum( *p ) ) {
191 // fixme: this is filtering out empty text...should it?
192 returnNode = new XMLText( document );
193 p = start; // Back it up, all the text counts.
194 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800195 else {
196 TIXMLASSERT( 0 );
197 }
198
199 *node = returnNode;
200 return p;
201}
202
203
204// --------- XMLNode ----------- //
205
206XMLNode::XMLNode( XMLDocument* doc ) :
207 document( doc ),
208 parent( 0 ),
Lee Thomason67d61312012-01-24 16:01:51 -0800209 isTextParent( false ),
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800210 firstChild( 0 ), lastChild( 0 ),
211 prev( 0 ), next( 0 )
212{
213
214}
215
216
217XMLNode::~XMLNode()
218{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800219 ClearChildren();
Lee Thomason7c913cd2012-01-26 18:32:34 -0800220 if ( parent ) {
221 parent->Unlink( this );
222 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800223}
224
225
226void XMLNode::ClearChildren()
227{
Lee Thomasond923c672012-01-23 08:44:25 -0800228 while( firstChild ) {
229 XMLNode* node = firstChild;
230 Unlink( node );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800231 delete node;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800232 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800233 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800234}
235
236
237void XMLNode::Unlink( XMLNode* child )
238{
239 TIXMLASSERT( child->parent == this );
240 if ( child == firstChild )
241 firstChild = firstChild->next;
242 if ( child == lastChild )
243 lastChild = lastChild->prev;
244
245 if ( child->prev ) {
246 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800247 }
Lee Thomasond923c672012-01-23 08:44:25 -0800248 if ( child->next ) {
249 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800250 }
Lee Thomasond923c672012-01-23 08:44:25 -0800251 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800252}
253
254
255XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
256{
257 if ( lastChild ) {
258 TIXMLASSERT( firstChild );
259 TIXMLASSERT( lastChild->next == 0 );
260 lastChild->next = addThis;
261 addThis->prev = lastChild;
262 lastChild = addThis;
263
264 addThis->parent = this;
265 addThis->next = 0;
266 }
267 else {
268 TIXMLASSERT( firstChild == 0 );
269 firstChild = lastChild = addThis;
270
271 addThis->parent = this;
272 addThis->prev = 0;
273 addThis->next = 0;
274 }
Lee Thomason67d61312012-01-24 16:01:51 -0800275 if ( addThis->ToText() ) {
276 SetTextParent();
277 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800278 return addThis;
279}
280
281
Lee Thomason5cae8972012-01-24 18:03:07 -0800282void XMLNode::Print( XMLStreamer* streamer )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800283{
284 for( XMLNode* node = firstChild; node; node=node->next ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800285 node->Print( streamer );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800286 }
287}
288
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800289
Lee Thomason67d61312012-01-24 16:01:51 -0800290char* XMLNode::ParseDeep( char* p )
291{
292 while( p && *p ) {
293 XMLNode* node = 0;
294 p = Identify( document, p, &node );
295 if ( p && node ) {
296 p = node->ParseDeep( p );
297 // FIXME: is it the correct closing element?
298 if ( node->IsClosingElement() ) {
299 delete node;
300 return p;
301 }
302 this->InsertEndChild( node );
303 }
304 }
305 return 0;
306}
307
Lee Thomason5492a1c2012-01-23 15:32:10 -0800308// --------- XMLText ---------- //
309char* XMLText::ParseDeep( char* p )
310{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800311 p = ParseText( p, &value, "<", StrPair::TEXT_ELEMENT );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800312 // consumes the end tag.
313 if ( p && *p ) {
314 return p-1;
315 }
316 return 0;
317}
318
319
Lee Thomason5cae8972012-01-24 18:03:07 -0800320void XMLText::Print( XMLStreamer* streamer )
Lee Thomason5492a1c2012-01-23 15:32:10 -0800321{
Lee Thomason67d61312012-01-24 16:01:51 -0800322 const char* v = value.GetStr();
Lee Thomason5cae8972012-01-24 18:03:07 -0800323 streamer->PushText( v );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800324}
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800325
326
Lee Thomason3f57d272012-01-11 15:30:03 -0800327// --------- XMLComment ---------- //
328
Lee Thomasone4422302012-01-20 17:59:50 -0800329XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800330{
331}
332
333
Lee Thomasonce0763e2012-01-11 15:43:54 -0800334XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800335{
Lee Thomasond923c672012-01-23 08:44:25 -0800336 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800337}
338
339
Lee Thomason5cae8972012-01-24 18:03:07 -0800340void XMLComment::Print( XMLStreamer* streamer )
Lee Thomasonce0763e2012-01-11 15:43:54 -0800341{
Lee Thomason5cae8972012-01-24 18:03:07 -0800342// XMLNode::Print( fp, depth );
343// fprintf( fp, "<!--%s-->\n", value.GetStr() );
344 streamer->PushComment( value.GetStr() );
Lee Thomasonce0763e2012-01-11 15:43:54 -0800345}
346
347
348char* XMLComment::ParseDeep( char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800349{
350 // Comment parses as text.
Lee Thomason18d68bd2012-01-26 18:17:26 -0800351 return ParseText( p, &value, "-->", StrPair::COMMENT );
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800352}
353
354
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800355// --------- XMLAttribute ---------- //
356char* XMLAttribute::ParseDeep( char* p )
357{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800358 p = ParseText( p, &name, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800359 if ( !p || !*p ) return 0;
360
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800361 char endTag[2] = { *p, 0 };
362 ++p;
Lee Thomason18d68bd2012-01-26 18:17:26 -0800363 p = ParseText( p, &value, endTag, StrPair::ATTRIBUTE_VALUE );
Lee Thomasone4422302012-01-20 17:59:50 -0800364 if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800365 return p;
366}
367
368
Lee Thomason5cae8972012-01-24 18:03:07 -0800369void XMLAttribute::Print( XMLStreamer* streamer )
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800370{
Lee Thomason22aead12012-01-23 13:29:35 -0800371 // fixme: sort out single vs. double quote
Lee Thomason5cae8972012-01-24 18:03:07 -0800372 //fprintf( cfile, "%s=\"%s\"", name.GetStr(), value.GetStr() );
373 streamer->PushAttribute( name.GetStr(), value.GetStr() );
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800374}
375
376
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800377// --------- XMLElement ---------- //
378XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800379 closing( false ),
380 rootAttribute( 0 ),
381 lastAttribute( 0 )
382{
383}
384
385
386XMLElement::~XMLElement()
387{
Lee Thomasond923c672012-01-23 08:44:25 -0800388 //printf( "~XMLElemen %x\n",this );
389
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800390 XMLAttribute* attribute = rootAttribute;
391 while( attribute ) {
392 XMLAttribute* next = attribute->next;
393 delete attribute;
394 attribute = next;
395 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800396}
397
398
Lee Thomason67d61312012-01-24 16:01:51 -0800399char* XMLElement::ParseAttributes( char* p, bool* closedElement )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800400{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800401 const char* start = p;
Lee Thomason67d61312012-01-24 16:01:51 -0800402 *closedElement = false;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800403
404 // Read the attributes.
405 while( p ) {
406 p = SkipWhiteSpace( p );
407 if ( !p || !(*p) ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800408 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, name.GetStr() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800409 return 0;
410 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800411
412 // attribute.
Lee Thomason22aead12012-01-23 13:29:35 -0800413 if ( IsAlpha( *p ) ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800414 XMLAttribute* attrib = new XMLAttribute( this );
415 p = attrib->ParseDeep( p );
416 if ( !p ) {
417 delete attrib;
Lee Thomasone4422302012-01-20 17:59:50 -0800418 document->SetError( XMLDocument::ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800419 return 0;
420 }
421 if ( rootAttribute ) {
422 TIXMLASSERT( lastAttribute );
423 lastAttribute->next = attrib;
424 lastAttribute = attrib;
425 }
426 else {
427 rootAttribute = lastAttribute = attrib;
428 }
429 }
Lee Thomasone4422302012-01-20 17:59:50 -0800430 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800431 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800432 if ( closing ) {
433 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
434 return 0;
435 }
Lee Thomason67d61312012-01-24 16:01:51 -0800436 *closedElement = true;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800437 return p+2; // done; sealed element.
438 }
Lee Thomasone4422302012-01-20 17:59:50 -0800439 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800440 else if ( *p == '>' ) {
441 ++p;
442 break;
443 }
Lee Thomasone4422302012-01-20 17:59:50 -0800444 else {
445 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
446 return 0;
447 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800448 }
Lee Thomason67d61312012-01-24 16:01:51 -0800449 return p;
450}
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800451
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800452
Lee Thomason67d61312012-01-24 16:01:51 -0800453//
454// <ele></ele>
455// <ele>foo<b>bar</b></ele>
456//
457char* XMLElement::ParseDeep( char* p )
458{
459 // Read the element name.
460 p = SkipWhiteSpace( p );
461 if ( !p ) return 0;
462 const char* start = p;
463
464 // The closing element is the </element> form. It is
465 // parsed just like a regular element then deleted from
466 // the DOM.
467 if ( *p == '/' ) {
468 closing = true;
469 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800470 }
Lee Thomason67d61312012-01-24 16:01:51 -0800471
472 p = ParseName( p, &name );
473 if ( name.Empty() ) return 0;
474
475 bool elementClosed=false;
476 p = ParseAttributes( p, &elementClosed );
477 if ( !p || !*p || elementClosed || closing )
478 return p;
479
480 p = XMLNode::ParseDeep( p );
481 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800482}
483
484
Lee Thomason5cae8972012-01-24 18:03:07 -0800485void XMLElement::Print( XMLStreamer* streamer )
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800486{
Lee Thomason5cae8972012-01-24 18:03:07 -0800487 //if ( !parent || !parent->IsTextParent() ) {
488 // PrintSpace( cfile, depth );
489 //}
490 //fprintf( cfile, "<%s", Name() );
491 streamer->OpenElement( Name(), IsTextParent() );
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800492
493 for( XMLAttribute* attrib=rootAttribute; attrib; attrib=attrib->next ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800494 //fprintf( cfile, " " );
495 attrib->Print( streamer );
496
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800497 }
498
Lee Thomason5cae8972012-01-24 18:03:07 -0800499 for( XMLNode* node=firstChild; node; node=node->next ) {
500 node->Print( streamer );
501 }
502 streamer->CloseElement();
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800503}
504
505
Lee Thomason3f57d272012-01-11 15:30:03 -0800506// --------- XMLDocument ----------- //
Lee Thomason67d61312012-01-24 16:01:51 -0800507XMLDocument::XMLDocument() :
Lee Thomason18d68bd2012-01-26 18:17:26 -0800508 XMLNode( 0 ),
U-Lama\Lee560bd472011-12-28 19:42:49 -0800509 charBuffer( 0 )
510{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800511 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -0800512}
U-Lama\Leee13c3e62011-12-28 14:36:55 -0800513
514
Lee Thomason3f57d272012-01-11 15:30:03 -0800515XMLDocument::~XMLDocument()
516{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800517 delete [] charBuffer;
Lee Thomason3f57d272012-01-11 15:30:03 -0800518}
519
520
Lee Thomason18d68bd2012-01-26 18:17:26 -0800521void XMLDocument::InitDocument()
522{
523 errorID = NO_ERROR;
524 errorStr1 = 0;
525 errorStr2 = 0;
526
527 delete [] charBuffer;
528 charBuffer = 0;
529
530}
531
Lee Thomason3f57d272012-01-11 15:30:03 -0800532
Lee Thomason7c913cd2012-01-26 18:32:34 -0800533int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800534{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800535 ClearChildren();
536 InitDocument();
537
538 if ( !p || !*p ) {
539 return true; // correctly parse an empty string?
540 }
541 size_t len = strlen( p );
542 charBuffer = new char[ len+1 ];
543 memcpy( charBuffer, p, len+1 );
Lee Thomason3f57d272012-01-11 15:30:03 -0800544 XMLNode* node = 0;
Lee Thomason85403d82012-01-11 15:55:05 -0800545
Lee Thomason18d68bd2012-01-26 18:17:26 -0800546 char* q = ParseDeep( charBuffer );
Lee Thomason7c913cd2012-01-26 18:32:34 -0800547 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -0800548}
549
550
Lee Thomason5cae8972012-01-24 18:03:07 -0800551void XMLDocument::Print( XMLStreamer* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -0800552{
Lee Thomason5cae8972012-01-24 18:03:07 -0800553 XMLStreamer stdStreamer( stdout );
554 if ( !streamer )
555 streamer = &stdStreamer;
Lee Thomason67d61312012-01-24 16:01:51 -0800556 for( XMLNode* node = firstChild; node; node=node->next ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800557 node->Print( streamer );
Lee Thomasonce0763e2012-01-11 15:43:54 -0800558 }
Lee Thomason3f57d272012-01-11 15:30:03 -0800559}
560
561
Lee Thomason67d61312012-01-24 16:01:51 -0800562void XMLDocument::SetError( int error, const char* str1, const char* str2 )
563{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800564 errorID = error;
565 printf( "ERROR: id=%d '%s' '%s'\n", error, str1, str2 ); // fixme: remove
566 errorStr1 = str1;
567 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -0800568}
569
Lee Thomason5cae8972012-01-24 18:03:07 -0800570
571StringStack::StringStack()
572{
Lee Thomason24767b02012-01-25 17:16:23 -0800573 *pool = 0;
574 mem = pool;
Lee Thomason5cae8972012-01-24 18:03:07 -0800575 inUse = 1; // always has a null
576 allocated = INIT;
577 nPositive = 0;
578}
579
580
Lee Thomason24767b02012-01-25 17:16:23 -0800581StringStack::~StringStack()
582{
583 if ( mem != pool ) {
584 delete [] mem;
585 }
586}
587
588
Lee Thomason5cae8972012-01-24 18:03:07 -0800589void StringStack::Push( const char* str ) {
590 int needed = strlen( str ) + 1;
591 if ( needed > 1 )
592 nPositive++;
Lee Thomason7c913cd2012-01-26 18:32:34 -0800593 if ( inUse+needed >= allocated ) {
Lee Thomason5cae8972012-01-24 18:03:07 -0800594 // fixme: power of 2
595 // less stupid allocation
596 int more = inUse+needed + 1000;
597
598 char* newMem = new char[more];
599 memcpy( newMem, mem, inUse );
Lee Thomason24767b02012-01-25 17:16:23 -0800600 if ( mem != pool ) {
601 delete [] mem;
602 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800603 mem = newMem;
604 }
605 strcpy( mem+inUse, str );
606 inUse += needed;
607}
608
609
610const char* StringStack::Pop() {
611 TIXMLASSERT( inUse > 1 );
612 const char* p = mem+inUse-2;
613 if ( *p ) {
614 nPositive--;
615 }
616 while( *p ) { // stack starts with a null, don't need to check for 'mem'
617 TIXMLASSERT( p > mem );
618 --p;
619 }
620 inUse = p-mem+1;
621 return p+1;
622}
623
624
Lee Thomason7c913cd2012-01-26 18:32:34 -0800625StringPtrStack::StringPtrStack()
626{
627 *pool = 0;
628 mem = pool;
629 inUse = 0;
630 allocated = INIT;
631 nPositive = 0;
632}
633
634
635StringPtrStack::~StringPtrStack()
636{
637 if ( mem != pool ) {
638 delete [] mem;
639 }
640}
641
642
643void StringPtrStack::Push( const char* str ) {
644 int needed = inUse + 1;
645 if ( str )
646 nPositive++;
647 if ( inUse+needed >= allocated ) {
648 // fixme: power of 2
649 // less stupid allocation
650 int more = inUse+needed + 1000;
651
652 char** newMem = new char*[more];
653 memcpy( newMem, mem, inUse*sizeof(char*) );
654 if ( mem != pool ) {
655 delete [] mem;
656 }
657 mem = newMem;
658 }
659 mem[inUse] = (char*)str;
660 inUse++;
661}
662
663
664const char* StringPtrStack::Pop() {
665 TIXMLASSERT( inUse > 0 );
666 inUse--;
667 const char* result = mem[inUse];
668 if ( result )
669 nPositive--;
670 return result;
671}
672
673
Lee Thomason5cae8972012-01-24 18:03:07 -0800674XMLStreamer::XMLStreamer( FILE* file ) : fp( file ), depth( 0 ), elementJustOpened( false )
675{
Lee Thomason857b8682012-01-25 17:50:25 -0800676 for( int i=0; i<ENTITY_RANGE; ++i ) {
677 entityFlag[i] = false;
678 }
679 for( int i=0; i<NUM_ENTITIES; ++i ) {
680 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
681 if ( entities[i].value < ENTITY_RANGE ) {
682 entityFlag[ entities[i].value ] = true;
683 }
684 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800685}
686
687
688void XMLStreamer::PrintSpace( int depth )
689{
690 for( int i=0; i<depth; ++i ) {
691 fprintf( fp, " " );
692 }
693}
694
695
Lee Thomason951d8832012-01-26 08:47:06 -0800696void XMLStreamer::PrintString( const char* p )
Lee Thomason857b8682012-01-25 17:50:25 -0800697{
Lee Thomason951d8832012-01-26 08:47:06 -0800698 // Look for runs of bytes between entities to print.
699 const char* q = p;
Lee Thomason857b8682012-01-25 17:50:25 -0800700
Lee Thomason951d8832012-01-26 08:47:06 -0800701 while ( *q ) {
702 if ( *q < ENTITY_RANGE ) {
703 // Check for entities. If one is found, flush
704 // the stream up until the entity, write the
705 // entity, and keep looking.
706 if ( entityFlag[*q] ) {
707 while ( p < q ) {
708 fputc( *p, fp );
709 ++p;
710 }
711 for( int i=0; i<NUM_ENTITIES; ++i ) {
712 if ( entities[i].value == *q ) {
713 fprintf( fp, "&%s;", entities[i].pattern );
714 break;
715 }
716 }
717 ++p;
718 }
719 }
720 ++q;
721 }
722 // Flush the remaining string. This will be the entire
723 // string if an entity wasn't found.
724 if ( q-p > 0 ) {
725 fprintf( fp, "%s", p );
726 }
Lee Thomason857b8682012-01-25 17:50:25 -0800727}
728
Lee Thomason5cae8972012-01-24 18:03:07 -0800729void XMLStreamer::OpenElement( const char* name, bool textParent )
730{
731 if ( elementJustOpened ) {
732 SealElement();
733 }
Lee Thomason24767b02012-01-25 17:16:23 -0800734 if ( text.NumPositive() == 0 ) {
735 PrintSpace( depth );
736 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800737 stack.Push( name );
738 text.Push( textParent ? "T" : "" );
739
Lee Thomason951d8832012-01-26 08:47:06 -0800740 // fixme: can names have entities?
Lee Thomason5cae8972012-01-24 18:03:07 -0800741 fprintf( fp, "<%s", name );
742 elementJustOpened = true;
743 ++depth;
744}
745
746
747void XMLStreamer::PushAttribute( const char* name, const char* value )
748{
749 TIXMLASSERT( elementJustOpened );
Lee Thomason18d68bd2012-01-26 18:17:26 -0800750 fprintf( fp, " %s=\"", name );
751 PrintString( value );
752 fprintf( fp, "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -0800753}
754
755
756void XMLStreamer::CloseElement()
757{
758 --depth;
759 const char* name = stack.Pop();
Lee Thomason24767b02012-01-25 17:16:23 -0800760 int wasPositive = text.NumPositive();
Lee Thomason5cae8972012-01-24 18:03:07 -0800761 text.Pop();
762
763 if ( elementJustOpened ) {
764 fprintf( fp, "/>" );
765 if ( text.NumPositive() == 0 ) {
766 fprintf( fp, "\n" );
767 }
768 }
769 else {
Lee Thomason24767b02012-01-25 17:16:23 -0800770 if ( wasPositive == 0 ) {
771 PrintSpace( depth );
772 }
Lee Thomason951d8832012-01-26 08:47:06 -0800773 // fixme can names have entities?
Lee Thomason5cae8972012-01-24 18:03:07 -0800774 fprintf( fp, "</%s>", name );
775 if ( text.NumPositive() == 0 ) {
776 fprintf( fp, "\n" );
777 }
778 }
779 elementJustOpened = false;
780}
781
782
783void XMLStreamer::SealElement()
784{
785 elementJustOpened = false;
786 fprintf( fp, ">" );
787 if ( text.NumPositive() == 0 ) {
788 fprintf( fp, "\n" );
789 }
790}
791
792
793void XMLStreamer::PushText( const char* text )
794{
795 if ( elementJustOpened ) {
796 SealElement();
797 }
Lee Thomason951d8832012-01-26 08:47:06 -0800798 PrintString( text );
Lee Thomason5cae8972012-01-24 18:03:07 -0800799}
800
801
802void XMLStreamer::PushComment( const char* comment )
803{
804 if ( elementJustOpened ) {
805 SealElement();
806 }
807 PrintSpace( depth );
808 fprintf( fp, "<!--%s-->\n", comment );
809}