diff --git a/AUTHORS b/AUTHORS index dd2de970e5..89d0485035 100644 --- a/AUTHORS +++ b/AUTHORS @@ -57,10 +57,3 @@ wxWidgets, which are Copyright (C) 2004-5 by John Labenski, and distributed under the wxWidgets license. The version included here is the version dated 20 July 2005; see http://wxcode.sourceforge.net for details on these classes. - -TinyXML parser: ---------------- -The graphical interface reads and writes files in XML format. The parsing -is done using the TinyXml package from http://www.grinninglizard.com/tinyxml, -and is distributed under the zlib license. These are the files named -tiny* in the sources/gui directory of the distribution. diff --git a/ChangeLog b/ChangeLog index 19965ca196..bce4d092d0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +## [17.0.0] - unreleased + +### Changed +- Implemented bespoke XML parser that handles the subset in the de-facto legacy XML workbook .gbt format; + removes dependency on tinyxml. (#897) + + ## [16.7.0] - 2026-07-11 ### Added diff --git a/Makefile.am b/Makefile.am index 573c3780bb..0ba2b10ca7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -223,11 +223,7 @@ core_SOURCES = \ src/core/rational.h \ src/core/matrix.cc \ src/core/function.cc \ - src/core/function.h \ - src/core/tinyxml.cc \ - src/core/tinyxmlerror.cc \ - src/core/tinyxmlparser.cc \ - src/core/tinyxml.h + src/core/function.h agg_SOURCES = \ src/games/gameagg.cc \ @@ -270,6 +266,8 @@ game_SOURCES = \ src/games/stratpure.h \ src/games/stratmixed.h \ src/games/file.cc \ + src/games/workspace.cc \ + src/games/workspace.h \ src/games/writer.cc \ src/games/writer.h \ src/games/layout.cc \ diff --git a/src/core/tinyxml.cc b/src/core/tinyxml.cc deleted file mode 100644 index 2f4e58d5f1..0000000000 --- a/src/core/tinyxml.cc +++ /dev/null @@ -1,1559 +0,0 @@ -/* -www.sourceforge.net/projects/tinyxml -Original code (2.0 and earlier )copyright (c) 2000-2002 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 "tinyxml.h" - -#ifdef TIXML_USE_STL -#include -#endif - -bool TiXmlBase::condenseWhiteSpace = true; - -void TiXmlBase::PutString(const TIXML_STRING &str, TIXML_OSTREAM *stream) -{ - TIXML_STRING buffer; - PutString(str, &buffer); - (*stream) << buffer; -} - -void TiXmlBase::PutString(const TIXML_STRING &str, TIXML_STRING *outString) -{ - int i = 0; - - while (i < (int)str.length()) { - auto 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. - // © -- 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; - } - } -} - -// <-- Strange class for a bug fix. Search for STL_STRING_BUG -TiXmlBase::StringToBuffer::StringToBuffer(const TIXML_STRING &str) - : buffer(new char[str.length() + 1]) -{ - if (buffer) { - strcpy(buffer, str.c_str()); - } -} - -TiXmlBase::StringToBuffer::~StringToBuffer() { delete[] buffer; } -// End strange bug fix. --> - -TiXmlNode::~TiXmlNode() -{ - const TiXmlNode *node = firstChild; - const TiXmlNode *temp = nullptr; - - while (node) { - temp = node; - node = node->next; - delete temp; - } -} - -void TiXmlNode::CopyTo(TiXmlNode *target) const -{ - target->SetValue(value.c_str()); - target->userData = userData; -} - -void TiXmlNode::Clear() -{ - const TiXmlNode *node = firstChild; - const TiXmlNode *temp = nullptr; - - while (node) { - temp = node; - node = node->next; - delete temp; - } - - firstChild = nullptr; - lastChild = nullptr; -} - -TiXmlNode *TiXmlNode::LinkEndChild(TiXmlNode *node) -{ - node->parent = this; - - node->prev = lastChild; - node->next = nullptr; - - if (lastChild) { - lastChild->next = node; - } - else { - firstChild = node; // it was an empty list. - } - - lastChild = node; - return node; -} - -TiXmlNode *TiXmlNode::InsertEndChild(const TiXmlNode &addThis) -{ - TiXmlNode *node = addThis.Clone(); - if (!node) { - return nullptr; - } - - return LinkEndChild(node); -} - -TiXmlNode *TiXmlNode::InsertBeforeChild(TiXmlNode *beforeThis, const TiXmlNode &addThis) -{ - if (!beforeThis || beforeThis->parent != this) { - return nullptr; - } - - TiXmlNode *node = addThis.Clone(); - if (!node) { - return nullptr; - } - 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 nullptr; - } - - TiXmlNode *node = addThis.Clone(); - if (!node) { - return nullptr; - } - 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->parent != this) { - return nullptr; - } - - TiXmlNode *node = withThis.Clone(); - if (!node) { - return nullptr; - } - - 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->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 nullptr; -} - -TiXmlNode *TiXmlNode::FirstChild(const char *_value) -{ - TiXmlNode *node; - for (node = firstChild; node; node = node->next) { - if (strcmp(node->Value(), _value) == 0) { - return node; - } - } - return nullptr; -} - -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 nullptr; -} - -TiXmlNode *TiXmlNode::LastChild(const char *_value) -{ - TiXmlNode *node; - for (node = lastChild; node; node = node->prev) { - if (strcmp(node->Value(), _value) == 0) { - return node; - } - } - return nullptr; -} - -const TiXmlNode *TiXmlNode::IterateChildren(const TiXmlNode *previous) const -{ - if (!previous) { - return FirstChild(); - } - else { - assert(previous->parent == this); - return previous->NextSibling(); - } -} - -TiXmlNode *TiXmlNode::IterateChildren(TiXmlNode *previous) -{ - 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); - } -} - -TiXmlNode *TiXmlNode::IterateChildren(const char *val, TiXmlNode *previous) -{ - 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 nullptr; -} - -TiXmlNode *TiXmlNode::NextSibling(const char *_value) -{ - TiXmlNode *node; - for (node = next; node; node = node->next) { - if (strcmp(node->Value(), _value) == 0) { - return node; - } - } - return nullptr; -} - -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 nullptr; -} - -TiXmlNode *TiXmlNode::PreviousSibling(const char *_value) -{ - TiXmlNode *node; - for (node = prev; node; node = node->prev) { - if (strcmp(node->Value(), _value) == 0) { - return node; - } - } - return nullptr; -} - -void TiXmlElement::RemoveAttribute(const char *name) -{ - TiXmlAttribute *node = attributeSet.Find(name); - 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 nullptr; -} - -TiXmlElement *TiXmlNode::FirstChildElement() -{ - TiXmlNode *node; - - for (node = FirstChild(); node; node = node->NextSibling()) { - if (node->ToElement()) { - return node->ToElement(); - } - } - return nullptr; -} - -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 nullptr; -} - -TiXmlElement *TiXmlNode::FirstChildElement(const char *_value) -{ - TiXmlNode *node; - - for (node = FirstChild(_value); node; node = node->NextSibling(_value)) { - if (node->ToElement()) { - return node->ToElement(); - } - } - return nullptr; -} - -const TiXmlElement *TiXmlNode::NextSiblingElement() const -{ - const TiXmlNode *node; - - for (node = NextSibling(); node; node = node->NextSibling()) { - if (node->ToElement()) { - return node->ToElement(); - } - } - return nullptr; -} - -TiXmlElement *TiXmlNode::NextSiblingElement() -{ - TiXmlNode *node; - - for (node = NextSibling(); node; node = node->NextSibling()) { - if (node->ToElement()) { - return node->ToElement(); - } - } - return nullptr; -} - -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 nullptr; -} - -TiXmlElement *TiXmlNode::NextSiblingElement(const char *_value) -{ - TiXmlNode *node; - - for (node = NextSibling(_value); node; node = node->NextSibling(_value)) { - if (node->ToElement()) { - return node->ToElement(); - } - } - return nullptr; -} - -const TiXmlDocument *TiXmlNode::GetDocument() const -{ - const TiXmlNode *node; - - for (node = this; node; node = node->parent) { - if (node->ToDocument()) { - return node->ToDocument(); - } - } - return nullptr; -} - -TiXmlDocument *TiXmlNode::GetDocument() -{ - TiXmlNode *node; - - for (node = this; node; node = node->parent) { - if (node->ToDocument()) { - return node->ToDocument(); - } - } - return nullptr; -} - -TiXmlElement::TiXmlElement(const char *_value) : TiXmlNode(TiXmlNode::ELEMENT) -{ - firstChild = lastChild = nullptr; - value = _value; -} - -#ifdef TIXML_USE_STL -TiXmlElement::TiXmlElement(const std::string &_value) : TiXmlNode(TiXmlNode::ELEMENT) -{ - firstChild = lastChild = nullptr; - value = _value; -} -#endif - -TiXmlElement::TiXmlElement(const TiXmlElement ©) : TiXmlNode(TiXmlNode::ELEMENT) -{ - firstChild = lastChild = nullptr; - 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 nullptr; -} - -const char *TiXmlElement::Attribute(const char *name, int *i) const -{ - const char *s = Attribute(name); - if (i) { - if (s) { - *i = atoi(s); - } - else { - *i = 0; - } - } - return s; -} - -const char *TiXmlElement::Attribute(const char *name, double *d) const -{ - const char *s = Attribute(name); - if (d) { - if (s) { - *d = atof(s); - } - else { - *d = 0; - } - } - return s; -} - -int TiXmlElement::QueryIntAttribute(const char *name, int *ival) const -{ - const TiXmlAttribute *node = attributeSet.Find(name); - if (!node) { - return TIXML_NO_ATTRIBUTE; - } - - return node->QueryIntValue(ival); -} - -int TiXmlElement::QueryDoubleAttribute(const char *name, double *dval) const -{ - const TiXmlAttribute *node = attributeSet.Find(name); - if (!node) { - return TIXML_NO_ATTRIBUTE; - } - - return node->QueryDoubleValue(dval); -} - -void TiXmlElement::SetAttribute(const char *name, int val) -{ - char buf[64]; -#if defined(TIXML_SNPRINTF) - TIXML_SNPRINTF(buf, sizeof(buf), "%d", val); -#else - sprintf(buf, "%d", val); -#endif - SetAttribute(name, buf); -} - -void TiXmlElement::SetDoubleAttribute(const char *name, double val) -{ - char buf[256]; -#if defined(TIXML_SNPRINTF) - TIXML_SNPRINTF(buf, sizeof(buf), "%f", val); -#else - sprintf(buf, "%f", val); -#endif - SetAttribute(name, buf); -} - -void TiXmlElement::SetAttribute(const char *name, const char *_value) -{ - TiXmlAttribute *node = attributeSet.Find(name); - if (node) { - node->SetValue(_value); - return; - } - - auto *attrib = new TiXmlAttribute(name, _value); - if (attrib) { - attributeSet.Add(attrib); - } - else { - TiXmlDocument *document = GetDocument(); - if (document) { - document->SetError(TIXML_ERROR_OUT_OF_MEMORY, nullptr, nullptr, TIXML_ENCODING_UNKNOWN); - } - } -} - -void TiXmlElement::Print(FILE *cfile, int depth) const -{ - int i; - 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 node - // 2) An element with only a text child is printed as text - // 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, "", 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, "", value.c_str()); - } -} - -void TiXmlElement::StreamOut(TIXML_OSTREAM *stream) const -{ - (*stream) << "<" << value; - - const TiXmlAttribute *attrib; - for (attrib = attributeSet.First(); attrib; attrib = attrib->Next()) { - (*stream) << " "; - attrib->StreamOut(stream); - } - - // If this node has children, give it a closing tag. Else - // make it an empty tag. - TiXmlNode *node; - if (firstChild) { - (*stream) << ">"; - - for (node = firstChild; node; node = node->NextSibling()) { - node->StreamOut(stream); - } - (*stream) << ""; - } - else { - (*stream) << " />"; - } -} - -void TiXmlElement::CopyTo(TiXmlElement *target) const -{ - // superclass: - TiXmlNode::CopyTo(target); - - // Element class: - // Clone the attributes, then clone the children. - const TiXmlAttribute *attribute = nullptr; - for (attribute = attributeSet.First(); attribute; attribute = attribute->Next()) { - target->SetAttribute(attribute->Name(), attribute->Value()); - } - - TiXmlNode *node = nullptr; - for (node = firstChild; node; node = node->NextSibling()) { - target->LinkEndChild(node->Clone()); - } -} - -TiXmlNode *TiXmlElement::Clone() const -{ - auto *clone = new TiXmlElement(Value()); - if (!clone) { - return nullptr; - } - - 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 nullptr; -} - -TiXmlDocument::TiXmlDocument() : TiXmlNode(TiXmlNode::DOCUMENT) { ClearError(); } - -TiXmlDocument::TiXmlDocument(const char *documentName) : TiXmlNode(TiXmlNode::DOCUMENT) -{ - value = documentName; - ClearError(); -} - -#ifdef TIXML_USE_STL -TiXmlDocument::TiXmlDocument(const std::string &documentName) : TiXmlNode(TiXmlNode::DOCUMENT) -{ - value = documentName; - ClearError(); -} -#endif - -TiXmlDocument::TiXmlDocument(const TiXmlDocument ©) : TiXmlNode(TiXmlNode::DOCUMENT) -{ - copy.CopyTo(this); -} - -void TiXmlDocument::operator=(const TiXmlDocument ©) -{ - Clear(); - copy.CopyTo(this); -} - -bool TiXmlDocument::LoadFile(TiXmlEncoding encoding) -{ - // See STL_STRING_BUG below. - const StringToBuffer buf(value); - - if (buf.buffer && LoadFile(buf.buffer, encoding)) { - return true; - } - - return false; -} - -bool TiXmlDocument::SaveFile() const -{ - // See STL_STRING_BUG below. - const StringToBuffer buf(value); - return (buf.buffer && SaveFile(buf.buffer)); -} - -bool TiXmlDocument::LoadFile(const char *filename, TiXmlEncoding encoding) -{ - // Delete the existing data: - Clear(); - location.Clear(); - - // There was a really terrifying little bug here. The code: - // value = filename - // in the STL case, cause the assignment method of the std::string to - // be called. What is strange, is that the std::string had the same - // address as it's c_str() method, and so bad things happen. Looks - // like a bug in the Microsoft STL implementation. - // See STL_STRING_BUG above. - // Fixed with the StringToBuffer class. - value = filename; - - // reading in binary mode so that tinyxml can normalize the EOL - FILE *file = fopen(value.c_str(), "rb"); - - if (file) { - // 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) { - fclose(file); - return false; - } - - // If we have a file, assume it is all one big XML file, and read it in. - // The document parser may decide the document ends sooner than the entire file, however. - TIXML_STRING data; - data.reserve(length); - - // Subtle bug here. TinyXml did use fgets. But from the XML spec: - // 2.11 End-of-Line Handling - // - // - // ...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. - // - // - // 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) { - // if ( fread( buf, 1, length, file ) != (size_t)length ) { - SetError(TIXML_ERROR_OPENING_FILE, nullptr, nullptr, TIXML_ENCODING_UNKNOWN); - fclose(file); - return false; - } - fclose(file); - - const char *lastPos = buf; - const char *p = buf; - - buf[length] = 0; - while (*p) { - assert(p < (buf + length)); - if (*p == 0xa) { - // Newline character. No special rules for this. Append all the characters - // since the last string, and include the newline. - data.append(lastPos, p - lastPos + 1); // append, include the newline - ++p; // move past the newline - lastPos = p; // and point to the new buffer (may be 0) - assert(p <= (buf + length)); - } - else if (*p == 0xd) { - // Carriage return. Append what we have so far, then - // handle moving forward in the buffer. - if ((p - lastPos) > 0) { - data.append(lastPos, p - lastPos); // do not add the CR - } - data += (char)0xa; // a proper newline - - if (*(p + 1) == 0xa) { - // Carriage return - new line sequence - p += 2; - lastPos = p; - assert(p <= (buf + length)); - } - else { - // it was followed by something else...that is presumably characters again. - ++p; - lastPos = p; - assert(p <= (buf + length)); - } - } - else { - ++p; - } - } - // Handle any left over characters. - if (p - lastPos) { - data.append(lastPos, p - lastPos); - } - delete[] buf; - buf = nullptr; - - Parse(data.c_str(), nullptr, encoding); - - if (Error()) { - return false; - } - else { - return true; - } - } - SetError(TIXML_ERROR_OPENING_FILE, nullptr, nullptr, TIXML_ENCODING_UNKNOWN); - return false; -} - -bool TiXmlDocument::SaveFile(const char *filename) const -{ - // The old c stuff lives on... - FILE *fp = fopen(filename, "w"); - if (fp) { - 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); - fclose(fp); - return true; - } - return false; -} - -void TiXmlDocument::CopyTo(TiXmlDocument *target) const -{ - TiXmlNode::CopyTo(target); - - target->error = error; - target->errorDesc = errorDesc.c_str(); - - TiXmlNode *node = nullptr; - for (node = firstChild; node; node = node->NextSibling()) { - target->LinkEndChild(node->Clone()); - } -} - -TiXmlNode *TiXmlDocument::Clone() const -{ - auto *clone = new TiXmlDocument(); - if (!clone) { - return nullptr; - } - - CopyTo(clone); - return clone; -} - -void TiXmlDocument::Print(FILE *cfile, int depth) const -{ - const TiXmlNode *node; - for (node = FirstChild(); node; node = node->NextSibling()) { - node->Print(cfile, depth); - fprintf(cfile, "\n"); - } -} - -void TiXmlDocument::StreamOut(TIXML_OSTREAM *out) const -{ - const TiXmlNode *node; - for (node = FirstChild(); node; node = node->NextSibling()) { - node->StreamOut(out); - - // Special rule for streams: stop after the root element. - // The stream in code will only read one element, so don't - // write more than one. - if (node->ToElement()) { - break; - } - } -} - -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 nullptr; - } - 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 nullptr; - } - 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 nullptr; - } - 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 nullptr; - } - return prev; -} - -void TiXmlAttribute::Print(FILE *cfile, int /*depth*/) const -{ - TIXML_STRING n, v; - - PutString(name, &n); - PutString(value, &v); - - if (value.find('\"') == TIXML_STRING::npos) { - fprintf(cfile, "%s=\"%s\"", n.c_str(), v.c_str()); - } - else { - fprintf(cfile, "%s='%s'", n.c_str(), v.c_str()); - } -} - -void TiXmlAttribute::StreamOut(TIXML_OSTREAM *stream) const -{ - if (value.find('\"') != TIXML_STRING::npos) { - PutString(name, stream); - (*stream) << "=" - << "'"; - PutString(value, stream); - (*stream) << "'"; - } - else { - PutString(name, stream); - (*stream) << "=" - << "\""; - PutString(value, stream); - (*stream) << "\""; - } -} - -int TiXmlAttribute::QueryIntValue(int *ival) const -{ - if (sscanf(value.c_str(), "%d", ival) == 1) { - return TIXML_SUCCESS; - } - return TIXML_WRONG_TYPE; -} - -int TiXmlAttribute::QueryDoubleValue(double *dval) const -{ - if (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), "%lf", _value); -#else - sprintf(buf, "%lf", _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 ©) : TiXmlNode(TiXmlNode::COMMENT) -{ - copy.CopyTo(this); -} - -void TiXmlComment::operator=(const TiXmlComment &base) -{ - Clear(); - base.CopyTo(this); -} - -void TiXmlComment::Print(FILE *cfile, int depth) const -{ - for (int i = 0; i < depth; i++) { - fputs(" ", cfile); - } - fprintf(cfile, "", value.c_str()); -} - -void TiXmlComment::StreamOut(TIXML_OSTREAM *stream) const -{ - (*stream) << ""; -} - -void TiXmlComment::CopyTo(TiXmlComment *target) const { TiXmlNode::CopyTo(target); } - -TiXmlNode *TiXmlComment::Clone() const -{ - auto *clone = new TiXmlComment(); - - if (!clone) { - return nullptr; - } - - CopyTo(clone); - return clone; -} - -void TiXmlText::Print(FILE *cfile, int depth) const -{ - if (cdata) { - int i; - fprintf(cfile, "\n"); - for (i = 0; i < depth; i++) { - fprintf(cfile, " "); - } - fprintf(cfile, "\n"); - } - else { - TIXML_STRING buffer; - PutString(value, &buffer); - fprintf(cfile, "%s", buffer.c_str()); - } -} - -void TiXmlText::StreamOut(TIXML_OSTREAM *stream) const -{ - if (cdata) { - (*stream) << ""; - } - else { - PutString(value, stream); - } -} - -void TiXmlText::CopyTo(TiXmlText *target) const -{ - TiXmlNode::CopyTo(target); - target->cdata = cdata; -} - -TiXmlNode *TiXmlText::Clone() const -{ - TiXmlText *clone = nullptr; - clone = new TiXmlText(""); - - if (!clone) { - return nullptr; - } - - CopyTo(clone); - return clone; -} - -TiXmlDeclaration::TiXmlDeclaration(const char *_version, const char *_encoding, - const char *_standalone) - : TiXmlNode(TiXmlNode::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::DECLARATION), version(_version), encoding(_encoding), - standalone(_standalone) -{ -} -#endif - -TiXmlDeclaration::TiXmlDeclaration(const TiXmlDeclaration ©) - : TiXmlNode(TiXmlNode::DECLARATION) -{ - copy.CopyTo(this); -} - -void TiXmlDeclaration::operator=(const TiXmlDeclaration ©) -{ - Clear(); - copy.CopyTo(this); -} - -void TiXmlDeclaration::Print(FILE *cfile, int /*depth*/) const -{ - fprintf(cfile, ""); -} - -void TiXmlDeclaration::StreamOut(TIXML_OSTREAM *stream) const -{ - (*stream) << ""; -} - -void TiXmlDeclaration::CopyTo(TiXmlDeclaration *target) const -{ - TiXmlNode::CopyTo(target); - - target->version = version; - target->encoding = encoding; - target->standalone = standalone; -} - -TiXmlNode *TiXmlDeclaration::Clone() const -{ - auto *clone = new TiXmlDeclaration(); - - if (!clone) { - return nullptr; - } - - 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::StreamOut(TIXML_OSTREAM *stream) const -{ - (*stream) << "<" << value << ">"; // Don't use entities here! It is unknown. -} - -void TiXmlUnknown::CopyTo(TiXmlUnknown *target) const { TiXmlNode::CopyTo(target); } - -TiXmlNode *TiXmlUnknown::Clone() const -{ - auto *clone = new TiXmlUnknown(); - - if (!clone) { - return nullptr; - } - - 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) -{ - assert(!Find(addMe->Name())); // Shouldn't be multiply adding to the set. - - 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 = nullptr; - node->prev = nullptr; - return; - } - } - assert(0); // we tried to remove a non-linked attribute. -} - -const TiXmlAttribute *TiXmlAttributeSet::Find(const char *name) const -{ - const TiXmlAttribute *node; - - for (node = sentinel.next; node != &sentinel; node = node->next) { - if (node->name == name) { - return node; - } - } - return nullptr; -} - -TiXmlAttribute *TiXmlAttributeSet::Find(const char *name) -{ - TiXmlAttribute *node; - - for (node = sentinel.next; node != &sentinel; node = node->next) { - if (node->name == name) { - return node; - } - } - return nullptr; -} - -#ifdef TIXML_USE_STL -TIXML_ISTREAM &operator>>(TIXML_ISTREAM &in, TiXmlNode &base) -{ - TIXML_STRING tag; - tag.reserve(8 * 1000); - base.StreamIn(&in, &tag); - - base.Parse(tag.c_str(), nullptr, TIXML_DEFAULT_ENCODING); - return in; -} -#endif - -TIXML_OSTREAM &operator<<(TIXML_OSTREAM &out, const TiXmlNode &base) -{ - base.StreamOut(&out); - return out; -} - -#ifdef TIXML_USE_STL -std::string &operator<<(std::string &out, const TiXmlNode &base) -{ - std::ostringstream os_stream(std::ostringstream::out); - base.StreamOut(&os_stream); - - out.append(os_stream.str()); - return out; -} -#endif - -TiXmlHandle TiXmlHandle::FirstChild() const -{ - if (node) { - TiXmlNode *child = node->FirstChild(); - if (child) { - return {child}; - } - } - return {nullptr}; -} - -TiXmlHandle TiXmlHandle::FirstChild(const char *value) const -{ - if (node) { - TiXmlNode *child = node->FirstChild(value); - if (child) { - return {child}; - } - } - return {nullptr}; -} - -TiXmlHandle TiXmlHandle::FirstChildElement() const -{ - if (node) { - TiXmlElement *child = node->FirstChildElement(); - if (child) { - return {child}; - } - } - return {nullptr}; -} - -TiXmlHandle TiXmlHandle::FirstChildElement(const char *value) const -{ - if (node) { - TiXmlElement *child = node->FirstChildElement(value); - if (child) { - return {child}; - } - } - return {nullptr}; -} - -TiXmlHandle TiXmlHandle::Child(int count) const -{ - if (node) { - int i; - TiXmlNode *child = node->FirstChild(); - for (i = 0; child && i < count; child = child->NextSibling(), ++i) { - // nothing - } - if (child) { - return {child}; - } - } - return {nullptr}; -} - -TiXmlHandle TiXmlHandle::Child(const char *value, int count) const -{ - if (node) { - int i; - TiXmlNode *child = node->FirstChild(value); - for (i = 0; child && i < count; child = child->NextSibling(value), ++i) { - // nothing - } - if (child) { - return {child}; - } - } - return {nullptr}; -} - -TiXmlHandle TiXmlHandle::ChildElement(int count) const -{ - if (node) { - int i; - TiXmlElement *child = node->FirstChildElement(); - for (i = 0; child && i < count; child = child->NextSiblingElement(), ++i) { - // nothing - } - if (child) { - return {child}; - } - } - return {nullptr}; -} - -TiXmlHandle TiXmlHandle::ChildElement(const char *value, int count) const -{ - if (node) { - int i; - TiXmlElement *child = node->FirstChildElement(value); - for (i = 0; child && i < count; child = child->NextSiblingElement(value), ++i) { - // nothing - } - if (child) { - return {child}; - } - } - return {nullptr}; -} diff --git a/src/core/tinyxml.h b/src/core/tinyxml.h deleted file mode 100644 index 209cf7c63b..0000000000 --- a/src/core/tinyxml.h +++ /dev/null @@ -1,1602 +0,0 @@ -/* -www.sourceforge.net/projects/tinyxml -Original code (2.0 and earlier )copyright (c) 2000-2002 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. -*/ - -#ifndef TINYXML_INCLUDED -#define TINYXML_INCLUDED - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4530) -#pragma warning(disable : 4786) -#endif - -#include -#include -#include -#include -#include - -// Help out windows: -#if defined(_DEBUG) && !defined(DEBUG) -#define DEBUG -#endif - -#if defined(DEBUG) && defined(_MSC_VER) -#include -#define TIXML_LOG OutputDebugString -#else -#define TIXML_LOG printf -#endif - -// Gambit change: we use STL, so we define this here. -#define TIXML_USE_STL 1 - -#ifdef TIXML_USE_STL -#include -#include -#define TIXML_STRING std::string -#define TIXML_ISTREAM std::istream -#define TIXML_OSTREAM std::ostream -#else -#include "tinystr.h" -#define TIXML_STRING TiXmlString -#define TIXML_OSTREAM TiXmlOutStream -#endif - -// Deprecated library function hell. Compilers want to use the -// new safe versions. This probably doesn't fully address the problem, -// but it gets closer. There are too many compilers for me to fully -// test. If you get compilation troubles, undefine TIXML_SAFE - -#define TIXML_SAFE // TinyXml isn't fully buffer overrun protected, safe code. This is work in - // progress. -#ifdef TIXML_SAFE -#if defined(_MSC_VER) && (_MSC_VER >= 1200) -// Microsoft visual studio, version 6 and higher. -// #pragma message( "Using _sn* functions." ) -#define TIXML_SNPRINTF _snprintf -#define TIXML_SNSCANF _snscanf -#elif defined(__GNUC__) && (__GNUC__ >= 3) -// GCC version 3 and higher.s -// #warning( "Using sn* functions." ) -#define TIXML_SNPRINTF snprintf -#define TIXML_SNSCANF snscanf -#endif -#endif - -class TiXmlDocument; -class TiXmlElement; -class TiXmlComment; -class TiXmlUnknown; -class TiXmlAttribute; -class TiXmlText; -class TiXmlDeclaration; -class TiXmlParsingData; - -const int TIXML_MAJOR_VERSION = 2; -const int TIXML_MINOR_VERSION = 4; -const int TIXML_PATCH_VERSION = 2; - -/* Internal structure for tracking location of items - in the XML file. -*/ -struct TiXmlCursor { - TiXmlCursor() = default; - void Clear() { row = col = -1; } - - int row{-1}; // 0 based. - int col{-1}; // 0 based. -}; - -// Only used by Attribute::Query functions -enum { TIXML_SUCCESS, TIXML_NO_ATTRIBUTE, TIXML_WRONG_TYPE }; - -// Used by the parsing routines. -enum TiXmlEncoding { TIXML_ENCODING_UNKNOWN, TIXML_ENCODING_UTF8, TIXML_ENCODING_LEGACY }; - -const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; - -/** TiXmlBase is a base class for every class in TinyXml. - It does little except to establish that TinyXml classes - can be printed and provide some utility functions. - - In XML, the document and elements can contain - other elements and other types of nodes. - - @verbatim - A Document can contain: Element (container or leaf) - Comment (leaf) - Unknown (leaf) - Declaration( leaf ) - - An Element can contain: Element (container or leaf) - Text (leaf) - Attributes (not on tree) - Comment (leaf) - Unknown (leaf) - - A Decleration contains: Attributes (not on tree) - @endverbatim -*/ -class TiXmlBase { - friend class TiXmlNode; - friend class TiXmlElement; - friend class TiXmlDocument; - -public: - TiXmlBase() = default; - virtual ~TiXmlBase() = default; - - /** All TinyXml classes can print themselves to a filestream. - This is a formatted print, and will insert tabs and newlines. - - (For an unformatted stream, use the << operator.) - */ - virtual void Print(FILE *cfile, int depth) const = 0; - - /** The world does not agree on whether white space should be kept or - not. In order to make everyone happy, these global, static functions - are provided to set whether or not TinyXml will condense all white space - into a single space or not. The default is to condense. Note changing this - values is not thread safe. - */ - static void SetCondenseWhiteSpace(bool condense) { condenseWhiteSpace = condense; } - - /// Return the current white space setting. - static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } - - /** Return the position, in the original source file, of this node or attribute. - The row and column are 1-based. (That is the first row and first column is - 1,1). If the returns values are 0 or less, then the parser does not have - a row and column value. - - Generally, the row and column value will be set when the TiXmlDocument::Load(), - TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set - when the DOM was created from operator>>. - - The values reflect the initial load. Once the DOM is modified programmatically - (by adding or changing nodes and attributes) the new values will NOT update to - reflect changes in the document. - - There is a minor performance cost to computing the row and column. Computation - can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. - - @sa TiXmlDocument::SetTabSize() - */ - int Row() const { return location.row + 1; } - int Column() const { return location.col + 1; } ///< See Row() - - void SetUserData(void *user) { userData = user; } - void *GetUserData() { return userData; } - - // Table that returs, for a given lead byte, the total number of bytes - // in the UTF-8 sequence. - static const int utf8ByteTable[256]; - - virtual const char *Parse(const char *p, TiXmlParsingData *data, - TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */) = 0; - - enum { - TIXML_NO_ERROR = 0, - TIXML_ERROR, - TIXML_ERROR_OPENING_FILE, - TIXML_ERROR_OUT_OF_MEMORY, - TIXML_ERROR_PARSING_ELEMENT, - TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, - TIXML_ERROR_READING_ELEMENT_VALUE, - TIXML_ERROR_READING_ATTRIBUTES, - TIXML_ERROR_PARSING_EMPTY, - TIXML_ERROR_READING_END_TAG, - TIXML_ERROR_PARSING_UNKNOWN, - TIXML_ERROR_PARSING_COMMENT, - TIXML_ERROR_PARSING_DECLARATION, - TIXML_ERROR_DOCUMENT_EMPTY, - TIXML_ERROR_EMBEDDED_NULL, - TIXML_ERROR_PARSING_CDATA, - - TIXML_ERROR_STRING_COUNT - }; - -protected: - // See STL_STRING_BUG - // Utility class to overcome a bug. - class StringToBuffer { - public: - StringToBuffer(const TIXML_STRING &str); - ~StringToBuffer(); - char *buffer; - }; - - static const char *SkipWhiteSpace(const char *, TiXmlEncoding encoding); - inline static bool IsWhiteSpace(char c) - { - return (isspace((unsigned char)c) || c == '\n' || c == '\r'); - } - - virtual void StreamOut(TIXML_OSTREAM *) const = 0; - -#ifdef TIXML_USE_STL - static bool StreamWhiteSpace(TIXML_ISTREAM *in, TIXML_STRING *tag); - static bool StreamTo(TIXML_ISTREAM *in, int character, TIXML_STRING *tag); -#endif - - /* Reads an XML name into the string provided. Returns - a pointer just past the last character of the name, - or 0 if the function has an error. - */ - static const char *ReadName(const char *p, TIXML_STRING *name, TiXmlEncoding encoding); - - /* Reads text. Returns a pointer past the given end tag. - Wickedly complex options, but it keeps the (sensitive) code in one place. - */ - static const char *ReadText(const char *in, // where to start - TIXML_STRING *text, // the string read - bool ignoreWhiteSpace, // whether to keep the white space - const char *endTag, // what ends this text - bool ignoreCase, // whether to ignore case in the end tag - TiXmlEncoding encoding); // the current encoding - - // If an entity has been found, transform it into a character. - static const char *GetEntity(const char *in, char *value, int *length, TiXmlEncoding encoding); - - // Get a character, while interpreting entities. - // The length can be from 0 to 4 bytes. - inline static const char *GetChar(const char *p, char *_value, int *length, - TiXmlEncoding encoding) - { - assert(p); - if (encoding == TIXML_ENCODING_UTF8) { - *length = utf8ByteTable[*(reinterpret_cast(p))]; - assert(*length >= 0 && *length < 5); - } - else { - *length = 1; - } - - if (*length == 1) { - if (*p == '&') { - return GetEntity(p, _value, length, encoding); - } - *_value = *p; - return p + 1; - } - else if (*length) { - // strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), - // and the null terminator isn't needed - for (int i = 0; p[i] && i < *length; ++i) { - _value[i] = p[i]; - } - return p + (*length); - } - else { - // Not valid text. - return nullptr; - } - } - - // Puts a string to a stream, expanding entities as it goes. - // Note this should not contian the '<', '>', etc, or they will be transformed into entities! - static void PutString(const TIXML_STRING &str, TIXML_OSTREAM *out); - - static void PutString(const TIXML_STRING &str, TIXML_STRING *out); - - // Return true if the next characters in the stream are any of the endTag sequences. - // Ignore case only works for english, and should only be relied on when comparing - // to English words: StringEqual( p, "version", true ) is fine. - static bool StringEqual(const char *p, const char *endTag, bool ignoreCase, - TiXmlEncoding encoding); - - static const char *errorString[TIXML_ERROR_STRING_COUNT]; - - TiXmlCursor location; - - /// Field containing a generic user pointer - void *userData{nullptr}; - - // None of these methods are reliable for any language except English. - // Good for approximation, not great for accuracy. - static int IsAlpha(unsigned char anyByte, TiXmlEncoding encoding); - static int IsAlphaNum(unsigned char anyByte, TiXmlEncoding encoding); - inline static int ToLower(int v, TiXmlEncoding encoding) - { - if (encoding == TIXML_ENCODING_UTF8) { - if (v < 128) { - return tolower(v); - } - return v; - } - else { - return tolower(v); - } - } - static void ConvertUTF32ToUTF8(unsigned long input, char *output, int *length); - -private: - TiXmlBase(const TiXmlBase &); // not implemented. - void operator=(const TiXmlBase &base); // not allowed. - - struct Entity { - const char *str; - unsigned int strLength; - char chr; - }; - enum { - NUM_ENTITY = 5, - MAX_ENTITY_LENGTH = 6 - - }; - static Entity entity[NUM_ENTITY]; - static bool condenseWhiteSpace; -}; - -/** The parent class for everything in the Document Object Model. - (Except for attributes). - Nodes have siblings, a parent, and children. A node can be - in a document, or stand on its own. The type of a TiXmlNode - can be queried, and it can be cast to its more defined type. -*/ -class TiXmlNode : public TiXmlBase { - friend class TiXmlDocument; - friend class TiXmlElement; - -public: -#ifdef TIXML_USE_STL - - /** An input stream operator, for every class. Tolerant of newlines and - formatting, but doesn't expect them. - */ - friend std::istream &operator>>(std::istream &in, TiXmlNode &base); - - /** An output stream operator, for every class. Note that this outputs - without any newlines or formatting, as opposed to Print(), which - includes tabs and new lines. - - The operator<< and operator>> are not completely symmetric. Writing - a node to a stream is very well defined. You'll get a nice stream - of output, without any extra whitespace or newlines. - - But reading is not as well defined. (As it always is.) If you create - a TiXmlElement (for example) and read that from an input stream, - the text needs to define an element or junk will result. This is - true of all input streams, but it's worth keeping in mind. - - A TiXmlDocument will read nodes until it reads a root element, and - all the children of that root element. - */ - friend std::ostream &operator<<(std::ostream &out, const TiXmlNode &base); - - /// Appends the XML node or attribute to a std::string. - friend std::string &operator<<(std::string &out, const TiXmlNode &base); - -#else - // Used internally, not part of the public API. - friend TIXML_OSTREAM &operator<<(TIXML_OSTREAM &out, const TiXmlNode &base); -#endif - - /** The types of XML nodes supported by TinyXml. (All the - unsupported types are picked up by UNKNOWN.) - */ - enum NodeType { DOCUMENT, ELEMENT, COMMENT, UNKNOWN, TEXT, DECLARATION, TYPECOUNT }; - - ~TiXmlNode() override; - - /** The meaning of 'value' changes for the specific type of - TiXmlNode. - @verbatim - Document: filename of the xml file - Element: name of the element - Comment: the comment text - Unknown: the tag contents - Text: the text string - @endverbatim - - The subclasses will wrap this function. - */ - const char *Value() const { return value.c_str(); } - -#ifdef TIXML_USE_STL - /** Return Value() as a std::string. If you only use STL, - this is more efficient than calling Value(). - Only available in STL mode. - */ - const std::string &ValueStr() const { return value; } -#endif - - /** Changes the value of the node. Defined as: - @verbatim - Document: filename of the xml file - Element: name of the element - Comment: the comment text - Unknown: the tag contents - Text: the text string - @endverbatim - */ - void SetValue(const char *_value) { value = _value; } - -#ifdef TIXML_USE_STL - /// STL std::string form. - void SetValue(const std::string &_value) - { - StringToBuffer const buf(_value); - SetValue(buf.buffer ? buf.buffer : ""); - } -#endif - - /// Delete all the children of this node. Does not affect 'this'. - void Clear(); - - /// One step up the DOM. - TiXmlNode *Parent() { return parent; } - const TiXmlNode *Parent() const { return parent; } - - const TiXmlNode *FirstChild() const - { - return firstChild; - } ///< The first child of this node. Will be null if there are no children. - TiXmlNode *FirstChild() { return firstChild; } - const TiXmlNode * - FirstChild(const char *value) const; ///< The first child of this node with the matching 'value'. - ///< Will be null if none found. - TiXmlNode *FirstChild(const char *value); ///< The first child of this node with the matching - ///< 'value'. Will be null if none found. - - const TiXmlNode *LastChild() const - { - return lastChild; - } /// The last child of this node. Will be null if there are no children. - TiXmlNode *LastChild() { return lastChild; } - const TiXmlNode * - LastChild(const char *value) const; /// The last child of this node matching 'value'. Will be - /// null if there are no children. - TiXmlNode *LastChild(const char *value); - -#ifdef TIXML_USE_STL - const TiXmlNode *FirstChild(const std::string &_value) const - { - return FirstChild(_value.c_str()); - } ///< STL std::string form. - TiXmlNode *FirstChild(const std::string &_value) - { - return FirstChild(_value.c_str()); - } ///< STL std::string form. - const TiXmlNode *LastChild(const std::string &_value) const - { - return LastChild(_value.c_str()); - } ///< STL std::string form. - TiXmlNode *LastChild(const std::string &_value) - { - return LastChild(_value.c_str()); - } ///< STL std::string form. -#endif - - /** An alternate way to walk the children of a node. - One way to iterate over nodes is: - @verbatim - for( child = parent->FirstChild(); child; child = child->NextSibling() ) - @endverbatim - - IterateChildren does the same thing with the syntax: - @verbatim - child = 0; - while( child = parent->IterateChildren( child ) ) - @endverbatim - - IterateChildren takes the previous child as input and finds - the next one. If the previous child is null, it returns the - first. IterateChildren will return null when done. - */ - const TiXmlNode *IterateChildren(const TiXmlNode *previous) const; - TiXmlNode *IterateChildren(TiXmlNode *previous); - - /// This flavor of IterateChildren searches for children with a particular 'value' - const TiXmlNode *IterateChildren(const char *value, const TiXmlNode *previous) const; - TiXmlNode *IterateChildren(const char *value, TiXmlNode *previous); - -#ifdef TIXML_USE_STL - const TiXmlNode *IterateChildren(const std::string &_value, const TiXmlNode *previous) const - { - return IterateChildren(_value.c_str(), previous); - } ///< STL std::string form. - TiXmlNode *IterateChildren(const std::string &_value, TiXmlNode *previous) - { - return IterateChildren(_value.c_str(), previous); - } ///< STL std::string form. -#endif - - /** Add a new node related to this. Adds a child past the LastChild. - Returns a pointer to the new object or NULL if an error occured. - */ - TiXmlNode *InsertEndChild(const TiXmlNode &addThis); - - /** Add a new node related to this. Adds a child past the LastChild. - - NOTE: the node to be added is passed by pointer, and will be - henceforth owned (and deleted) by tinyXml. This method is efficient - and avoids an extra copy, but should be used with care as it - uses a different memory model than the other insert functions. - - @sa InsertEndChild - */ - TiXmlNode *LinkEndChild(TiXmlNode *addThis); - - /** Add a new node related to this. Adds a child before the specified child. - Returns a pointer to the new object or NULL if an error occured. - */ - TiXmlNode *InsertBeforeChild(TiXmlNode *beforeThis, const TiXmlNode &addThis); - - /** Add a new node related to this. Adds a child after the specified child. - Returns a pointer to the new object or NULL if an error occured. - */ - TiXmlNode *InsertAfterChild(TiXmlNode *afterThis, const TiXmlNode &addThis); - - /** Replace a child of this node. - Returns a pointer to the new object or NULL if an error occured. - */ - TiXmlNode *ReplaceChild(TiXmlNode *replaceThis, const TiXmlNode &withThis); - - /// Delete a child of this node. - bool RemoveChild(TiXmlNode *removeThis); - - /// Navigate to a sibling node. - const TiXmlNode *PreviousSibling() const { return prev; } - TiXmlNode *PreviousSibling() { return prev; } - - /// Navigate to a sibling node. - const TiXmlNode *PreviousSibling(const char *) const; - TiXmlNode *PreviousSibling(const char *); - -#ifdef TIXML_USE_STL - const TiXmlNode *PreviousSibling(const std::string &_value) const - { - return PreviousSibling(_value.c_str()); - } ///< STL std::string form. - TiXmlNode *PreviousSibling(const std::string &_value) - { - return PreviousSibling(_value.c_str()); - } ///< STL std::string form. - const TiXmlNode *NextSibling(const std::string &_value) const - { - return NextSibling(_value.c_str()); - } ///< STL std::string form. - TiXmlNode *NextSibling(const std::string &_value) - { - return NextSibling(_value.c_str()); - } ///< STL std::string form. -#endif - - /// Navigate to a sibling node. - const TiXmlNode *NextSibling() const { return next; } - TiXmlNode *NextSibling() { return next; } - - /// Navigate to a sibling node with the given 'value'. - const TiXmlNode *NextSibling(const char *) const; - TiXmlNode *NextSibling(const char *); - - /** Convenience function to get through elements. - Calls NextSibling and ToElement. Will skip all non-Element - nodes. Returns 0 if there is not another element. - */ - const TiXmlElement *NextSiblingElement() const; - TiXmlElement *NextSiblingElement(); - - /** Convenience function to get through elements. - Calls NextSibling and ToElement. Will skip all non-Element - nodes. Returns 0 if there is not another element. - */ - const TiXmlElement *NextSiblingElement(const char *) const; - TiXmlElement *NextSiblingElement(const char *); - -#ifdef TIXML_USE_STL - const TiXmlElement *NextSiblingElement(const std::string &_value) const - { - return NextSiblingElement(_value.c_str()); - } ///< STL std::string form. - TiXmlElement *NextSiblingElement(const std::string &_value) - { - return NextSiblingElement(_value.c_str()); - } ///< STL std::string form. -#endif - - /// Convenience function to get through elements. - const TiXmlElement *FirstChildElement() const; - TiXmlElement *FirstChildElement(); - - /// Convenience function to get through elements. - const TiXmlElement *FirstChildElement(const char *value) const; - TiXmlElement *FirstChildElement(const char *value); - -#ifdef TIXML_USE_STL - const TiXmlElement *FirstChildElement(const std::string &_value) const - { - return FirstChildElement(_value.c_str()); - } ///< STL std::string form. - TiXmlElement *FirstChildElement(const std::string &_value) - { - return FirstChildElement(_value.c_str()); - } ///< STL std::string form. -#endif - - /** Query the type (as an enumerated value, above) of this node. - The possible types are: DOCUMENT, ELEMENT, COMMENT, - UNKNOWN, TEXT, and DECLARATION. - */ - int Type() const { return type; } - - /** Return a pointer to the Document this node lives in. - Returns null if not in a document. - */ - const TiXmlDocument *GetDocument() const; - TiXmlDocument *GetDocument(); - - /// Returns true if this node has no children. - bool NoChildren() const { return !firstChild; } - - const TiXmlDocument *ToDocument() const; - const TiXmlElement *ToElement() const; - const TiXmlComment *ToComment() const; - const TiXmlUnknown *ToUnknown() const; - const TiXmlText *ToText() const; - const TiXmlDeclaration *ToDeclaration() const; - - TiXmlDocument *ToDocument(); - TiXmlElement *ToElement(); - TiXmlComment *ToComment(); - TiXmlUnknown *ToUnknown(); - TiXmlText *ToText(); - TiXmlDeclaration *ToDeclaration(); - - /** Create an exact duplicate of this node and return it. The memory must be deleted - by the caller. - */ - virtual TiXmlNode *Clone() const = 0; - -protected: - TiXmlNode(NodeType _type) : TiXmlBase(), type(_type) {} - - // Copy to the allocated object. Shared functionality between Clone, Copy constructor, - // and the assignment operator. - void CopyTo(TiXmlNode *target) const; - -#ifdef TIXML_USE_STL - // The real work of the input operator. - virtual void StreamIn(TIXML_ISTREAM *in, TIXML_STRING *tag) = 0; -#endif - - // Figure out what is at *p, and parse it. Returns null if it is not an xml node. - TiXmlNode *Identify(const char *start, TiXmlEncoding encoding); - - TiXmlNode *parent{nullptr}; - NodeType type; - - TiXmlNode *firstChild{nullptr}; - TiXmlNode *lastChild{nullptr}; - - TIXML_STRING value; - - TiXmlNode *prev{nullptr}; - TiXmlNode *next{nullptr}; - -private: - TiXmlNode(const TiXmlNode &); // not implemented. - void operator=(const TiXmlNode &base); // not allowed. -}; - -/** An attribute is a name-value pair. Elements have an arbitrary - number of attributes, each with a unique name. - - @note The attributes are not TiXmlNodes, since they are not - part of the tinyXML document object model. There are other - suggested ways to look at this problem. -*/ -class TiXmlAttribute : public TiXmlBase { - friend class TiXmlAttributeSet; - -public: - /// Construct an empty attribute. - TiXmlAttribute() : TiXmlBase(), document(nullptr) { prev = next = nullptr; } - -#ifdef TIXML_USE_STL - /// std::string constructor. - TiXmlAttribute(const std::string &_name, const std::string &_value) - : document(nullptr), name(_name), value(_value) - { - prev = next = nullptr; - } -#endif - - /// Construct an attribute with a name and value. - TiXmlAttribute(const char *_name, const char *_value) - : document(nullptr), name(_name), value(_value) - { - prev = next = nullptr; - } - - const char *Name() const { return name.c_str(); } ///< Return the name of this attribute. - const char *Value() const { return value.c_str(); } ///< Return the value of this attribute. - int IntValue() const; ///< Return the value of this attribute, converted to an integer. - double DoubleValue() const; ///< Return the value of this attribute, converted to a double. - - /** QueryIntValue examines the value string. It is an alternative to the - IntValue() method with richer error checking. - If the value is an integer, it is stored in 'value' and - the call returns TIXML_SUCCESS. If it is not - an integer, it returns TIXML_WRONG_TYPE. - - A specialized but useful call. Note that for success it returns 0, - which is the opposite of almost all other TinyXml calls. - */ - int QueryIntValue(int *_value) const; - /// QueryDoubleValue examines the value string. See QueryIntValue(). - int QueryDoubleValue(double *_value) const; - - void SetName(const char *_name) { name = _name; } ///< Set the name of this attribute. - void SetValue(const char *_value) { value = _value; } ///< Set the value. - - void SetIntValue(int _value); ///< Set the value from an integer. - void SetDoubleValue(double _value); ///< Set the value from a double. - -#ifdef TIXML_USE_STL - /// STL std::string form. - void SetName(const std::string &_name) - { - const StringToBuffer buf(_name); - SetName(buf.buffer ? buf.buffer : "error"); - } - /// STL std::string form. - void SetValue(const std::string &_value) - { - const StringToBuffer buf(_value); - SetValue(buf.buffer ? buf.buffer : "error"); - } -#endif - - /// Get the next sibling attribute in the DOM. Returns null at end. - const TiXmlAttribute *Next() const; - TiXmlAttribute *Next(); - /// Get the previous sibling attribute in the DOM. Returns null at beginning. - const TiXmlAttribute *Previous() const; - TiXmlAttribute *Previous(); - - bool operator==(const TiXmlAttribute &rhs) const { return rhs.name == name; } - bool operator<(const TiXmlAttribute &rhs) const { return name < rhs.name; } - bool operator>(const TiXmlAttribute &rhs) const { return name > rhs.name; } - - /* Attribute parsing starts: first letter of the name - returns: the next char after the value end quote - */ - const char *Parse(const char *p, TiXmlParsingData *data, TiXmlEncoding encoding) override; - - // Prints this Attribute to a FILE stream. - void Print(FILE *cfile, int depth) const override; - - void StreamOut(TIXML_OSTREAM *out) const override; - // [internal use] - // Set the document pointer so the attribute can report errors. - void SetDocument(TiXmlDocument *doc) { document = doc; } - -private: - TiXmlAttribute(const TiXmlAttribute &); // not implemented. - void operator=(const TiXmlAttribute &base); // not allowed. - - TiXmlDocument *document; // A pointer back to a document, for error reporting. - TIXML_STRING name; - TIXML_STRING value; - TiXmlAttribute *prev; - TiXmlAttribute *next; -}; - -/* A class used to manage a group of attributes. - It is only used internally, both by the ELEMENT and the DECLARATION. - - The set can be changed transparent to the Element and Declaration - classes that use it, but NOT transparent to the Attribute - which has to implement a next() and previous() method. Which makes - it a bit problematic and prevents the use of STL. - - This version is implemented with circular lists because: - - I like circular lists - - it demonstrates some independence from the (typical) doubly linked list. -*/ -class TiXmlAttributeSet { -public: - TiXmlAttributeSet(); - ~TiXmlAttributeSet(); - - void Add(TiXmlAttribute *attribute); - void Remove(TiXmlAttribute *attribute); - - const TiXmlAttribute *First() const - { - return (sentinel.next == &sentinel) ? nullptr : sentinel.next; - } - TiXmlAttribute *First() { return (sentinel.next == &sentinel) ? nullptr : sentinel.next; } - const TiXmlAttribute *Last() const - { - return (sentinel.prev == &sentinel) ? nullptr : sentinel.prev; - } - TiXmlAttribute *Last() { return (sentinel.prev == &sentinel) ? nullptr : sentinel.prev; } - - const TiXmlAttribute *Find(const char *name) const; - TiXmlAttribute *Find(const char *name); - - TiXmlAttributeSet(const TiXmlAttributeSet &) = delete; // not allowed - void operator=(const TiXmlAttributeSet &) = delete; // not allowed (as TiXmlAttribute) - -private: - TiXmlAttribute sentinel; -}; - -/** The element is a container class. It has a value, the element name, - and can contain other elements, text, comments, and unknowns. - Elements also contain an arbitrary number of attributes. -*/ -class TiXmlElement : public TiXmlNode { -public: - /// Construct an element. - TiXmlElement(const char *in_value); - -#ifdef TIXML_USE_STL - /// std::string constructor. - TiXmlElement(const std::string &_value); -#endif - - TiXmlElement(const TiXmlElement &); - - void operator=(const TiXmlElement &base); - - ~TiXmlElement() override; - - /** Given an attribute name, Attribute() returns the value - for the attribute of that name, or null if none exists. - */ - const char *Attribute(const char *name) const; - - /** Given an attribute name, Attribute() returns the value - for the attribute of that name, or null if none exists. - If the attribute exists and can be converted to an integer, - the integer value will be put in the return 'i', if 'i' - is non-null. - */ - const char *Attribute(const char *name, int *i) const; - - /** Given an attribute name, Attribute() returns the value - for the attribute of that name, or null if none exists. - If the attribute exists and can be converted to an double, - the double value will be put in the return 'd', if 'd' - is non-null. - */ - const char *Attribute(const char *name, double *d) const; - - /** QueryIntAttribute examines the attribute - it is an alternative to the - Attribute() method with richer error checking. - If the attribute is an integer, it is stored in 'value' and - the call returns TIXML_SUCCESS. If it is not - an integer, it returns TIXML_WRONG_TYPE. If the attribute - does not exist, then TIXML_NO_ATTRIBUTE is returned. - */ - int QueryIntAttribute(const char *name, int *_value) const; - /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). - int QueryDoubleAttribute(const char *name, double *_value) const; - /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). - int QueryFloatAttribute(const char *name, float *_value) const - { - double d; - const int result = QueryDoubleAttribute(name, &d); - if (result == TIXML_SUCCESS) { - *_value = (float)d; - } - return result; - } - - /** Sets an attribute of name to a given value. The attribute - will be created if it does not exist, or changed if it does. - */ - void SetAttribute(const char *name, const char *_value); - -#ifdef TIXML_USE_STL - const char *Attribute(const std::string &name) const { return Attribute(name.c_str()); } - const char *Attribute(const std::string &name, int *i) const - { - return Attribute(name.c_str(), i); - } - const char *Attribute(const std::string &name, double *d) const - { - return Attribute(name.c_str(), d); - } - int QueryIntAttribute(const std::string &name, int *_value) const - { - return QueryIntAttribute(name.c_str(), _value); - } - int QueryDoubleAttribute(const std::string &name, double *_value) const - { - return QueryDoubleAttribute(name.c_str(), _value); - } - - /// STL std::string form. - void SetAttribute(const std::string &name, const std::string &_value) - { - const StringToBuffer n(name); - const StringToBuffer v(_value); - if (n.buffer && v.buffer) { - SetAttribute(n.buffer, v.buffer); - } - } - ///< STL std::string form. - void SetAttribute(const std::string &name, int _value) - { - const StringToBuffer n(name); - if (n.buffer) { - SetAttribute(n.buffer, _value); - } - } -#endif - - /** Sets an attribute of name to a given value. The attribute - will be created if it does not exist, or changed if it does. - */ - void SetAttribute(const char *name, int value); - - /** Sets an attribute of name to a given value. The attribute - will be created if it does not exist, or changed if it does. - */ - void SetDoubleAttribute(const char *name, double value); - - /** Deletes an attribute with the given name. - */ - void RemoveAttribute(const char *name); -#ifdef TIXML_USE_STL - void RemoveAttribute(const std::string &name) - { - RemoveAttribute(name.c_str()); - } ///< STL std::string form. -#endif - - const TiXmlAttribute *FirstAttribute() const - { - return attributeSet.First(); - } ///< Access the first attribute in this element. - TiXmlAttribute *FirstAttribute() { return attributeSet.First(); } - const TiXmlAttribute *LastAttribute() const - { - return attributeSet.Last(); - } ///< Access the last attribute in this element. - TiXmlAttribute *LastAttribute() { return attributeSet.Last(); } - - /** Convenience function for easy access to the text inside an element. Although easy - and concise, GetText() is limited compared to getting the TiXmlText child - and accessing it directly. - - If the first child of 'this' is a TiXmlText, the GetText() - returns the character string of the Text node, else null is returned. - - This is a convenient method for getting the text of simple contained text: - @verbatim - This is text - const char* str = fooElement->GetText(); - @endverbatim - - 'str' will be a pointer to "This is text". - - Note that this function can be misleading. If the element foo was created from - this XML: - @verbatim - This is text - @endverbatim - - then the value of str would be null. The first child node isn't a text node, it is - another element. From this XML: - @verbatim - This is text - @endverbatim - GetText() will return "This is ". - - WARNING: GetText() accesses a child node - don't become confused with the - similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are - safe type casts on the referenced node. - */ - const char *GetText() const; - - /// Creates a new Element and returns it - the returned element is a copy. - TiXmlNode *Clone() const override; - // Print the Element to a FILE stream. - void Print(FILE *cfile, int depth) const override; - - /* Attribtue parsing starts: next char past '<' - returns: next char past '>' - */ - const char *Parse(const char *p, TiXmlParsingData *data, TiXmlEncoding encoding) override; - -protected: - void CopyTo(TiXmlElement *target) const; - void ClearThis(); // like clear, but initializes 'this' object as well - -// Used to be public [internal use] -#ifdef TIXML_USE_STL - void StreamIn(TIXML_ISTREAM *in, TIXML_STRING *tag) override; -#endif - void StreamOut(TIXML_OSTREAM *out) const override; - - /* [internal use] - Reads the "value" of the element -- another element, or text. - This should terminate with the current end tag. - */ - const char *ReadValue(const char *in, TiXmlParsingData *prevData, TiXmlEncoding encoding); - -private: - TiXmlAttributeSet attributeSet; -}; - -/** An XML comment. - */ -class TiXmlComment : public TiXmlNode { -public: - /// Constructs an empty comment. - TiXmlComment() : TiXmlNode(TiXmlNode::COMMENT) {} - TiXmlComment(const TiXmlComment &); - void operator=(const TiXmlComment &base); - - ~TiXmlComment() override = default; - - /// Returns a copy of this Comment. - TiXmlNode *Clone() const override; - /// Write this Comment to a FILE stream. - void Print(FILE *cfile, int depth) const override; - - /* Attribtue parsing starts: at the ! of the !-- - returns: next char past '>' - */ - const char *Parse(const char *p, TiXmlParsingData *data, TiXmlEncoding encoding) override; - -protected: - void CopyTo(TiXmlComment *target) const; - -// used to be public -#ifdef TIXML_USE_STL - void StreamIn(TIXML_ISTREAM *in, TIXML_STRING *tag) override; -#endif - void StreamOut(TIXML_OSTREAM *out) const override; - -private: -}; - -/** XML text. A text node can have 2 ways to output the next. "normal" output - and CDATA. It will default to the mode it was parsed from the XML file and - you generally want to leave it alone, but you can change the output mode with - SetCDATA() and query it with CDATA(). -*/ -class TiXmlText : public TiXmlNode { - friend class TiXmlElement; - -public: - /** Constructor for text element. By default, it is treated as - normal, encoded text. If you want it be output as a CDATA text - element, set the parameter _cdata to 'true' - */ - TiXmlText(const char *initValue) : TiXmlNode(TiXmlNode::TEXT), cdata(false) - { - SetValue(initValue); - } - ~TiXmlText() override = default; - -#ifdef TIXML_USE_STL - /// Constructor. - TiXmlText(const std::string &initValue) : TiXmlNode(TiXmlNode::TEXT), cdata(false) - { - SetValue(initValue); - } -#endif - - TiXmlText(const TiXmlText ©) : TiXmlNode(TiXmlNode::TEXT), cdata(copy.cdata) - { - copy.CopyTo(this); - } - void operator=(const TiXmlText &base) { base.CopyTo(this); } - - /// Write this text object to a FILE stream. - void Print(FILE *cfile, int depth) const override; - - /// Queries whether this represents text using a CDATA section. - bool CDATA() { return cdata; } - /// Turns on or off a CDATA representation of text. - void SetCDATA(bool _cdata) { cdata = _cdata; } - - const char *Parse(const char *p, TiXmlParsingData *data, TiXmlEncoding encoding) override; - -protected: - /// [internal use] Creates a new Element and returns it. - TiXmlNode *Clone() const override; - void CopyTo(TiXmlText *target) const; - - void StreamOut(TIXML_OSTREAM *out) const override; - bool Blank() const; // returns true if all white space and new lines -// [internal use] -#ifdef TIXML_USE_STL - void StreamIn(TIXML_ISTREAM *in, TIXML_STRING *tag) override; -#endif - -private: - bool cdata; // true if this should be input and output as a CDATA style text element -}; - -/** In correct XML the declaration is the first entry in the file. - @verbatim - - @endverbatim - - TinyXml will happily read or write files without a declaration, - however. There are 3 possible attributes to the declaration: - version, encoding, and standalone. - - Note: In this version of the code, the attributes are - handled as special cases, not generic attributes, simply - because there can only be at most 3 and they are always the same. -*/ -class TiXmlDeclaration : public TiXmlNode { -public: - /// Construct an empty declaration. - TiXmlDeclaration() : TiXmlNode(TiXmlNode::DECLARATION) {} - -#ifdef TIXML_USE_STL - /// Constructor. - TiXmlDeclaration(const std::string &_version, const std::string &_encoding, - const std::string &_standalone); -#endif - - /// Construct. - TiXmlDeclaration(const char *_version, const char *_encoding, const char *_standalone); - - TiXmlDeclaration(const TiXmlDeclaration ©); - void operator=(const TiXmlDeclaration ©); - - ~TiXmlDeclaration() override = default; - - /// Version. Will return an empty string if none was found. - const char *Version() const { return version.c_str(); } - /// Encoding. Will return an empty string if none was found. - const char *Encoding() const { return encoding.c_str(); } - /// Is this a standalone document? - const char *Standalone() const { return standalone.c_str(); } - - /// Creates a copy of this Declaration and returns it. - TiXmlNode *Clone() const override; - /// Print this declaration to a FILE stream. - void Print(FILE *cfile, int depth) const override; - - const char *Parse(const char *p, TiXmlParsingData *data, TiXmlEncoding encoding) override; - -protected: - void CopyTo(TiXmlDeclaration *target) const; -// used to be public -#ifdef TIXML_USE_STL - void StreamIn(TIXML_ISTREAM *in, TIXML_STRING *tag) override; -#endif - void StreamOut(TIXML_OSTREAM *out) const override; - -private: - TIXML_STRING version; - TIXML_STRING encoding; - TIXML_STRING standalone; -}; - -/** Any tag that tinyXml doesn't recognize is saved as an - unknown. It is a tag of text, but should not be modified. - It will be written back to the XML, unchanged, when the file - is saved. - - DTD tags get thrown into TiXmlUnknowns. -*/ -class TiXmlUnknown : public TiXmlNode { -public: - TiXmlUnknown() : TiXmlNode(TiXmlNode::UNKNOWN) {} - ~TiXmlUnknown() override = default; - - TiXmlUnknown(const TiXmlUnknown ©) : TiXmlNode(TiXmlNode::UNKNOWN) { copy.CopyTo(this); } - void operator=(const TiXmlUnknown ©) { copy.CopyTo(this); } - - /// Creates a copy of this Unknown and returns it. - TiXmlNode *Clone() const override; - /// Print this Unknown to a FILE stream. - void Print(FILE *cfile, int depth) const override; - - const char *Parse(const char *p, TiXmlParsingData *data, TiXmlEncoding encoding) override; - -protected: - void CopyTo(TiXmlUnknown *target) const; - -#ifdef TIXML_USE_STL - void StreamIn(TIXML_ISTREAM *in, TIXML_STRING *tag) override; -#endif - void StreamOut(TIXML_OSTREAM *out) const override; - -private: -}; - -/** Always the top level node. A document binds together all the - XML pieces. It can be saved, loaded, and printed to the screen. - The 'value' of a document node is the xml file name. -*/ -class TiXmlDocument : public TiXmlNode { -public: - /// Create an empty document, that has no name. - TiXmlDocument(); - /// Create a document with a name. The name of the document is also the filename of the xml. - TiXmlDocument(const char *documentName); - -#ifdef TIXML_USE_STL - /// Constructor. - TiXmlDocument(const std::string &documentName); -#endif - - TiXmlDocument(const TiXmlDocument ©); - void operator=(const TiXmlDocument ©); - - ~TiXmlDocument() override = default; - - /** Load a file using the current document value. - Returns true if successful. Will delete any existing - document data before loading. - */ - bool LoadFile(TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING); - /// Save a file using the current document value. Returns true if successful. - bool SaveFile() const; - /// Load a file using the given filename. Returns true if successful. - bool LoadFile(const char *filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING); - /// Save a file using the given filename. Returns true if successful. - bool SaveFile(const char *filename) const; - -#ifdef TIXML_USE_STL - bool LoadFile(const std::string &filename, - TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING) ///< STL std::string version. - { - const StringToBuffer f(filename); - return (f.buffer && LoadFile(f.buffer, encoding)); - } - bool SaveFile(const std::string &filename) const ///< STL std::string version. - { - const StringToBuffer f(filename); - return (f.buffer && SaveFile(f.buffer)); - } -#endif - - /** Parse the given null terminated block of xml data. Passing in an encoding to this - method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml - to use that encoding, regardless of what TinyXml might otherwise try to detect. - */ - const char *Parse(const char *p, TiXmlParsingData *data = nullptr, - TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING) override; - - /** Get the root element -- the only top level element -- of the document. - In well formed XML, there should only be one. TinyXml is tolerant of - multiple elements at the document level. - */ - const TiXmlElement *RootElement() const { return FirstChildElement(); } - TiXmlElement *RootElement() { return FirstChildElement(); } - - /** If an error occurs, Error will be set to true. Also, - - The ErrorId() will contain the integer identifier of the error (not generally useful) - - The ErrorDesc() method will return the name of the error. (very useful) - - The ErrorRow() and ErrorCol() will return the location of the error (if known) - */ - bool Error() const { return error; } - - /// Contains a textual (english) description of the error if one occurs. - const char *ErrorDesc() const { return errorDesc.c_str(); } - - /** Generally, you probably want the error string ( ErrorDesc() ). But if you - prefer the ErrorId, this function will fetch it. - */ - int ErrorId() const { return errorId; } - - /** Returns the location (if known) of the error. The first column is column 1, - and the first row is row 1. A value of 0 means the row and column wasn't applicable - (memory errors, for example, have no row/column) or the parser lost the error. (An - error in the error reporting, in that case.) - - @sa SetTabSize, Row, Column - */ - int ErrorRow() { return errorLocation.row + 1; } - int ErrorCol() - { - return errorLocation.col + 1; - } ///< The column where the error occured. See ErrorRow() - - /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) - to report the correct values for row and column. It does not change the output - or input in any way. - - By calling this method, with a tab size - greater than 0, the row and column of each node and attribute is stored - when the file is loaded. Very useful for tracking the DOM back in to - the source file. - - The tab size is required for calculating the location of nodes. If not - set, the default of 4 is used. The tabsize is set per document. Setting - the tabsize to 0 disables row/column tracking. - - Note that row and column tracking is not supported when using operator>>. - - The tab size needs to be enabled before the parse or load. Correct usage: - @verbatim - TiXmlDocument doc; - doc.SetTabSize( 8 ); - doc.Load( "myfile.xml" ); - @endverbatim - - @sa Row, Column - */ - void SetTabSize(int _tabsize) { tabsize = _tabsize; } - - int TabSize() const { return tabsize; } - - /** If you have handled the error, it can be reset with this call. The error - state is automatically cleared if you Parse a new XML block. - */ - void ClearError() - { - error = false; - errorId = 0; - errorDesc = ""; - errorLocation.row = errorLocation.col = 0; - // errorLocation.last = 0; - } - - /** Dump the document to standard out. */ - void Print() const { Print(stdout, 0); } - - /// Print this Document to a FILE stream. - void Print(FILE *cfile, int depth = 0) const override; - // [internal use] - void SetError(int err, const char *errorLocation, TiXmlParsingData *prevData, - TiXmlEncoding encoding); - -protected: - void StreamOut(TIXML_OSTREAM *out) const override; - // [internal use] - TiXmlNode *Clone() const override; -#ifdef TIXML_USE_STL - void StreamIn(TIXML_ISTREAM *in, TIXML_STRING *tag) override; -#endif - -private: - void CopyTo(TiXmlDocument *target) const; - - bool error{false}; - int errorId{0}; - TIXML_STRING errorDesc; - int tabsize{4}; - TiXmlCursor errorLocation; - bool useMicrosoftBOM{false}; // the UTF-8 BOM were found when read. Note this, and try to write. -}; - -/** - A TiXmlHandle is a class that wraps a node pointer with null checks; this is - an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml - DOM structure. It is a separate utility class. - - Take an example: - @verbatim - - - - - - - @endverbatim - - Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very - easy to write a *lot* of code that looks like: - - @verbatim - TiXmlElement* root = document.FirstChildElement( "Document" ); - if ( root ) - { - TiXmlElement* element = root->FirstChildElement( "Element" ); - if ( element ) - { - TiXmlElement* child = element->FirstChildElement( "Child" ); - if ( child ) - { - TiXmlElement* child2 = child->NextSiblingElement( "Child" ); - if ( child2 ) - { - // Finally do something useful. - @endverbatim - - And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity - of such code. A TiXmlHandle checks for null pointers so it is perfectly safe - and correct to use: - - @verbatim - TiXmlHandle docHandle( &document ); - TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( - "Child", 1 ).Element(); if ( child2 ) - { - // do something useful - @endverbatim - - Which is MUCH more concise and useful. - - It is also safe to copy handles - internally they are nothing more than node pointers. - @verbatim - TiXmlHandle handleCopy = handle; - @endverbatim - - What they should not be used for is iteration: - - @verbatim - int i=0; - while ( true ) - { - TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" - ).Child( "Child", i ).Element(); if ( !child ) break; - // do something - ++i; - } - @endverbatim - - It seems reasonable, but it is in fact two embedded while loops. The Child method is - a linear walk to find the element, so this code would iterate much more than it needs - to. Instead, prefer: - - @verbatim - TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" - ).FirstChild( "Child" ).Element(); - - for( child; child; child=child->NextSiblingElement() ) - { - // do something - } - @endverbatim -*/ -class TiXmlHandle { -public: - /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. - TiXmlHandle(TiXmlNode *_node) : node(_node) {} - /// Copy constructor - TiXmlHandle(const TiXmlHandle &ref) = default; - TiXmlHandle operator=(const TiXmlHandle &ref) - { - this->node = ref.node; - return *this; - } - - /// Return a handle to the first child node. - TiXmlHandle FirstChild() const; - /// Return a handle to the first child node with the given name. - TiXmlHandle FirstChild(const char *value) const; - /// Return a handle to the first child element. - TiXmlHandle FirstChildElement() const; - /// Return a handle to the first child element with the given name. - TiXmlHandle FirstChildElement(const char *value) const; - - /** Return a handle to the "index" child with the given name. - The first child is 0, the second 1, etc. - */ - TiXmlHandle Child(const char *value, int index) const; - /** Return a handle to the "index" child. - The first child is 0, the second 1, etc. - */ - TiXmlHandle Child(int index) const; - /** Return a handle to the "index" child element with the given name. - The first child element is 0, the second 1, etc. Note that only TiXmlElements - are indexed: other types are not counted. - */ - TiXmlHandle ChildElement(const char *value, int index) const; - /** Return a handle to the "index" child element. - The first child element is 0, the second 1, etc. Note that only TiXmlElements - are indexed: other types are not counted. - */ - TiXmlHandle ChildElement(int index) const; - -#ifdef TIXML_USE_STL - TiXmlHandle FirstChild(const std::string &_value) const { return FirstChild(_value.c_str()); } - TiXmlHandle FirstChildElement(const std::string &_value) const - { - return FirstChildElement(_value.c_str()); - } - - TiXmlHandle Child(const std::string &_value, int index) const - { - return Child(_value.c_str(), index); - } - TiXmlHandle ChildElement(const std::string &_value, int index) const - { - return ChildElement(_value.c_str(), index); - } -#endif - - /// Return the handle as a TiXmlNode. This may return null. - TiXmlNode *Node() const { return node; } - /// Return the handle as a TiXmlElement. This may return null. - TiXmlElement *Element() const - { - return ((node && node->ToElement()) ? node->ToElement() : nullptr); - } - /// Return the handle as a TiXmlText. This may return null. - TiXmlText *Text() const { return ((node && node->ToText()) ? node->ToText() : nullptr); } - /// Return the handle as a TiXmlUnknown. This may return null; - TiXmlUnknown *Unknown() const - { - return ((node && node->ToUnknown()) ? node->ToUnknown() : nullptr); - } - -private: - TiXmlNode *node; -}; - -inline const TiXmlDocument *TiXmlNode::ToDocument() const -{ - return (type == DOCUMENT) ? dynamic_cast(this) : nullptr; -} -inline const TiXmlElement *TiXmlNode::ToElement() const -{ - return (type == ELEMENT) ? dynamic_cast(this) : nullptr; -} -inline const TiXmlComment *TiXmlNode::ToComment() const -{ - return (type == COMMENT) ? dynamic_cast(this) : nullptr; -} -inline const TiXmlUnknown *TiXmlNode::ToUnknown() const -{ - return (type == UNKNOWN) ? dynamic_cast(this) : nullptr; -} -inline const TiXmlText *TiXmlNode::ToText() const -{ - return (type == TEXT) ? dynamic_cast(this) : nullptr; -} -inline const TiXmlDeclaration *TiXmlNode::ToDeclaration() const -{ - return (type == DECLARATION) ? dynamic_cast(this) : nullptr; -} - -inline TiXmlDocument *TiXmlNode::ToDocument() -{ - return (type == DOCUMENT) ? dynamic_cast(this) : nullptr; -} -inline TiXmlElement *TiXmlNode::ToElement() -{ - return (type == ELEMENT) ? dynamic_cast(this) : nullptr; -} -inline TiXmlComment *TiXmlNode::ToComment() -{ - return (type == COMMENT) ? dynamic_cast(this) : nullptr; -} -inline TiXmlUnknown *TiXmlNode::ToUnknown() -{ - return (type == UNKNOWN) ? dynamic_cast(this) : nullptr; -} -inline TiXmlText *TiXmlNode::ToText() -{ - return (type == TEXT) ? dynamic_cast(this) : nullptr; -} -inline TiXmlDeclaration *TiXmlNode::ToDeclaration() -{ - return (type == DECLARATION) ? dynamic_cast(this) : nullptr; -} - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#endif diff --git a/src/core/tinyxmlerror.cc b/src/core/tinyxmlerror.cc deleted file mode 100644 index 1c1466b011..0000000000 --- a/src/core/tinyxmlerror.cc +++ /dev/null @@ -1,51 +0,0 @@ -/* -www.sourceforge.net/projects/tinyxml -Original code (2.0 and earlier )copyright (c) 2000-2002 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 "tinyxml.h" - -// The goal of the seperate error file is to make the first -// step towards localization. tinyxml (currently) only supports -// english error messages, but the could now be translated. -// -// It also cleans up the code a bit. -// - -const char *TiXmlBase::errorString[TIXML_ERROR_STRING_COUNT] = { - "No error", - "Error", - "Failed to open file", - "Memory allocation failed.", - "Error parsing Element.", - "Failed to read Element name", - "Error reading Element value.", - "Error reading Attributes.", - "Error: empty tag.", - "Error reading end tag.", - "Error parsing Unknown.", - "Error parsing Comment.", - "Error parsing Declaration.", - "Error document empty.", - "Error null (0) or unexpected EOF found in input stream.", - "Error parsing CDATA.", -}; diff --git a/src/core/tinyxmlparser.cc b/src/core/tinyxmlparser.cc deleted file mode 100644 index 7c0c11e137..0000000000 --- a/src/core/tinyxmlparser.cc +++ /dev/null @@ -1,1511 +0,0 @@ -/* -www.sourceforge.net/projects/tinyxml -Original code (2.0 and earlier )copyright (c) 2000-2002 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 -#include - -#include "tinyxml.h" - -// #define DEBUG_PARSER - -// Note tha "PutString" hardcodes the same list. This -// is less flexible than it appears. Changing the entries -// or order will break putstring. -TiXmlBase::Entity TiXmlBase::entity[NUM_ENTITY] = {{"&", 5, '&'}, - {"<", 4, '<'}, - {">", 4, '>'}, - {""", 6, '\"'}, - {"'", 6, '\''}}; - -// Bunch of unicode info at: -// http://www.unicode.org/faq/utf_bom.html -// Including the basic of this table, which determines the #bytes in the -// sequence from the lead byte. 1 placed for invalid sequences -- -// although the result will be junk, pass it through as much as possible. -// Beware of the non-characters in UTF-8: -// ef bb bf (Microsoft "lead bytes") -// ef bf be -// ef bf bf - -const unsigned char TIXML_UTF_LEAD_0 = 0xefU; -const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; -const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; - -const int TiXmlBase::utf8ByteTable[256] = { - // 0 1 2 3 4 5 6 7 8 9 a b - // c d e f - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 - 1, 1, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte - 4, 4, 4, 4, 4, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid -}; - -void TiXmlBase::ConvertUTF32ToUTF8(unsigned long input, char *output, int *length) -{ - const unsigned long BYTE_MASK = 0xBF; - const unsigned long BYTE_MARK = 0x80; - const unsigned long FIRST_BYTE_MARK[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC}; - - if (input < 0x80) { - *length = 1; - } - else if (input < 0x800) { - *length = 2; - } - else if (input < 0x10000) { - *length = 3; - } - else if (input < 0x200000) { - *length = 4; - } - else { - *length = 0; - return; - } // This code won't covert this correctly anyway. - - output += *length; - - // Scary scary fall throughs. - switch (*length) { - case 4: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 3: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 2: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 1: - --output; - *output = (char)(input | FIRST_BYTE_MARK[*length]); - } -} - -/*static*/ int TiXmlBase::IsAlpha(unsigned char anyByte, TiXmlEncoding /*encoding*/) -{ - // This will only work for low-ascii, everything else is assumed to be a valid - // letter. I'm not sure this is the best approach, but it is quite tricky trying - // to figure out alhabetical vs. not across encoding. So take a very - // conservative approach. - - // if ( encoding == TIXML_ENCODING_UTF8 ) - // { - if (anyByte < 127) { - return isalpha(anyByte); - } - else { - return 1; // What else to do? The unicode set is huge...get the english ones right. - } - // } - // else - // { - // return isalpha( anyByte ); - // } -} - -/*static*/ int TiXmlBase::IsAlphaNum(unsigned char anyByte, TiXmlEncoding /*encoding*/) -{ - // This will only work for low-ascii, everything else is assumed to be a valid - // letter. I'm not sure this is the best approach, but it is quite tricky trying - // to figure out alhabetical vs. not across encoding. So take a very - // conservative approach. - - // if ( encoding == TIXML_ENCODING_UTF8 ) - // { - if (anyByte < 127) { - return isalnum(anyByte); - } - else { - return 1; // What else to do? The unicode set is huge...get the english ones right. - } - // } - // else - // { - // return isalnum( anyByte ); - // } -} - -class TiXmlParsingData { - friend class TiXmlDocument; - -public: - void Stamp(const char *now, TiXmlEncoding encoding); - - const TiXmlCursor &Cursor() { return cursor; } - -private: - // Only used by the document! - TiXmlParsingData(const char *start, int _tabsize, int row, int col) - { - assert(start); - stamp = start; - tabsize = _tabsize; - cursor.row = row; - cursor.col = col; - } - - TiXmlCursor cursor; - const char *stamp; - int tabsize; -}; - -void TiXmlParsingData::Stamp(const char *now, TiXmlEncoding encoding) -{ - assert(now); - - // Do nothing if the tabsize is 0. - if (tabsize < 1) { - return; - } - - // Get the current row, column. - int row = cursor.row; - int col = cursor.col; - const char *p = stamp; - assert(p); - - while (p < now) { - // Treat p as unsigned, so we have a happy compiler. - const auto *pU = reinterpret_cast(p); - - // Code contributed by Fletcher Dunn: (modified by lee) - switch (*pU) { - case 0: - // We *should* never get here, but in case we do, don't - // advance past the terminating null character, ever - return; - - case '\r': - // bump down to the next line - ++row; - col = 0; - // Eat the character - ++p; - - // Check for \r\n sequence, and treat this as a single character - if (*p == '\n') { - ++p; - } - break; - - case '\n': - // bump down to the next line - ++row; - col = 0; - - // Eat the character - ++p; - - // Check for \n\r sequence, and treat this as a single - // character. (Yes, this bizarre thing does occur still - // on some arcane platforms...) - if (*p == '\r') { - ++p; - } - break; - - case '\t': - // Eat the character - ++p; - - // Skip to next tab stop - col = (col / tabsize + 1) * tabsize; - break; - - case TIXML_UTF_LEAD_0: - if (encoding == TIXML_ENCODING_UTF8) { - if (*(p + 1) && *(p + 2)) { - // In these cases, don't advance the column. These are - // 0-width spaces. - if (*(pU + 1) == TIXML_UTF_LEAD_1 && *(pU + 2) == TIXML_UTF_LEAD_2) { - p += 3; - } - else if (*(pU + 1) == 0xbfU && *(pU + 2) == 0xbeU) { - p += 3; - } - else if (*(pU + 1) == 0xbfU && *(pU + 2) == 0xbfU) { - p += 3; - } - else { - p += 3; - ++col; - } // A normal character. - } - } - else { - ++p; - ++col; - } - break; - - default: - if (encoding == TIXML_ENCODING_UTF8) { - // Eat the 1 to 4 byte utf8 character. - int step = TiXmlBase::utf8ByteTable[*reinterpret_cast(p)]; - if (step == 0) { - step = 1; // Error case from bad encoding, but handle gracefully. - } - p += step; - - // Just advance one column, of course. - ++col; - } - else { - ++p; - ++col; - } - break; - } - } - cursor.row = row; - cursor.col = col; - assert(cursor.row >= -1); - assert(cursor.col >= -1); - stamp = p; - assert(stamp); -} - -const char *TiXmlBase::SkipWhiteSpace(const char *p, TiXmlEncoding encoding) -{ - if (!p || !*p) { - return nullptr; - } - if (encoding == TIXML_ENCODING_UTF8) { - while (*p) { - const auto *pU = reinterpret_cast(p); - - // Skip the stupid Microsoft UTF-8 Byte order marks - if (*(pU + 0) == TIXML_UTF_LEAD_0 && *(pU + 1) == TIXML_UTF_LEAD_1 && - *(pU + 2) == TIXML_UTF_LEAD_2) { - p += 3; - continue; - } - else if (*(pU + 0) == TIXML_UTF_LEAD_0 && *(pU + 1) == 0xbfU && *(pU + 2) == 0xbeU) { - p += 3; - continue; - } - else if (*(pU + 0) == TIXML_UTF_LEAD_0 && *(pU + 1) == 0xbfU && *(pU + 2) == 0xbfU) { - p += 3; - continue; - } - - if (IsWhiteSpace(*p) || *p == '\n' || *p == '\r') { // Still using old rules for white space. - ++p; - } - else { - break; - } - } - } - else { - while (*p && (IsWhiteSpace(*p) || *p == '\n' || *p == '\r')) { - ++p; - } - } - - return p; -} - -#ifdef TIXML_USE_STL -/*static*/ bool TiXmlBase::StreamWhiteSpace(TIXML_ISTREAM *in, TIXML_STRING *tag) -{ - for (;;) { - if (!in->good()) { - return false; - } - - const int c = in->peek(); - // At this scope, we can't get to a document. So fail silently. - if (!IsWhiteSpace(c) || c <= 0) { - return true; - } - - *tag += (char)in->get(); - } -} - -/*static*/ bool TiXmlBase::StreamTo(TIXML_ISTREAM *in, int character, TIXML_STRING *tag) -{ - // assert( character > 0 && character < 128 ); // else it won't work in utf-8 - while (in->good()) { - const int c = in->peek(); - if (c == character) { - return true; - } - if (c <= 0) { // Silent failure: can't get document at this scope - return false; - } - - in->get(); - *tag += (char)c; - } - return false; -} -#endif - -const char *TiXmlBase::ReadName(const char *p, TIXML_STRING *name, TiXmlEncoding encoding) -{ - *name = ""; - assert(p); - - // Names start with letters or underscores. - // Of course, in unicode, tinyxml has no idea what a letter *is*. The - // algorithm is generous. - // - // After that, they can be letters, underscores, numbers, - // hyphens, or colons. (Colons are valid ony for namespaces, - // but tinyxml can't tell namespaces from names.) - if (p && *p && (IsAlpha((unsigned char)*p, encoding) || *p == '_')) { - while (p && *p && - (IsAlphaNum((unsigned char)*p, encoding) || *p == '_' || *p == '-' || *p == '.' || - *p == ':')) { - (*name) += *p; - ++p; - } - return p; - } - return nullptr; -} - -const char *TiXmlBase::GetEntity(const char *p, char *value, int *length, TiXmlEncoding encoding) -{ - // Presume an entity, and pull it out. - const TIXML_STRING ent; - int i; - *length = 0; - - if (*(p + 1) && *(p + 1) == '#' && *(p + 2)) { - unsigned long ucs = 0; - ptrdiff_t delta = 0; - unsigned mult = 1; - - if (*(p + 2) == 'x') { - // Hexadecimal. - if (!*(p + 3)) { - return nullptr; - } - - const char *q = p + 3; - q = strchr(q, ';'); - - if (!q || !*q) { - return nullptr; - } - - delta = q - p; - --q; - - while (*q != 'x') { - if (*q >= '0' && *q <= '9') { - ucs += mult * (*q - '0'); - } - else if (*q >= 'a' && *q <= 'f') { - ucs += mult * (*q - 'a' + 10); - } - else if (*q >= 'A' && *q <= 'F') { - ucs += mult * (*q - 'A' + 10); - } - else { - return nullptr; - } - mult *= 16; - --q; - } - } - else { - // Decimal. - if (!*(p + 2)) { - return nullptr; - } - - const char *q = p + 2; - q = strchr(q, ';'); - - if (!q || !*q) { - return nullptr; - } - - delta = q - p; - --q; - - while (*q != '#') { - if (*q >= '0' && *q <= '9') { - ucs += mult * (*q - '0'); - } - else { - return nullptr; - } - mult *= 10; - --q; - } - } - if (encoding == TIXML_ENCODING_UTF8) { - // convert the UCS to UTF-8 - ConvertUTF32ToUTF8(ucs, value, length); - } - else { - *value = (char)ucs; - *length = 1; - } - return p + delta + 1; - } - - // Now try to match it. - for (i = 0; i < NUM_ENTITY; ++i) { - if (strncmp(entity[i].str, p, entity[i].strLength) == 0) { - assert(strlen(entity[i].str) == entity[i].strLength); - *value = entity[i].chr; - *length = 1; - return (p + entity[i].strLength); - } - } - - // So it wasn't an entity, its unrecognized, or something like that. - *value = *p; // Don't put back the last one, since we return it! - return p + 1; -} - -bool TiXmlBase::StringEqual(const char *p, const char *tag, bool ignoreCase, - TiXmlEncoding encoding) -{ - assert(p); - assert(tag); - if (!p || !*p) { - assert(0); - return false; - } - - const char *q = p; - - if (ignoreCase) { - while (*q && *tag && ToLower(*q, encoding) == ToLower(*tag, encoding)) { - ++q; - ++tag; - } - - if (*tag == 0) { - return true; - } - } - else { - while (*q && *tag && *q == *tag) { - ++q; - ++tag; - } - - if (*tag == 0) { // Have we found the end of the tag, and everything equal? - return true; - } - } - return false; -} - -const char *TiXmlBase::ReadText(const char *p, TIXML_STRING *text, bool trimWhiteSpace, - const char *endTag, bool caseInsensitive, TiXmlEncoding encoding) -{ - *text = ""; - if (!trimWhiteSpace // certain tags always keep whitespace - || !condenseWhiteSpace) // if true, whitespace is always kept - { - // Keep all the white space. - while (p && *p && !StringEqual(p, endTag, caseInsensitive, encoding)) { - int len; - char cArr[4] = {0, 0, 0, 0}; - p = GetChar(p, cArr, &len, encoding); - text->append(cArr, len); - } - } - else { - bool whitespace = false; - - // Remove leading white space: - p = SkipWhiteSpace(p, encoding); - while (p && *p && !StringEqual(p, endTag, caseInsensitive, encoding)) { - if (*p == '\r' || *p == '\n') { - whitespace = true; - ++p; - } - else if (IsWhiteSpace(*p)) { - whitespace = true; - ++p; - } - else { - // If we've found whitespace, add it before the - // new character. Any whitespace just becomes a space. - if (whitespace) { - (*text) += ' '; - whitespace = false; - } - int len; - char cArr[4] = {0, 0, 0, 0}; - p = GetChar(p, cArr, &len, encoding); - if (len == 1) { - (*text) += cArr[0]; // more efficient - } - else { - text->append(cArr, len); - } - } - } - } - return p + strlen(endTag); -} - -#ifdef TIXML_USE_STL - -void TiXmlDocument::StreamIn(TIXML_ISTREAM *in, TIXML_STRING *tag) -{ - // The basic issue with a document is that we don't know what we're - // streaming. Read something presumed to be a tag (and hope), then - // identify it, and call the appropriate stream method on the tag. - // - // This "pre-streaming" will never read the closing ">" so the - // sub-tag can orient itself. - - if (!StreamTo(in, '<', tag)) { - SetError(TIXML_ERROR_PARSING_EMPTY, nullptr, nullptr, TIXML_ENCODING_UNKNOWN); - return; - } - - while (in->good()) { - const int tagIndex = (int)tag->length(); - while (in->good() && in->peek() != '>') { - const int c = in->get(); - if (c <= 0) { - SetError(TIXML_ERROR_EMBEDDED_NULL, nullptr, nullptr, TIXML_ENCODING_UNKNOWN); - break; - } - (*tag) += (char)c; - } - - if (in->good()) { - // We now have something we presume to be a node of - // some sort. Identify it, and call the node to - // continue streaming. - TiXmlNode *node = Identify(tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING); - - if (node) { - node->StreamIn(in, tag); - const bool isElement = node->ToElement() != nullptr; - delete node; - node = nullptr; - - // If this is the root element, we're done. Parsing will be - // done by the >> operator. - if (isElement) { - return; - } - } - else { - SetError(TIXML_ERROR, nullptr, nullptr, TIXML_ENCODING_UNKNOWN); - return; - } - } - } - // We should have returned sooner. - SetError(TIXML_ERROR, nullptr, nullptr, TIXML_ENCODING_UNKNOWN); -} - -#endif - -const char *TiXmlDocument::Parse(const char *p, TiXmlParsingData *prevData, TiXmlEncoding encoding) -{ - ClearError(); - - // Parse away, at the document level. Since a document - // contains nothing but other tags, most of what happens - // here is skipping white space. - if (!p || !*p) { - SetError(TIXML_ERROR_DOCUMENT_EMPTY, nullptr, nullptr, TIXML_ENCODING_UNKNOWN); - return nullptr; - } - - // Note that, for a document, this needs to come - // before the while space skip, so that parsing - // starts from the pointer we are given. - location.Clear(); - if (prevData) { - location.row = prevData->cursor.row; - location.col = prevData->cursor.col; - } - else { - location.row = 0; - location.col = 0; - } - TiXmlParsingData data(p, TabSize(), location.row, location.col); - location = data.Cursor(); - - if (encoding == TIXML_ENCODING_UNKNOWN) { - // Check for the Microsoft UTF-8 lead bytes. - const auto *pU = reinterpret_cast(p); - if (*(pU + 0) && *(pU + 0) == TIXML_UTF_LEAD_0 && *(pU + 1) && *(pU + 1) == TIXML_UTF_LEAD_1 && - *(pU + 2) && *(pU + 2) == TIXML_UTF_LEAD_2) { - encoding = TIXML_ENCODING_UTF8; - useMicrosoftBOM = true; - } - } - - p = SkipWhiteSpace(p, encoding); - if (!p) { - SetError(TIXML_ERROR_DOCUMENT_EMPTY, nullptr, nullptr, TIXML_ENCODING_UNKNOWN); - return nullptr; - } - - while (p && *p) { - TiXmlNode *node = Identify(p, encoding); - if (node) { - p = node->Parse(p, &data, encoding); - LinkEndChild(node); - } - else { - break; - } - - // Did we get encoding info? - if (encoding == TIXML_ENCODING_UNKNOWN && node->ToDeclaration()) { - const TiXmlDeclaration *dec = node->ToDeclaration(); - const char *enc = dec->Encoding(); - assert(enc); - - if (*enc == 0) { - encoding = TIXML_ENCODING_UTF8; - } - else if (StringEqual(enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN)) { - encoding = TIXML_ENCODING_UTF8; - } - else if (StringEqual(enc, "UTF8", true, TIXML_ENCODING_UNKNOWN)) { - encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice - } - else { - encoding = TIXML_ENCODING_LEGACY; - } - } - - p = SkipWhiteSpace(p, encoding); - } - - // Was this empty? - if (!firstChild) { - SetError(TIXML_ERROR_DOCUMENT_EMPTY, nullptr, nullptr, encoding); - return nullptr; - } - - // All is well. - return p; -} - -void TiXmlDocument::SetError(int err, const char *pError, TiXmlParsingData *data, - TiXmlEncoding encoding) -{ - // The first error in a chain is more accurate - don't set again! - if (error) { - return; - } - - assert(err > 0 && err < TIXML_ERROR_STRING_COUNT); - error = true; - errorId = err; - errorDesc = errorString[errorId]; - - errorLocation.Clear(); - if (pError && data) { - data->Stamp(pError, encoding); - errorLocation = data->Cursor(); - } -} - -TiXmlNode *TiXmlNode::Identify(const char *p, TiXmlEncoding encoding) -{ - TiXmlNode *returnNode = nullptr; - - p = SkipWhiteSpace(p, encoding); - if (!p || !*p || *p != '<') { - return nullptr; - } - - TiXmlDocument *doc = GetDocument(); - p = SkipWhiteSpace(p, encoding); - - if (!p || !*p) { - return nullptr; - } - - // What is this thing? - // - Elements start with a letter or underscore, but xml is reserved. - // - Comments: "; - - if (!StringEqual(p, startTag, false, encoding)) { - document->SetError(TIXML_ERROR_PARSING_COMMENT, p, data, encoding); - return nullptr; - } - p += strlen(startTag); - p = ReadText(p, &value, false, endTag, false, encoding); - return p; -} - -const char *TiXmlAttribute::Parse(const char *p, TiXmlParsingData *data, TiXmlEncoding encoding) -{ - p = SkipWhiteSpace(p, encoding); - if (!p || !*p) { - return nullptr; - } - - // int tabsize = 4; - // if ( document ) { - // tabsize = document->TabSize(); - // } - - if (data) { - data->Stamp(p, encoding); - location = data->Cursor(); - } - // Read the name, the '=' and the value. - const char *pErr = p; - p = ReadName(p, &name, encoding); - if (!p || !*p) { - if (document) { - document->SetError(TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding); - } - return nullptr; - } - p = SkipWhiteSpace(p, encoding); - if (!p || !*p || *p != '=') { - if (document) { - document->SetError(TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding); - } - return nullptr; - } - - ++p; // skip '=' - p = SkipWhiteSpace(p, encoding); - if (!p || !*p) { - if (document) { - document->SetError(TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding); - } - return nullptr; - } - - const char *end; - - if (*p == '\'') { - ++p; - end = "\'"; - p = ReadText(p, &value, false, end, false, encoding); - } - else if (*p == '"') { - ++p; - end = "\""; - p = ReadText(p, &value, false, end, false, encoding); - } - else { - // All attribute values should be in single or double quotes. - // But this is such a common error that the parser will try - // its best, even without them. - value = ""; - while (p && *p // existence - && !IsWhiteSpace(*p) && *p != '\n' && *p != '\r' // whitespace - && *p != '/' && *p != '>') // tag end - { - value += *p; - ++p; - } - } - return p; -} - -#ifdef TIXML_USE_STL -void TiXmlText::StreamIn(TIXML_ISTREAM *in, TIXML_STRING *tag) -{ - if (cdata) { - const int c = in->get(); - if (c <= 0) { - TiXmlDocument *document = GetDocument(); - if (document) { - document->SetError(TIXML_ERROR_EMBEDDED_NULL, nullptr, nullptr, TIXML_ENCODING_UNKNOWN); - } - return; - } - - (*tag) += (char)c; - - if (c == '>' && tag->at(tag->length() - 2) == ']' && tag->at(tag->length() - 3) == ']') { - // All is well. - return; - } - } - else { - while (in->good()) { - const int c = in->peek(); - if (c == '<') { - return; - } - if (c <= 0) { - TiXmlDocument *document = GetDocument(); - if (document) { - document->SetError(TIXML_ERROR_EMBEDDED_NULL, nullptr, nullptr, TIXML_ENCODING_UNKNOWN); - } - return; - } - - (*tag) += (char)c; - in->get(); - } - } -} -#endif - -const char *TiXmlText::Parse(const char *p, TiXmlParsingData *data, TiXmlEncoding encoding) -{ - value = ""; - TiXmlDocument *document = GetDocument(); - - if (data) { - data->Stamp(p, encoding); - location = data->Cursor(); - } - - const char *const startTag = ""; - - if (cdata || StringEqual(p, startTag, false, encoding)) { - cdata = true; - - if (!StringEqual(p, startTag, false, encoding)) { - document->SetError(TIXML_ERROR_PARSING_CDATA, p, data, encoding); - return nullptr; - } - p += strlen(startTag); - - // Keep all the white space, ignore the encoding, etc. - while (p && *p && !StringEqual(p, endTag, false, encoding)) { - value += *p; - ++p; - } - - TIXML_STRING dummy; - p = ReadText(p, &dummy, false, endTag, false, encoding); - return p; - } - else { - const bool ignoreWhite = true; - - const char *end = "<"; - p = ReadText(p, &value, ignoreWhite, end, false, encoding); - if (p) { - return p - 1; // don't truncate the '<' - } - return nullptr; - } -} - -#ifdef TIXML_USE_STL -void TiXmlDeclaration::StreamIn(TIXML_ISTREAM *in, TIXML_STRING *tag) -{ - while (in->good()) { - const int c = in->get(); - if (c <= 0) { - TiXmlDocument *document = GetDocument(); - if (document) { - document->SetError(TIXML_ERROR_EMBEDDED_NULL, nullptr, nullptr, TIXML_ENCODING_UNKNOWN); - } - return; - } - (*tag) += (char)c; - - if (c == '>') { - // All is well. - return; - } - } -} -#endif - -const char *TiXmlDeclaration::Parse(const char *p, TiXmlParsingData *data, TiXmlEncoding _encoding) -{ - p = SkipWhiteSpace(p, _encoding); - // Find the beginning, find the end, and look for - // the stuff in-between. - TiXmlDocument *document = GetDocument(); - if (!p || !*p || !StringEqual(p, "SetError(TIXML_ERROR_PARSING_DECLARATION, nullptr, nullptr, _encoding); - } - return nullptr; - } - if (data) { - data->Stamp(p, _encoding); - location = data->Cursor(); - } - p += 5; - - version = ""; - encoding = ""; - standalone = ""; - - while (p && *p) { - if (*p == '>') { - ++p; - return p; - } - - p = SkipWhiteSpace(p, _encoding); - if (StringEqual(p, "version", true, _encoding)) { - TiXmlAttribute attrib; - p = attrib.Parse(p, data, _encoding); - version = attrib.Value(); - } - else if (StringEqual(p, "encoding", true, _encoding)) { - TiXmlAttribute attrib; - p = attrib.Parse(p, data, _encoding); - encoding = attrib.Value(); - } - else if (StringEqual(p, "standalone", true, _encoding)) { - TiXmlAttribute attrib; - p = attrib.Parse(p, data, _encoding); - standalone = attrib.Value(); - } - else { - // Read over whatever it is. - while (p && *p && *p != '>' && !IsWhiteSpace(*p)) { - ++p; - } - } - } - return nullptr; -} - -bool TiXmlText::Blank() const -{ - for (const char c : value) { - if (!IsWhiteSpace(c)) { - return false; - } - } - return true; -} diff --git a/src/games/file.cc b/src/games/file.cc index 0d772d18f2..db2adc1b5e 100644 --- a/src/games/file.cc +++ b/src/games/file.cc @@ -854,56 +854,10 @@ void ParseNode(GameFileLexer &p_state, Game p_game, GameNode p_node, TreeData &p } // end of anonymous namespace -#include "core/tinyxml.h" +#include "workspace.h" namespace Gambit { -class GameXMLSavefile { -private: - TiXmlDocument doc; - -public: - explicit GameXMLSavefile(const std::string &p_xml); - ~GameXMLSavefile() = default; - - Game GetGame() const; -}; - -GameXMLSavefile::GameXMLSavefile(const std::string &p_xml) -{ - doc.Parse(p_xml.c_str()); - if (doc.Error()) { - throw InvalidFileException("Not a valid XML document"); - } -} - -Game GameXMLSavefile::GetGame() const -{ - const TiXmlNode *docroot = doc.FirstChild("gambit:document"); - if (!docroot) { - throw InvalidFileException("Not a Gambit game savefile document"); - } - - const TiXmlNode *game = docroot->FirstChild("game"); - if (!game) { - throw InvalidFileException("No game representation found in document"); - } - - const TiXmlNode *efgfile = game->FirstChild("efgfile"); - if (efgfile) { - std::istringstream s(efgfile->FirstChild()->Value()); - return ReadGame(s); - } - - const TiXmlNode *nfgfile = game->FirstChild("nfgfile"); - if (nfgfile) { - std::istringstream s(nfgfile->FirstChild()->Value()); - return ReadGame(s); - } - - throw InvalidFileException("No game representation found in document"); -} - void NormalizeGameLabels(const Game &p_game) { const auto get_label = [](const auto &e) { return e->GetLabel(); }; @@ -988,11 +942,19 @@ Game ReadNfgFile(std::istream &p_stream) Game ReadGbtFile(std::istream &p_stream) { - std::stringstream buffer; - buffer << p_stream.rdbuf(); - auto game = GameXMLSavefile(buffer.str()).GetGame(); - NormalizeGameLabels(game); - return game; + try { + const LegacyWorkspaceFile workspace = ReadLegacyWorkspace(p_stream); + std::istringstream game_text(workspace.game); + auto game = ReadGame(game_text); + NormalizeGameLabels(game); + return game; + } + catch (const InvalidFileException &) { + throw; + } + catch (const std::runtime_error &) { + throw InvalidFileException("Not a valid .gbt document"); + } } Game ReadAggFile(std::istream &p_stream) diff --git a/src/games/workspace.cc b/src/games/workspace.cc new file mode 100644 index 0000000000..a2aaaad877 --- /dev/null +++ b/src/games/workspace.cc @@ -0,0 +1,556 @@ +// +// This file is part of Gambit +// Copyright (c) 1994-2026, The Gambit Project (https://www.gambit-project.org) +// +// FILE: src/games/workspace.cc +// Reader and writer for Gambit's legacy .gbt workspace format +// +// 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 2 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, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// + +#include "workspace.h" + +#include +#include +#include +#include +#include +#include + +namespace Gambit { +namespace { + +class Element { + friend class Parser; + + std::string m_name; + std::string m_text; + std::map m_attributes; + std::vector> m_children; + +public: + const std::string &Name() const { return m_name; } + const std::string &Text() const { return m_text; } + const std::string *Attribute(const std::string &p_name) const; + bool IntAttribute(const std::string &p_name, int &p_value) const; + const Element *Child(const std::string &p_name) const; + std::vector Children(const std::string &p_name = {}) const; +}; + +class Parser { + const std::string &m_input; + size_t m_pos{0}; + + [[noreturn]] void Fail(const std::string &p_message) const + { + throw std::runtime_error("Invalid .gbt XML at byte " + std::to_string(m_pos) + ": " + + p_message); + } + + bool StartsWith(const std::string &p_text) const + { + return m_input.compare(m_pos, p_text.size(), p_text) == 0; + } + + void SkipWhitespace() + { + while (m_pos < m_input.size() && (m_input[m_pos] == ' ' || m_input[m_pos] == '\t' || + m_input[m_pos] == '\r' || m_input[m_pos] == '\n')) { + ++m_pos; + } + } + + void SkipMarkup() + { + if (StartsWith("", m_pos + 2); + if (end == std::string::npos) { + Fail("unterminated processing instruction"); + } + m_pos = end + 2; + } + else if (StartsWith("", m_pos + 4); + if (end == std::string::npos) { + Fail("unterminated comment"); + } + m_pos = end + 3; + } + else { + Fail("unsupported markup"); + } + } + + std::string Name() + { + const size_t begin = m_pos; + while (m_pos < m_input.size()) { + const char c = m_input[m_pos]; + if (!(std::isalnum(static_cast(c)) || c == '_' || c == '-' || c == ':' || + c == '.')) { + break; + } + ++m_pos; + } + if (begin == m_pos) { + Fail("expected a name"); + } + return m_input.substr(begin, m_pos - begin); + } + + static void AppendUtf8(std::string &p_out, unsigned long p_codepoint) + { + if (p_codepoint <= 0x7f) { + p_out.push_back(static_cast(p_codepoint)); + } + else if (p_codepoint <= 0x7ff) { + p_out.push_back(static_cast(0xc0 | (p_codepoint >> 6))); + p_out.push_back(static_cast(0x80 | (p_codepoint & 0x3f))); + } + else if (p_codepoint <= 0xffff) { + p_out.push_back(static_cast(0xe0 | (p_codepoint >> 12))); + p_out.push_back(static_cast(0x80 | ((p_codepoint >> 6) & 0x3f))); + p_out.push_back(static_cast(0x80 | (p_codepoint & 0x3f))); + } + else if (p_codepoint <= 0x10ffff) { + p_out.push_back(static_cast(0xf0 | (p_codepoint >> 18))); + p_out.push_back(static_cast(0x80 | ((p_codepoint >> 12) & 0x3f))); + p_out.push_back(static_cast(0x80 | ((p_codepoint >> 6) & 0x3f))); + p_out.push_back(static_cast(0x80 | (p_codepoint & 0x3f))); + } + else { + throw std::runtime_error("Invalid Unicode character in .gbt XML"); + } + } + + std::string Decode(const std::string &p_text) const + { + std::string result; + for (size_t pos = 0; pos < p_text.size();) { + if (p_text[pos] != '&') { + result.push_back(p_text[pos++]); + continue; + } + const auto end = p_text.find(';', pos + 1); + if (end == std::string::npos) { + throw std::runtime_error("Invalid entity in .gbt XML"); + } + const std::string entity = p_text.substr(pos + 1, end - pos - 1); + if (entity == "amp") { + result.push_back('&'); + } + else if (entity == "lt") { + result.push_back('<'); + } + else if (entity == "gt") { + result.push_back('>'); + } + else if (entity == "quot") { + result.push_back('"'); + } + else if (entity == "apos") { + result.push_back('\''); + } + else if (!entity.empty() && entity[0] == '#') { + const bool hex = entity.size() > 1 && (entity[1] == 'x' || entity[1] == 'X'); + const std::string digits = entity.substr(hex ? 2 : 1); + unsigned long value = 0; + try { + value = std::stoul(digits, nullptr, hex ? 16 : 10); + } + catch (...) { + throw std::runtime_error("Invalid numeric entity in .gbt XML"); + } + AppendUtf8(result, value); + } + else { + throw std::runtime_error("Unknown entity in .gbt XML"); + } + pos = end + 1; + } + return result; + } + +public: + explicit Parser(const std::string &p_input) : m_input(p_input) {} + + std::unique_ptr ParseElement() + { + if (m_pos >= m_input.size() || m_input[m_pos++] != '<') { + Fail("expected '<'"); + } + auto element = std::make_unique(); + element->m_name = Name(); + while (true) { + SkipWhitespace(); + if (StartsWith("/>")) { + m_pos += 2; + return element; + } + if (StartsWith(">")) { + ++m_pos; + break; + } + const std::string name = Name(); + SkipWhitespace(); + if (m_pos >= m_input.size() || m_input[m_pos++] != '=') { + Fail("expected '='"); + } + SkipWhitespace(); + if (m_pos >= m_input.size() || (m_input[m_pos] != '"' && m_input[m_pos] != '\'')) { + Fail("expected quoted attribute value"); + } + const char quote = m_input[m_pos++]; + const size_t begin = m_pos; + const auto end = m_input.find(quote, begin); + if (end == std::string::npos) { + Fail("unterminated attribute value"); + } + element->m_attributes[name] = Decode(m_input.substr(begin, end - begin)); + m_pos = end + 1; + } + + // SaveWorkspace embeds these payloads verbatim rather than XML-escaping + // them. Treat them as opaque text so game labels and descriptions that + // contain '<' or '&' remain readable. + if (element->m_name == "efgfile" || element->m_name == "nfgfile" || + element->m_name == "description" || element->m_name == "profile") { + const std::string close = "m_name + ">"; + const auto end = m_input.find(close, m_pos); + if (end == std::string::npos) { + Fail("unterminated element " + element->m_name); + } + element->m_text = m_input.substr(m_pos, end - m_pos); + m_pos = end + close.size(); + return element; + } + + while (true) { + if (m_pos >= m_input.size()) { + Fail("unterminated element " + element->m_name); + } + if (StartsWith("= m_input.size() || m_input[m_pos++] != '>') { + Fail("expected '>'"); + } + if (closing != element->m_name) { + Fail("mismatched closing element " + closing); + } + return element; + } + if (StartsWith("