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