fsTinyXml.cpp
Engine/source/persistence/taml/fsTinyXml.cpp
Public Functions
TiXmlNode *
TiNodeIdentify(TiXmlNode * parent, const char * p, TiXmlEncoding encoding)
Detailed Description
Public Functions
TiNodeIdentify(TiXmlNode * parent, const char * p, TiXmlEncoding encoding)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2013 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "fsTinyXml.h" 25#include "console/console.h" 26 27bool fsTiXmlDocument::LoadFile( const char * pFilename, TiXmlEncoding encoding ) 28{ 29 // Expand the file-path. 30 char filenameBuffer[1024]; 31 Con::expandToolScriptFilename( filenameBuffer, sizeof(filenameBuffer), pFilename ); 32 33 FileStream stream; 34 35#ifdef TORQUE_OS_ANDROID 36 if (strlen(pFilename) > strlen(filenameBuffer)) { 37 dStrcpy(filenameBuffer, pFilename, 1024); 38 } 39#endif 40 41 // File open for read? 42 if ( !stream.open( filenameBuffer, Torque::FS::File::Read ) ) 43 { 44 // No, so warn. 45 Con::warnf("TamlXmlParser::parse() - Could not open filename '%s' for parse.", filenameBuffer ); 46 return false; 47 } 48 49 // Load document from stream. 50 if ( !LoadFile( stream ) ) 51 { 52 // Warn! 53 Con::warnf("TamlXmlParser: Could not load Taml XML file from stream."); 54 return false; 55 } 56 57 // Close the stream. 58 stream.close(); 59 return true; 60} 61 62bool fsTiXmlDocument::SaveFile( const char * pFilename ) const 63{ 64 // Expand the file-name into the file-path buffer. 65 char filenameBuffer[1024]; 66 Con::expandToolScriptFilename( filenameBuffer, sizeof(filenameBuffer), pFilename ); 67 68 FileStream stream; 69 70 // File opened? 71 if ( !stream.open( filenameBuffer, Torque::FS::File::Write ) ) 72 { 73 // No, so warn. 74 Con::warnf("Taml::writeFile() - Could not open filename '%s' for write.", filenameBuffer ); 75 return false; 76 } 77 78 bool ret = SaveFile(stream); 79 80 stream.close(); 81 return ret; 82} 83 84bool fsTiXmlDocument::LoadFile( FileStream &stream, TiXmlEncoding encoding ) 85{ 86 // Delete the existing data: 87 Clear(); 88 //TODO: Can't clear location, investigate if this gives issues. 89 //doc.location.Clear(); 90 91 // Get the file size, so we can pre-allocate the string. HUGE speed impact. 92 long length = stream.getStreamSize(); 93 94 // Strange case, but good to handle up front. 95 if ( length <= 0 ) 96 { 97 SetError( TiXmlDocument::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); 98 return false; 99 } 100 101 // Subtle bug here. TinyXml did use fgets. But from the XML spec: 102 // 2.11 End-of-Line Handling 103 // <snip> 104 // <quote> 105 // ...the XML processor MUST behave as if it normalized all line breaks in external 106 // parsed entities (including the document entity) on input, before parsing, by translating 107 // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to 108 // a single #xA character. 109 // </quote> 110 // 111 // It is not clear fgets does that, and certainly isn't clear it works cross platform. 112 // Generally, you expect fgets to translate from the convention of the OS to the c/unix 113 // convention, and not work generally. 114 115 /* 116 while( fgets( buf, sizeof(buf), file ) ) 117 { 118 data += buf; 119 } 120 */ 121 122 char* buf = new char[ length+1 ]; 123 buf[0] = 0; 124 125 if ( !stream.read( length, buf ) ) { 126 delete [] buf; 127 SetError( TiXmlDocument::TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); 128 return false; 129 } 130 131 // Process the buffer in place to normalize new lines. (See comment above.) 132 // Copies from the 'p' to 'q' pointer, where p can advance faster if 133 // a newline-carriage return is hit. 134 // 135 // Wikipedia: 136 // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or 137 // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)... 138 // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others 139 // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS 140 // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9 141 142 const char* p = buf; // the read head 143 char* q = buf; // the write head 144 const char CR = 0x0d; 145 const char LF = 0x0a; 146 147 buf[length] = 0; 148 while( *p ) { 149 assert( p < (buf+length) ); 150 assert( q <= (buf+length) ); 151 assert( q <= p ); 152 153 if ( *p == CR ) { 154 *q++ = LF; 155 p++; 156 if ( *p == LF ) { // check for CR+LF (and skip LF) 157 p++; 158 } 159 } 160 else { 161 *q++ = *p++; 162 } 163 } 164 assert( q <= (buf+length) ); 165 *q = 0; 166 167 Parse( buf, 0, encoding ); 168 169 delete [] buf; 170 return !Error(); 171} 172 173bool fsTiXmlDocument::SaveFile( FileStream &stream ) const 174{ 175 if ( useMicrosoftBOM ) 176 { 177 const unsigned char TIXML_UTF_LEAD_0 = 0xefU; 178 const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; 179 const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; 180 181 stream.write( TIXML_UTF_LEAD_0 ); 182 stream.write( TIXML_UTF_LEAD_1 ); 183 stream.write( TIXML_UTF_LEAD_2 ); 184 } 185 Print( stream, 0 ); 186 return true; 187} 188 189void fsTiXmlDocument::Print( FileStream& stream, int depth ) const 190{ 191 for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) 192 { 193 //AttemptPrintTiNode(const_cast<TiXmlNode*>(node), stream, depth); 194 dynamic_cast<const fsTiXmlNode*>(node)->Print( stream, depth ); 195 stream.writeText( "\n" ); 196 } 197} 198 199void fsTiXmlAttribute::Print( FileStream& stream, int depth, TIXML_STRING* str ) const 200{ 201 TIXML_STRING n, v; 202 203 TiXmlString val = TiXmlString(Value()); 204 205 EncodeString( NameTStr(), &n ); 206 EncodeString( val, &v ); 207 208 for ( int i=0; i< depth; i++ ) { 209 stream.writeText( " " ); 210 } 211 212 if (val.find ('\"') == TIXML_STRING::npos) { 213 const char* pValue = v.c_str(); 214 char buffer[4096]; 215 const S32 length = dSprintf(buffer, sizeof(buffer), "%s=\"%s\"", n.c_str(), pValue); 216 stream.write(length, buffer); 217 if ( str ) { 218 (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; 219 } 220 } 221 else { 222 char buffer[4096]; 223 const S32 length = dSprintf(buffer, sizeof(buffer), "%s='%s'", n.c_str(), v.c_str()); 224 stream.write(length, buffer); 225 if ( str ) { 226 (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; 227 } 228 } 229} 230 231void fsTiXmlDeclaration::Print(FileStream& stream, int depth, TiXmlString* str) const 232{ 233 stream.writeStringBuffer( "<?xml " ); 234 if ( str ) (*str) += "<?xml "; 235 236 if ( !version.empty() ) { 237 stream.writeFormattedBuffer( "version=\"%s\" ", version.c_str ()); 238 if ( str ) { (*str) += "version=\""; (*str) += version; (*str) += "\" "; } 239 } 240 if ( !encoding.empty() ) { 241 stream.writeFormattedBuffer( "encoding=\"%s\" ", encoding.c_str ()); 242 if ( str ) { (*str) += "encoding=\""; (*str) += encoding; (*str) += "\" "; } 243 } 244 if ( !standalone.empty() ) { 245 stream.writeFormattedBuffer( "standalone=\"%s\" ", standalone.c_str ()); 246 if ( str ) { (*str) += "standalone=\""; (*str) += standalone; (*str) += "\" "; } 247 } 248 stream.writeStringBuffer( "?>" ); 249 if ( str ) (*str) += "?>"; 250} 251 252void fsTiXmlElement::Print(FileStream& stream, int depth) const 253{ 254 int i; 255 for ( i=0; i<depth; i++ ) { 256 stream.writeStringBuffer( " " ); 257 } 258 259 stream.writeFormattedBuffer( "<%s", value.c_str() ); 260 261 const TiXmlAttribute* attrib; 262 for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() ) 263 { 264 stream.writeStringBuffer( "\n" ); 265 dynamic_cast<const fsTiXmlAttribute*>(attrib)->Print( stream, depth+1 ); 266 } 267 268 // There are 3 different formatting approaches: 269 // 1) An element without children is printed as a <foo /> node 270 // 2) An element with only a text child is printed as <foo> text </foo> 271 // 3) An element with children is printed on multiple lines. 272 TiXmlNode* node; 273 if ( !firstChild ) 274 { 275 stream.writeStringBuffer( " />" ); 276 } 277 else if ( firstChild == lastChild && firstChild->ToText() ) 278 { 279 stream.writeStringBuffer( ">" ); 280 dynamic_cast<const fsTiXmlNode*>(firstChild)->Print( stream, depth + 1 ); 281 stream.writeFormattedBuffer( "</%s>", value.c_str() ); 282 } 283 else 284 { 285 stream.writeStringBuffer( ">" ); 286 287 for ( node = firstChild; node; node=node->NextSibling() ) 288 { 289 if ( !node->ToText() ) 290 { 291 stream.writeStringBuffer( "\n" ); 292 } 293 dynamic_cast<const fsTiXmlNode*>(node)->Print( stream, depth+1 ); 294 } 295 stream.writeStringBuffer( "\n" ); 296 for( i=0; i<depth; ++i ) { 297 stream.writeStringBuffer( " " ); 298 } 299 stream.writeFormattedBuffer( "</%s>", value.c_str() ); 300 } 301} 302 303void fsTiXmlComment::Print(FileStream& stream, int depth) const 304{ 305 for ( int i=0; i<depth; i++ ) 306 { 307 stream.writeStringBuffer( " " ); 308 } 309 stream.writeFormattedBuffer( "<!--%s-->", value.c_str() ); 310} 311 312void fsTiXmlText::Print(FileStream& stream, int depth) const 313{ 314 if ( cdata ) 315 { 316 int i; 317 stream.writeStringBuffer( "\n" ); 318 for ( i=0; i<depth; i++ ) { 319 stream.writeStringBuffer( " " ); 320 } 321 stream.writeFormattedBuffer( "<![CDATA[%s]]>\n", value.c_str() ); // unformatted output 322 } 323 else 324 { 325 TIXML_STRING buffer; 326 EncodeString( value, &buffer ); 327 stream.writeFormattedBuffer( "%s", buffer.c_str() ); 328 } 329} 330 331void fsTiXmlUnknown::Print(FileStream& stream, int depth) const 332{ 333 for ( int i=0; i<depth; i++ ) 334 stream.writeStringBuffer( " " ); 335 336 stream.writeFormattedBuffer( "<%s>", value.c_str() ); 337} 338 339static TiXmlNode* TiNodeIdentify( TiXmlNode* parent, const char* p, TiXmlEncoding encoding ) 340{ 341 TiXmlNode* returnNode = 0; 342 343 p = TiXmlNode::SkipWhiteSpace( p, encoding ); 344 if( !p || !*p || *p != '<' ) 345 { 346 return 0; 347 } 348 349 p = TiXmlNode::SkipWhiteSpace( p, encoding ); 350 351 if ( !p || !*p ) 352 { 353 return 0; 354 } 355 356 // What is this thing? 357 // - Elements start with a letter or underscore, but xml is reserved. 358 // - Comments: <!-- 359 // - Decleration: <?xml 360 // - Everthing else is unknown to tinyxml. 361 // 362 363 const char* xmlHeader = { "<?xml" }; 364 const char* commentHeader = { "<!--" }; 365 const char* dtdHeader = { "<!" }; 366 const char* cdataHeader = { "<![CDATA[" }; 367 368 if ( TiXmlNode::StringEqual( p, xmlHeader, true, encoding ) ) 369 { 370 #ifdef DEBUG_PARSER 371 TIXML_LOG( "XML parsing Declaration\n" ); 372 #endif 373 returnNode = new fsTiXmlDeclaration(); 374 } 375 else if ( TiXmlNode::StringEqual( p, commentHeader, false, encoding ) ) 376 { 377 #ifdef DEBUG_PARSER 378 TIXML_LOG( "XML parsing Comment\n" ); 379 #endif 380 returnNode = new fsTiXmlComment(); 381 } 382 else if ( TiXmlNode::StringEqual( p, cdataHeader, false, encoding ) ) 383 { 384 #ifdef DEBUG_PARSER 385 TIXML_LOG( "XML parsing CDATA\n" ); 386 #endif 387 TiXmlText* text = new fsTiXmlText( "" ); 388 text->SetCDATA( true ); 389 returnNode = text; 390 } 391 else if ( TiXmlNode::StringEqual( p, dtdHeader, false, encoding ) ) 392 { 393 #ifdef DEBUG_PARSER 394 TIXML_LOG( "XML parsing Unknown(1)\n" ); 395 #endif 396 returnNode = new fsTiXmlUnknown(); 397 } 398 else if ( TiXmlNode::IsAlpha( *(p+1), encoding ) 399 || *(p+1) == '_' ) 400 { 401 #ifdef DEBUG_PARSER 402 TIXML_LOG( "XML parsing Element\n" ); 403 #endif 404 returnNode = new fsTiXmlElement( "" ); 405 } 406 else 407 { 408 #ifdef DEBUG_PARSER 409 TIXML_LOG( "XML parsing Unknown(2)\n" ); 410 #endif 411 returnNode = new fsTiXmlUnknown(); 412 } 413 414 if ( returnNode ) 415 { 416 // Set the parent, so it can report errors 417 returnNode->parent = parent; 418 } 419 return returnNode; 420} 421 422TiXmlNode* fsTiXmlDocument::Identify( const char* p, TiXmlEncoding encoding ) 423{ 424 return TiNodeIdentify(this, p, encoding); 425} 426 427TiXmlNode* fsTiXmlElement::Identify( const char* p, TiXmlEncoding encoding ) 428{ 429 return TiNodeIdentify(this, p, encoding); 430} 431 432const char* fsTiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) 433{ 434 p = SkipWhiteSpace( p, encoding ); 435 TiXmlDocument* document = GetDocument(); 436 437 if ( !p || !*p ) 438 { 439 if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding ); 440 return 0; 441 } 442 443 if ( data ) 444 { 445 data->Stamp( p, encoding ); 446 location = data->Cursor(); 447 } 448 449 if ( *p != '<' ) 450 { 451 if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding ); 452 return 0; 453 } 454 455 p = SkipWhiteSpace( p+1, encoding ); 456 457 // Read the name. 458 const char* pErr = p; 459 460 p = ReadName( p, &value, encoding ); 461 if ( !p || !*p ) 462 { 463 if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding ); 464 return 0; 465 } 466 467 TIXML_STRING endTag ("</"); 468 endTag += value; 469 470 // Check for and read attributes. Also look for an empty 471 // tag or an end tag. 472 while ( p && *p ) 473 { 474 pErr = p; 475 p = SkipWhiteSpace( p, encoding ); 476 if ( !p || !*p ) 477 { 478 if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); 479 return 0; 480 } 481 if ( *p == '/' ) 482 { 483 ++p; 484 // Empty tag. 485 if ( *p != '>' ) 486 { 487 if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding ); 488 return 0; 489 } 490 return (p+1); 491 } 492 else if ( *p == '>' ) 493 { 494 // Done with attributes (if there were any.) 495 // Read the value -- which can include other 496 // elements -- read the end tag, and return. 497 ++p; 498 p = ReadValue( p, data, encoding ); // Note this is an Element method, and will set the error if one happens. 499 if ( !p || !*p ) { 500 // We were looking for the end tag, but found nothing. 501 // Fix for [ 1663758 ] Failure to report error on bad XML 502 if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); 503 return 0; 504 } 505 506 // We should find the end tag now 507 // note that: 508 // </foo > and 509 // </foo> 510 // are both valid end tags. 511 if ( StringEqual( p, endTag.c_str(), false, encoding ) ) 512 { 513 p += endTag.length(); 514 p = SkipWhiteSpace( p, encoding ); 515 if ( p && *p && *p == '>' ) { 516 ++p; 517 return p; 518 } 519 if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); 520 return 0; 521 } 522 else 523 { 524 if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); 525 return 0; 526 } 527 } 528 else 529 { 530 // Try to read an attribute: 531 TiXmlAttribute* attrib = new fsTiXmlAttribute(); 532 if ( !attrib ) 533 { 534 return 0; 535 } 536 537 attrib->SetDocument( document ); 538 pErr = p; 539 p = attrib->Parse( p, data, encoding ); 540 541 if ( !p || !*p ) 542 { 543 if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding ); 544 delete attrib; 545 return 0; 546 } 547 548 // Handle the strange case of double attributes: 549 #ifdef TIXML_USE_STL 550 TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() ); 551 #else 552 TiXmlAttribute* node = attributeSet.Find( attrib->Name() ); 553 #endif 554 if ( node ) 555 { 556 if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding ); 557 delete attrib; 558 return 0; 559 } 560 561 attributeSet.Add( attrib ); 562 } 563 } 564 return p; 565} 566 567/* 568TiXmlNode* fsTiXmlNode::Identify(char const* p, TiXmlEncoding encoding) 569{ 570TiXmlNode* returnNode = 0; 571 572p = TiXmlBase::SkipWhiteSpace( p, encoding ); 573if( !p || !*p || *p != '<' ) 574{ 575return 0; 576} 577 578p = TiXmlBase::SkipWhiteSpace( p, encoding ); 579 580if ( !p || !*p ) 581{ 582return 0; 583} 584 585// What is this thing? 586// - Elements start with a letter or underscore, but xml is reserved. 587// - Comments: <!-- 588// - Decleration: <?xml 589// - Everthing else is unknown to tinyxml. 590// 591 592const char* xmlHeader = { "<?xml" }; 593const char* commentHeader = { "<!--" }; 594const char* dtdHeader = { "<!" }; 595const char* cdataHeader = { "<![CDATA[" }; 596 597if ( TiXmlBase::StringEqual( p, xmlHeader, true, encoding ) ) 598{ 599#ifdef DEBUG_PARSER 600TIXML_LOG( "XML parsing Declaration\n" ); 601#endif 602returnNode = new fsTiXmlDeclaration(); 603} 604else if ( TiXmlBase::StringEqual( p, commentHeader, false, encoding ) ) 605{ 606#ifdef DEBUG_PARSER 607TIXML_LOG( "XML parsing Comment\n" ); 608#endif 609returnNode = new fsTiXmlComment(); 610} 611else if ( TiXmlBase::StringEqual( p, cdataHeader, false, encoding ) ) 612{ 613#ifdef DEBUG_PARSER 614TIXML_LOG( "XML parsing CDATA\n" ); 615#endif 616fsTiXmlText* text = new fsTiXmlText( "" ); 617text->SetCDATA( true ); 618returnNode = text; 619} 620else if ( TiXmlBase::StringEqual( p, dtdHeader, false, encoding ) ) 621{ 622#ifdef DEBUG_PARSER 623TIXML_LOG( "XML parsing Unknown(1)\n" ); 624#endif 625returnNode = new fsTiXmlUnknown(); 626} 627else if ( TiXmlBase::IsAlpha( *(p+1), encoding ) 628|| *(p+1) == '_' ) 629{ 630#ifdef DEBUG_PARSER 631TIXML_LOG( "XML parsing Element\n" ); 632#endif 633returnNode = new fsTiXmlElement( "" ); 634} 635else 636{ 637#ifdef DEBUG_PARSER 638TIXML_LOG( "XML parsing Unknown(2)\n" ); 639#endif 640returnNode = new fsTiXmlUnknown(); 641} 642 643if ( returnNode ) 644{ 645// Set the parent, so it can report errors 646returnNode->parent = this; 647} 648return returnNode; 649} 650 651const unsigned char TIXML_UTF_LEAD_0 = 0xefU; 652const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; 653const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; 654 655char const* fsTiXmlDocument::Parse(char const* p, TiXmlParsingData* prevData, TiXmlEncoding encoding) 656{ 657ClearError(); 658 659// Parse away, at the document level. Since a document 660// contains nothing but other tags, most of what happens 661// here is skipping white space. 662if ( !p || !*p ) 663{ 664SetError( TiXmlBase::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); 665return 0; 666} 667 668// Note that, for a document, this needs to come 669// before the while space skip, so that parsing 670// starts from the pointer we are given. 671location.Clear(); 672if ( prevData ) 673{ 674location.row = prevData->Cursor().row; 675location.col = prevData->Cursor().col; 676} 677else 678{ 679location.row = 0; 680location.col = 0; 681} 682TiXmlParsingData data( p, TabSize(), location.row, location.col ); 683location = data.Cursor(); 684 685if ( encoding == TIXML_ENCODING_UNKNOWN ) 686{ 687// Check for the Microsoft UTF-8 lead bytes. 688const unsigned char* pU = (const unsigned char*)p; 689if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 690&& *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 691&& *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) 692{ 693encoding = TIXML_ENCODING_UTF8; 694useMicrosoftBOM = true; 695} 696} 697 698p = TiXmlBase::SkipWhiteSpace( p, encoding ); 699if ( !p ) 700{ 701SetError( TiXmlBase::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); 702return 0; 703} 704 705while ( p && *p ) 706{ 707TiXmlNode* node = fsTiXmlNode::Identify( p, encoding ); 708if ( node ) 709{ 710p = node->Parse( p, &data, encoding ); 711LinkEndChild( node ); 712} 713else 714{ 715break; 716} 717 718// Did we get encoding info? 719if ( encoding == TIXML_ENCODING_UNKNOWN 720&& node->ToDeclaration() ) 721{ 722TiXmlDeclaration* dec = node->ToDeclaration(); 723const char* enc = dec->Encoding(); 724assert( enc ); 725 726if ( *enc == 0 ) 727encoding = TIXML_ENCODING_UTF8; 728else if ( TiXmlBase::StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) 729encoding = TIXML_ENCODING_UTF8; 730else if ( TiXmlBase::StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) 731encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice 732else 733encoding = TIXML_ENCODING_LEGACY; 734} 735 736p = TiXmlBase::SkipWhiteSpace( p, encoding ); 737} 738 739// Was this empty? 740if ( !firstChild ) { 741SetError( TiXmlBase::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); 742return 0; 743} 744 745// All is well. 746return p; 747} 748*/ 749