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 llvm::StringRef XMLNode::GetAttributeValue(const char *name, 134 const char *fail_value) const { 135 const char *attr_value = nullptr; 136 #if LLDB_ENABLE_LIBXML2 137 138 if (IsValid()) 139 attr_value = (const char *)xmlGetProp(m_node, (const xmlChar *)name); 140 else 141 attr_value = fail_value; 142 #else 143 attr_value = fail_value; 144 #endif 145 if (attr_value) 146 return llvm::StringRef(attr_value); 147 else 148 return llvm::StringRef(); 149 } 150 151 bool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value, 152 uint64_t fail_value, int base) const { 153 value = fail_value; 154 return llvm::to_integer(GetAttributeValue(name, ""), value, base); 155 } 156 157 void XMLNode::ForEachChildNode(NodeCallback const &callback) const { 158 #if LLDB_ENABLE_LIBXML2 159 if (IsValid()) 160 GetChild().ForEachSiblingNode(callback); 161 #endif 162 } 163 164 void XMLNode::ForEachChildElement(NodeCallback const &callback) const { 165 #if LLDB_ENABLE_LIBXML2 166 XMLNode child = GetChild(); 167 if (child) 168 child.ForEachSiblingElement(callback); 169 #endif 170 } 171 172 void XMLNode::ForEachChildElementWithName(const char *name, 173 NodeCallback const &callback) const { 174 #if LLDB_ENABLE_LIBXML2 175 XMLNode child = GetChild(); 176 if (child) 177 child.ForEachSiblingElementWithName(name, callback); 178 #endif 179 } 180 181 void XMLNode::ForEachAttribute(AttributeCallback const &callback) const { 182 #if LLDB_ENABLE_LIBXML2 183 184 if (IsValid()) { 185 for (xmlAttrPtr attr = m_node->properties; attr != nullptr; 186 attr = attr->next) { 187 // check if name matches 188 if (attr->name) { 189 // check child is a text node 190 xmlNodePtr child = attr->children; 191 if (child->type == XML_TEXT_NODE) { 192 llvm::StringRef attr_value; 193 if (child->content) 194 attr_value = llvm::StringRef((const char *)child->content); 195 if (!callback(llvm::StringRef((const char *)attr->name), attr_value)) 196 return; 197 } 198 } 199 } 200 } 201 #endif 202 } 203 204 void XMLNode::ForEachSiblingNode(NodeCallback const &callback) const { 205 #if LLDB_ENABLE_LIBXML2 206 207 if (IsValid()) { 208 // iterate through all siblings 209 for (xmlNodePtr node = m_node; node; node = node->next) { 210 if (!callback(XMLNode(node))) 211 return; 212 } 213 } 214 #endif 215 } 216 217 void XMLNode::ForEachSiblingElement(NodeCallback const &callback) const { 218 #if LLDB_ENABLE_LIBXML2 219 220 if (IsValid()) { 221 // iterate through all siblings 222 for (xmlNodePtr node = m_node; node; node = node->next) { 223 // we are looking for element nodes only 224 if (node->type != XML_ELEMENT_NODE) 225 continue; 226 227 if (!callback(XMLNode(node))) 228 return; 229 } 230 } 231 #endif 232 } 233 234 void XMLNode::ForEachSiblingElementWithName( 235 const char *name, NodeCallback const &callback) const { 236 #if LLDB_ENABLE_LIBXML2 237 238 if (IsValid()) { 239 // iterate through all siblings 240 for (xmlNodePtr node = m_node; node; node = node->next) { 241 // we are looking for element nodes only 242 if (node->type != XML_ELEMENT_NODE) 243 continue; 244 245 // If name is nullptr, we take all nodes of type "t", else just the ones 246 // whose name matches 247 if (name) { 248 if (strcmp((const char *)node->name, name) != 0) 249 continue; // Name mismatch, ignore this one 250 } else { 251 if (node->name) 252 continue; // nullptr name specified and this element has a name, 253 // ignore this one 254 } 255 256 if (!callback(XMLNode(node))) 257 return; 258 } 259 } 260 #endif 261 } 262 263 llvm::StringRef XMLNode::GetName() const { 264 #if LLDB_ENABLE_LIBXML2 265 if (IsValid()) { 266 if (m_node->name) 267 return llvm::StringRef((const char *)m_node->name); 268 } 269 #endif 270 return llvm::StringRef(); 271 } 272 273 bool XMLNode::GetElementText(std::string &text) const { 274 text.clear(); 275 #if LLDB_ENABLE_LIBXML2 276 if (IsValid()) { 277 bool success = false; 278 if (m_node->type == XML_ELEMENT_NODE) { 279 // check child is a text node 280 for (xmlNodePtr node = m_node->children; node != nullptr; 281 node = node->next) { 282 if (node->type == XML_TEXT_NODE) { 283 text.append((const char *)node->content); 284 success = true; 285 } 286 } 287 } 288 return success; 289 } 290 #endif 291 return false; 292 } 293 294 bool XMLNode::GetElementTextAsUnsigned(uint64_t &value, uint64_t fail_value, 295 int base) const { 296 std::string text; 297 298 value = fail_value; 299 return GetElementText(text) && llvm::to_integer(text, value, base); 300 } 301 302 bool XMLNode::GetElementTextAsFloat(double &value, double fail_value) const { 303 std::string text; 304 305 value = fail_value; 306 return GetElementText(text) && llvm::to_float(text, value); 307 } 308 309 bool XMLNode::NameIs(const char *name) const { 310 #if LLDB_ENABLE_LIBXML2 311 312 if (IsValid()) { 313 // In case we are looking for a nullptr name or an exact pointer match 314 if (m_node->name == (const xmlChar *)name) 315 return true; 316 if (m_node->name) 317 return strcmp((const char *)m_node->name, name) == 0; 318 } 319 #endif 320 return false; 321 } 322 323 XMLNode XMLNode::FindFirstChildElementWithName(const char *name) const { 324 XMLNode result_node; 325 326 #if LLDB_ENABLE_LIBXML2 327 ForEachChildElementWithName( 328 name, [&result_node](const XMLNode &node) -> bool { 329 result_node = node; 330 // Stop iterating, we found the node we wanted 331 return false; 332 }); 333 #endif 334 335 return result_node; 336 } 337 338 bool XMLNode::IsValid() const { return m_node != nullptr; } 339 340 bool XMLNode::IsElement() const { 341 #if LLDB_ENABLE_LIBXML2 342 if (IsValid()) 343 return m_node->type == XML_ELEMENT_NODE; 344 #endif 345 return false; 346 } 347 348 XMLNode XMLNode::GetElementForPath(const NamePath &path) { 349 #if LLDB_ENABLE_LIBXML2 350 351 if (IsValid()) { 352 if (path.empty()) 353 return *this; 354 else { 355 XMLNode node = FindFirstChildElementWithName(path[0].c_str()); 356 const size_t n = path.size(); 357 for (size_t i = 1; node && i < n; ++i) 358 node = node.FindFirstChildElementWithName(path[i].c_str()); 359 return node; 360 } 361 } 362 #endif 363 364 return XMLNode(); 365 } 366 367 #pragma mark-- ApplePropertyList 368 369 ApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {} 370 371 ApplePropertyList::ApplePropertyList(const char *path) 372 : m_xml_doc(), m_dict_node() { 373 ParseFile(path); 374 } 375 376 ApplePropertyList::~ApplePropertyList() = default; 377 378 llvm::StringRef ApplePropertyList::GetErrors() const { 379 return m_xml_doc.GetErrors(); 380 } 381 382 bool ApplePropertyList::ParseFile(const char *path) { 383 if (m_xml_doc.ParseFile(path)) { 384 XMLNode plist = m_xml_doc.GetRootElement("plist"); 385 if (plist) { 386 plist.ForEachChildElementWithName("dict", 387 [this](const XMLNode &dict) -> bool { 388 this->m_dict_node = dict; 389 return false; // Stop iterating 390 }); 391 return (bool)m_dict_node; 392 } 393 } 394 return false; 395 } 396 397 bool ApplePropertyList::IsValid() const { return (bool)m_dict_node; } 398 399 bool ApplePropertyList::GetValueAsString(const char *key, 400 std::string &value) const { 401 XMLNode value_node = GetValueNode(key); 402 if (value_node) 403 return ApplePropertyList::ExtractStringFromValueNode(value_node, value); 404 return false; 405 } 406 407 XMLNode ApplePropertyList::GetValueNode(const char *key) const { 408 XMLNode value_node; 409 #if LLDB_ENABLE_LIBXML2 410 411 if (IsValid()) { 412 m_dict_node.ForEachChildElementWithName( 413 "key", [key, &value_node](const XMLNode &key_node) -> bool { 414 std::string key_name; 415 if (key_node.GetElementText(key_name)) { 416 if (key_name == key) { 417 value_node = key_node.GetSibling(); 418 while (value_node && !value_node.IsElement()) 419 value_node = value_node.GetSibling(); 420 return false; // Stop iterating 421 } 422 } 423 return true; // Keep iterating 424 }); 425 } 426 #endif 427 return value_node; 428 } 429 430 bool ApplePropertyList::ExtractStringFromValueNode(const XMLNode &node, 431 std::string &value) { 432 value.clear(); 433 #if LLDB_ENABLE_LIBXML2 434 if (node.IsValid()) { 435 llvm::StringRef element_name = node.GetName(); 436 if (element_name == "true" || element_name == "false") { 437 // The text value _is_ the element name itself... 438 value = element_name.str(); 439 return true; 440 } else if (element_name == "dict" || element_name == "array") 441 return false; // dictionaries and arrays have no text value, so we fail 442 else 443 return node.GetElementText(value); 444 } 445 #endif 446 return false; 447 } 448 449 #if LLDB_ENABLE_LIBXML2 450 451 static StructuredData::ObjectSP CreatePlistValue(XMLNode node) { 452 llvm::StringRef element_name = node.GetName(); 453 if (element_name == "array") { 454 std::shared_ptr<StructuredData::Array> array_sp( 455 new StructuredData::Array()); 456 node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool { 457 array_sp->AddItem(CreatePlistValue(node)); 458 return true; // Keep iterating through all child elements of the array 459 }); 460 return array_sp; 461 } else if (element_name == "dict") { 462 XMLNode key_node; 463 std::shared_ptr<StructuredData::Dictionary> dict_sp( 464 new StructuredData::Dictionary()); 465 node.ForEachChildElement( 466 [&key_node, &dict_sp](const XMLNode &node) -> bool { 467 if (node.NameIs("key")) { 468 // This is a "key" element node 469 key_node = node; 470 } else { 471 // This is a value node 472 if (key_node) { 473 std::string key_name; 474 key_node.GetElementText(key_name); 475 dict_sp->AddItem(key_name, CreatePlistValue(node)); 476 key_node.Clear(); 477 } 478 } 479 return true; // Keep iterating through all child elements of the 480 // dictionary 481 }); 482 return dict_sp; 483 } else if (element_name == "real") { 484 double value = 0.0; 485 node.GetElementTextAsFloat(value); 486 return StructuredData::ObjectSP(new StructuredData::Float(value)); 487 } else if (element_name == "integer") { 488 uint64_t value = 0; 489 node.GetElementTextAsUnsigned(value, 0, 0); 490 return StructuredData::ObjectSP(new StructuredData::Integer(value)); 491 } else if ((element_name == "string") || (element_name == "data") || 492 (element_name == "date")) { 493 std::string text; 494 node.GetElementText(text); 495 return StructuredData::ObjectSP( 496 new StructuredData::String(std::move(text))); 497 } else if (element_name == "true") { 498 return StructuredData::ObjectSP(new StructuredData::Boolean(true)); 499 } else if (element_name == "false") { 500 return StructuredData::ObjectSP(new StructuredData::Boolean(false)); 501 } 502 return StructuredData::ObjectSP(new StructuredData::Null()); 503 } 504 #endif 505 506 StructuredData::ObjectSP ApplePropertyList::GetStructuredData() { 507 StructuredData::ObjectSP root_sp; 508 #if LLDB_ENABLE_LIBXML2 509 if (IsValid()) { 510 return CreatePlistValue(m_dict_node); 511 } 512 #endif 513 return root_sp; 514 } 515