blob: a11be4bcb21e1750a3514e4145d1f1859e1a8f40 [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 Thomasonfde6a752012-01-14 18:08:12 -080010static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
11static const char LF = LINE_FEED;
12static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
13static const char CR = CARRIAGE_RETURN;
14
15
Lee Thomason3f57d272012-01-11 15:30:03 -080016// --------- CharBuffer ----------- //
U-Lama\Lee560bd472011-12-28 19:42:49 -080017/*static*/ CharBuffer* CharBuffer::Construct( const char* in )
18{
19 size_t len = strlen( in );
20 size_t size = len + sizeof( CharBuffer );
21 CharBuffer* cb = (CharBuffer*) malloc( size );
22 cb->length = len;
23 strcpy( cb->mem, in );
24 return cb;
25}
26
27
28/*static*/ void CharBuffer::Free( CharBuffer* cb )
29{
30 free( cb );
31}
32
33
Lee Thomason8a5dfee2012-01-18 17:43:40 -080034// --------- XMLBase ----------- //
35const char* XMLBase::ParseText( char* p, const char* endTag, char** next )
Lee Thomason3f57d272012-01-11 15:30:03 -080036{
37 TIXMLASSERT( endTag && *endTag );
38
Lee Thomasonfde6a752012-01-14 18:08:12 -080039 char* start = p;
40 char* q = p; // q (target) <= p (src) in same buffer.
41 char endChar = *endTag;
42 int length = strlen( endTag );
43 char* nextTag = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -080044 *next = 0;
Lee Thomason3f57d272012-01-11 15:30:03 -080045
Lee Thomasonfde6a752012-01-14 18:08:12 -080046 // Inner loop of text parsing.
Lee Thomason3f57d272012-01-11 15:30:03 -080047 while ( *p ) {
Lee Thomasonfde6a752012-01-14 18:08:12 -080048 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
49 *q = 0;
50 nextTag = p + length;
51 break;
Lee Thomason3f57d272012-01-11 15:30:03 -080052 }
Lee Thomasonfde6a752012-01-14 18:08:12 -080053 else if ( *p == CR ) {
54 // CR-LF pair becomes LF
55 // CR alone becomes LF
56 // LF-CR becomes LF
57 if ( *(p+1) == LF ) {
58 p += 2;
59 }
60 else {
61 ++p;
62 }
63 *q = LF;
64 }
65 else if ( *p == LF ) {
66 if ( *(p+1) == CR ) {
67 p += 2;
68 }
69 else {
70 ++p;
71 }
72 *q = LF;
73 }
74 else {
75 *q = *p;
76 ++p;
77 }
78 ++q;
Lee Thomason3f57d272012-01-11 15:30:03 -080079 }
Lee Thomasonfde6a752012-01-14 18:08:12 -080080
81 // Error? If we don't have a text tag, something went wrong. (Although
82 // what the nextTag points at may be null.)
83 if ( nextTag == 0 ) {
84 return 0;
85 }
86 *next = nextTag;
87 return start;
Lee Thomason3f57d272012-01-11 15:30:03 -080088}
89
90
Lee Thomason8a5dfee2012-01-18 17:43:40 -080091const char* XMLBase::ParseName( char* p, char** next )
92{
93 char* start = p;
94 char* nextTag = 0;
95 *next = 0;
96
97 start = p;
98 if ( !start || !(*start) ) {
99 return 0;
100 }
101
102 if ( !IsAlpha( *p ) ) {
103 return 0;
104 }
105
106 while( *p && (
107 IsAlphaNum( (unsigned char) *p )
108 || *p == '_'
109 || *p == '-'
110 || *p == '.'
111 || *p == ':' ))
112 {
113 ++p;
114 }
115 *p = 0;
116
117 if ( p > start ) {
118 *next = p;
119 return start;
120 }
121 return p+1;
122}
123
124
125char* XMLBase::Identify( XMLDocument* document, char* p, XMLNode** node )
126{
127 XMLNode* returnNode = 0;
128
129 p = XMLNode::SkipWhiteSpace( p );
130 if( !p || !*p || *p != '<' )
131 {
132 return 0;
133 }
134
135 // What is this thing?
136 // - Elements start with a letter or underscore, but xml is reserved.
137 // - Comments: <!--
138 // - Decleration: <?xml
139 // - Everthing else is unknown to tinyxml.
140 //
141
142 static const char* xmlHeader = { "<?xml" };
143 static const char* commentHeader = { "<!--" };
144 static const char* dtdHeader = { "<!" };
145 static const char* cdataHeader = { "<![CDATA[" };
146
147 static const int xmlHeaderLen = 5;
148 static const int commentHeaderLen = 4;
149 static const int dtdHeaderLen = 2;
150 static const int cdataHeaderLen = 9;
151
152 if ( XMLNode::StringEqual( p, commentHeader, commentHeaderLen ) ) {
153 returnNode = new XMLComment( document );
154 p += commentHeaderLen;
155 }
156 else {
157 TIXMLASSERT( 0 );
158 }
159
160 *node = returnNode;
161 return p;
162}
163
164
165// --------- XMLNode ----------- //
166
167XMLNode::XMLNode( XMLDocument* doc ) :
168 document( doc ),
169 parent( 0 ),
170 firstChild( 0 ), lastChild( 0 ),
171 prev( 0 ), next( 0 )
172{
173
174}
175
176
177XMLNode::~XMLNode()
178{
179 XMLNode* node=firstChild;
180 while( node ) {
181 XMLNode* temp = node->next;
182 delete node;
183 node = temp;
184 }
185 if ( prev ) {
186 prev->next = next;
187 }
188 if ( next ) {
189 next->prev = prev;
190 }
191}
192
193
194XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
195{
196 if ( lastChild ) {
197 TIXMLASSERT( firstChild );
198 TIXMLASSERT( lastChild->next == 0 );
199 lastChild->next = addThis;
200 addThis->prev = lastChild;
201 lastChild = addThis;
202
203 addThis->parent = this;
204 addThis->next = 0;
205 }
206 else {
207 TIXMLASSERT( firstChild == 0 );
208 firstChild = lastChild = addThis;
209
210 addThis->parent = this;
211 addThis->prev = 0;
212 addThis->next = 0;
213 }
214 return addThis;
215}
216
217
218void XMLNode::Print( FILE* fp, int depth )
219{
220 for( XMLNode* node = firstChild; node; node=node->next ) {
221 node->Print( fp, depth );
222 }
223}
224
225void XMLNode::PrintSpace( FILE* fp, int depth )
226{
227 for( int i=0; i<depth; ++i ) {
228 fprintf( fp, " " );
229 }
230}
231
232
233
234
Lee Thomason3f57d272012-01-11 15:30:03 -0800235// --------- XMLComment ---------- //
236
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800237XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ), value( 0 )
Lee Thomason3f57d272012-01-11 15:30:03 -0800238{
239}
240
241
Lee Thomasonce0763e2012-01-11 15:43:54 -0800242XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800243{
Lee Thomason3f57d272012-01-11 15:30:03 -0800244}
245
246
Lee Thomasonce0763e2012-01-11 15:43:54 -0800247void XMLComment::Print( FILE* fp, int depth )
248{
249 XMLNode::Print( fp, depth );
Lee Thomasonfde6a752012-01-14 18:08:12 -0800250 fprintf( fp, "<!--%s-->\n", value );
Lee Thomasonce0763e2012-01-11 15:43:54 -0800251}
252
253
254char* XMLComment::ParseDeep( char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800255{
256 // Comment parses as text.
257 value = ParseText( p, "-->", &p );
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800258 return p;
259}
260
261
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800262// --------- XMLAttribute ---------- //
263char* XMLAttribute::ParseDeep( char* p )
264{
265 char endTag[2] = { *p, 0 };
266 ++p;
267 value = ParseText( p, endTag, &p );
268 if ( !value ) return 0;
269 return p;
270}
271
272
273// --------- XMLElement ---------- //
274XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
275 name( 0 ),
276 closing( false ),
277 rootAttribute( 0 ),
278 lastAttribute( 0 )
279{
280}
281
282
283XMLElement::~XMLElement()
284{
285 XMLAttribute* attribute = rootAttribute;
286 while( attribute ) {
287 XMLAttribute* next = attribute->next;
288 delete attribute;
289 attribute = next;
290 }
291
292 XMLNode* child = firstChild;
293 while( child ) {
294 XMLNode* next = child->next;
295 delete child;
296 child = next;
297 }
298}
299
300
301char* XMLElement::ParseDeep( char* p )
302{
303 // Read the element name.
304 p = SkipWhiteSpace( p );
305 if ( !p ) return 0;
306 const char* start = p;
307
308 // The closing element is the </element> form. It is
309 // parsed just like a regular element then deleted from
310 // the DOM.
311 if ( *p == '/' ) {
312 closing = true;
313 ++p;
314 }
315
316 name = ParseName( p, &p );
317 if ( !name ) return 0;
318
319 // Read the attributes.
320 while( p ) {
321 p = SkipWhiteSpace( p );
322 if ( !p || !(*p) ) {
323 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, name );
324 return 0;
325 }
326 const char* saveP = p;
327
328 // attribute.
329 if ( *p == '\'' || *p == '\"' ) {
330 XMLAttribute* attrib = new XMLAttribute( this );
331 p = attrib->ParseDeep( p );
332 if ( !p ) {
333 delete attrib;
334 document->SetError( XMLDocument::ERROR_PARSING_ATTRIBUTE, start, saveP );
335 return 0;
336 }
337 if ( rootAttribute ) {
338 TIXMLASSERT( lastAttribute );
339 lastAttribute->next = attrib;
340 lastAttribute = attrib;
341 }
342 else {
343 rootAttribute = lastAttribute = attrib;
344 }
345 }
346 else if ( *p == '/' && *(p+1) == '>' ) {
347 // end tag.
348 if ( closing ) {
349 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
350 return 0;
351 }
352 return p+2; // done; sealed element.
353 }
354 else if ( *p == '>' ) {
355 ++p;
356 break;
357 }
358 }
359
360 while( p && *p ) {
361 XMLNode* node = 0;
362 p = Identify( document, p, &node );
363 if ( p && node ) {
364 node->ParseDeep( p );
365
366 XMLElement* element = node->ToElement();
367 if ( element && element->Closing() ) {
368 if ( StringEqual( element->Name(), this->Name() ) ) {
369 // All good, this is closing tag.
370 delete node;
371 p = 0;
372 }
373 else {
374 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
375 delete node;
376 }
377 return p;
378 }
379 else {
380 this->InsertEndChild( node );
381 }
382 }
383 }
384 return 0;
385}
386
387
Lee Thomason3f57d272012-01-11 15:30:03 -0800388// --------- XMLDocument ----------- //
U-Lama\Lee560bd472011-12-28 19:42:49 -0800389XMLDocument::XMLDocument() :
390 charBuffer( 0 )
391{
Lee Thomason85403d82012-01-11 15:55:05 -0800392 root = new XMLNode( this );
U-Lama\Lee560bd472011-12-28 19:42:49 -0800393}
U-Lama\Leee13c3e62011-12-28 14:36:55 -0800394
395
Lee Thomason3f57d272012-01-11 15:30:03 -0800396XMLDocument::~XMLDocument()
397{
398 delete root;
399 delete charBuffer;
400}
401
402
403
404bool XMLDocument::Parse( const char* p )
405{
Lee Thomasonce0763e2012-01-11 15:43:54 -0800406 charBuffer = CharBuffer::Construct( p );
Lee Thomason3f57d272012-01-11 15:30:03 -0800407 XMLNode* node = 0;
Lee Thomason85403d82012-01-11 15:55:05 -0800408
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800409 char* q = Identify( this, charBuffer->mem, &node );
Lee Thomason85403d82012-01-11 15:55:05 -0800410 root->InsertEndChild( node );
Lee Thomasonce0763e2012-01-11 15:43:54 -0800411 node->ParseDeep( q );
Lee Thomason85403d82012-01-11 15:55:05 -0800412
Lee Thomasonce0763e2012-01-11 15:43:54 -0800413 return true;
Lee Thomason3f57d272012-01-11 15:30:03 -0800414}
415
416
Lee Thomasonce0763e2012-01-11 15:43:54 -0800417void XMLDocument::Print( FILE* fp, int depth )
Lee Thomason3f57d272012-01-11 15:30:03 -0800418{
Lee Thomasonce0763e2012-01-11 15:43:54 -0800419 for( XMLNode* node = root->firstChild; node; node=node->next ) {
420 node->Print( fp, depth );
421 }
Lee Thomason3f57d272012-01-11 15:30:03 -0800422}
423
424