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-exception 6// 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-- XMLDocument 18 19XMLDocument::XMLDocument() = default; 20 21XMLDocument::~XMLDocument() { Clear(); } 22 23void XMLDocument::Clear() { 24#if LLDB_ENABLE_LIBXML2 25 if (m_document) { 26 xmlDocPtr doc = m_document; 27 m_document = nullptr; 28 xmlFreeDoc(doc); 29 } 30#endif 31} 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_LIBXML2 46 Clear(); 47 xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback); 48 m_document = xmlParseFile(path); 49 xmlSetGenericErrorFunc(nullptr, nullptr); 50#endif 51 return IsValid(); 52} 53 54bool XMLDocument::ParseMemory(const char *xml, size_t xml_length, 55 const char *url) { 56#if LLDB_ENABLE_LIBXML2 57 Clear(); 58 xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback); 59 m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0); 60 xmlSetGenericErrorFunc(nullptr, nullptr); 61#endif 62 return IsValid(); 63} 64 65XMLNode XMLDocument::GetRootElement(const char *required_name) { 66#if LLDB_ENABLE_LIBXML2 67 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#endif 78 return XMLNode(); 79} 80 81llvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); } 82 83bool XMLDocument::XMLEnabled() { 84#if LLDB_ENABLE_LIBXML2 85 return true; 86#else 87 return false; 88#endif 89} 90 91#pragma mark-- XMLNode 92 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_LIBXML2 103 if (IsValid()) 104 return XMLNode(m_node->parent); 105 else 106 return XMLNode(); 107#else 108 return XMLNode(); 109#endif 110} 111 112XMLNode XMLNode::GetSibling() const { 113#if LLDB_ENABLE_LIBXML2 114 if (IsValid()) 115 return XMLNode(m_node->next); 116 else 117 return XMLNode(); 118#else 119 return XMLNode(); 120#endif 121} 122 123XMLNode XMLNode::GetChild() const { 124#if LLDB_ENABLE_LIBXML2 125 126 if (IsValid()) 127 return XMLNode(m_node->children); 128 else 129 return XMLNode(); 130#else 131 return XMLNode(); 132#endif 133} 134 135std::string XMLNode::GetAttributeValue(const char *name, 136 const char *fail_value) const { 137 std::string attr_value; 138#if LLDB_ENABLE_LIBXML2 139 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#else 150 if (fail_value) 151 attr_value = fail_value; 152#endif 153 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_LIBXML2 164 if (IsValid()) 165 GetChild().ForEachSiblingNode(callback); 166#endif 167} 168 169void XMLNode::ForEachChildElement(NodeCallback const &callback) const { 170#if LLDB_ENABLE_LIBXML2 171 XMLNode child = GetChild(); 172 if (child) 173 child.ForEachSiblingElement(callback); 174#endif 175} 176 177void XMLNode::ForEachChildElementWithName(const char *name, 178 NodeCallback const &callback) const { 179#if LLDB_ENABLE_LIBXML2 180 XMLNode child = GetChild(); 181 if (child) 182 child.ForEachSiblingElementWithName(name, callback); 183#endif 184} 185 186void XMLNode::ForEachAttribute(AttributeCallback const &callback) const { 187#if LLDB_ENABLE_LIBXML2 188 189 if (IsValid()) { 190 for (xmlAttrPtr attr = m_node->properties; attr != nullptr; 191 attr = attr->next) { 192 // check if name matches 193 if (attr->name) { 194 // check child is a text node 195 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#endif 207} 208 209void XMLNode::ForEachSiblingNode(NodeCallback const &callback) const { 210#if LLDB_ENABLE_LIBXML2 211 212 if (IsValid()) { 213 // iterate through all siblings 214 for (xmlNodePtr node = m_node; node; node = node->next) { 215 if (!callback(XMLNode(node))) 216 return; 217 } 218 } 219#endif 220} 221 222void XMLNode::ForEachSiblingElement(NodeCallback const &callback) const { 223#if LLDB_ENABLE_LIBXML2 224 225 if (IsValid()) { 226 // iterate through all siblings 227 for (xmlNodePtr node = m_node; node; node = node->next) { 228 // we are looking for element nodes only 229 if (node->type != XML_ELEMENT_NODE) 230 continue; 231 232 if (!callback(XMLNode(node))) 233 return; 234 } 235 } 236#endif 237} 238 239void XMLNode::ForEachSiblingElementWithName( 240 const char *name, NodeCallback const &callback) const { 241#if LLDB_ENABLE_LIBXML2 242 243 if (IsValid()) { 244 // iterate through all siblings 245 for (xmlNodePtr node = m_node; node; node = node->next) { 246 // we are looking for element nodes only 247 if (node->type != XML_ELEMENT_NODE) 248 continue; 249 250 // If name is nullptr, we take all nodes of type "t", else just the ones 251 // whose name matches 252 if (name) { 253 if (strcmp((const char *)node->name, name) != 0) 254 continue; // Name mismatch, ignore this one 255 } else { 256 if (node->name) 257 continue; // nullptr name specified and this element has a name, 258 // ignore this one 259 } 260 261 if (!callback(XMLNode(node))) 262 return; 263 } 264 } 265#endif 266} 267 268llvm::StringRef XMLNode::GetName() const { 269#if LLDB_ENABLE_LIBXML2 270 if (IsValid()) { 271 if (m_node->name) 272 return llvm::StringRef((const char *)m_node->name); 273 } 274#endif 275 return llvm::StringRef(); 276} 277 278bool XMLNode::GetElementText(std::string &text) const { 279 text.clear(); 280#if LLDB_ENABLE_LIBXML2 281 if (IsValid()) { 282 bool success = false; 283 if (m_node->type == XML_ELEMENT_NODE) { 284 // check child is a text node 285 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#endif 296 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_LIBXML2 316 317 if (IsValid()) { 318 // In case we are looking for a nullptr name or an exact pointer match 319 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#endif 325 return false; 326} 327 328XMLNode XMLNode::FindFirstChildElementWithName(const char *name) const { 329 XMLNode result_node; 330 331#if LLDB_ENABLE_LIBXML2 332 ForEachChildElementWithName( 333 name, [&result_node](const XMLNode &node) -> bool { 334 result_node = node; 335 // Stop iterating, we found the node we wanted 336 return false; 337 }); 338#endif 339 340 return result_node; 341} 342 343bool XMLNode::IsValid() const { return m_node != nullptr; } 344 345bool XMLNode::IsElement() const { 346#if LLDB_ENABLE_LIBXML2 347 if (IsValid()) 348 return m_node->type == XML_ELEMENT_NODE; 349#endif 350 return false; 351} 352 353XMLNode XMLNode::GetElementForPath(const NamePath &path) { 354#if LLDB_ENABLE_LIBXML2 355 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#endif 368 369 return XMLNode(); 370} 371 372#pragma mark-- ApplePropertyList 373 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 iterating 395 }); 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_LIBXML2 415 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 iterating 426 } 427 } 428 return true; // Keep iterating 429 }); 430 } 431#endif 432 return value_node; 433} 434 435bool ApplePropertyList::ExtractStringFromValueNode(const XMLNode &node, 436 std::string &value) { 437 value.clear(); 438#if LLDB_ENABLE_LIBXML2 439 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 fail 447 else 448 return node.GetElementText(value); 449 } 450#endif 451 return false; 452} 453 454#if LLDB_ENABLE_LIBXML2 455 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 array 464 }); 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 node 474 key_node = node; 475 } else { 476 // This is a value node 477 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 the 485 // dictionary 486 }); 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#endif 510 511StructuredData::ObjectSP ApplePropertyList::GetStructuredData() { 512 StructuredData::ObjectSP root_sp; 513#if LLDB_ENABLE_LIBXML2 514 if (IsValid()) { 515 return CreatePlistValue(m_dict_node); 516 } 517#endif 518 return root_sp; 519} 520