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 15 using namespace lldb; 16 using namespace lldb_private; 17 18 #pragma mark-- XMLDocument 19 20 XMLDocument::XMLDocument() : m_document(nullptr) {} 21 22 XMLDocument::~XMLDocument() { Clear(); } 23 24 void 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 34 bool XMLDocument::IsValid() const { return m_document != nullptr; } 35 36 void 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 45 bool 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 55 bool 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 66 XMLNode 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 82 llvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); } 83 84 bool XMLDocument::XMLEnabled() { 85 #if LLDB_ENABLE_LIBXML2 86 return true; 87 #else 88 return false; 89 #endif 90 } 91 92 #pragma mark-- XMLNode 93 94 XMLNode::XMLNode() : m_node(nullptr) {} 95 96 XMLNode::XMLNode(XMLNodeImpl node) : m_node(node) {} 97 98 XMLNode::~XMLNode() {} 99 100 void XMLNode::Clear() { m_node = nullptr; } 101 102 XMLNode 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 113 XMLNode 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 124 XMLNode 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 136 llvm::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 154 bool 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 166 void XMLNode::ForEachChildNode(NodeCallback const &callback) const { 167 #if LLDB_ENABLE_LIBXML2 168 if (IsValid()) 169 GetChild().ForEachSiblingNode(callback); 170 #endif 171 } 172 173 void 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 181 void 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 190 void 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 213 void 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 226 void 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 243 void 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 272 llvm::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 282 bool 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 303 bool 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 318 bool 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 334 bool 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 348 XMLNode 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 363 bool XMLNode::IsValid() const { return m_node != nullptr; } 364 365 bool 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 373 XMLNode 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 394 ApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {} 395 396 ApplePropertyList::ApplePropertyList(const char *path) 397 : m_xml_doc(), m_dict_node() { 398 ParseFile(path); 399 } 400 401 ApplePropertyList::~ApplePropertyList() {} 402 403 llvm::StringRef ApplePropertyList::GetErrors() const { 404 return m_xml_doc.GetErrors(); 405 } 406 407 bool 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 422 bool ApplePropertyList::IsValid() const { return (bool)m_dict_node; } 423 424 bool 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 432 XMLNode 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 455 bool 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 476 namespace { 477 478 StructuredData::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 534 StructuredData::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