fsTinyXml.cpp

Engine/source/persistence/taml/fsTinyXml.cpp

More...

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