//===-- 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 /* 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 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 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; }