1//===-- XML.cpp -------------------------------------------------*- C++ -*-===// 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 <stdlib.h> /* atof */ 10 11#include "lldb/Host/Config.h" 12#include "lldb/Host/StringConvert.h" 13#include "lldb/Host/XML.h" 14 15using namespace lldb; 16using namespace lldb_private; 17 18#pragma mark-- XMLDocument 19 20XMLDocument::XMLDocument() : m_document(nullptr) {} 21 22XMLDocument::~XMLDocument() { Clear(); } 23 24void XMLDocument::Clear() { 25#if LLDB_ENABLE_LIBXML2 26 if (m_document) { 27 xmlDocPtr doc = m_document; 28 m_document = nullptr; 29 xmlFreeDoc(doc); 30 } 31#endif 32} 33 34bool XMLDocument::IsValid() const { return m_document != nullptr; } 35 36void XMLDocument::ErrorCallback(void *ctx, const char *format, ...) { 37 XMLDocument *document = (XMLDocument *)ctx; 38 va_list args; 39 va_start(args, format); 40 document->m_errors.PrintfVarArg(format, args); 41 document->m_errors.EOL(); 42 va_end(args); 43} 44 45bool XMLDocument::ParseFile(const char *path) { 46#if LLDB_ENABLE_LIBXML2 47 Clear(); 48 xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback); 49 m_document = xmlParseFile(path); 50 xmlSetGenericErrorFunc(nullptr, nullptr); 51#endif 52 return IsValid(); 53} 54 55bool XMLDocument::ParseMemory(const char *xml, size_t xml_length, 56 const char *url) { 57#if LLDB_ENABLE_LIBXML2 58 Clear(); 59 xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback); 60 m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0); 61 xmlSetGenericErrorFunc(nullptr, nullptr); 62#endif 63 return IsValid(); 64} 65 66XMLNode XMLDocument::GetRootElement(const char *required_name) { 67#if LLDB_ENABLE_LIBXML2 68 if (IsValid()) { 69 XMLNode root_node(xmlDocGetRootElement(m_document)); 70 if (required_name) { 71 llvm::StringRef actual_name = root_node.GetName(); 72 if (actual_name == required_name) 73 return root_node; 74 } else { 75 return root_node; 76 } 77 } 78#endif 79 return XMLNode(); 80} 81 82llvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); } 83 84bool XMLDocument::XMLEnabled() { 85#if LLDB_ENABLE_LIBXML2 86 return true; 87#else 88 return false; 89#endif 90} 91 92#pragma mark-- XMLNode 93 94XMLNode::XMLNode() : m_node(nullptr) {} 95 96XMLNode::XMLNode(XMLNodeImpl node) : m_node(node) {} 97 98XMLNode::~XMLNode() {} 99 100void XMLNode::Clear() { m_node = nullptr; } 101 102XMLNode XMLNode::GetParent() const { 103#if LLDB_ENABLE_LIBXML2 104 if (IsValid()) 105 return XMLNode(m_node->parent); 106 else 107 return XMLNode(); 108#else 109 return XMLNode(); 110#endif 111} 112 113XMLNode XMLNode::GetSibling() const { 114#if LLDB_ENABLE_LIBXML2 115 if (IsValid()) 116 return XMLNode(m_node->next); 117 else 118 return XMLNode(); 119#else 120 return XMLNode(); 121#endif 122} 123 124XMLNode XMLNode::GetChild() const { 125#if LLDB_ENABLE_LIBXML2 126 127 if (IsValid()) 128 return XMLNode(m_node->children); 129 else 130 return XMLNode(); 131#else 132 return XMLNode(); 133#endif 134} 135 136llvm::StringRef XMLNode::GetAttributeValue(const char *name, 137 const char *fail_value) const { 138 const char *attr_value = nullptr; 139#if LLDB_ENABLE_LIBXML2 140 141 if (IsValid()) 142 attr_value = (const char *)xmlGetProp(m_node, (const xmlChar *)name); 143 else 144 attr_value = fail_value; 145#else 146 attr_value = fail_value; 147#endif 148 if (attr_value) 149 return llvm::StringRef(attr_value); 150 else 151 return llvm::StringRef(); 152} 153 154bool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value, 155 uint64_t fail_value, int base) const { 156#if LLDB_ENABLE_LIBXML2 157 llvm::StringRef str_value = GetAttributeValue(name, ""); 158#else 159 llvm::StringRef str_value; 160#endif 161 bool success = false; 162 value = StringConvert::ToUInt64(str_value.data(), fail_value, base, &success); 163 return success; 164} 165 166void XMLNode::ForEachChildNode(NodeCallback const &callback) const { 167#if LLDB_ENABLE_LIBXML2 168 if (IsValid()) 169 GetChild().ForEachSiblingNode(callback); 170#endif 171} 172 173void XMLNode::ForEachChildElement(NodeCallback const &callback) const { 174#if LLDB_ENABLE_LIBXML2 175 XMLNode child = GetChild(); 176 if (child) 177 child.ForEachSiblingElement(callback); 178#endif 179} 180 181void XMLNode::ForEachChildElementWithName(const char *name, 182 NodeCallback const &callback) const { 183#if LLDB_ENABLE_LIBXML2 184 XMLNode child = GetChild(); 185 if (child) 186 child.ForEachSiblingElementWithName(name, callback); 187#endif 188} 189 190void XMLNode::ForEachAttribute(AttributeCallback const &callback) const { 191#if LLDB_ENABLE_LIBXML2 192 193 if (IsValid()) { 194 for (xmlAttrPtr attr = m_node->properties; attr != nullptr; 195 attr = attr->next) { 196 // check if name matches 197 if (attr->name) { 198 // check child is a text node 199 xmlNodePtr child = attr->children; 200 if (child->type == XML_TEXT_NODE) { 201 llvm::StringRef attr_value; 202 if (child->content) 203 attr_value = llvm::StringRef((const char *)child->content); 204 if (!callback(llvm::StringRef((const char *)attr->name), attr_value)) 205 return; 206 } 207 } 208 } 209 } 210#endif 211} 212 213void XMLNode::ForEachSiblingNode(NodeCallback const &callback) const { 214#if LLDB_ENABLE_LIBXML2 215 216 if (IsValid()) { 217 // iterate through all siblings 218 for (xmlNodePtr node = m_node; node; node = node->next) { 219 if (!callback(XMLNode(node))) 220 return; 221 } 222 } 223#endif 224} 225 226void XMLNode::ForEachSiblingElement(NodeCallback const &callback) const { 227#if LLDB_ENABLE_LIBXML2 228 229 if (IsValid()) { 230 // iterate through all siblings 231 for (xmlNodePtr node = m_node; node; node = node->next) { 232 // we are looking for element nodes only 233 if (node->type != XML_ELEMENT_NODE) 234 continue; 235 236 if (!callback(XMLNode(node))) 237 return; 238 } 239 } 240#endif 241} 242 243void XMLNode::ForEachSiblingElementWithName( 244 const char *name, NodeCallback const &callback) const { 245#if LLDB_ENABLE_LIBXML2 246 247 if (IsValid()) { 248 // iterate through all siblings 249 for (xmlNodePtr node = m_node; node; node = node->next) { 250 // we are looking for element nodes only 251 if (node->type != XML_ELEMENT_NODE) 252 continue; 253 254 // If name is nullptr, we take all nodes of type "t", else just the ones 255 // whose name matches 256 if (name) { 257 if (strcmp((const char *)node->name, name) != 0) 258 continue; // Name mismatch, ignore this one 259 } else { 260 if (node->name) 261 continue; // nullptr name specified and this element has a name, 262 // ignore this one 263 } 264 265 if (!callback(XMLNode(node))) 266 return; 267 } 268 } 269#endif 270} 271 272llvm::StringRef XMLNode::GetName() const { 273#if LLDB_ENABLE_LIBXML2 274 if (IsValid()) { 275 if (m_node->name) 276 return llvm::StringRef((const char *)m_node->name); 277 } 278#endif 279 return llvm::StringRef(); 280} 281 282bool XMLNode::GetElementText(std::string &text) const { 283 text.clear(); 284#if LLDB_ENABLE_LIBXML2 285 if (IsValid()) { 286 bool success = false; 287 if (m_node->type == XML_ELEMENT_NODE) { 288 // check child is a text node 289 for (xmlNodePtr node = m_node->children; node != nullptr; 290 node = node->next) { 291 if (node->type == XML_TEXT_NODE) { 292 text.append((const char *)node->content); 293 success = true; 294 } 295 } 296 } 297 return success; 298 } 299#endif 300 return false; 301} 302 303bool XMLNode::GetElementTextAsUnsigned(uint64_t &value, uint64_t fail_value, 304 int base) const { 305 bool success = false; 306#if LLDB_ENABLE_LIBXML2 307 if (IsValid()) { 308 std::string text; 309 if (GetElementText(text)) 310 value = StringConvert::ToUInt64(text.c_str(), fail_value, base, &success); 311 } 312#endif 313 if (!success) 314 value = fail_value; 315 return success; 316} 317 318bool XMLNode::GetElementTextAsFloat(double &value, double fail_value) const { 319 bool success = false; 320#if LLDB_ENABLE_LIBXML2 321 if (IsValid()) { 322 std::string text; 323 if (GetElementText(text)) { 324 value = atof(text.c_str()); 325 success = true; 326 } 327 } 328#endif 329 if (!success) 330 value = fail_value; 331 return success; 332} 333 334bool XMLNode::NameIs(const char *name) const { 335#if LLDB_ENABLE_LIBXML2 336 337 if (IsValid()) { 338 // In case we are looking for a nullptr name or an exact pointer match 339 if (m_node->name == (const xmlChar *)name) 340 return true; 341 if (m_node->name) 342 return strcmp((const char *)m_node->name, name) == 0; 343 } 344#endif 345 return false; 346} 347 348XMLNode XMLNode::FindFirstChildElementWithName(const char *name) const { 349 XMLNode result_node; 350 351#if LLDB_ENABLE_LIBXML2 352 ForEachChildElementWithName( 353 name, [&result_node](const XMLNode &node) -> bool { 354 result_node = node; 355 // Stop iterating, we found the node we wanted 356 return false; 357 }); 358#endif 359 360 return result_node; 361} 362 363bool XMLNode::IsValid() const { return m_node != nullptr; } 364 365bool XMLNode::IsElement() const { 366#if LLDB_ENABLE_LIBXML2 367 if (IsValid()) 368 return m_node->type == XML_ELEMENT_NODE; 369#endif 370 return false; 371} 372 373XMLNode XMLNode::GetElementForPath(const NamePath &path) { 374#if LLDB_ENABLE_LIBXML2 375 376 if (IsValid()) { 377 if (path.empty()) 378 return *this; 379 else { 380 XMLNode node = FindFirstChildElementWithName(path[0].c_str()); 381 const size_t n = path.size(); 382 for (size_t i = 1; node && i < n; ++i) 383 node = node.FindFirstChildElementWithName(path[i].c_str()); 384 return node; 385 } 386 } 387#endif 388 389 return XMLNode(); 390} 391 392#pragma mark-- ApplePropertyList 393 394ApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {} 395 396ApplePropertyList::ApplePropertyList(const char *path) 397 : m_xml_doc(), m_dict_node() { 398 ParseFile(path); 399} 400 401ApplePropertyList::~ApplePropertyList() {} 402 403llvm::StringRef ApplePropertyList::GetErrors() const { 404 return m_xml_doc.GetErrors(); 405} 406 407bool ApplePropertyList::ParseFile(const char *path) { 408 if (m_xml_doc.ParseFile(path)) { 409 XMLNode plist = m_xml_doc.GetRootElement("plist"); 410 if (plist) { 411 plist.ForEachChildElementWithName("dict", 412 [this](const XMLNode &dict) -> bool { 413 this->m_dict_node = dict; 414 return false; // Stop iterating 415 }); 416 return (bool)m_dict_node; 417 } 418 } 419 return false; 420} 421 422bool ApplePropertyList::IsValid() const { return (bool)m_dict_node; } 423 424bool ApplePropertyList::GetValueAsString(const char *key, 425 std::string &value) const { 426 XMLNode value_node = GetValueNode(key); 427 if (value_node) 428 return ApplePropertyList::ExtractStringFromValueNode(value_node, value); 429 return false; 430} 431 432XMLNode ApplePropertyList::GetValueNode(const char *key) const { 433 XMLNode value_node; 434#if LLDB_ENABLE_LIBXML2 435 436 if (IsValid()) { 437 m_dict_node.ForEachChildElementWithName( 438 "key", [key, &value_node](const XMLNode &key_node) -> bool { 439 std::string key_name; 440 if (key_node.GetElementText(key_name)) { 441 if (key_name == key) { 442 value_node = key_node.GetSibling(); 443 while (value_node && !value_node.IsElement()) 444 value_node = value_node.GetSibling(); 445 return false; // Stop iterating 446 } 447 } 448 return true; // Keep iterating 449 }); 450 } 451#endif 452 return value_node; 453} 454 455bool ApplePropertyList::ExtractStringFromValueNode(const XMLNode &node, 456 std::string &value) { 457 value.clear(); 458#if LLDB_ENABLE_LIBXML2 459 if (node.IsValid()) { 460 llvm::StringRef element_name = node.GetName(); 461 if (element_name == "true" || element_name == "false") { 462 // The text value _is_ the element name itself... 463 value = element_name.str(); 464 return true; 465 } else if (element_name == "dict" || element_name == "array") 466 return false; // dictionaries and arrays have no text value, so we fail 467 else 468 return node.GetElementText(value); 469 } 470#endif 471 return false; 472} 473 474#if LLDB_ENABLE_LIBXML2 475 476namespace { 477 478StructuredData::ObjectSP CreatePlistValue(XMLNode node) { 479 llvm::StringRef element_name = node.GetName(); 480 if (element_name == "array") { 481 std::shared_ptr<StructuredData::Array> array_sp( 482 new StructuredData::Array()); 483 node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool { 484 array_sp->AddItem(CreatePlistValue(node)); 485 return true; // Keep iterating through all child elements of the array 486 }); 487 return array_sp; 488 } else if (element_name == "dict") { 489 XMLNode key_node; 490 std::shared_ptr<StructuredData::Dictionary> dict_sp( 491 new StructuredData::Dictionary()); 492 node.ForEachChildElement( 493 [&key_node, &dict_sp](const XMLNode &node) -> bool { 494 if (node.NameIs("key")) { 495 // This is a "key" element node 496 key_node = node; 497 } else { 498 // This is a value node 499 if (key_node) { 500 std::string key_name; 501 key_node.GetElementText(key_name); 502 dict_sp->AddItem(key_name, CreatePlistValue(node)); 503 key_node.Clear(); 504 } 505 } 506 return true; // Keep iterating through all child elements of the 507 // dictionary 508 }); 509 return dict_sp; 510 } else if (element_name == "real") { 511 double value = 0.0; 512 node.GetElementTextAsFloat(value); 513 return StructuredData::ObjectSP(new StructuredData::Float(value)); 514 } else if (element_name == "integer") { 515 uint64_t value = 0; 516 node.GetElementTextAsUnsigned(value, 0, 0); 517 return StructuredData::ObjectSP(new StructuredData::Integer(value)); 518 } else if ((element_name == "string") || (element_name == "data") || 519 (element_name == "date")) { 520 std::string text; 521 node.GetElementText(text); 522 return StructuredData::ObjectSP( 523 new StructuredData::String(std::move(text))); 524 } else if (element_name == "true") { 525 return StructuredData::ObjectSP(new StructuredData::Boolean(true)); 526 } else if (element_name == "false") { 527 return StructuredData::ObjectSP(new StructuredData::Boolean(false)); 528 } 529 return StructuredData::ObjectSP(new StructuredData::Null()); 530} 531} 532#endif 533 534StructuredData::ObjectSP ApplePropertyList::GetStructuredData() { 535 StructuredData::ObjectSP root_sp; 536#if LLDB_ENABLE_LIBXML2 537 if (IsValid()) { 538 return CreatePlistValue(m_dict_node); 539 } 540#endif 541 return root_sp; 542} 543