520 lines · cpp
1//===-- XML.cpp -----------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Host/Config.h"10#include "lldb/Host/XML.h"11 12#include "llvm/ADT/StringExtras.h"13 14using namespace lldb;15using namespace lldb_private;16 17#pragma mark-- XMLDocument18 19XMLDocument::XMLDocument() = default;20 21XMLDocument::~XMLDocument() { Clear(); }22 23void XMLDocument::Clear() {24#if LLDB_ENABLE_LIBXML225 if (m_document) {26 xmlDocPtr doc = m_document;27 m_document = nullptr;28 xmlFreeDoc(doc);29 }30#endif31}32 33bool XMLDocument::IsValid() const { return m_document != nullptr; }34 35void XMLDocument::ErrorCallback(void *ctx, const char *format, ...) {36 XMLDocument *document = (XMLDocument *)ctx;37 va_list args;38 va_start(args, format);39 document->m_errors.PrintfVarArg(format, args);40 document->m_errors.EOL();41 va_end(args);42}43 44bool XMLDocument::ParseFile(const char *path) {45#if LLDB_ENABLE_LIBXML246 Clear();47 xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);48 m_document = xmlParseFile(path);49 xmlSetGenericErrorFunc(nullptr, nullptr);50#endif51 return IsValid();52}53 54bool XMLDocument::ParseMemory(const char *xml, size_t xml_length,55 const char *url) {56#if LLDB_ENABLE_LIBXML257 Clear();58 xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);59 m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0);60 xmlSetGenericErrorFunc(nullptr, nullptr);61#endif62 return IsValid();63}64 65XMLNode XMLDocument::GetRootElement(const char *required_name) {66#if LLDB_ENABLE_LIBXML267 if (IsValid()) {68 XMLNode root_node(xmlDocGetRootElement(m_document));69 if (required_name) {70 llvm::StringRef actual_name = root_node.GetName();71 if (actual_name == required_name)72 return root_node;73 } else {74 return root_node;75 }76 }77#endif78 return XMLNode();79}80 81llvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); }82 83bool XMLDocument::XMLEnabled() {84#if LLDB_ENABLE_LIBXML285 return true;86#else87 return false;88#endif89}90 91#pragma mark-- XMLNode92 93XMLNode::XMLNode() = default;94 95XMLNode::XMLNode(XMLNodeImpl node) : m_node(node) {}96 97XMLNode::~XMLNode() = default;98 99void XMLNode::Clear() { m_node = nullptr; }100 101XMLNode XMLNode::GetParent() const {102#if LLDB_ENABLE_LIBXML2103 if (IsValid())104 return XMLNode(m_node->parent);105 else106 return XMLNode();107#else108 return XMLNode();109#endif110}111 112XMLNode XMLNode::GetSibling() const {113#if LLDB_ENABLE_LIBXML2114 if (IsValid())115 return XMLNode(m_node->next);116 else117 return XMLNode();118#else119 return XMLNode();120#endif121}122 123XMLNode XMLNode::GetChild() const {124#if LLDB_ENABLE_LIBXML2125 126 if (IsValid())127 return XMLNode(m_node->children);128 else129 return XMLNode();130#else131 return XMLNode();132#endif133}134 135std::string XMLNode::GetAttributeValue(const char *name,136 const char *fail_value) const {137 std::string attr_value;138#if LLDB_ENABLE_LIBXML2139 if (IsValid()) {140 xmlChar *value = xmlGetProp(m_node, (const xmlChar *)name);141 if (value) {142 attr_value = (const char *)value;143 xmlFree(value);144 }145 } else {146 if (fail_value)147 attr_value = fail_value;148 }149#else150 if (fail_value)151 attr_value = fail_value;152#endif153 return attr_value;154}155 156bool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value,157 uint64_t fail_value, int base) const {158 value = fail_value;159 return llvm::to_integer(GetAttributeValue(name, ""), value, base);160}161 162void XMLNode::ForEachChildNode(NodeCallback const &callback) const {163#if LLDB_ENABLE_LIBXML2164 if (IsValid())165 GetChild().ForEachSiblingNode(callback);166#endif167}168 169void XMLNode::ForEachChildElement(NodeCallback const &callback) const {170#if LLDB_ENABLE_LIBXML2171 XMLNode child = GetChild();172 if (child)173 child.ForEachSiblingElement(callback);174#endif175}176 177void XMLNode::ForEachChildElementWithName(const char *name,178 NodeCallback const &callback) const {179#if LLDB_ENABLE_LIBXML2180 XMLNode child = GetChild();181 if (child)182 child.ForEachSiblingElementWithName(name, callback);183#endif184}185 186void XMLNode::ForEachAttribute(AttributeCallback const &callback) const {187#if LLDB_ENABLE_LIBXML2188 189 if (IsValid()) {190 for (xmlAttrPtr attr = m_node->properties; attr != nullptr;191 attr = attr->next) {192 // check if name matches193 if (attr->name) {194 // check child is a text node195 xmlNodePtr child = attr->children;196 if (child->type == XML_TEXT_NODE) {197 llvm::StringRef attr_value;198 if (child->content)199 attr_value = llvm::StringRef((const char *)child->content);200 if (!callback(llvm::StringRef((const char *)attr->name), attr_value))201 return;202 }203 }204 }205 }206#endif207}208 209void XMLNode::ForEachSiblingNode(NodeCallback const &callback) const {210#if LLDB_ENABLE_LIBXML2211 212 if (IsValid()) {213 // iterate through all siblings214 for (xmlNodePtr node = m_node; node; node = node->next) {215 if (!callback(XMLNode(node)))216 return;217 }218 }219#endif220}221 222void XMLNode::ForEachSiblingElement(NodeCallback const &callback) const {223#if LLDB_ENABLE_LIBXML2224 225 if (IsValid()) {226 // iterate through all siblings227 for (xmlNodePtr node = m_node; node; node = node->next) {228 // we are looking for element nodes only229 if (node->type != XML_ELEMENT_NODE)230 continue;231 232 if (!callback(XMLNode(node)))233 return;234 }235 }236#endif237}238 239void XMLNode::ForEachSiblingElementWithName(240 const char *name, NodeCallback const &callback) const {241#if LLDB_ENABLE_LIBXML2242 243 if (IsValid()) {244 // iterate through all siblings245 for (xmlNodePtr node = m_node; node; node = node->next) {246 // we are looking for element nodes only247 if (node->type != XML_ELEMENT_NODE)248 continue;249 250 // If name is nullptr, we take all nodes of type "t", else just the ones251 // whose name matches252 if (name) {253 if (strcmp((const char *)node->name, name) != 0)254 continue; // Name mismatch, ignore this one255 } else {256 if (node->name)257 continue; // nullptr name specified and this element has a name,258 // ignore this one259 }260 261 if (!callback(XMLNode(node)))262 return;263 }264 }265#endif266}267 268llvm::StringRef XMLNode::GetName() const {269#if LLDB_ENABLE_LIBXML2270 if (IsValid()) {271 if (m_node->name)272 return llvm::StringRef((const char *)m_node->name);273 }274#endif275 return llvm::StringRef();276}277 278bool XMLNode::GetElementText(std::string &text) const {279 text.clear();280#if LLDB_ENABLE_LIBXML2281 if (IsValid()) {282 bool success = false;283 if (m_node->type == XML_ELEMENT_NODE) {284 // check child is a text node285 for (xmlNodePtr node = m_node->children; node != nullptr;286 node = node->next) {287 if (node->type == XML_TEXT_NODE) {288 text.append((const char *)node->content);289 success = true;290 }291 }292 }293 return success;294 }295#endif296 return false;297}298 299bool XMLNode::GetElementTextAsUnsigned(uint64_t &value, uint64_t fail_value,300 int base) const {301 std::string text;302 303 value = fail_value;304 return GetElementText(text) && llvm::to_integer(text, value, base);305}306 307bool XMLNode::GetElementTextAsFloat(double &value, double fail_value) const {308 std::string text;309 310 value = fail_value;311 return GetElementText(text) && llvm::to_float(text, value);312}313 314bool XMLNode::NameIs(const char *name) const {315#if LLDB_ENABLE_LIBXML2316 317 if (IsValid()) {318 // In case we are looking for a nullptr name or an exact pointer match319 if (m_node->name == (const xmlChar *)name)320 return true;321 if (m_node->name)322 return strcmp((const char *)m_node->name, name) == 0;323 }324#endif325 return false;326}327 328XMLNode XMLNode::FindFirstChildElementWithName(const char *name) const {329 XMLNode result_node;330 331#if LLDB_ENABLE_LIBXML2332 ForEachChildElementWithName(333 name, [&result_node](const XMLNode &node) -> bool {334 result_node = node;335 // Stop iterating, we found the node we wanted336 return false;337 });338#endif339 340 return result_node;341}342 343bool XMLNode::IsValid() const { return m_node != nullptr; }344 345bool XMLNode::IsElement() const {346#if LLDB_ENABLE_LIBXML2347 if (IsValid())348 return m_node->type == XML_ELEMENT_NODE;349#endif350 return false;351}352 353XMLNode XMLNode::GetElementForPath(const NamePath &path) {354#if LLDB_ENABLE_LIBXML2355 356 if (IsValid()) {357 if (path.empty())358 return *this;359 else {360 XMLNode node = FindFirstChildElementWithName(path[0].c_str());361 const size_t n = path.size();362 for (size_t i = 1; node && i < n; ++i)363 node = node.FindFirstChildElementWithName(path[i].c_str());364 return node;365 }366 }367#endif368 369 return XMLNode();370}371 372#pragma mark-- ApplePropertyList373 374ApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {}375 376ApplePropertyList::ApplePropertyList(const char *path)377 : m_xml_doc(), m_dict_node() {378 ParseFile(path);379}380 381ApplePropertyList::~ApplePropertyList() = default;382 383llvm::StringRef ApplePropertyList::GetErrors() const {384 return m_xml_doc.GetErrors();385}386 387bool ApplePropertyList::ParseFile(const char *path) {388 if (m_xml_doc.ParseFile(path)) {389 XMLNode plist = m_xml_doc.GetRootElement("plist");390 if (plist) {391 plist.ForEachChildElementWithName("dict",392 [this](const XMLNode &dict) -> bool {393 this->m_dict_node = dict;394 return false; // Stop iterating395 });396 return (bool)m_dict_node;397 }398 }399 return false;400}401 402bool ApplePropertyList::IsValid() const { return (bool)m_dict_node; }403 404bool ApplePropertyList::GetValueAsString(const char *key,405 std::string &value) const {406 XMLNode value_node = GetValueNode(key);407 if (value_node)408 return ApplePropertyList::ExtractStringFromValueNode(value_node, value);409 return false;410}411 412XMLNode ApplePropertyList::GetValueNode(const char *key) const {413 XMLNode value_node;414#if LLDB_ENABLE_LIBXML2415 416 if (IsValid()) {417 m_dict_node.ForEachChildElementWithName(418 "key", [key, &value_node](const XMLNode &key_node) -> bool {419 std::string key_name;420 if (key_node.GetElementText(key_name)) {421 if (key_name == key) {422 value_node = key_node.GetSibling();423 while (value_node && !value_node.IsElement())424 value_node = value_node.GetSibling();425 return false; // Stop iterating426 }427 }428 return true; // Keep iterating429 });430 }431#endif432 return value_node;433}434 435bool ApplePropertyList::ExtractStringFromValueNode(const XMLNode &node,436 std::string &value) {437 value.clear();438#if LLDB_ENABLE_LIBXML2439 if (node.IsValid()) {440 llvm::StringRef element_name = node.GetName();441 if (element_name == "true" || element_name == "false") {442 // The text value _is_ the element name itself...443 value = element_name.str();444 return true;445 } else if (element_name == "dict" || element_name == "array")446 return false; // dictionaries and arrays have no text value, so we fail447 else448 return node.GetElementText(value);449 }450#endif451 return false;452}453 454#if LLDB_ENABLE_LIBXML2455 456static StructuredData::ObjectSP CreatePlistValue(XMLNode node) {457 llvm::StringRef element_name = node.GetName();458 if (element_name == "array") {459 std::shared_ptr<StructuredData::Array> array_sp(460 new StructuredData::Array());461 node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool {462 array_sp->AddItem(CreatePlistValue(node));463 return true; // Keep iterating through all child elements of the array464 });465 return array_sp;466 } else if (element_name == "dict") {467 XMLNode key_node;468 std::shared_ptr<StructuredData::Dictionary> dict_sp(469 new StructuredData::Dictionary());470 node.ForEachChildElement(471 [&key_node, &dict_sp](const XMLNode &node) -> bool {472 if (node.NameIs("key")) {473 // This is a "key" element node474 key_node = node;475 } else {476 // This is a value node477 if (key_node) {478 std::string key_name;479 key_node.GetElementText(key_name);480 dict_sp->AddItem(key_name, CreatePlistValue(node));481 key_node.Clear();482 }483 }484 return true; // Keep iterating through all child elements of the485 // dictionary486 });487 return dict_sp;488 } else if (element_name == "real") {489 double value = 0.0;490 node.GetElementTextAsFloat(value);491 return StructuredData::ObjectSP(new StructuredData::Float(value));492 } else if (element_name == "integer") {493 uint64_t value = 0;494 node.GetElementTextAsUnsigned(value, 0, 0);495 return StructuredData::ObjectSP(new StructuredData::UnsignedInteger(value));496 } else if ((element_name == "string") || (element_name == "data") ||497 (element_name == "date")) {498 std::string text;499 node.GetElementText(text);500 return StructuredData::ObjectSP(501 new StructuredData::String(std::move(text)));502 } else if (element_name == "true") {503 return StructuredData::ObjectSP(new StructuredData::Boolean(true));504 } else if (element_name == "false") {505 return StructuredData::ObjectSP(new StructuredData::Boolean(false));506 }507 return StructuredData::ObjectSP(new StructuredData::Null());508}509#endif510 511StructuredData::ObjectSP ApplePropertyList::GetStructuredData() {512 StructuredData::ObjectSP root_sp;513#if LLDB_ENABLE_LIBXML2514 if (IsValid()) {515 return CreatePlistValue(m_dict_node);516 }517#endif518 return root_sp;519}520