1285101Semaste//===-- XML.cpp -------------------------------------------------*- C++ -*-===// 2285101Semaste// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6285101Semaste// 7285101Semaste//===----------------------------------------------------------------------===// 8285101Semaste 9314564Sdim#include <stdlib.h> /* atof */ 10285101Semaste 11360784Sdim#include "lldb/Host/Config.h" 12314564Sdim#include "lldb/Host/StringConvert.h" 13285101Semaste#include "lldb/Host/XML.h" 14285101Semaste 15285101Semasteusing namespace lldb; 16285101Semasteusing namespace lldb_private; 17285101Semaste 18314564Sdim#pragma mark-- XMLDocument 19285101Semaste 20314564SdimXMLDocument::XMLDocument() : m_document(nullptr) {} 21285101Semaste 22314564SdimXMLDocument::~XMLDocument() { Clear(); } 23285101Semaste 24314564Sdimvoid XMLDocument::Clear() { 25360784Sdim#if LLDB_ENABLE_LIBXML2 26314564Sdim if (m_document) { 27314564Sdim xmlDocPtr doc = m_document; 28314564Sdim m_document = nullptr; 29314564Sdim xmlFreeDoc(doc); 30314564Sdim } 31285101Semaste#endif 32285101Semaste} 33285101Semaste 34314564Sdimbool XMLDocument::IsValid() const { return m_document != nullptr; } 35285101Semaste 36314564Sdimvoid XMLDocument::ErrorCallback(void *ctx, const char *format, ...) { 37314564Sdim XMLDocument *document = (XMLDocument *)ctx; 38314564Sdim va_list args; 39314564Sdim va_start(args, format); 40314564Sdim document->m_errors.PrintfVarArg(format, args); 41314564Sdim document->m_errors.EOL(); 42314564Sdim va_end(args); 43285101Semaste} 44285101Semaste 45314564Sdimbool XMLDocument::ParseFile(const char *path) { 46360784Sdim#if LLDB_ENABLE_LIBXML2 47314564Sdim Clear(); 48314564Sdim xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback); 49314564Sdim m_document = xmlParseFile(path); 50314564Sdim xmlSetGenericErrorFunc(nullptr, nullptr); 51285101Semaste#endif 52314564Sdim return IsValid(); 53285101Semaste} 54285101Semaste 55314564Sdimbool XMLDocument::ParseMemory(const char *xml, size_t xml_length, 56314564Sdim const char *url) { 57360784Sdim#if LLDB_ENABLE_LIBXML2 58314564Sdim Clear(); 59314564Sdim xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback); 60314564Sdim m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0); 61314564Sdim xmlSetGenericErrorFunc(nullptr, nullptr); 62285101Semaste#endif 63314564Sdim return IsValid(); 64285101Semaste} 65285101Semaste 66314564SdimXMLNode XMLDocument::GetRootElement(const char *required_name) { 67360784Sdim#if LLDB_ENABLE_LIBXML2 68314564Sdim if (IsValid()) { 69314564Sdim XMLNode root_node(xmlDocGetRootElement(m_document)); 70314564Sdim if (required_name) { 71314564Sdim llvm::StringRef actual_name = root_node.GetName(); 72314564Sdim if (actual_name == required_name) 73314564Sdim return root_node; 74314564Sdim } else { 75314564Sdim return root_node; 76285101Semaste } 77314564Sdim } 78285101Semaste#endif 79314564Sdim return XMLNode(); 80285101Semaste} 81285101Semaste 82314564Sdimllvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); } 83285101Semaste 84314564Sdimbool XMLDocument::XMLEnabled() { 85360784Sdim#if LLDB_ENABLE_LIBXML2 86314564Sdim return true; 87285101Semaste#else 88314564Sdim return false; 89285101Semaste#endif 90285101Semaste} 91285101Semaste 92314564Sdim#pragma mark-- XMLNode 93285101Semaste 94314564SdimXMLNode::XMLNode() : m_node(nullptr) {} 95285101Semaste 96314564SdimXMLNode::XMLNode(XMLNodeImpl node) : m_node(node) {} 97285101Semaste 98314564SdimXMLNode::~XMLNode() {} 99285101Semaste 100314564Sdimvoid XMLNode::Clear() { m_node = nullptr; } 101285101Semaste 102314564SdimXMLNode XMLNode::GetParent() const { 103360784Sdim#if LLDB_ENABLE_LIBXML2 104314564Sdim if (IsValid()) 105314564Sdim return XMLNode(m_node->parent); 106314564Sdim else 107314564Sdim return XMLNode(); 108285101Semaste#else 109314564Sdim return XMLNode(); 110285101Semaste#endif 111285101Semaste} 112285101Semaste 113314564SdimXMLNode XMLNode::GetSibling() const { 114360784Sdim#if LLDB_ENABLE_LIBXML2 115314564Sdim if (IsValid()) 116314564Sdim return XMLNode(m_node->next); 117314564Sdim else 118314564Sdim return XMLNode(); 119285101Semaste#else 120314564Sdim return XMLNode(); 121285101Semaste#endif 122285101Semaste} 123285101Semaste 124314564SdimXMLNode XMLNode::GetChild() const { 125360784Sdim#if LLDB_ENABLE_LIBXML2 126285101Semaste 127314564Sdim if (IsValid()) 128314564Sdim return XMLNode(m_node->children); 129314564Sdim else 130314564Sdim return XMLNode(); 131285101Semaste#else 132314564Sdim return XMLNode(); 133285101Semaste#endif 134285101Semaste} 135285101Semaste 136314564Sdimllvm::StringRef XMLNode::GetAttributeValue(const char *name, 137314564Sdim const char *fail_value) const { 138353358Sdim const char *attr_value = nullptr; 139360784Sdim#if LLDB_ENABLE_LIBXML2 140285101Semaste 141314564Sdim if (IsValid()) 142314564Sdim attr_value = (const char *)xmlGetProp(m_node, (const xmlChar *)name); 143314564Sdim else 144314564Sdim attr_value = fail_value; 145285101Semaste#else 146314564Sdim attr_value = fail_value; 147285101Semaste#endif 148314564Sdim if (attr_value) 149314564Sdim return llvm::StringRef(attr_value); 150314564Sdim else 151314564Sdim return llvm::StringRef(); 152285101Semaste} 153285101Semaste 154341825Sdimbool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value, 155341825Sdim uint64_t fail_value, int base) const { 156360784Sdim#if LLDB_ENABLE_LIBXML2 157341825Sdim llvm::StringRef str_value = GetAttributeValue(name, ""); 158341825Sdim#else 159341825Sdim llvm::StringRef str_value; 160341825Sdim#endif 161341825Sdim bool success = false; 162341825Sdim value = StringConvert::ToUInt64(str_value.data(), fail_value, base, &success); 163341825Sdim return success; 164341825Sdim} 165341825Sdim 166314564Sdimvoid XMLNode::ForEachChildNode(NodeCallback const &callback) const { 167360784Sdim#if LLDB_ENABLE_LIBXML2 168314564Sdim if (IsValid()) 169314564Sdim GetChild().ForEachSiblingNode(callback); 170285101Semaste#endif 171285101Semaste} 172285101Semaste 173314564Sdimvoid XMLNode::ForEachChildElement(NodeCallback const &callback) const { 174360784Sdim#if LLDB_ENABLE_LIBXML2 175314564Sdim XMLNode child = GetChild(); 176314564Sdim if (child) 177314564Sdim child.ForEachSiblingElement(callback); 178285101Semaste#endif 179285101Semaste} 180285101Semaste 181314564Sdimvoid XMLNode::ForEachChildElementWithName(const char *name, 182314564Sdim NodeCallback const &callback) const { 183360784Sdim#if LLDB_ENABLE_LIBXML2 184314564Sdim XMLNode child = GetChild(); 185314564Sdim if (child) 186314564Sdim child.ForEachSiblingElementWithName(name, callback); 187285101Semaste#endif 188285101Semaste} 189285101Semaste 190314564Sdimvoid XMLNode::ForEachAttribute(AttributeCallback const &callback) const { 191360784Sdim#if LLDB_ENABLE_LIBXML2 192285101Semaste 193314564Sdim if (IsValid()) { 194314564Sdim for (xmlAttrPtr attr = m_node->properties; attr != nullptr; 195314564Sdim attr = attr->next) { 196314564Sdim // check if name matches 197314564Sdim if (attr->name) { 198314564Sdim // check child is a text node 199314564Sdim xmlNodePtr child = attr->children; 200314564Sdim if (child->type == XML_TEXT_NODE) { 201314564Sdim llvm::StringRef attr_value; 202314564Sdim if (child->content) 203314564Sdim attr_value = llvm::StringRef((const char *)child->content); 204344779Sdim if (!callback(llvm::StringRef((const char *)attr->name), attr_value)) 205314564Sdim return; 206285101Semaste } 207314564Sdim } 208285101Semaste } 209314564Sdim } 210285101Semaste#endif 211285101Semaste} 212285101Semaste 213314564Sdimvoid XMLNode::ForEachSiblingNode(NodeCallback const &callback) const { 214360784Sdim#if LLDB_ENABLE_LIBXML2 215285101Semaste 216314564Sdim if (IsValid()) { 217314564Sdim // iterate through all siblings 218314564Sdim for (xmlNodePtr node = m_node; node; node = node->next) { 219344779Sdim if (!callback(XMLNode(node))) 220314564Sdim return; 221285101Semaste } 222314564Sdim } 223285101Semaste#endif 224285101Semaste} 225285101Semaste 226314564Sdimvoid XMLNode::ForEachSiblingElement(NodeCallback const &callback) const { 227360784Sdim#if LLDB_ENABLE_LIBXML2 228314564Sdim 229314564Sdim if (IsValid()) { 230314564Sdim // iterate through all siblings 231314564Sdim for (xmlNodePtr node = m_node; node; node = node->next) { 232314564Sdim // we are looking for element nodes only 233314564Sdim if (node->type != XML_ELEMENT_NODE) 234314564Sdim continue; 235314564Sdim 236344779Sdim if (!callback(XMLNode(node))) 237314564Sdim return; 238285101Semaste } 239314564Sdim } 240285101Semaste#endif 241285101Semaste} 242285101Semaste 243314564Sdimvoid XMLNode::ForEachSiblingElementWithName( 244314564Sdim const char *name, NodeCallback const &callback) const { 245360784Sdim#if LLDB_ENABLE_LIBXML2 246314564Sdim 247314564Sdim if (IsValid()) { 248314564Sdim // iterate through all siblings 249314564Sdim for (xmlNodePtr node = m_node; node; node = node->next) { 250314564Sdim // we are looking for element nodes only 251314564Sdim if (node->type != XML_ELEMENT_NODE) 252314564Sdim continue; 253314564Sdim 254341825Sdim // If name is nullptr, we take all nodes of type "t", else just the ones 255341825Sdim // whose name matches 256314564Sdim if (name) { 257314564Sdim if (strcmp((const char *)node->name, name) != 0) 258314564Sdim continue; // Name mismatch, ignore this one 259314564Sdim } else { 260314564Sdim if (node->name) 261314564Sdim continue; // nullptr name specified and this element has a name, 262314564Sdim // ignore this one 263314564Sdim } 264314564Sdim 265344779Sdim if (!callback(XMLNode(node))) 266314564Sdim return; 267285101Semaste } 268314564Sdim } 269285101Semaste#endif 270285101Semaste} 271285101Semaste 272314564Sdimllvm::StringRef XMLNode::GetName() const { 273360784Sdim#if LLDB_ENABLE_LIBXML2 274314564Sdim if (IsValid()) { 275314564Sdim if (m_node->name) 276314564Sdim return llvm::StringRef((const char *)m_node->name); 277314564Sdim } 278285101Semaste#endif 279314564Sdim return llvm::StringRef(); 280285101Semaste} 281285101Semaste 282314564Sdimbool XMLNode::GetElementText(std::string &text) const { 283314564Sdim text.clear(); 284360784Sdim#if LLDB_ENABLE_LIBXML2 285314564Sdim if (IsValid()) { 286314564Sdim bool success = false; 287314564Sdim if (m_node->type == XML_ELEMENT_NODE) { 288314564Sdim // check child is a text node 289314564Sdim for (xmlNodePtr node = m_node->children; node != nullptr; 290314564Sdim node = node->next) { 291314564Sdim if (node->type == XML_TEXT_NODE) { 292314564Sdim text.append((const char *)node->content); 293314564Sdim success = true; 294285101Semaste } 295314564Sdim } 296285101Semaste } 297314564Sdim return success; 298314564Sdim } 299285101Semaste#endif 300314564Sdim return false; 301285101Semaste} 302285101Semaste 303314564Sdimbool XMLNode::GetElementTextAsUnsigned(uint64_t &value, uint64_t fail_value, 304314564Sdim int base) const { 305314564Sdim bool success = false; 306360784Sdim#if LLDB_ENABLE_LIBXML2 307314564Sdim if (IsValid()) { 308314564Sdim std::string text; 309314564Sdim if (GetElementText(text)) 310314564Sdim value = StringConvert::ToUInt64(text.c_str(), fail_value, base, &success); 311314564Sdim } 312285101Semaste#endif 313314564Sdim if (!success) 314314564Sdim value = fail_value; 315314564Sdim return success; 316285101Semaste} 317285101Semaste 318314564Sdimbool XMLNode::GetElementTextAsFloat(double &value, double fail_value) const { 319314564Sdim bool success = false; 320360784Sdim#if LLDB_ENABLE_LIBXML2 321314564Sdim if (IsValid()) { 322314564Sdim std::string text; 323314564Sdim if (GetElementText(text)) { 324314564Sdim value = atof(text.c_str()); 325314564Sdim success = true; 326285101Semaste } 327314564Sdim } 328285101Semaste#endif 329314564Sdim if (!success) 330314564Sdim value = fail_value; 331314564Sdim return success; 332285101Semaste} 333285101Semaste 334314564Sdimbool XMLNode::NameIs(const char *name) const { 335360784Sdim#if LLDB_ENABLE_LIBXML2 336285101Semaste 337314564Sdim if (IsValid()) { 338314564Sdim // In case we are looking for a nullptr name or an exact pointer match 339314564Sdim if (m_node->name == (const xmlChar *)name) 340314564Sdim return true; 341314564Sdim if (m_node->name) 342314564Sdim return strcmp((const char *)m_node->name, name) == 0; 343314564Sdim } 344285101Semaste#endif 345314564Sdim return false; 346285101Semaste} 347285101Semaste 348314564SdimXMLNode XMLNode::FindFirstChildElementWithName(const char *name) const { 349314564Sdim XMLNode result_node; 350285101Semaste 351360784Sdim#if LLDB_ENABLE_LIBXML2 352314564Sdim ForEachChildElementWithName( 353327952Sdim name, [&result_node](const XMLNode &node) -> bool { 354285101Semaste result_node = node; 355285101Semaste // Stop iterating, we found the node we wanted 356285101Semaste return false; 357314564Sdim }); 358285101Semaste#endif 359285101Semaste 360314564Sdim return result_node; 361285101Semaste} 362285101Semaste 363314564Sdimbool XMLNode::IsValid() const { return m_node != nullptr; } 364285101Semaste 365314564Sdimbool XMLNode::IsElement() const { 366360784Sdim#if LLDB_ENABLE_LIBXML2 367314564Sdim if (IsValid()) 368314564Sdim return m_node->type == XML_ELEMENT_NODE; 369285101Semaste#endif 370314564Sdim return false; 371285101Semaste} 372285101Semaste 373314564SdimXMLNode XMLNode::GetElementForPath(const NamePath &path) { 374360784Sdim#if LLDB_ENABLE_LIBXML2 375285101Semaste 376314564Sdim if (IsValid()) { 377314564Sdim if (path.empty()) 378314564Sdim return *this; 379314564Sdim else { 380314564Sdim XMLNode node = FindFirstChildElementWithName(path[0].c_str()); 381314564Sdim const size_t n = path.size(); 382314564Sdim for (size_t i = 1; node && i < n; ++i) 383314564Sdim node = node.FindFirstChildElementWithName(path[i].c_str()); 384314564Sdim return node; 385285101Semaste } 386314564Sdim } 387285101Semaste#endif 388285101Semaste 389314564Sdim return XMLNode(); 390285101Semaste} 391285101Semaste 392314564Sdim#pragma mark-- ApplePropertyList 393285101Semaste 394314564SdimApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {} 395285101Semaste 396314564SdimApplePropertyList::ApplePropertyList(const char *path) 397314564Sdim : m_xml_doc(), m_dict_node() { 398314564Sdim ParseFile(path); 399285101Semaste} 400285101Semaste 401314564SdimApplePropertyList::~ApplePropertyList() {} 402285101Semaste 403314564Sdimllvm::StringRef ApplePropertyList::GetErrors() const { 404314564Sdim return m_xml_doc.GetErrors(); 405285101Semaste} 406285101Semaste 407314564Sdimbool ApplePropertyList::ParseFile(const char *path) { 408314564Sdim if (m_xml_doc.ParseFile(path)) { 409314564Sdim XMLNode plist = m_xml_doc.GetRootElement("plist"); 410314564Sdim if (plist) { 411314564Sdim plist.ForEachChildElementWithName("dict", 412314564Sdim [this](const XMLNode &dict) -> bool { 413314564Sdim this->m_dict_node = dict; 414314564Sdim return false; // Stop iterating 415314564Sdim }); 416314564Sdim return (bool)m_dict_node; 417314564Sdim } 418314564Sdim } 419314564Sdim return false; 420285101Semaste} 421285101Semaste 422314564Sdimbool ApplePropertyList::IsValid() const { return (bool)m_dict_node; } 423285101Semaste 424314564Sdimbool ApplePropertyList::GetValueAsString(const char *key, 425314564Sdim std::string &value) const { 426314564Sdim XMLNode value_node = GetValueNode(key); 427314564Sdim if (value_node) 428314564Sdim return ApplePropertyList::ExtractStringFromValueNode(value_node, value); 429314564Sdim return false; 430285101Semaste} 431285101Semaste 432314564SdimXMLNode ApplePropertyList::GetValueNode(const char *key) const { 433314564Sdim XMLNode value_node; 434360784Sdim#if LLDB_ENABLE_LIBXML2 435285101Semaste 436314564Sdim if (IsValid()) { 437314564Sdim m_dict_node.ForEachChildElementWithName( 438314564Sdim "key", [key, &value_node](const XMLNode &key_node) -> bool { 439314564Sdim std::string key_name; 440314564Sdim if (key_node.GetElementText(key_name)) { 441344779Sdim if (key_name == key) { 442314564Sdim value_node = key_node.GetSibling(); 443314564Sdim while (value_node && !value_node.IsElement()) 444314564Sdim value_node = value_node.GetSibling(); 445314564Sdim return false; // Stop iterating 446285101Semaste } 447314564Sdim } 448314564Sdim return true; // Keep iterating 449285101Semaste }); 450314564Sdim } 451285101Semaste#endif 452314564Sdim return value_node; 453285101Semaste} 454285101Semaste 455314564Sdimbool ApplePropertyList::ExtractStringFromValueNode(const XMLNode &node, 456314564Sdim std::string &value) { 457314564Sdim value.clear(); 458360784Sdim#if LLDB_ENABLE_LIBXML2 459314564Sdim if (node.IsValid()) { 460314564Sdim llvm::StringRef element_name = node.GetName(); 461314564Sdim if (element_name == "true" || element_name == "false") { 462314564Sdim // The text value _is_ the element name itself... 463314564Sdim value = element_name.str(); 464314564Sdim return true; 465314564Sdim } else if (element_name == "dict" || element_name == "array") 466314564Sdim return false; // dictionaries and arrays have no text value, so we fail 467314564Sdim else 468314564Sdim return node.GetElementText(value); 469314564Sdim } 470285101Semaste#endif 471314564Sdim return false; 472285101Semaste} 473285101Semaste 474360784Sdim#if LLDB_ENABLE_LIBXML2 475285101Semaste 476285101Semastenamespace { 477285101Semaste 478314564SdimStructuredData::ObjectSP CreatePlistValue(XMLNode node) { 479314564Sdim llvm::StringRef element_name = node.GetName(); 480314564Sdim if (element_name == "array") { 481314564Sdim std::shared_ptr<StructuredData::Array> array_sp( 482314564Sdim new StructuredData::Array()); 483314564Sdim node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool { 484314564Sdim array_sp->AddItem(CreatePlistValue(node)); 485314564Sdim return true; // Keep iterating through all child elements of the array 486314564Sdim }); 487314564Sdim return array_sp; 488314564Sdim } else if (element_name == "dict") { 489314564Sdim XMLNode key_node; 490314564Sdim std::shared_ptr<StructuredData::Dictionary> dict_sp( 491314564Sdim new StructuredData::Dictionary()); 492314564Sdim node.ForEachChildElement( 493314564Sdim [&key_node, &dict_sp](const XMLNode &node) -> bool { 494314564Sdim if (node.NameIs("key")) { 495314564Sdim // This is a "key" element node 496314564Sdim key_node = node; 497314564Sdim } else { 498314564Sdim // This is a value node 499314564Sdim if (key_node) { 500314564Sdim std::string key_name; 501314564Sdim key_node.GetElementText(key_name); 502314564Sdim dict_sp->AddItem(key_name, CreatePlistValue(node)); 503314564Sdim key_node.Clear(); 504314564Sdim } 505314564Sdim } 506314564Sdim return true; // Keep iterating through all child elements of the 507314564Sdim // dictionary 508314564Sdim }); 509314564Sdim return dict_sp; 510314564Sdim } else if (element_name == "real") { 511314564Sdim double value = 0.0; 512314564Sdim node.GetElementTextAsFloat(value); 513314564Sdim return StructuredData::ObjectSP(new StructuredData::Float(value)); 514314564Sdim } else if (element_name == "integer") { 515314564Sdim uint64_t value = 0; 516314564Sdim node.GetElementTextAsUnsigned(value, 0, 0); 517314564Sdim return StructuredData::ObjectSP(new StructuredData::Integer(value)); 518314564Sdim } else if ((element_name == "string") || (element_name == "data") || 519314564Sdim (element_name == "date")) { 520314564Sdim std::string text; 521314564Sdim node.GetElementText(text); 522314564Sdim return StructuredData::ObjectSP( 523314564Sdim new StructuredData::String(std::move(text))); 524314564Sdim } else if (element_name == "true") { 525314564Sdim return StructuredData::ObjectSP(new StructuredData::Boolean(true)); 526314564Sdim } else if (element_name == "false") { 527314564Sdim return StructuredData::ObjectSP(new StructuredData::Boolean(false)); 528314564Sdim } 529314564Sdim return StructuredData::ObjectSP(new StructuredData::Null()); 530285101Semaste} 531314564Sdim} 532285101Semaste#endif 533285101Semaste 534314564SdimStructuredData::ObjectSP ApplePropertyList::GetStructuredData() { 535314564Sdim StructuredData::ObjectSP root_sp; 536360784Sdim#if LLDB_ENABLE_LIBXML2 537314564Sdim if (IsValid()) { 538314564Sdim return CreatePlistValue(m_dict_node); 539314564Sdim } 540285101Semaste#endif 541314564Sdim return root_sp; 542285101Semaste} 543