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 14 using namespace lldb; 15 using namespace lldb_private; 16 17 #pragma mark-- XMLDocument 18 19 XMLDocument::XMLDocument() = default; 20 21 XMLDocument::~XMLDocument() { Clear(); } 22 23 void 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 33 bool XMLDocument::IsValid() const { return m_document != nullptr; } 34 35 void 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 44 bool 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 54 bool 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 65 XMLNode 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 81 llvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); } 82 83 bool XMLDocument::XMLEnabled() { 84 #if LLDB_ENABLE_LIBXML2 85 return true; 86 #else 87 return false; 88 #endif 89 } 90 91 #pragma mark-- XMLNode 92 93 XMLNode::XMLNode() = default; 94 95 XMLNode::XMLNode(XMLNodeImpl node) : m_node(node) {} 96 97 XMLNode::~XMLNode() = default; 98 99 void XMLNode::Clear() { m_node = nullptr; } 100 101 XMLNode 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 112 XMLNode 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 123 XMLNode 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 135 std::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 156 bool 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 162 void XMLNode::ForEachChildNode(NodeCallback const &callback) const { 163 #if LLDB_ENABLE_LIBXML2 164 if (IsValid()) 165 GetChild().ForEachSiblingNode(callback); 166 #endif 167 } 168 169 void 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 177 void 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 186 void 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 209 void 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 222 void 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 239 void 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 268 llvm::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 278 bool 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 299 bool 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 307 bool 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 314 bool 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 328 XMLNode 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 343 bool XMLNode::IsValid() const { return m_node != nullptr; } 344 345 bool 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 353 XMLNode 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 374 ApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {} 375 376 ApplePropertyList::ApplePropertyList(const char *path) 377 : m_xml_doc(), m_dict_node() { 378 ParseFile(path); 379 } 380 381 ApplePropertyList::~ApplePropertyList() = default; 382 383 llvm::StringRef ApplePropertyList::GetErrors() const { 384 return m_xml_doc.GetErrors(); 385 } 386 387 bool 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 402 bool ApplePropertyList::IsValid() const { return (bool)m_dict_node; } 403 404 bool 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 412 XMLNode 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 435 bool 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 456 static 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 511 StructuredData::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