diff options
Diffstat (limited to 'lldb/source/Host/common/XML.cpp')
| -rw-r--r-- | lldb/source/Host/common/XML.cpp | 541 | 
1 files changed, 541 insertions, 0 deletions
diff --git a/lldb/source/Host/common/XML.cpp b/lldb/source/Host/common/XML.cpp new file mode 100644 index 000000000000..cb23ac17ef53 --- /dev/null +++ b/lldb/source/Host/common/XML.cpp @@ -0,0 +1,541 @@ +//===-- XML.cpp -------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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 = nullptr; +#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)) +            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))) +        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))) +        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))) +        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 == key) { +              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; +}  | 
