694 lines
15 KiB
C++
694 lines
15 KiB
C++
//===-- 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" || element_name == "false")
|
|
{
|
|
// The text value _is_ the element name itself...
|
|
value = std::move(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;
|
|
}
|
|
|
|
|