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