| //===-- XML.cpp -------------------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include <stdlib.h> /* atof */ |
| |
| #include "lldb/Host/XML.h" |
| #include "lldb/Host/StringConvert.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| |
| #pragma mark -- XMLDocument |
| |
| XMLDocument::XMLDocument () : |
| m_document (nullptr) |
| { |
| } |
| |
| XMLDocument::~XMLDocument () |
| { |
| Clear(); |
| } |
| |
| void |
| XMLDocument::Clear() |
| { |
| #if defined( LIBXML2_DEFINED ) |
| if (m_document) |
| { |
| xmlDocPtr doc = m_document; |
| m_document = nullptr; |
| xmlFreeDoc(doc); |
| } |
| #endif |
| } |
| |
| bool |
| XMLDocument::IsValid() const |
| { |
| return m_document != nullptr; |
| } |
| |
| void |
| XMLDocument::ErrorCallback (void *ctx, const char *format, ...) |
| { |
| XMLDocument *document = (XMLDocument *)ctx; |
| va_list args; |
| va_start (args, format); |
| document->m_errors.PrintfVarArg(format, args); |
| document->m_errors.EOL(); |
| va_end (args); |
| } |
| |
| bool |
| XMLDocument::ParseFile (const char *path) |
| { |
| #if defined( LIBXML2_DEFINED ) |
| Clear(); |
| xmlSetGenericErrorFunc( (void *)this, XMLDocument::ErrorCallback ); |
| m_document = xmlParseFile(path); |
| xmlSetGenericErrorFunc(nullptr, nullptr); |
| #endif |
| return IsValid(); |
| } |
| |
| bool |
| XMLDocument::ParseMemory (const char *xml, size_t xml_length, const char *url) |
| { |
| #if defined( LIBXML2_DEFINED ) |
| Clear(); |
| xmlSetGenericErrorFunc( (void *)this, XMLDocument::ErrorCallback ); |
| m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0); |
| xmlSetGenericErrorFunc(nullptr, nullptr); |
| #endif |
| return IsValid(); |
| |
| } |
| |
| XMLNode |
| XMLDocument::GetRootElement(const char *required_name) |
| { |
| #if defined( LIBXML2_DEFINED ) |
| if (IsValid()) |
| { |
| XMLNode root_node(xmlDocGetRootElement(m_document)); |
| if (required_name) |
| { |
| llvm::StringRef actual_name = root_node.GetName(); |
| if (actual_name == required_name) |
| return root_node; |
| } |
| else |
| { |
| return root_node; |
| } |
| } |
| #endif |
| return XMLNode(); |
| } |
| |
| const std::string & |
| XMLDocument::GetErrors() const |
| { |
| return m_errors.GetString(); |
| } |
| |
| bool |
| XMLDocument::XMLEnabled () |
| { |
| #if defined( LIBXML2_DEFINED ) |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| #pragma mark -- XMLNode |
| |
| XMLNode::XMLNode() : |
| m_node(nullptr) |
| { |
| } |
| |
| XMLNode::XMLNode(XMLNodeImpl node) : |
| m_node(node) |
| { |
| } |
| |
| XMLNode::~XMLNode() |
| { |
| |
| } |
| |
| void |
| XMLNode::Clear() |
| { |
| m_node = nullptr; |
| } |
| |
| XMLNode |
| XMLNode::GetParent() const |
| { |
| #if defined( LIBXML2_DEFINED ) |
| if (IsValid()) |
| return XMLNode(m_node->parent); |
| else |
| return XMLNode(); |
| #else |
| return XMLNode(); |
| #endif |
| |
| } |
| |
| XMLNode |
| XMLNode::GetSibling() const |
| { |
| #if defined( LIBXML2_DEFINED ) |
| if (IsValid()) |
| return XMLNode(m_node->next); |
| else |
| return XMLNode(); |
| #else |
| return XMLNode(); |
| #endif |
| |
| } |
| |
| XMLNode |
| XMLNode::GetChild () const |
| { |
| #if defined( LIBXML2_DEFINED ) |
| |
| if (IsValid()) |
| return XMLNode(m_node->children); |
| else |
| return XMLNode(); |
| #else |
| return XMLNode(); |
| #endif |
| |
| } |
| |
| llvm::StringRef |
| XMLNode::GetAttributeValue(const char *name, const char *fail_value) const |
| { |
| const char *attr_value = NULL; |
| #if defined( LIBXML2_DEFINED ) |
| |
| if (IsValid()) |
| attr_value = (const char *)xmlGetProp(m_node, (const xmlChar *)name); |
| else |
| attr_value = fail_value; |
| #else |
| attr_value = fail_value; |
| #endif |
| if (attr_value) |
| return llvm::StringRef(attr_value); |
| else |
| return llvm::StringRef(); |
| } |
| |
| |
| |
| |
| void |
| XMLNode::ForEachChildNode (NodeCallback const &callback) const |
| { |
| #if defined( LIBXML2_DEFINED ) |
| if (IsValid()) |
| GetChild().ForEachSiblingNode(callback); |
| #endif |
| } |
| |
| void |
| XMLNode::ForEachChildElement (NodeCallback const &callback) const |
| { |
| #if defined( LIBXML2_DEFINED ) |
| XMLNode child = GetChild(); |
| if (child) |
| child.ForEachSiblingElement(callback); |
| #endif |
| } |
| |
| void |
| XMLNode::ForEachChildElementWithName (const char *name, NodeCallback const &callback) const |
| { |
| #if defined( LIBXML2_DEFINED ) |
| XMLNode child = GetChild(); |
| if (child) |
| child.ForEachSiblingElementWithName(name, callback); |
| #endif |
| } |
| |
| void |
| XMLNode::ForEachAttribute (AttributeCallback const &callback) const |
| { |
| #if defined( LIBXML2_DEFINED ) |
| |
| if (IsValid()) |
| { |
| for (xmlAttrPtr attr = m_node->properties; attr != nullptr; attr=attr->next) |
| { |
| // check if name matches |
| if (attr->name) |
| { |
| // check child is a text node |
| xmlNodePtr child = attr->children; |
| if (child->type == XML_TEXT_NODE) |
| { |
| llvm::StringRef attr_value; |
| if (child->content) |
| attr_value = llvm::StringRef((const char *)child->content); |
| if (callback(llvm::StringRef((const char *)attr->name), attr_value) == false) |
| return; |
| } |
| } |
| } |
| } |
| #endif |
| } |
| |
| |
| void |
| XMLNode::ForEachSiblingNode (NodeCallback const &callback) const |
| { |
| #if defined( LIBXML2_DEFINED ) |
| |
| if (IsValid()) |
| { |
| // iterate through all siblings |
| for (xmlNodePtr node = m_node; node; node=node->next) |
| { |
| if (callback(XMLNode(node)) == false) |
| return; |
| } |
| } |
| #endif |
| } |
| |
| void |
| XMLNode::ForEachSiblingElement (NodeCallback const &callback) const |
| { |
| #if defined( LIBXML2_DEFINED ) |
| |
| if (IsValid()) |
| { |
| // iterate through all siblings |
| for (xmlNodePtr node = m_node; node; node=node->next) |
| { |
| // we are looking for element nodes only |
| if (node->type != XML_ELEMENT_NODE) |
| continue; |
| |
| if (callback(XMLNode(node)) == false) |
| return; |
| } |
| } |
| #endif |
| } |
| |
| void |
| XMLNode::ForEachSiblingElementWithName (const char *name, NodeCallback const &callback) const |
| { |
| #if defined( LIBXML2_DEFINED ) |
| |
| if (IsValid()) |
| { |
| // iterate through all siblings |
| for (xmlNodePtr node = m_node; node; node=node->next) |
| { |
| // we are looking for element nodes only |
| if (node->type != XML_ELEMENT_NODE) |
| continue; |
| |
| // If name is nullptr, we take all nodes of type "t", else |
| // just the ones whose name matches |
| if (name) |
| { |
| if (strcmp((const char *)node->name, name) != 0) |
| continue; // Name mismatch, ignore this one |
| } |
| else |
| { |
| if (node->name) |
| continue; // nullptr name specified and this elemnt has a name, ignore this one |
| } |
| |
| if (callback(XMLNode(node)) == false) |
| return; |
| } |
| } |
| #endif |
| } |
| |
| llvm::StringRef |
| XMLNode::GetName() const |
| { |
| #if defined( LIBXML2_DEFINED ) |
| if (IsValid()) |
| { |
| if (m_node->name) |
| return llvm::StringRef((const char *)m_node->name); |
| } |
| #endif |
| return llvm::StringRef(); |
| } |
| |
| bool |
| XMLNode::GetElementText (std::string &text) const |
| { |
| text.clear(); |
| #if defined( LIBXML2_DEFINED ) |
| if (IsValid()) |
| { |
| bool success = false; |
| if (m_node->type == XML_ELEMENT_NODE) |
| { |
| // check child is a text node |
| for (xmlNodePtr node = m_node->children; |
| node != nullptr; |
| node = node->next) |
| { |
| if (node->type == XML_TEXT_NODE) |
| { |
| text.append((const char *)node->content); |
| success = true; |
| } |
| } |
| } |
| return success; |
| } |
| #endif |
| return false; |
| } |
| |
| |
| bool |
| XMLNode::GetElementTextAsUnsigned (uint64_t &value, uint64_t fail_value, int base) const |
| { |
| bool success = false; |
| #if defined( LIBXML2_DEFINED ) |
| if (IsValid()) |
| { |
| std::string text; |
| if (GetElementText(text)) |
| value = StringConvert::ToUInt64(text.c_str(), fail_value, base, &success); |
| } |
| #endif |
| if (!success) |
| value = fail_value; |
| return success; |
| } |
| |
| bool |
| XMLNode::GetElementTextAsFloat (double &value, double fail_value) const |
| { |
| bool success = false; |
| #if defined( LIBXML2_DEFINED ) |
| if (IsValid()) |
| { |
| std::string text; |
| if (GetElementText(text)) |
| { |
| value = atof(text.c_str()); |
| success = true; |
| } |
| } |
| #endif |
| if (!success) |
| value = fail_value; |
| return success; |
| } |
| |
| |
| |
| bool |
| XMLNode::NameIs (const char *name) const |
| { |
| #if defined( LIBXML2_DEFINED ) |
| |
| if (IsValid()) |
| { |
| // In case we are looking for a nullptr name or an exact pointer match |
| if (m_node->name == (const xmlChar *)name) |
| return true; |
| if (m_node->name) |
| return strcmp((const char *)m_node->name, name) == 0; |
| } |
| #endif |
| return false; |
| } |
| |
| XMLNode |
| XMLNode::FindFirstChildElementWithName (const char *name) const |
| { |
| XMLNode result_node; |
| |
| #if defined( LIBXML2_DEFINED ) |
| ForEachChildElementWithName(name, [&result_node, name](const XMLNode& node) -> bool { |
| result_node = node; |
| // Stop iterating, we found the node we wanted |
| return false; |
| }); |
| #endif |
| |
| return result_node; |
| } |
| |
| bool |
| XMLNode::IsValid() const |
| { |
| return m_node != nullptr; |
| } |
| |
| bool |
| XMLNode::IsElement () const |
| { |
| #if defined( LIBXML2_DEFINED ) |
| if (IsValid()) |
| return m_node->type == XML_ELEMENT_NODE; |
| #endif |
| return false; |
| } |
| |
| |
| XMLNode |
| XMLNode::GetElementForPath (const NamePath &path) |
| { |
| #if defined( LIBXML2_DEFINED ) |
| |
| if (IsValid()) |
| { |
| if (path.empty()) |
| return *this; |
| else |
| { |
| XMLNode node = FindFirstChildElementWithName(path[0].c_str()); |
| const size_t n = path.size(); |
| for (size_t i=1; node && i<n; ++i) |
| node = node.FindFirstChildElementWithName(path[i].c_str()); |
| return node; |
| } |
| } |
| #endif |
| |
| return XMLNode(); |
| } |
| |
| |
| #pragma mark -- ApplePropertyList |
| |
| ApplePropertyList::ApplePropertyList() : |
| m_xml_doc(), |
| m_dict_node() |
| { |
| |
| } |
| |
| ApplePropertyList::ApplePropertyList (const char *path) : |
| m_xml_doc(), |
| m_dict_node() |
| { |
| ParseFile(path); |
| } |
| |
| ApplePropertyList::~ApplePropertyList() |
| { |
| } |
| |
| const std::string & |
| ApplePropertyList::GetErrors() const |
| { |
| return m_xml_doc.GetErrors(); |
| } |
| |
| |
| bool |
| ApplePropertyList::ParseFile (const char *path) |
| { |
| if (m_xml_doc.ParseFile(path)) |
| { |
| XMLNode plist = m_xml_doc.GetRootElement("plist"); |
| if (plist) |
| { |
| plist.ForEachChildElementWithName("dict", [this](const XMLNode &dict) -> bool { |
| this->m_dict_node = dict; |
| return false; // Stop iterating |
| }); |
| return (bool)m_dict_node; |
| } |
| } |
| return false; |
| } |
| |
| bool |
| ApplePropertyList::IsValid() const |
| { |
| return (bool)m_dict_node; |
| } |
| |
| bool |
| ApplePropertyList::GetValueAsString (const char *key, std::string &value) const |
| { |
| XMLNode value_node = GetValueNode (key); |
| if (value_node) |
| return ApplePropertyList::ExtractStringFromValueNode(value_node, value); |
| return false; |
| } |
| |
| XMLNode |
| ApplePropertyList::GetValueNode (const char *key) const |
| { |
| XMLNode value_node; |
| #if defined( LIBXML2_DEFINED ) |
| |
| if (IsValid()) |
| { |
| m_dict_node.ForEachChildElementWithName("key", [key, &value_node](const XMLNode &key_node) -> bool { |
| std::string key_name; |
| if (key_node.GetElementText(key_name)) |
| { |
| if (key_name.compare(key) == 0) |
| { |
| value_node = key_node.GetSibling(); |
| while (value_node && !value_node.IsElement()) |
| value_node = value_node.GetSibling(); |
| return false; // Stop iterating |
| } |
| } |
| return true; // Keep iterating |
| }); |
| } |
| #endif |
| return value_node; |
| } |
| |
| bool |
| ApplePropertyList::ExtractStringFromValueNode (const XMLNode &node, std::string &value) |
| { |
| value.clear(); |
| #if defined( LIBXML2_DEFINED ) |
| if (node.IsValid()) |
| { |
| llvm::StringRef element_name = node.GetName(); |
| if (element_name == "true" or element_name == "false") |
| { |
| // The text value _is_ the element name itself... |
| value = std::move(element_name.str()); |
| return true; |
| } |
| else if (element_name == "dict" or element_name == "array") |
| return false; // dictionaries and arrays have no text value, so we fail |
| else |
| return node.GetElementText(value); |
| } |
| #endif |
| return false; |
| } |
| |
| #if defined( LIBXML2_DEFINED ) |
| |
| namespace { |
| |
| StructuredData::ObjectSP |
| CreatePlistValue (XMLNode node) |
| { |
| llvm::StringRef element_name = node.GetName(); |
| if (element_name == "array") |
| { |
| std::shared_ptr<StructuredData::Array> array_sp(new StructuredData::Array()); |
| node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool { |
| array_sp->AddItem(CreatePlistValue(node)); |
| return true; // Keep iterating through all child elements of the array |
| }); |
| return array_sp; |
| } |
| else if (element_name == "dict") |
| { |
| XMLNode key_node; |
| std::shared_ptr<StructuredData::Dictionary> dict_sp(new StructuredData::Dictionary()); |
| node.ForEachChildElement([&key_node, &dict_sp](const XMLNode &node) -> bool { |
| if (node.NameIs("key")) |
| { |
| // This is a "key" element node |
| key_node = node; |
| } |
| else |
| { |
| // This is a value node |
| if (key_node) |
| { |
| std::string key_name; |
| key_node.GetElementText(key_name); |
| dict_sp->AddItem(key_name, CreatePlistValue(node)); |
| key_node.Clear(); |
| } |
| } |
| return true; // Keep iterating through all child elements of the dictionary |
| }); |
| return dict_sp; |
| } |
| else if (element_name == "real") |
| { |
| double value = 0.0; |
| node.GetElementTextAsFloat(value); |
| return StructuredData::ObjectSP(new StructuredData::Float(value)); |
| } |
| else if (element_name == "integer") |
| { |
| uint64_t value = 0; |
| node.GetElementTextAsUnsigned(value, 0, 0); |
| return StructuredData::ObjectSP(new StructuredData::Integer(value)); |
| } |
| else if ((element_name == "string") || (element_name == "data") || (element_name == "date")) |
| { |
| std::string text; |
| node.GetElementText(text); |
| return StructuredData::ObjectSP(new StructuredData::String(std::move(text))); |
| } |
| else if (element_name == "true") |
| { |
| return StructuredData::ObjectSP(new StructuredData::Boolean(true)); |
| } |
| else if (element_name == "false") |
| { |
| return StructuredData::ObjectSP(new StructuredData::Boolean(false)); |
| } |
| return StructuredData::ObjectSP(new StructuredData::Null()); |
| } |
| } |
| #endif |
| |
| StructuredData::ObjectSP |
| ApplePropertyList::GetStructuredData() |
| { |
| StructuredData::ObjectSP root_sp; |
| #if defined( LIBXML2_DEFINED ) |
| if (IsValid()) |
| { |
| return CreatePlistValue(m_dict_node); |
| } |
| #endif |
| return root_sp; |
| } |
| |
| |