blob: 456c879b0148e922dd427f7db76ef97844232651 [file] [log] [blame]
//===-- XML.cpp -----------------------------------------------------------===//
//
// 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/Config.h"
#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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
if (IsValid())
return XMLNode(m_node->parent);
else
return XMLNode();
#else
return XMLNode();
#endif
}
XMLNode XMLNode::GetSibling() const {
#if LLDB_ENABLE_LIBXML2
if (IsValid())
return XMLNode(m_node->next);
else
return XMLNode();
#else
return XMLNode();
#endif
}
XMLNode XMLNode::GetChild() const {
#if LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
if (IsValid())
GetChild().ForEachSiblingNode(callback);
#endif
}
void XMLNode::ForEachChildElement(NodeCallback const &callback) const {
#if LLDB_ENABLE_LIBXML2
XMLNode child = GetChild();
if (child)
child.ForEachSiblingElement(callback);
#endif
}
void XMLNode::ForEachChildElementWithName(const char *name,
NodeCallback const &callback) const {
#if LLDB_ENABLE_LIBXML2
XMLNode child = GetChild();
if (child)
child.ForEachSiblingElementWithName(name, callback);
#endif
}
void XMLNode::ForEachAttribute(AttributeCallback const &callback) const {
#if LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
if (IsValid())
return m_node->type == XML_ELEMENT_NODE;
#endif
return false;
}
XMLNode XMLNode::GetElementForPath(const NamePath &path) {
#if LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
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 LLDB_ENABLE_LIBXML2
if (IsValid()) {
return CreatePlistValue(m_dict_node);
}
#endif
return root_sp;
}