| //===-- 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/StringConvert.h" |
| #include "lldb/Host/XML.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(); |
| } |
| |
| llvm::StringRef 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(); |
| } |
| |
| bool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value, |
| uint64_t fail_value, int base) const { |
| #if defined(LIBXML2_DEFINED) |
| llvm::StringRef str_value = GetAttributeValue(name, ""); |
| #else |
| llvm::StringRef str_value; |
| #endif |
| bool success = false; |
| value = StringConvert::ToUInt64(str_value.data(), fail_value, base, &success); |
| return success; |
| } |
| |
| 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 element 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](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() {} |
| |
| llvm::StringRef 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" || element_name == "false") { |
| // The text value _is_ the element name itself... |
| value = element_name.str(); |
| return true; |
| } else if (element_name == "dict" || 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; |
| } |