tinyxmlparser.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928
  1. /*
  2. Copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
  3. This software is provided 'as-is', without any express or implied
  4. warranty. In no event will the authors be held liable for any
  5. damages arising from the use of this software.
  6. Permission is granted to anyone to use this software for any
  7. purpose, including commercial applications, and to alter it and
  8. redistribute it freely, subject to the following restrictions:
  9. 1. The origin of this software must not be misrepresented; you must
  10. not claim that you wrote the original software. If you use this
  11. software in a product, an acknowledgment in the product documentation
  12. would be appreciated but is not required.
  13. 2. Altered source versions must be plainly marked as such, and
  14. must not be misrepresented as being the original software.
  15. 3. This notice may not be removed or altered from any source
  16. distribution.
  17. */
  18. #include "tinyxml.h"
  19. #include <ctype.h>
  20. #include <strstream>
  21. using namespace std;
  22. //#define DEBUG_PARSER
  23. TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] =
  24. {
  25. { "&amp;", 5, '&' },
  26. { "&lt;", 4, '<' },
  27. { "&gt;", 4, '>' },
  28. { "&quot;", 6, '\"' },
  29. { "&apos;", 6, '\'' }
  30. };
  31. const char* TiXmlBase::SkipWhiteSpace( const char* p )
  32. {
  33. if ( !p || !*p )
  34. {
  35. return 0;
  36. }
  37. while ( p && *p )
  38. {
  39. if ( isspace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space.
  40. ++p;
  41. else
  42. break;
  43. }
  44. return p;
  45. }
  46. /*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream* in, std::string* tag )
  47. {
  48. for( ;; )
  49. {
  50. if ( !in->good() ) return false;
  51. int c = in->peek();
  52. if ( !IsWhiteSpace( c ) )
  53. return true;
  54. *tag += in->get();
  55. }
  56. }
  57. /*static*/ bool TiXmlBase::StreamTo( std::istream* in, int character, std::string* tag )
  58. {
  59. while ( in->good() )
  60. {
  61. int c = in->peek();
  62. if ( c == character )
  63. return true;
  64. in->get();
  65. *tag += c;
  66. }
  67. return false;
  68. }
  69. const char* TiXmlBase::ReadName( const char* p, string* name )
  70. {
  71. *name = "";
  72. assert( p );
  73. // Names start with letters or underscores.
  74. // After that, they can be letters, underscores, numbers,
  75. // hyphens, or colons. (Colons are valid ony for namespaces,
  76. // but tinyxml can't tell namespaces from names.)
  77. if ( p && *p
  78. && ( isalpha( (unsigned char) *p ) || *p == '_' ) )
  79. {
  80. while( p && *p
  81. && ( isalnum( (unsigned char ) *p )
  82. || *p == '_'
  83. || *p == '-'
  84. || *p == ':' ) )
  85. {
  86. (*name) += *p;
  87. ++p;
  88. }
  89. return p;
  90. }
  91. return 0;
  92. }
  93. const char* TiXmlBase::GetEntity( const char* p, char* value )
  94. {
  95. // Presume an entity, and pull it out.
  96. string ent;
  97. int i;
  98. // Ignore the &#x entities.
  99. if ( strncmp( "&#x", p, 3 ) == 0 )
  100. {
  101. *value = *p;
  102. return p+1;
  103. }
  104. // Now try to match it.
  105. for( i=0; i<NUM_ENTITY; ++i )
  106. {
  107. if ( strncmp( entity[i].str, p, entity[i].strLength ) == 0 )
  108. {
  109. assert( strlen( entity[i].str ) == entity[i].strLength );
  110. *value = entity[i].chr;
  111. return ( p + entity[i].strLength );
  112. }
  113. }
  114. // So it wasn't an entity, its unrecognized, or something like that.
  115. *value = *p; // Don't put back the last one, since we return it!
  116. return p+1;
  117. }
  118. bool TiXmlBase::StringEqual( const char* p,
  119. const char* tag,
  120. bool ignoreCase )
  121. {
  122. assert( p );
  123. if ( !p || !*p )
  124. {
  125. assert( 0 );
  126. return false;
  127. }
  128. if ( tolower( *p ) == tolower( *tag ) )
  129. {
  130. const char* q = p;
  131. if (ignoreCase)
  132. {
  133. while ( *q && *tag && *q == *tag )
  134. {
  135. ++q;
  136. ++tag;
  137. }
  138. if ( *tag == 0 ) // Have we found the end of the tag, and everything equal?
  139. {
  140. return true;
  141. }
  142. }
  143. else
  144. {
  145. while ( *q && *tag && tolower( *q ) == tolower( *tag ) )
  146. {
  147. ++q;
  148. ++tag;
  149. }
  150. if ( *tag == 0 )
  151. {
  152. return true;
  153. }
  154. }
  155. }
  156. return false;
  157. }
  158. const char* TiXmlBase::ReadText( const char* p,
  159. string* text,
  160. bool trimWhiteSpace,
  161. const char* endTag,
  162. bool caseInsensitive )
  163. {
  164. *text = "";
  165. if ( !trimWhiteSpace // certain tags always keep whitespace
  166. || !condenseWhiteSpace ) // if true, whitespace is always kept
  167. {
  168. // Keep all the white space.
  169. while ( p && *p
  170. && !StringEqual( p, endTag, caseInsensitive )
  171. )
  172. {
  173. char c;
  174. p = GetChar( p, &c );
  175. text->append( &c, 1 );
  176. }
  177. }
  178. else
  179. {
  180. bool whitespace = false;
  181. // Remove leading white space:
  182. p = SkipWhiteSpace( p );
  183. while ( p && *p
  184. && !StringEqual( p, endTag, caseInsensitive ) )
  185. {
  186. if ( *p == '\r' || *p == '\n' )
  187. {
  188. whitespace = true;
  189. ++p;
  190. }
  191. else if ( isspace( *p ) )
  192. {
  193. whitespace = true;
  194. ++p;
  195. }
  196. else
  197. {
  198. // If we've found whitespace, add it before the
  199. // new character. Any whitespace just becomes a space.
  200. if ( whitespace )
  201. {
  202. text->append( " ", 1 );
  203. whitespace = false;
  204. }
  205. char c;
  206. p = GetChar( p, &c );
  207. text->append( &c, 1 );
  208. }
  209. }
  210. }
  211. return p + strlen( endTag );
  212. }
  213. void TiXmlDocument::StreamIn( std::istream* in, std::string* tag )
  214. {
  215. // The basic issue with a document is that we don't know what we're
  216. // streaming. Read something presumed to be a tag (and hope), then
  217. // identify it, and call the appropriate stream method on the tag.
  218. //
  219. // This "pre-streaming" will never read the closing ">" so the
  220. // sub-tag can orient itself.
  221. if ( !StreamTo( in, '<', tag ) )
  222. {
  223. SetError( TIXML_ERROR_PARSING_EMPTY );
  224. return;
  225. }
  226. while ( in->good() )
  227. {
  228. int tagIndex = tag->length();
  229. while ( in->good() && in->peek() != '>' )
  230. {
  231. int c = in->get();
  232. (*tag) += (char) c;
  233. }
  234. if ( in->good() )
  235. {
  236. // We now have something we presume to be a node of
  237. // some sort. Identify it, and call the node to
  238. // continue streaming.
  239. TiXmlNode* node = Identify( tag->c_str() + tagIndex );
  240. if ( node )
  241. {
  242. node->StreamIn( in, tag );
  243. bool isElement = node->ToElement() != 0;
  244. delete node;
  245. node = 0;
  246. // If this is the root element, we're done. Parsing will be
  247. // done by the >> operator.
  248. if ( isElement )
  249. {
  250. return;
  251. }
  252. }
  253. else
  254. {
  255. SetError( TIXML_ERROR );
  256. return;
  257. }
  258. }
  259. }
  260. // We should have returned sooner.
  261. SetError( TIXML_ERROR );
  262. }
  263. const char* TiXmlDocument::Parse( const char* p )
  264. {
  265. // Parse away, at the document level. Since a document
  266. // contains nothing but other tags, most of what happens
  267. // here is skipping white space.
  268. //
  269. // In this variant (as opposed to stream and Parse) we
  270. // read everything we can.
  271. if ( !p || !*p || !( p = SkipWhiteSpace( p ) ) )
  272. {
  273. SetError( TIXML_ERROR_DOCUMENT_EMPTY );
  274. return false;
  275. }
  276. while ( p && *p )
  277. {
  278. TiXmlNode* node = Identify( p );
  279. if ( node )
  280. {
  281. p = node->Parse( p );
  282. LinkEndChild( node );
  283. }
  284. else
  285. {
  286. break;
  287. }
  288. p = SkipWhiteSpace( p );
  289. }
  290. // All is well.
  291. return p;
  292. }
  293. TiXmlNode* TiXmlNode::Identify( const char* p )
  294. {
  295. TiXmlNode* returnNode = 0;
  296. p = SkipWhiteSpace( p );
  297. if( !p || !*p || *p != '<' )
  298. {
  299. return 0;
  300. }
  301. TiXmlDocument* doc = GetDocument();
  302. p = SkipWhiteSpace( p );
  303. if ( !p || !*p )
  304. {
  305. return 0;
  306. }
  307. // What is this thing?
  308. // - Elements start with a letter or underscore, but xml is reserved.
  309. // - Comments: <!--
  310. // - Decleration: <?xml
  311. // - Everthing else is unknown to tinyxml.
  312. //
  313. const char* xmlHeader = { "<?xml" };
  314. const char* commentHeader = { "<!--" };
  315. if ( StringEqual( p, xmlHeader, true ) )
  316. {
  317. #ifdef DEBUG_PARSER
  318. TIXML_LOG( "XML parsing Declaration\n" );
  319. #endif
  320. returnNode = new TiXmlDeclaration();
  321. }
  322. else if ( isalpha( *(p+1) )
  323. || *(p+1) == '_' )
  324. {
  325. #ifdef DEBUG_PARSER
  326. TIXML_LOG( "XML parsing Element\n" );
  327. #endif
  328. returnNode = new TiXmlElement( "" );
  329. }
  330. else if ( StringEqual( p, commentHeader, false ) )
  331. {
  332. #ifdef DEBUG_PARSER
  333. TIXML_LOG( "XML parsing Comment\n" );
  334. #endif
  335. returnNode = new TiXmlComment();
  336. }
  337. else
  338. {
  339. #ifdef DEBUG_PARSER
  340. TIXML_LOG( "XML parsing Unknown\n" );
  341. #endif
  342. returnNode = new TiXmlUnknown();
  343. }
  344. if ( returnNode )
  345. {
  346. // Set the parent, so it can report errors
  347. returnNode->parent = this;
  348. //p = returnNode->Parse( p );
  349. }
  350. else
  351. {
  352. if ( doc )
  353. doc->SetError( TIXML_ERROR_OUT_OF_MEMORY );
  354. }
  355. return returnNode;
  356. }
  357. void TiXmlElement::StreamIn( std::istream* in, std::string* tag )
  358. {
  359. // We're called with some amount of pre-parsing. That is, some of "this"
  360. // element is in "tag". Go ahead and stream to the closing ">"
  361. while( in->good() )
  362. {
  363. int c = in->get();
  364. (*tag) += (char) c ;
  365. if ( c == '>' )
  366. break;
  367. }
  368. if ( tag->length() < 3 ) return;
  369. // Okay...if we are a "/>" tag, then we're done. We've read a complete tag.
  370. // If not, identify and stream.
  371. if ( tag->at( tag->length() - 1 ) == '>'
  372. && tag->at( tag->length() - 2 ) == '/' )
  373. {
  374. // All good!
  375. return;
  376. }
  377. else if ( tag->at( tag->length() - 1 ) == '>' )
  378. {
  379. // There is more. Could be:
  380. // text
  381. // closing tag
  382. // another node.
  383. for ( ;; )
  384. {
  385. StreamWhiteSpace( in, tag );
  386. // Do we have text?
  387. if ( in->peek() != '<' )
  388. {
  389. // Yep, text.
  390. TiXmlText text( "" );
  391. text.StreamIn( in, tag );
  392. // What follows text is a closing tag or another node.
  393. // Go around again and figure it out.
  394. continue;
  395. }
  396. // We now have either a closing tag...or another node.
  397. // We should be at a "<", regardless.
  398. if ( !in->good() ) return;
  399. assert( in->peek() == '<' );
  400. int tagIndex = tag->length();
  401. bool closingTag = false;
  402. bool firstCharFound = false;
  403. for( ;; )
  404. {
  405. if ( !in->good() )
  406. return;
  407. int c = in->peek();
  408. if ( c == '>' )
  409. break;
  410. *tag += c;
  411. in->get();
  412. if ( !firstCharFound && c != '<' && !IsWhiteSpace( c ) )
  413. {
  414. firstCharFound = true;
  415. if ( c == '/' )
  416. closingTag = true;
  417. }
  418. }
  419. // If it was a closing tag, then read in the closing '>' to clean up the input stream.
  420. // If it was not, the streaming will be done by the tag.
  421. if ( closingTag )
  422. {
  423. int c = in->get();
  424. assert( c == '>' );
  425. *tag += c;
  426. // We are done, once we've found our closing tag.
  427. return;
  428. }
  429. else
  430. {
  431. // If not a closing tag, id it, and stream.
  432. const char* tagloc = tag->c_str() + tagIndex;
  433. TiXmlNode* node = Identify( tagloc );
  434. if ( !node )
  435. return;
  436. node->StreamIn( in, tag );
  437. delete node;
  438. node = 0;
  439. // No return: go around from the beginning: text, closing tag, or node.
  440. }
  441. }
  442. }
  443. }
  444. const char* TiXmlElement::Parse( const char* p )
  445. {
  446. p = SkipWhiteSpace( p );
  447. TiXmlDocument* document = GetDocument();
  448. if ( !p || !*p || *p != '<' )
  449. {
  450. if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT );
  451. return false;
  452. }
  453. p = SkipWhiteSpace( p+1 );
  454. // Read the name.
  455. p = ReadName( p, &value );
  456. if ( !p || !*p )
  457. {
  458. if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME );
  459. return false;
  460. }
  461. string endTag = "</";
  462. endTag += value;
  463. endTag += ">";
  464. // Check for and read attributes. Also look for an empty
  465. // tag or an end tag.
  466. while ( p && *p )
  467. {
  468. p = SkipWhiteSpace( p );
  469. if ( !p || !*p )
  470. {
  471. if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES );
  472. return 0;
  473. }
  474. if ( *p == '/' )
  475. {
  476. ++p;
  477. // Empty tag.
  478. if ( *p != '>' )
  479. {
  480. if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY );
  481. return 0;
  482. }
  483. return (p+1);
  484. }
  485. else if ( *p == '>' )
  486. {
  487. // Done with attributes (if there were any.)
  488. // Read the value -- which can include other
  489. // elements -- read the end tag, and return.
  490. ++p;
  491. p = ReadValue( p ); // Note this is an Element method, and will set the error if one happens.
  492. if ( !p || !*p )
  493. return 0;
  494. // We should find the end tag now
  495. if ( StringEqual( p, endTag.c_str(), false ) )
  496. {
  497. p += endTag.length();
  498. return p;
  499. }
  500. else
  501. {
  502. if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG );
  503. return 0;
  504. }
  505. }
  506. else
  507. {
  508. // Try to read an element:
  509. TiXmlAttribute attrib;
  510. attrib.SetDocument( document );
  511. p = attrib.Parse( p );
  512. if ( !p || !*p )
  513. {
  514. if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT );
  515. return 0;
  516. }
  517. SetAttribute( attrib.Name(), attrib.Value() );
  518. }
  519. }
  520. return p;
  521. }
  522. const char* TiXmlElement::ReadValue( const char* p )
  523. {
  524. TiXmlDocument* document = GetDocument();
  525. // Read in text and elements in any order.
  526. p = SkipWhiteSpace( p );
  527. while ( p && *p )
  528. {
  529. // string text;
  530. // while ( p && *p && *p != '<' )
  531. // {
  532. // text += (*p);
  533. // ++p;
  534. // }
  535. //
  536. // p = SkipWhiteSpace( p );
  537. if ( *p != '<' )
  538. {
  539. // Take what we have, make a text element.
  540. TiXmlText* textNode = new TiXmlText( "" );
  541. if ( !textNode )
  542. {
  543. if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY );
  544. return 0;
  545. }
  546. p = textNode->Parse( p );
  547. if ( !textNode->Blank() )
  548. LinkEndChild( textNode );
  549. else
  550. delete textNode;
  551. }
  552. else
  553. {
  554. // We hit a '<'
  555. // Have we hit a new element or an end tag?
  556. if ( StringEqual( p, "</", false ) )
  557. {
  558. return p;
  559. }
  560. else
  561. {
  562. TiXmlNode* node = Identify( p );
  563. if ( node )
  564. {
  565. p = node->Parse( p );
  566. LinkEndChild( node );
  567. }
  568. else
  569. {
  570. return 0;
  571. }
  572. }
  573. }
  574. p = SkipWhiteSpace( p );
  575. }
  576. if ( !p )
  577. {
  578. if ( document ) document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE );
  579. }
  580. return p;
  581. }
  582. void TiXmlUnknown::StreamIn( std::istream* in, std::string* tag )
  583. {
  584. while ( in->good() )
  585. {
  586. int c = in->get();
  587. (*tag) += c;
  588. if ( c == '>' )
  589. {
  590. // All is well.
  591. return;
  592. }
  593. }
  594. }
  595. const char* TiXmlUnknown::Parse( const char* p )
  596. {
  597. TiXmlDocument* document = GetDocument();
  598. p = SkipWhiteSpace( p );
  599. if ( !p || !*p || *p != '<' )
  600. {
  601. if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN );
  602. return 0;
  603. }
  604. ++p;
  605. value = "";
  606. while ( p && *p && *p != '>' )
  607. {
  608. value += *p;
  609. ++p;
  610. }
  611. if ( !p )
  612. {
  613. if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN );
  614. }
  615. if ( *p == '>' )
  616. return p+1;
  617. return p;
  618. }
  619. void TiXmlComment::StreamIn( std::istream* in, std::string* tag )
  620. {
  621. while ( in->good() )
  622. {
  623. int c = in->get();
  624. (*tag) += c;
  625. if ( c == '>'
  626. && tag->at( tag->length() - 2 ) == '-'
  627. && tag->at( tag->length() - 3 ) == '-' )
  628. {
  629. // All is well.
  630. return;
  631. }
  632. }
  633. }
  634. const char* TiXmlComment::Parse( const char* p )
  635. {
  636. TiXmlDocument* document = GetDocument();
  637. value = "";
  638. p = SkipWhiteSpace( p );
  639. const char* startTag = "<!--";
  640. const char* endTag = "-->";
  641. if ( !StringEqual( p, startTag, false ) )
  642. {
  643. document->SetError( TIXML_ERROR_PARSING_COMMENT );
  644. return 0;
  645. }
  646. p += strlen( startTag );
  647. p = ReadText( p, &value, false, endTag, false );
  648. return p;
  649. }
  650. const char* TiXmlAttribute::Parse( const char* p )
  651. {
  652. p = SkipWhiteSpace( p );
  653. if ( !p || !*p ) return 0;
  654. // Read the name, the '=' and the value.
  655. p = ReadName( p, &name );
  656. if ( !p || !*p )
  657. {
  658. if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES );
  659. return 0;
  660. }
  661. p = SkipWhiteSpace( p );
  662. if ( !p || !*p || *p != '=' )
  663. {
  664. if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES );
  665. return 0;
  666. }
  667. ++p; // skip '='
  668. p = SkipWhiteSpace( p );
  669. if ( !p || !*p )
  670. {
  671. if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES );
  672. return 0;
  673. }
  674. const char* end;
  675. if ( *p == '\'' )
  676. {
  677. ++p;
  678. end = "\'";
  679. p = ReadText( p, &value, false, end, false );
  680. }
  681. else if ( *p == '"' )
  682. {
  683. ++p;
  684. end = "\"";
  685. p = ReadText( p, &value, false, end, false );
  686. }
  687. else
  688. {
  689. // All attribute values should be in single or double quotes.
  690. // But this is such a common error that the parser will try
  691. // its best, even without them.
  692. value = "";
  693. while ( p && *p // existence
  694. && !isspace( *p ) && *p != '\n' && *p != '\r' // whitespace
  695. && *p != '/' && *p != '>' ) // tag end
  696. {
  697. value += *p;
  698. ++p;
  699. }
  700. }
  701. return p;
  702. }
  703. void TiXmlText::StreamIn( std::istream* in, std::string* tag )
  704. {
  705. while ( in->good() )
  706. {
  707. int c = in->peek();
  708. if ( c == '<' )
  709. return;
  710. (*tag) += c;
  711. in->get();
  712. }
  713. }
  714. const char* TiXmlText::Parse( const char* p )
  715. {
  716. value = "";
  717. //TiXmlDocument* doc = GetDocument();
  718. bool ignoreWhite = true;
  719. // if ( doc && !doc->IgnoreWhiteSpace() ) ignoreWhite = false;
  720. const char* end = "<";
  721. p = ReadText( p, &value, ignoreWhite, end, false );
  722. if ( p )
  723. return p-1; // don't truncate the '<'
  724. return 0;
  725. }
  726. void TiXmlDeclaration::StreamIn( std::istream* in, std::string* tag )
  727. {
  728. while ( in->good() )
  729. {
  730. int c = in->get();
  731. (*tag) += c;
  732. if ( c == '>' )
  733. {
  734. // All is well.
  735. return;
  736. }
  737. }
  738. }
  739. const char* TiXmlDeclaration::Parse( const char* p )
  740. {
  741. p = SkipWhiteSpace( p );
  742. // Find the beginning, find the end, and look for
  743. // the stuff in-between.
  744. TiXmlDocument* document = GetDocument();
  745. if ( !p || !*p || !StringEqual( p, "<?xml", true ) )
  746. {
  747. if ( document ) document->SetError( TIXML_ERROR_PARSING_DECLARATION );
  748. return 0;
  749. }
  750. p += 5;
  751. // const char* start = p+5;
  752. // const char* end = strstr( start, "?>" );
  753. version = "";
  754. encoding = "";
  755. standalone = "";
  756. while ( p && *p )
  757. {
  758. if ( *p == '>' )
  759. {
  760. ++p;
  761. return p;
  762. }
  763. p = SkipWhiteSpace( p );
  764. if ( StringEqual( p, "version", true ) )
  765. {
  766. // p += 7;
  767. TiXmlAttribute attrib;
  768. p = attrib.Parse( p );
  769. version = attrib.Value();
  770. }
  771. else if ( StringEqual( p, "encoding", true ) )
  772. {
  773. // p += 8;
  774. TiXmlAttribute attrib;
  775. p = attrib.Parse( p );
  776. encoding = attrib.Value();
  777. }
  778. else if ( StringEqual( p, "standalone", true ) )
  779. {
  780. // p += 10;
  781. TiXmlAttribute attrib;
  782. p = attrib.Parse( p );
  783. standalone = attrib.Value();
  784. }
  785. else
  786. {
  787. // Read over whatever it is.
  788. while( p && *p && *p != '>' && !isspace( *p ) )
  789. ++p;
  790. }
  791. }
  792. return 0;
  793. }
  794. bool TiXmlText::Blank() const
  795. {
  796. for ( unsigned i=0; i<value.size(); i++ )
  797. if ( !isspace( value[i] ) )
  798. return false;
  799. return true;
  800. }