Skip to content
Browse files

WIP SVN support.

  • Loading branch information...
1 parent 42b22ff commit 174e9bae7da8444044fd866fb17d246f10b8c90a @acaudwell committed Dec 2, 2010
Showing with 6,086 additions and 23 deletions.
  1. +2 −2 .gitignore
  2. +5 −0 Makefile.am
  3. +15 −10 README
  4. +14 −9 data/gource.1
  5. +15 −0 src/gource.cpp
  6. +1 −0 src/gource.h
  7. +9 −2 src/gource_settings.cpp
  8. +1 −0 src/gource_settings.h
  9. +221 −0 src/svn.cpp
  10. +43 −0 src/svn.h
  11. +116 −0 src/tinyxml/tinystr.cpp
  12. +319 −0 src/tinyxml/tinystr.h
  13. +1,839 −0 src/tinyxml/tinyxml.cpp
  14. +1,799 −0 src/tinyxml/tinyxml.h
  15. +52 −0 src/tinyxml/tinyxmlerror.cpp
  16. +1,635 −0 src/tinyxml/tinyxmlparser.cpp
View
4 .gitignore
@@ -19,5 +19,5 @@ logs/*
/missing
*.swp
*.d
-/src/.deps/
-/src/.dirstamp
+.deps
+.dirstamp
View
5 Makefile.am
@@ -45,6 +45,11 @@ gource_SOURCES = \
src/pawn.cpp src/pawn.h \
src/slider.cpp src/slider.h \
src/spline.cpp src/spline.h \
+ src/svn.cpp src/svn.h \
+ src/tinyxml/tinyxmlerror.cpp \
+ src/tinyxml/tinystr.cpp src/tinyxml/tinystr.h \
+ src/tinyxml/tinyxml.cpp src/tinyxml/tinyxml.h \
+ src/tinyxml/tinyxmlparser.cpp src/tinyxml/tinyxmlparser.h \
src/user.cpp src/user.h \
src/zoomcamera.cpp src/zoomcamera.h
View
25 README
@@ -242,7 +242,7 @@ options:
If path is ommited, gource will attempt to read a log from the
current directory.
-Git, Bazaar and Mercurial Examples:
+Git, Bazaar, Mercurial and SVN Examples:
View the log of the respository in the current path:
@@ -252,24 +252,29 @@ View the log of a project in the specified directory:
gource my-project-dir
-Save a copy of the log using in a special log format and play it back
-(example is for Git):
+For large projects, generating a log of the project history may take a long
+time. For centralized VCS like SVN, generating the log may also put load on
+the central VCS server.
- cd my-git-project
- `gource --git-log-command` > my-git-project.log
- gource my-git-project.log
+In these cases, you may like to save a copy of the log for later use.
-NOTE: --git-log-command assumes you have a recent version git of that supports
- all the options. You may need to modify the command line to work with an
- older version (changing %aN to %an for instance).
+You can generate a log in the VCS specific log format using
+the --log-command VCS option:
+
+ cd my-svn-project
+ `gource --log-command svn` > my-svn-project.log
+ gource my-svn-project.log
+
+You can also have Gource write a copy of the log file in its own format:
+
+ gource --output-custom-log my-project-custom.log
Other Version Control Systems:
Visit the Gource homepage for guides and examples of using Gource with other
version control systems:
CVS - http://code.google.com/p/gource/wiki/CVS
- SVN - http://code.google.com/p/gource/wiki/SVN
Custom Log Format:
View
23 data/gource.1
@@ -208,7 +208,7 @@ Either a supported version control directory, a pre-generated log file (see log
If path is ommited, gource will attempt to read a log from the current directory.
-.SH GIT, BAZAAR AND MERCURIAL EXAMPLES
+.SH GIT, BAZAAR, MERCURIAL AND SVN EXAMPLES
View the log of the respository in the current path:
@@ -220,25 +220,30 @@ View the log of a project in the specified directory:
.ti 10
\fIgource\fR my\-project\-dir
-Save a copy of the log using in a special log format and play it back (example is for Git):
+For large projects, generating a log of the project history may take a long time. For centralized VCS like SVN, generating the log will put load on the central VCS server.
+
+In these cases, you may like to save a copy of the log for later use.
+
+You can generate a log in the VCS specific log format using the \-\-log\-command VCS option:
.ti 10
-cd my\-git\-project
+cd my\-svn\-project
.ti 10
-\`\fIgource\fR \-\-git\-log\-command\` > my\-git\-project.log
+\`\fIgource\fR \-\-log\-command svn\` > my\-svn\-project.log
.ti 10
-\fIgource\fR my\-git\-project.log
+\fIgource\fR my\-svn\-project.log
-NOTE: \-\-git-log-command assumes you have a recent version of git that supports all the options. You may need to modify the command line to work with an older version (changing %aN to %an for instance).
+You can also have Gource write a copy of the log file in its own format:
+
+.ti 10
+\fIgource\fR \-\-output\-custom\-log my\-project\-custom.log
.SH OTHER VERSION CONTROL SYSTEMS
The Gource homepage has guides and examples for using Gource with other version control systems:
.ti 10
-CVS - http://code.google.com/p/gource/wiki/CVS
-.ti 10
-SVN - http://code.google.com/p/gource/wiki/SVN
+CVS \- http://code.google.com/p/gource/wiki/CVS
.SH CUSTOM LOG FORMAT
View
15 src/gource.cpp
@@ -197,6 +197,12 @@ RCommitLog* Gource::determineFormat(const std::string& logfile) {
delete clog;
}
+ if(gGourceSettings.log_format == "svn") {
+ clog = new SVNCommitLog(logfile);
+ if(clog->checkFormat()) return clog;
+ delete clog;
+ }
+
return 0;
}
@@ -235,6 +241,13 @@ RCommitLog* Gource::determineFormat(const std::string& logfile) {
delete clog;
+ //svn
+ debugLog("tryin svn...\n");
+ clog = new SVNCommitLog(logfile);
+ if(clog->checkFormat()) return clog;
+
+ delete clog;
+
//custom
debugLog("trying custom...\n");
clog = new CustomLog(logfile);
@@ -786,6 +799,8 @@ void Gource::reset() {
manual_zoom = false;
rotation_remaining_angle = 0.0f;
+ message_timer = 0.0f;
+
cursor_move = vec2f(0.0f, 0.0f);
selectedUser = 0;
View
1 src/gource.h
@@ -44,6 +44,7 @@
#include "cvs-exp.h"
#include "custom.h"
#include "apache.h"
+#include "svn.h"
#include "slider.h"
View
11 src/gource_settings.cpp
@@ -63,8 +63,8 @@ void GourceSettings::help(bool extended_help) {
printf(" --max-files NUMBER Max number of active files (default: 1000)\n");
printf(" --max-file-lag SECONDS Max time files of a commit can take to appear\n\n");
- printf(" --log-command VCS Show the log command used by gource (git,cvs,hg,bzr)\n");
- printf(" --log-format VCS Specify format of the log (git,cvs,hg,bzr,custom)\n");
+ printf(" --log-command VCS Show the log command used by gource (git,svn,cvs,hg,bzr)\n");
+ printf(" --log-format VCS Specify format of the log (git,cvs,svn,hg,bzr,custom)\n");
printf(" --git-branch Get the git log of a particular branch\n\n");
printf(" --load-config CONF_FILE Load a config file\n");
@@ -172,6 +172,7 @@ GourceSettings::GourceSettings() {
conf_sections["cvs-exp-command"] = "command-line";
conf_sections["hg-log-command"] = "command-line";
conf_sections["bzr-log-command"] = "command-line";
+ conf_sections["svn-log-command"] = "command-line";
conf_sections["load-config"] = "command-line";
conf_sections["save-config"] = "command-line";
conf_sections["output-custom-log"] = "command-line";
@@ -204,6 +205,7 @@ GourceSettings::GourceSettings() {
arg_types["git-log-command"]= "bool";
arg_types["cvs-exp-command"]= "bool";
+ arg_types["svn-log-command"]= "bool";
arg_types["hg-log-command"] = "bool";
arg_types["bzr-log-command"]= "bool";
@@ -382,6 +384,10 @@ void GourceSettings::commandLineOption(const std::string& name, const std::strin
SDLAppInfo(gGourceCvsExpLogCommand);
}
+ if(name == "svn-log-command" || log_command == "svn") {
+ SDLAppInfo(gGourceSVNLogCommand);
+ }
+
if(name == "hg-log-command" || log_command == "hg") {
std::string command = gGourceMercurialCommand();
SDLAppInfo(command);
@@ -550,6 +556,7 @@ void GourceSettings::importGourceSettings(ConfFile& conffile, ConfSection* gourc
if( log_format != "git"
&& log_format != "cvs"
+ && log_format != "svn"
&& log_format != "custom"
&& log_format != "hg"
&& log_format != "bzr"
View
1 src/gource_settings.h
@@ -26,6 +26,7 @@
#include "git.h"
#include "bzr.h"
#include "cvs-exp.h"
+#include "svn.h"
#include "core/settings.h"
#include "core/regex.h"
View
221 src/svn.cpp
@@ -0,0 +1,221 @@
+/*
+ Copyright (C) 2010 Andrew Caudwell (acaudwell@gmail.com)
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version
+ 3 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "svn.h"
+
+Regex svn_xml_tag("^<\\??xml");
+Regex svn_logentry_start("^<logentry");
+Regex svn_logentry_end("^</logentry>");
+Regex svn_logentry_timestamp("(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})");
+
+std::string gGourceSVNLogCommand = "svn log -r 1:HEAD --xml --verbose --quiet";
+
+SVNCommitLog::SVNCommitLog(const std::string& logfile) : RCommitLog(logfile, '<') {
+
+ log_command = gGourceSVNLogCommand;
+
+ //can generate log from directory
+ if(!logf && is_dir) {
+ logf = generateLog(logfile);
+
+ if(logf) {
+ success = true;
+ seekable = true;
+ }
+ }
+
+ logentry.reserve(1024);
+}
+
+
+BaseLog* SVNCommitLog::generateLog(const std::string& dir) {
+ //get working directory
+ char cwd_buff[1024];
+
+ if(getcwd(cwd_buff, 1024) != cwd_buff) {
+ return 0;
+ }
+
+ //does directory have a .svn ?
+ std::string gitdir = dir + std::string("/.svn");
+ struct stat dirinfo;
+ int stat_rc = stat(gitdir.c_str(), &dirinfo);
+ if(stat_rc!=0 || !(dirinfo.st_mode & S_IFDIR)) {
+ return 0;
+ }
+
+ std::string command = getLogCommand();
+
+ //create temp file
+ createTempLog();
+
+ if(temp_file.size()==0) return 0;
+
+ if(chdir(dir.c_str()) != 0) {
+ return 0;
+ }
+
+ char cmd_buff[2048];
+ sprintf(cmd_buff, "%s > %s", command.c_str(), temp_file.c_str());
+
+ int command_rc = system(cmd_buff);
+
+ chdir(cwd_buff);
+
+ if(command_rc != 0) {
+ return 0;
+ }
+
+ BaseLog* seeklog = new SeekLog(temp_file);
+
+ return seeklog;
+}
+
+bool SVNCommitLog::parseCommit(RCommit& commit) {
+
+ //fprintf(stderr,"parsing svn log\n");
+
+ std::string line;
+
+ if(!getNextLine(line)) return false;
+
+ //start of log entry
+ if(!svn_logentry_start.match(line)) {
+
+ //is this the start of the document
+ if(!svn_xml_tag.match(line)) return false;
+
+ //fprintf(stderr,"found xml tag\n");
+
+ //if so find the first logentry tag
+
+ bool found_logentry = false;
+
+ while(getNextLine(line)) {
+ if(svn_logentry_start.match(line)) {
+ found_logentry = true;
+ break;
+ }
+ }
+
+ if(!found_logentry) return false;
+ }
+
+ //fprintf(stderr,"found logentry\n");
+
+ logentry.clear();
+
+ logentry.append(line);
+ logentry.append("\n");
+
+ //fprintf(stderr,"found opening tag\n");
+
+ bool endfound = false;
+
+ while(getNextLine(line)) {
+ logentry.append(line);
+ logentry.append("\n");
+ if(svn_logentry_end.match(line)) {
+ //fprintf(stderr,"found closing tag\n");
+ endfound=true;
+ break;
+ }
+ }
+
+ //incomplete commit
+ if(!endfound) return false;
+
+ //fprintf(stderr,"read logentry\n");
+
+ TiXmlDocument doc;
+
+ if(!doc.Parse(logentry.c_str())) return false;
+
+ //fprintf(stderr,"try to parse logentry: %s\n", logentry.c_str());
+
+ TiXmlElement* leE = doc.FirstChildElement( "logentry" );
+
+ std::vector<std::string> entries;
+
+ if(!leE) return false;
+
+ //parse date
+ TiXmlElement* dateE = leE->FirstChildElement( "date" );
+
+ if(!dateE) return false;
+
+ std::string timestamp_str(dateE->GetText());
+
+ if(!svn_logentry_timestamp.match(timestamp_str, &entries))
+ return false;
+
+ struct tm time_str;
+
+ time_str.tm_year = atoi(entries[0].c_str()) - 1900;
+ time_str.tm_mon = atoi(entries[1].c_str()) - 1;
+ time_str.tm_mday = atoi(entries[2].c_str());
+ time_str.tm_hour = atoi(entries[3].c_str());
+ time_str.tm_min = atoi(entries[4].c_str());
+ time_str.tm_sec = atoi(entries[5].c_str());
+ time_str.tm_isdst = -1;
+
+ commit.timestamp = mktime(&time_str);
+
+ //parse author
+ TiXmlElement* authorE = leE->FirstChildElement("author");
+
+ if(authorE != 0) {
+
+ std::string author(authorE->GetText());
+
+ if(author.empty()) author = "Unknown";
+
+ commit.username = author;
+ }
+
+ TiXmlElement* pathsE = leE->FirstChildElement( "paths" );
+
+ //parse changes
+
+ for(TiXmlElement* pathE = pathsE->FirstChildElement("path"); pathE != 0; pathE = pathE->NextSiblingElement()) {
+ //parse path
+
+ const char* kind = pathE->Attribute("kind");
+ const char* action = pathE->Attribute("action");
+
+ //check for action
+ if(action == 0) continue;
+
+ //if has the 'kind' attribute (old versions of svn dont have this), check it is not a dir
+ if(kind != 0 && strcmp(kind,"dir") == 0)
+ continue;
+
+ std::string file(pathE->GetText());
+ std::string status(action);
+
+ if(file.empty()) continue;
+ if(status.empty()) continue;
+
+ commit.addFile(file, status);
+ }
+
+ //fprintf(stderr,"parsed logentry\n");
+
+ //read files
+
+ return true;
+}
View
43 src/svn.h
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2010 Andrew Caudwell (acaudwell@gmail.com)
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version
+ 3 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SVNLOG_H
+#define SVNLOG_H
+
+#include "gource_settings.h"
+
+#include "commitlog.h"
+
+#include <sstream>
+
+#include "tinyxml/tinyxml.h"
+
+#include <unistd.h>
+
+extern std::string gGourceSVNLogCommand;
+
+class SVNCommitLog : public RCommitLog {
+protected:
+ bool parseCommit(RCommit& commit);
+ BaseLog* generateLog(const std::string& dir);
+
+ std::string logentry;
+public:
+ SVNCommitLog(const std::string& logfile);
+};
+
+#endif
View
116 src/tinyxml/tinystr.cpp
@@ -0,0 +1,116 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original file by Yves Berquin.
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+/*
+ * THIS FILE WAS ALTERED BY Tyge Løvset, 7. April 2005.
+ */
+
+
+#ifndef TIXML_USE_STL
+
+#include "tinystr.h"
+
+// Error value for find primitive
+const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1);
+
+
+// Null rep.
+TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } };
+
+
+void TiXmlString::reserve (size_type cap)
+{
+ if (cap > capacity())
+ {
+ TiXmlString tmp;
+ tmp.init(length(), cap);
+ memcpy(tmp.start(), data(), length());
+ swap(tmp);
+ }
+}
+
+
+TiXmlString& TiXmlString::assign(const char* str, size_type len)
+{
+ size_type cap = capacity();
+ if (len > cap || cap > 3*(len + 8))
+ {
+ TiXmlString tmp;
+ tmp.init(len);
+ memcpy(tmp.start(), str, len);
+ swap(tmp);
+ }
+ else
+ {
+ memmove(start(), str, len);
+ set_size(len);
+ }
+ return *this;
+}
+
+
+TiXmlString& TiXmlString::append(const char* str, size_type len)
+{
+ size_type newsize = length() + len;
+ if (newsize > capacity())
+ {
+ reserve (newsize + capacity());
+ }
+ memmove(finish(), str, len);
+ set_size(newsize);
+ return *this;
+}
+
+
+TiXmlString operator + (const TiXmlString & a, const TiXmlString & b)
+{
+ TiXmlString tmp;
+ tmp.reserve(a.length() + b.length());
+ tmp += a;
+ tmp += b;
+ return tmp;
+}
+
+TiXmlString operator + (const TiXmlString & a, const char* b)
+{
+ TiXmlString tmp;
+ TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>( strlen(b) );
+ tmp.reserve(a.length() + b_len);
+ tmp += a;
+ tmp.append(b, b_len);
+ return tmp;
+}
+
+TiXmlString operator + (const char* a, const TiXmlString & b)
+{
+ TiXmlString tmp;
+ TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>( strlen(a) );
+ tmp.reserve(a_len + b.length());
+ tmp.append(a, a_len);
+ tmp += b;
+ return tmp;
+}
+
+
+#endif // TIXML_USE_STL
View
319 src/tinyxml/tinystr.h
@@ -0,0 +1,319 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original file by Yves Berquin.
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+/*
+ * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005.
+ *
+ * - completely rewritten. compact, clean, and fast implementation.
+ * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems)
+ * - fixed reserve() to work as per specification.
+ * - fixed buggy compares operator==(), operator<(), and operator>()
+ * - fixed operator+=() to take a const ref argument, following spec.
+ * - added "copy" constructor with length, and most compare operators.
+ * - added swap(), clear(), size(), capacity(), operator+().
+ */
+
+#ifndef TIXML_USE_STL
+
+#ifndef TIXML_STRING_INCLUDED
+#define TIXML_STRING_INCLUDED
+
+#include <assert.h>
+#include <string.h>
+
+/* The support for explicit isn't that universal, and it isn't really
+ required - it is used to check that the TiXmlString class isn't incorrectly
+ used. Be nice to old compilers and macro it here:
+*/
+#if defined(_MSC_VER) && (_MSC_VER >= 1200 )
+ // Microsoft visual studio, version 6 and higher.
+ #define TIXML_EXPLICIT explicit
+#elif defined(__GNUC__) && (__GNUC__ >= 3 )
+ // GCC version 3 and higher.s
+ #define TIXML_EXPLICIT explicit
+#else
+ #define TIXML_EXPLICIT
+#endif
+
+
+/*
+ TiXmlString is an emulation of a subset of the std::string template.
+ Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.
+ Only the member functions relevant to the TinyXML project have been implemented.
+ The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase
+ a string and there's no more room, we allocate a buffer twice as big as we need.
+*/
+class TiXmlString
+{
+ public :
+ // The size type used
+ typedef size_t size_type;
+
+ // Error value for find primitive
+ static const size_type npos; // = -1;
+
+
+ // TiXmlString empty constructor
+ TiXmlString () : rep_(&nullrep_)
+ {
+ }
+
+ // TiXmlString copy constructor
+ TiXmlString ( const TiXmlString & copy) : rep_(0)
+ {
+ init(copy.length());
+ memcpy(start(), copy.data(), length());
+ }
+
+ // TiXmlString constructor, based on a string
+ TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0)
+ {
+ init( static_cast<size_type>( strlen(copy) ));
+ memcpy(start(), copy, length());
+ }
+
+ // TiXmlString constructor, based on a string
+ TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0)
+ {
+ init(len);
+ memcpy(start(), str, len);
+ }
+
+ // TiXmlString destructor
+ ~TiXmlString ()
+ {
+ quit();
+ }
+
+ // = operator
+ TiXmlString& operator = (const char * copy)
+ {
+ return assign( copy, (size_type)strlen(copy));
+ }
+
+ // = operator
+ TiXmlString& operator = (const TiXmlString & copy)
+ {
+ return assign(copy.start(), copy.length());
+ }
+
+
+ // += operator. Maps to append
+ TiXmlString& operator += (const char * suffix)
+ {
+ return append(suffix, static_cast<size_type>( strlen(suffix) ));
+ }
+
+ // += operator. Maps to append
+ TiXmlString& operator += (char single)
+ {
+ return append(&single, 1);
+ }
+
+ // += operator. Maps to append
+ TiXmlString& operator += (const TiXmlString & suffix)
+ {
+ return append(suffix.data(), suffix.length());
+ }
+
+
+ // Convert a TiXmlString into a null-terminated char *
+ const char * c_str () const { return rep_->str; }
+
+ // Convert a TiXmlString into a char * (need not be null terminated).
+ const char * data () const { return rep_->str; }
+
+ // Return the length of a TiXmlString
+ size_type length () const { return rep_->size; }
+
+ // Alias for length()
+ size_type size () const { return rep_->size; }
+
+ // Checks if a TiXmlString is empty
+ bool empty () const { return rep_->size == 0; }
+
+ // Return capacity of string
+ size_type capacity () const { return rep_->capacity; }
+
+
+ // single char extraction
+ const char& at (size_type index) const
+ {
+ assert( index < length() );
+ return rep_->str[ index ];
+ }
+
+ // [] operator
+ char& operator [] (size_type index) const
+ {
+ assert( index < length() );
+ return rep_->str[ index ];
+ }
+
+ // find a char in a string. Return TiXmlString::npos if not found
+ size_type find (char lookup) const
+ {
+ return find(lookup, 0);
+ }
+
+ // find a char in a string from an offset. Return TiXmlString::npos if not found
+ size_type find (char tofind, size_type offset) const
+ {
+ if (offset >= length()) return npos;
+
+ for (const char* p = c_str() + offset; *p != '\0'; ++p)
+ {
+ if (*p == tofind) return static_cast< size_type >( p - c_str() );
+ }
+ return npos;
+ }
+
+ void clear ()
+ {
+ //Lee:
+ //The original was just too strange, though correct:
+ // TiXmlString().swap(*this);
+ //Instead use the quit & re-init:
+ quit();
+ init(0,0);
+ }
+
+ /* Function to reserve a big amount of data when we know we'll need it. Be aware that this
+ function DOES NOT clear the content of the TiXmlString if any exists.
+ */
+ void reserve (size_type cap);
+
+ TiXmlString& assign (const char* str, size_type len);
+
+ TiXmlString& append (const char* str, size_type len);
+
+ void swap (TiXmlString& other)
+ {
+ Rep* r = rep_;
+ rep_ = other.rep_;
+ other.rep_ = r;
+ }
+
+ private:
+
+ void init(size_type sz) { init(sz, sz); }
+ void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; }
+ char* start() const { return rep_->str; }
+ char* finish() const { return rep_->str + rep_->size; }
+
+ struct Rep
+ {
+ size_type size, capacity;
+ char str[1];
+ };
+
+ void init(size_type sz, size_type cap)
+ {
+ if (cap)
+ {
+ // Lee: the original form:
+ // rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap));
+ // doesn't work in some cases of new being overloaded. Switching
+ // to the normal allocation, although use an 'int' for systems
+ // that are overly picky about structure alignment.
+ const size_type bytesNeeded = sizeof(Rep) + cap;
+ const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int );
+ rep_ = reinterpret_cast<Rep*>( new int[ intsNeeded ] );
+
+ rep_->str[ rep_->size = sz ] = '\0';
+ rep_->capacity = cap;
+ }
+ else
+ {
+ rep_ = &nullrep_;
+ }
+ }
+
+ void quit()
+ {
+ if (rep_ != &nullrep_)
+ {
+ // The rep_ is really an array of ints. (see the allocator, above).
+ // Cast it back before delete, so the compiler won't incorrectly call destructors.
+ delete [] ( reinterpret_cast<int*>( rep_ ) );
+ }
+ }
+
+ Rep * rep_;
+ static Rep nullrep_;
+
+} ;
+
+
+inline bool operator == (const TiXmlString & a, const TiXmlString & b)
+{
+ return ( a.length() == b.length() ) // optimization on some platforms
+ && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare
+}
+inline bool operator < (const TiXmlString & a, const TiXmlString & b)
+{
+ return strcmp(a.c_str(), b.c_str()) < 0;
+}
+
+inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); }
+inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; }
+inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); }
+inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); }
+
+inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; }
+inline bool operator == (const char* a, const TiXmlString & b) { return b == a; }
+inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); }
+inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); }
+
+TiXmlString operator + (const TiXmlString & a, const TiXmlString & b);
+TiXmlString operator + (const TiXmlString & a, const char* b);
+TiXmlString operator + (const char* a, const TiXmlString & b);
+
+
+/*
+ TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
+ Only the operators that we need for TinyXML have been developped.
+*/
+class TiXmlOutStream : public TiXmlString
+{
+public :
+
+ // TiXmlOutStream << operator.
+ TiXmlOutStream & operator << (const TiXmlString & in)
+ {
+ *this += in;
+ return *this;
+ }
+
+ // TiXmlOutStream << operator.
+ TiXmlOutStream & operator << (const char * in)
+ {
+ *this += in;
+ return *this;
+ }
+
+} ;
+
+#endif // TIXML_STRING_INCLUDED
+#endif // TIXML_USE_STL
View
1,839 src/tinyxml/tinyxml.cpp
@@ -0,0 +1,1839 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#include <ctype.h>
+
+#ifdef TIXML_USE_STL
+#include <sstream>
+#include <iostream>
+#endif
+
+#include "tinyxml.h"
+
+FILE* TiXmlFOpen( const char* filename, const char* mode );
+
+bool TiXmlBase::condenseWhiteSpace = true;
+
+// Microsoft compiler security
+FILE* TiXmlFOpen( const char* filename, const char* mode )
+{
+ #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
+ FILE* fp = 0;
+ errno_t err = fopen_s( &fp, filename, mode );
+ if ( !err && fp )
+ return fp;
+ return 0;
+ #else
+ return fopen( filename, mode );
+ #endif
+}
+
+void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString )
+{
+ int i=0;
+
+ while( i<(int)str.length() )
+ {
+ unsigned char c = (unsigned char) str[i];
+
+ if ( c == '&'
+ && i < ( (int)str.length() - 2 )
+ && str[i+1] == '#'
+ && str[i+2] == 'x' )
+ {
+ // Hexadecimal character reference.
+ // Pass through unchanged.
+ // &#xA9; -- copyright symbol, for example.
+ //
+ // The -1 is a bug fix from Rob Laveaux. It keeps
+ // an overflow from happening if there is no ';'.
+ // There are actually 2 ways to exit this loop -
+ // while fails (error case) and break (semicolon found).
+ // However, there is no mechanism (currently) for
+ // this function to return an error.
+ while ( i<(int)str.length()-1 )
+ {
+ outString->append( str.c_str() + i, 1 );
+ ++i;
+ if ( str[i] == ';' )
+ break;
+ }
+ }
+ else if ( c == '&' )
+ {
+ outString->append( entity[0].str, entity[0].strLength );
+ ++i;
+ }
+ else if ( c == '<' )
+ {
+ outString->append( entity[1].str, entity[1].strLength );
+ ++i;
+ }
+ else if ( c == '>' )
+ {
+ outString->append( entity[2].str, entity[2].strLength );
+ ++i;
+ }
+ else if ( c == '\"' )
+ {
+ outString->append( entity[3].str, entity[3].strLength );
+ ++i;
+ }
+ else if ( c == '\'' )
+ {
+ outString->append( entity[4].str, entity[4].strLength );
+ ++i;
+ }
+ else if ( c < 32 )
+ {
+ // Easy pass at non-alpha/numeric/symbol
+ // Below 32 is symbolic.
+ char buf[ 32 ];
+
+ #if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) );
+ #else
+ sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) );
+ #endif
+
+ //*ME: warning C4267: convert 'size_t' to 'int'
+ //*ME: Int-Cast to make compiler happy ...
+ outString->append( buf, (int)strlen( buf ) );
+ ++i;
+ }
+ else
+ {
+ //char realc = (char) c;
+ //outString->append( &realc, 1 );
+ *outString += (char) c; // somewhat more efficient function call.
+ ++i;
+ }
+ }
+}
+
+
+TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase()
+{
+ parent = 0;
+ type = _type;
+ firstChild = 0;
+ lastChild = 0;
+ prev = 0;
+ next = 0;
+}
+
+
+TiXmlNode::~TiXmlNode()
+{
+ TiXmlNode* node = firstChild;
+ TiXmlNode* temp = 0;
+
+ while ( node )
+ {
+ temp = node;
+ node = node->next;
+ delete temp;
+ }
+}
+
+
+void TiXmlNode::CopyTo( TiXmlNode* target ) const
+{
+ target->SetValue (value.c_str() );
+ target->userData = userData;
+ target->location = location;
+}
+
+
+void TiXmlNode::Clear()
+{
+ TiXmlNode* node = firstChild;
+ TiXmlNode* temp = 0;
+
+ while ( node )
+ {
+ temp = node;
+ node = node->next;
+ delete temp;
+ }
+
+ firstChild = 0;
+ lastChild = 0;
+}
+
+
+TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node )
+{
+ assert( node->parent == 0 || node->parent == this );
+ assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() );
+
+ if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ delete node;
+ if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ node->parent = this;
+
+ node->prev = lastChild;
+ node->next = 0;
+
+ if ( lastChild )
+ lastChild->next = node;
+ else
+ firstChild = node; // it was an empty list.
+
+ lastChild = node;
+ return node;
+}
+
+
+TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis )
+{
+ if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+ TiXmlNode* node = addThis.Clone();
+ if ( !node )
+ return 0;
+
+ return LinkEndChild( node );
+}
+
+
+TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis )
+{
+ if ( !beforeThis || beforeThis->parent != this ) {
+ return 0;
+ }
+ if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ TiXmlNode* node = addThis.Clone();
+ if ( !node )
+ return 0;
+ node->parent = this;
+
+ node->next = beforeThis;
+ node->prev = beforeThis->prev;
+ if ( beforeThis->prev )
+ {
+ beforeThis->prev->next = node;
+ }
+ else
+ {
+ assert( firstChild == beforeThis );
+ firstChild = node;
+ }
+ beforeThis->prev = node;
+ return node;
+}
+
+
+TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis )
+{
+ if ( !afterThis || afterThis->parent != this ) {
+ return 0;
+ }
+ if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ TiXmlNode* node = addThis.Clone();
+ if ( !node )
+ return 0;
+ node->parent = this;
+
+ node->prev = afterThis;
+ node->next = afterThis->next;
+ if ( afterThis->next )
+ {
+ afterThis->next->prev = node;
+ }
+ else
+ {
+ assert( lastChild == afterThis );
+ lastChild = node;
+ }
+ afterThis->next = node;
+ return node;
+}
+
+
+TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis )
+{
+ if ( !replaceThis )
+ return 0;
+
+ if ( replaceThis->parent != this )
+ return 0;
+
+ if ( withThis.ToDocument() ) {
+ // A document can never be a child. Thanks to Noam.
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ TiXmlNode* node = withThis.Clone();
+ if ( !node )
+ return 0;
+
+ node->next = replaceThis->next;
+ node->prev = replaceThis->prev;
+
+ if ( replaceThis->next )
+ replaceThis->next->prev = node;
+ else
+ lastChild = node;
+
+ if ( replaceThis->prev )
+ replaceThis->prev->next = node;
+ else
+ firstChild = node;
+
+ delete replaceThis;
+ node->parent = this;
+ return node;
+}
+
+
+bool TiXmlNode::RemoveChild( TiXmlNode* removeThis )
+{
+ if ( !removeThis ) {
+ return false;
+ }
+
+ if ( removeThis->parent != this )
+ {
+ assert( 0 );
+ return false;
+ }
+
+ if ( removeThis->next )
+ removeThis->next->prev = removeThis->prev;
+ else
+ lastChild = removeThis->prev;
+
+ if ( removeThis->prev )
+ removeThis->prev->next = removeThis->next;
+ else
+ firstChild = removeThis->next;
+
+ delete removeThis;
+ return true;
+}
+
+const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = firstChild; node; node = node->next )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = lastChild; node; node = node->prev )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const
+{
+ if ( !previous )
+ {
+ return FirstChild();
+ }
+ else
+ {
+ assert( previous->parent == this );
+ return previous->NextSibling();
+ }
+}
+
+
+const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const
+{
+ if ( !previous )
+ {
+ return FirstChild( val );
+ }
+ else
+ {
+ assert( previous->parent == this );
+ return previous->NextSibling( val );
+ }
+}
+
+
+const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = next; node; node = node->next )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = prev; node; node = node->prev )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+void TiXmlElement::RemoveAttribute( const char * name )
+{
+ #ifdef TIXML_USE_STL
+ TIXML_STRING str( name );
+ TiXmlAttribute* node = attributeSet.Find( str );
+ #else
+ TiXmlAttribute* node = attributeSet.Find( name );
+ #endif
+ if ( node )
+ {
+ attributeSet.Remove( node );
+ delete node;
+ }
+}
+
+const TiXmlElement* TiXmlNode::FirstChildElement() const
+{
+ const TiXmlNode* node;
+
+ for ( node = FirstChild();
+ node;
+ node = node->NextSibling() )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const
+{
+ const TiXmlNode* node;
+
+ for ( node = FirstChild( _value );
+ node;
+ node = node->NextSibling( _value ) )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlElement* TiXmlNode::NextSiblingElement() const
+{
+ const TiXmlNode* node;
+
+ for ( node = NextSibling();
+ node;
+ node = node->NextSibling() )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const
+{
+ const TiXmlNode* node;
+
+ for ( node = NextSibling( _value );
+ node;
+ node = node->NextSibling( _value ) )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlDocument* TiXmlNode::GetDocument() const
+{
+ const TiXmlNode* node;
+
+ for( node = this; node; node = node->parent )
+ {
+ if ( node->ToDocument() )
+ return node->ToDocument();
+ }
+ return 0;
+}
+
+
+TiXmlElement::TiXmlElement (const char * _value)
+ : TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
+{
+ firstChild = lastChild = 0;
+ value = _value;
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlElement::TiXmlElement( const std::string& _value )
+ : TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
+{
+ firstChild = lastChild = 0;
+ value = _value;
+}
+#endif
+
+
+TiXmlElement::TiXmlElement( const TiXmlElement& copy)
+ : TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
+{
+ firstChild = lastChild = 0;
+ copy.CopyTo( this );
+}
+
+
+void TiXmlElement::operator=( const TiXmlElement& base )
+{
+ ClearThis();
+ base.CopyTo( this );
+}
+
+
+TiXmlElement::~TiXmlElement()
+{
+ ClearThis();
+}
+
+
+void TiXmlElement::ClearThis()
+{
+ Clear();
+ while( attributeSet.First() )
+ {
+ TiXmlAttribute* node = attributeSet.First();
+ attributeSet.Remove( node );
+ delete node;
+ }
+}
+
+
+const char* TiXmlElement::Attribute( const char* name ) const
+{
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( node )
+ return node->Value();
+ return 0;
+}
+
+
+#ifdef TIXML_USE_STL
+const std::string* TiXmlElement::Attribute( const std::string& name ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( attrib )
+ return &attrib->ValueStr();
+ return 0;
+}
+#endif
+
+
+const char* TiXmlElement::Attribute( const char* name, int* i ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const char* result = 0;
+
+ if ( attrib ) {
+ result = attrib->Value();
+ if ( i ) {
+ attrib->QueryIntValue( i );
+ }
+ }
+ return result;
+}
+
+
+#ifdef TIXML_USE_STL
+const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const std::string* result = 0;
+
+ if ( attrib ) {
+ result = &attrib->ValueStr();
+ if ( i ) {
+ attrib->QueryIntValue( i );
+ }
+ }
+ return result;
+}
+#endif
+
+
+const char* TiXmlElement::Attribute( const char* name, double* d ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const char* result = 0;
+
+ if ( attrib ) {
+ result = attrib->Value();
+ if ( d ) {
+ attrib->QueryDoubleValue( d );
+ }
+ }
+ return result;
+}
+
+
+#ifdef TIXML_USE_STL
+const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const std::string* result = 0;
+
+ if ( attrib ) {
+ result = &attrib->ValueStr();
+ if ( d ) {
+ attrib->QueryDoubleValue( d );
+ }
+ }
+ return result;
+}
+#endif
+
+
+int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryIntValue( ival );
+}
+
+
+#ifdef TIXML_USE_STL
+int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryIntValue( ival );
+}
+#endif
+
+
+int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryDoubleValue( dval );
+}
+
+
+#ifdef TIXML_USE_STL
+int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryDoubleValue( dval );
+}
+#endif
+
+
+void TiXmlElement::SetAttribute( const char * name, int val )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetIntValue( val );
+ }
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetAttribute( const std::string& name, int val )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetIntValue( val );
+ }
+}
+#endif
+
+
+void TiXmlElement::SetDoubleAttribute( const char * name, double val )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetDoubleValue( val );
+ }
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetDoubleAttribute( const std::string& name, double val )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetDoubleValue( val );
+ }
+}
+#endif
+
+
+void TiXmlElement::SetAttribute( const char * cname, const char * cvalue )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname );
+ if ( attrib ) {
+ attrib->SetValue( cvalue );
+ }
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name );
+ if ( attrib ) {
+ attrib->SetValue( _value );
+ }
+}
+#endif
+
+
+void TiXmlElement::Print( FILE* cfile, int depth ) const
+{
+ int i;
+ assert( cfile );
+ for ( i=0; i<depth; i++ ) {
+ fprintf( cfile, " " );
+ }
+
+ fprintf( cfile, "<%s", value.c_str() );
+
+ const TiXmlAttribute* attrib;
+ for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() )
+ {
+ fprintf( cfile, " " );
+ attrib->Print( cfile, depth );
+ }
+
+ // There are 3 different formatting approaches:
+ // 1) An element without children is printed as a <foo /> node
+ // 2) An element with only a text child is printed as <foo> text </foo>
+ // 3) An element with children is printed on multiple lines.
+ TiXmlNode* node;
+ if ( !firstChild )
+ {
+ fprintf( cfile, " />" );
+ }
+ else if ( firstChild == lastChild && firstChild->ToText() )
+ {
+ fprintf( cfile, ">" );
+ firstChild->Print( cfile, depth + 1 );
+ fprintf( cfile, "</%s>", value.c_str() );
+ }
+ else
+ {
+ fprintf( cfile, ">" );
+
+ for ( node = firstChild; node; node=node->NextSibling() )
+ {
+ if ( !node->ToText() )
+ {
+ fprintf( cfile, "\n" );
+ }
+ node->Print( cfile, depth+1 );
+ }
+ fprintf( cfile, "\n" );
+ for( i=0; i<depth; ++i ) {
+ fprintf( cfile, " " );
+ }
+ fprintf( cfile, "</%s>", value.c_str() );
+ }
+}
+
+
+void TiXmlElement::CopyTo( TiXmlElement* target ) const
+{
+ // superclass:
+ TiXmlNode::CopyTo( target );
+
+ // Element class:
+ // Clone the attributes, then clone the children.
+ const TiXmlAttribute* attribute = 0;
+ for( attribute = attributeSet.First();
+ attribute;
+ attribute = attribute->Next() )
+ {
+ target->SetAttribute( attribute->Name(), attribute->Value() );
+ }
+
+ TiXmlNode* node = 0;
+ for ( node = firstChild; node; node = node->NextSibling() )
+ {
+ target->LinkEndChild( node->Clone() );
+ }
+}
+
+bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const
+{
+ if ( visitor->VisitEnter( *this, attributeSet.First() ) )
+ {
+ for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
+ {
+ if ( !node->Accept( visitor ) )
+ break;
+ }
+ }
+ return visitor->VisitExit( *this );
+}
+
+
+TiXmlNode* TiXmlElement::Clone() const
+{
+ TiXmlElement* clone = new TiXmlElement( Value() );
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+const char* TiXmlElement::GetText() const
+{
+ const TiXmlNode* child = this->FirstChild();
+ if ( child ) {
+ const TiXmlText* childText = child->ToText();
+ if ( childText ) {
+ return childText->Value();
+ }
+ }
+ return 0;
+}
+
+
+TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ tabsize = 4;
+ useMicrosoftBOM = false;
+ ClearError();
+}
+
+TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ tabsize = 4;
+ useMicrosoftBOM = false;
+ value = documentName;
+ ClearError();
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ tabsize = 4;
+ useMicrosoftBOM = false;
+ value = documentName;
+ ClearError();
+}
+#endif
+
+
+TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ copy.CopyTo( this );
+}
+
+
+void TiXmlDocument::operator=( const TiXmlDocument& copy )
+{
+ Clear();
+ copy.CopyTo( this );
+}
+
+
+bool TiXmlDocument::LoadFile( TiXmlEncoding encoding )
+{
+ return LoadFile( Value(), encoding );
+}
+
+
+bool TiXmlDocument::SaveFile() const
+{
+ return SaveFile( Value() );
+}
+
+bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding )
+{
+ TIXML_STRING filename( _filename );
+ value = filename;
+
+ // reading in binary mode so that tinyxml can normalize the EOL
+ FILE* file = TiXmlFOpen( value.c_str (), "rb" );
+
+ if ( file )
+ {
+ bool result = LoadFile( file, encoding );
+ fclose( file );
+ return result;
+ }
+ else
+ {
+ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+}
+
+bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding )
+{
+ if ( !file )
+ {
+ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+
+ // Delete the existing data:
+ Clear();
+ location.Clear();
+
+ // Get the file size, so we can pre-allocate the string. HUGE speed impact.
+ long length = 0;
+ fseek( file, 0, SEEK_END );
+ length = ftell( file );
+ fseek( file, 0, SEEK_SET );
+
+ // Strange case, but good to handle up front.
+ if ( length <= 0 )
+ {
+ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+
+ // Subtle bug here. TinyXml did use fgets. But from the XML spec:
+ // 2.11 End-of-Line Handling
+ // <snip>
+ // <quote>
+ // ...the XML processor MUST behave as if it normalized all line breaks in external
+ // parsed entities (including the document entity) on input, before parsing, by translating
+ // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to
+ // a single #xA character.
+ // </quote>
+ //
+ // It is not clear fgets does that, and certainly isn't clear it works cross platform.
+ // Generally, you expect fgets to translate from the convention of the OS to the c/unix
+ // convention, and not work generally.
+
+ /*
+ while( fgets( buf, sizeof(buf), file ) )
+ {
+ data += buf;
+ }
+ */
+
+ char* buf = new char[ length+1 ];
+ buf[0] = 0;
+
+ if ( fread( buf, length, 1, file ) != 1 ) {
+ delete [] buf;
+ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+
+ // Process the buffer in place to normalize new lines. (See comment above.)
+ // Copies from the 'p' to 'q' pointer, where p can advance faster if
+ // a newline-carriage return is hit.
+ //
+ // Wikipedia:
+ // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or
+ // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)...
+ // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others
+ // * 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
+ // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9
+
+ const char* p = buf; // the read head
+ char* q = buf; // the write head
+ const char CR = 0x0d;
+ const char LF = 0x0a;
+
+ buf[length] = 0;
+ while( *p ) {
+ assert( p < (buf+length) );
+ assert( q <= (buf+length) );
+ assert( q <= p );
+
+ if ( *p == CR ) {
+ *q++ = LF;
+ p++;
+ if ( *p == LF ) { // check for CR+LF (and skip LF)
+ p++;
+ }
+ }
+ else {
+ *q++ = *p++;
+ }
+ }
+ assert( q <= (buf+length) );
+ *q = 0;
+
+ Parse( buf, 0, encoding );
+
+ delete [] buf;
+ return !Error();
+}
+
+
+bool TiXmlDocument::SaveFile( const char * filename ) const
+{
+ // The old c stuff lives on...
+ FILE* fp = TiXmlFOpen( filename, "w" );
+ if ( fp )
+ {
+ bool result = SaveFile( fp );
+ fclose( fp );
+ return result;
+ }
+ return false;
+}
+
+
+bool TiXmlDocument::SaveFile( FILE* fp ) const
+{
+ if ( useMicrosoftBOM )
+ {
+ const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+ const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+ const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+ fputc( TIXML_UTF_LEAD_0, fp );
+ fputc( TIXML_UTF_LEAD_1, fp );
+ fputc( TIXML_UTF_LEAD_2, fp );
+ }
+ Print( fp, 0 );
+ return (ferror(fp) == 0);
+}
+
+
+void TiXmlDocument::CopyTo( TiXmlDocument* target ) const
+{
+ TiXmlNode::CopyTo( target );
+
+ target->error = error;
+ target->errorId = errorId;
+ target->errorDesc = errorDesc;
+ target->tabsize = tabsize;
+ target->errorLocation = errorLocation;
+ target->useMicrosoftBOM = useMicrosoftBOM;
+
+ TiXmlNode* node = 0;
+ for ( node = firstChild; node; node = node->NextSibling() )
+ {
+ target->LinkEndChild( node->Clone() );
+ }
+}
+
+
+TiXmlNode* TiXmlDocument::Clone() const
+{
+ TiXmlDocument* clone = new TiXmlDocument();
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+void TiXmlDocument::Print( FILE* cfile, int depth ) const
+{
+ assert( cfile );
+ for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
+ {
+ node->Print( cfile, depth );
+ fprintf( cfile, "\n" );
+ }
+}
+
+
+bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const
+{
+ if ( visitor->VisitEnter( *this ) )
+ {
+ for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
+ {
+ if ( !node->Accept( visitor ) )
+ break;
+ }
+ }
+ return visitor->VisitExit( *this );
+}
+
+
+const TiXmlAttribute* TiXmlAttribute::Next() const
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( next->value.empty() && next->name.empty() )
+ return 0;
+ return next;
+}
+
+/*
+TiXmlAttribute* TiXmlAttribute::Next()
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( next->value.empty() && next->name.empty() )
+ return 0;
+ return next;
+}
+*/
+
+const TiXmlAttribute* TiXmlAttribute::Previous() const
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( prev->value.empty() && prev->name.empty() )
+ return 0;
+ return prev;
+}
+
+/*
+TiXmlAttribute* TiXmlAttribute::Previous()
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( prev->value.empty() && prev->name.empty() )
+ return 0;
+ return prev;
+}
+*/
+
+void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const
+{
+ TIXML_STRING n, v;
+
+ EncodeString( name, &n );
+ EncodeString( value, &v );
+
+ if (value.find ('\"') == TIXML_STRING::npos) {
+ if ( cfile ) {
+ fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() );
+ }
+ if ( str ) {
+ (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\"";
+ }
+ }
+ else {
+ if ( cfile ) {
+ fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() );
+ }
+ if ( str ) {
+ (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'";
+ }
+ }
+}
+
+
+int TiXmlAttribute::QueryIntValue( int* ival ) const
+{
+ if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 )
+ return TIXML_SUCCESS;
+ return TIXML_WRONG_TYPE;
+}
+
+int TiXmlAttribute::QueryDoubleValue( double* dval ) const
+{
+ if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 )
+ return TIXML_SUCCESS;
+ return TIXML_WRONG_TYPE;
+}
+
+void TiXmlAttribute::SetIntValue( int _value )
+{
+ char buf [64];
+ #if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value);
+ #else
+ sprintf (buf, "%d", _value);
+ #endif
+ SetValue (buf);
+}
+
+void TiXmlAttribute::SetDoubleValue( double _value )
+{
+ char buf [256];
+ #if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF( buf, sizeof(buf), "%g", _value);
+ #else
+ sprintf (buf, "%g", _value);
+ #endif
+ SetValue (buf);
+}
+
+int TiXmlAttribute::IntValue() const
+{
+ return atoi (value.c_str ());
+}
+
+double TiXmlAttribute::DoubleValue() const
+{
+ return atof (value.c_str ());
+}
+
+
+TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT )
+{
+ copy.CopyTo( this );
+}
+
+
+void TiXmlComment::operator=( const TiXmlComment& base )
+{
+ Clear();
+ base.CopyTo( this );
+}
+
+
+void TiXmlComment::Print( FILE* cfile, int depth ) const
+{
+ assert( cfile );
+ for ( int i=0; i<depth; i++ )
+ {
+ fprintf( cfile, " " );
+ }
+ fprintf( cfile, "<!--%s-->", value.c_str() );
+}
+
+
+void TiXmlComment::CopyTo( TiXmlComment* target ) const
+{
+ TiXmlNode::CopyTo( target );
+}
+
+
+bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlComment::Clone() const
+{
+ TiXmlComment* clone = new TiXmlComment();
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+void TiXmlText::Print( FILE* cfile, int depth ) const
+{
+ assert( cfile );
+ if ( cdata )
+ {
+ int i;
+ fprintf( cfile, "\n" );
+ for ( i=0; i<depth; i++ ) {
+ fprintf( cfile, " " );
+ }
+ fprintf( cfile, "<![CDATA[%s]]>\n", value.c_str() ); // unformatted output
+ }
+ else
+ {
+ TIXML_STRING buffer;
+ EncodeString( value, &buffer );
+ fprintf( cfile, "%s", buffer.c_str() );
+ }
+}
+
+
+void TiXmlText::CopyTo( TiXmlText* target ) const
+{
+ TiXmlNode::CopyTo( target );
+ target->cdata = cdata;
+}
+
+
+bool TiXmlText::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlText::Clone() const
+{
+ TiXmlText* clone = 0;
+ clone = new TiXmlText( "" );
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+TiXmlDeclaration::TiXmlDeclaration( const char * _version,
+ const char * _encoding,
+ const char * _standalone )
+ : TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
+{
+ version = _version;
+ encoding = _encoding;
+ standalone = _standalone;
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlDeclaration::TiXmlDeclaration( const std::string& _version,
+ const std::string& _encoding,
+ const std::string& _standalone )
+ : TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
+{
+ version = _version;
+ encoding = _encoding;
+ standalone = _standalone;
+}
+#endif
+
+
+TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy )
+ : TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
+{
+ copy.CopyTo( this );
+}
+
+
+void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy )
+{
+ Clear();
+ copy.CopyTo( this );
+}
+
+
+void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const
+{
+ if ( cfile ) fprintf( cfile, "<?xml " );
+ if ( str ) (*str) += "<?xml ";
+
+ if ( !version.empty() ) {
+ if ( cfile ) fprintf (cfile, "version=\"%s\" ", version.c_str ());
+ if ( str ) { (*str) += "version=\""; (*str) += version; (*str) += "\" "; }
+ }
+ if ( !encoding.empty() ) {
+ if ( cfile ) fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ());
+ if ( str ) { (*str) += "encoding=\""; (*str) += encoding; (*str) += "\" "; }
+ }
+ if ( !standalone.empty() ) {
+ if ( cfile ) fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ());
+ if ( str ) { (*str) += "standalone=\""; (*str) += standalone; (*str) += "\" "; }
+ }
+ if ( cfile ) fprintf( cfile, "?>" );
+ if ( str ) (*str) += "?>";
+}
+
+
+void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const
+{
+ TiXmlNode::CopyTo( target );
+
+ target->version = version;
+ target->encoding = encoding;
+ target->standalone = standalone;
+}
+
+
+bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlDeclaration::Clone() const
+{
+ TiXmlDeclaration* clone = new TiXmlDeclaration();
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+void TiXmlUnknown::Print( FILE* cfile, int depth ) const
+{
+ for ( int i=0; i<depth; i++ )
+ fprintf( cfile, " " );
+ fprintf( cfile, "<%s>", value.c_str() );
+}
+
+
+void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const
+{
+ TiXmlNode::CopyTo( target );
+}
+
+
+bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlUnknown::Clone() const
+{
+ TiXmlUnknown* clone = new TiXmlUnknown();
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+TiXmlAttributeSet::TiXmlAttributeSet()
+{
+ sentinel.next = &sentinel;
+ sentinel.prev = &sentinel;
+}
+
+
+TiXmlAttributeSet::~TiXmlAttributeSet()
+{
+ assert( sentinel.next == &sentinel );
+ assert( sentinel.prev == &sentinel );
+}
+
+
+void TiXmlAttributeSet::Add( TiXmlAttribute* addMe )
+{
+ #ifdef TIXML_USE_STL
+ assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set.
+ #else
+ assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set.
+ #endif
+
+ addMe->next = &sentinel;
+ addMe->prev = sentinel.prev;
+
+ sentinel.prev->next = addMe;
+ sentinel.prev = addMe;
+}
+
+void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe )
+{
+ TiXmlAttribute* node;
+
+ for( node = sentinel.next; node != &sentinel; node = node->next )
+ {
+ if ( node == removeMe )
+ {
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ node->next = 0;
+ node->prev = 0;
+ return;
+ }
+ }
+ assert( 0 ); // we tried to remove a non-linked attribute.
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const
+{
+ for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
+ {
+ if ( node->name == name )
+ return node;
+ }
+ return 0;
+}
+
+TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name )
+{
+ TiXmlAttribute* attrib = Find( _name );
+ if ( !attrib ) {
+ attrib = new TiXmlAttribute();
+ Add( attrib );
+ attrib->SetName( _name );
+ }
+ return attrib;
+}
+#endif
+
+
+TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const
+{
+ for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
+ {
+ if ( strcmp( node->name.c_str(), name ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name )
+{
+ TiXmlAttribute* attrib = Find( _name );
+ if ( !attrib ) {
+ attrib = new TiXmlAttribute();
+ Add( attrib );
+ attrib->SetName( _name );
+ }
+ return attrib;
+}
+
+
+#ifdef TIXML_USE_STL
+std::istream& operator>> (std::istream & in, TiXmlNode & base)
+{
+ TIXML_STRING tag;
+ tag.reserve( 8 * 1000 );
+ base.StreamIn( &in, &tag );
+
+ base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING );
+ return in;
+}
+#endif
+
+
+#ifdef TIXML_USE_STL
+std::ostream& operator<< (std::ostream & out, const TiXmlNode & base)
+{
+ TiXmlPrinter printer;
+ printer.SetStreamPrinting();
+ base.Accept( &printer );
+ out << printer.Str();
+
+ return out;
+}
+
+
+std::string& operator<< (std::string& out, const TiXmlNode& base )
+{
+ TiXmlPrinter printer;
+ printer.SetStreamPrinting();
+ base.Accept( &printer );
+ out.append( printer.Str() );
+
+ return out;
+}
+#endif
+
+
+TiXmlHandle TiXmlHandle::FirstChild() const
+{
+ if ( node )
+ {
+ TiXmlNode* child = node->FirstChild();
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const
+{
+ if ( node )
+ {
+ TiXmlNode* child = node->FirstChild( value );
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChildElement() const