1 //===-- XML.cpp -------------------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include <stdlib.h> /* atof */ 11 12 #include "lldb/Host/XML.h" 13 #include "lldb/Host/StringConvert.h" 14 15 using namespace lldb; 16 using namespace lldb_private; 17 18 19 #pragma mark -- XMLDocument 20 21 XMLDocument::XMLDocument () : 22 m_document (nullptr) 23 { 24 } 25 26 XMLDocument::~XMLDocument () 27 { 28 Clear(); 29 } 30 31 void 32 XMLDocument::Clear() 33 { 34 #if defined( LIBXML2_DEFINED ) 35 if (m_document) 36 { 37 xmlDocPtr doc = m_document; 38 m_document = nullptr; 39 xmlFreeDoc(doc); 40 } 41 #endif 42 } 43 44 bool 45 XMLDocument::IsValid() const 46 { 47 return m_document != nullptr; 48 } 49 50 void 51 XMLDocument::ErrorCallback (void *ctx, const char *format, ...) 52 { 53 XMLDocument *document = (XMLDocument *)ctx; 54 va_list args; 55 va_start (args, format); 56 document->m_errors.PrintfVarArg(format, args); 57 document->m_errors.EOL(); 58 va_end (args); 59 } 60 61 bool 62 XMLDocument::ParseFile (const char *path) 63 { 64 #if defined( LIBXML2_DEFINED ) 65 Clear(); 66 xmlSetGenericErrorFunc( (void *)this, XMLDocument::ErrorCallback ); 67 m_document = xmlParseFile(path); 68 xmlSetGenericErrorFunc(nullptr, nullptr); 69 #endif 70 return IsValid(); 71 } 72 73 bool 74 XMLDocument::ParseMemory (const char *xml, size_t xml_length, const char *url) 75 { 76 #if defined( LIBXML2_DEFINED ) 77 Clear(); 78 xmlSetGenericErrorFunc( (void *)this, XMLDocument::ErrorCallback ); 79 m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0); 80 xmlSetGenericErrorFunc(nullptr, nullptr); 81 #endif 82 return IsValid(); 83 84 } 85 86 XMLNode 87 XMLDocument::GetRootElement(const char *required_name) 88 { 89 #if defined( LIBXML2_DEFINED ) 90 if (IsValid()) 91 { 92 XMLNode root_node(xmlDocGetRootElement(m_document)); 93 if (required_name) 94 { 95 llvm::StringRef actual_name = root_node.GetName(); 96 if (actual_name == required_name) 97 return root_node; 98 } 99 else 100 { 101 return root_node; 102 } 103 } 104 #endif 105 return XMLNode(); 106 } 107 108 const std::string & 109 XMLDocument::GetErrors() const 110 { 111 return m_errors.GetString(); 112 } 113 114 bool 115 XMLDocument::XMLEnabled () 116 { 117 #if defined( LIBXML2_DEFINED ) 118 return true; 119 #else 120 return false; 121 #endif 122 } 123 124 #pragma mark -- XMLNode 125 126 XMLNode::XMLNode() : 127 m_node(nullptr) 128 { 129 } 130 131 XMLNode::XMLNode(XMLNodeImpl node) : 132 m_node(node) 133 { 134 } 135 136 XMLNode::~XMLNode() 137 { 138 139 } 140 141 void 142 XMLNode::Clear() 143 { 144 m_node = nullptr; 145 } 146 147 XMLNode 148 XMLNode::GetParent() const 149 { 150 #if defined( LIBXML2_DEFINED ) 151 if (IsValid()) 152 return XMLNode(m_node->parent); 153 else 154 return XMLNode(); 155 #else 156 return XMLNode(); 157 #endif 158 159 } 160 161 XMLNode 162 XMLNode::GetSibling() const 163 { 164 #if defined( LIBXML2_DEFINED ) 165 if (IsValid()) 166 return XMLNode(m_node->next); 167 else 168 return XMLNode(); 169 #else 170 return XMLNode(); 171 #endif 172 173 } 174 175 XMLNode 176 XMLNode::GetChild () const 177 { 178 #if defined( LIBXML2_DEFINED ) 179 180 if (IsValid()) 181 return XMLNode(m_node->children); 182 else 183 return XMLNode(); 184 #else 185 return XMLNode(); 186 #endif 187 188 } 189 190 llvm::StringRef 191 XMLNode::GetAttributeValue(const char *name, const char *fail_value) const 192 { 193 const char *attr_value = NULL; 194 #if defined( LIBXML2_DEFINED ) 195 196 if (IsValid()) 197 attr_value = (const char *)xmlGetProp(m_node, (const xmlChar *)name); 198 else 199 attr_value = fail_value; 200 #else 201 attr_value = fail_value; 202 #endif 203 if (attr_value) 204 return llvm::StringRef(attr_value); 205 else 206 return llvm::StringRef(); 207 } 208 209 210 211 212 void 213 XMLNode::ForEachChildNode (NodeCallback const &callback) const 214 { 215 #if defined( LIBXML2_DEFINED ) 216 if (IsValid()) 217 GetChild().ForEachSiblingNode(callback); 218 #endif 219 } 220 221 void 222 XMLNode::ForEachChildElement (NodeCallback const &callback) const 223 { 224 #if defined( LIBXML2_DEFINED ) 225 XMLNode child = GetChild(); 226 if (child) 227 child.ForEachSiblingElement(callback); 228 #endif 229 } 230 231 void 232 XMLNode::ForEachChildElementWithName (const char *name, NodeCallback const &callback) const 233 { 234 #if defined( LIBXML2_DEFINED ) 235 XMLNode child = GetChild(); 236 if (child) 237 child.ForEachSiblingElementWithName(name, callback); 238 #endif 239 } 240 241 void 242 XMLNode::ForEachAttribute (AttributeCallback const &callback) const 243 { 244 #if defined( LIBXML2_DEFINED ) 245 246 if (IsValid()) 247 { 248 for (xmlAttrPtr attr = m_node->properties; attr != nullptr; attr=attr->next) 249 { 250 // check if name matches 251 if (attr->name) 252 { 253 // check child is a text node 254 xmlNodePtr child = attr->children; 255 if (child->type == XML_TEXT_NODE) 256 { 257 llvm::StringRef attr_value; 258 if (child->content) 259 attr_value = llvm::StringRef((const char *)child->content); 260 if (callback(llvm::StringRef((const char *)attr->name), attr_value) == false) 261 return; 262 } 263 } 264 } 265 } 266 #endif 267 } 268 269 270 void 271 XMLNode::ForEachSiblingNode (NodeCallback const &callback) const 272 { 273 #if defined( LIBXML2_DEFINED ) 274 275 if (IsValid()) 276 { 277 // iterate through all siblings 278 for (xmlNodePtr node = m_node; node; node=node->next) 279 { 280 if (callback(XMLNode(node)) == false) 281 return; 282 } 283 } 284 #endif 285 } 286 287 void 288 XMLNode::ForEachSiblingElement (NodeCallback const &callback) const 289 { 290 #if defined( LIBXML2_DEFINED ) 291 292 if (IsValid()) 293 { 294 // iterate through all siblings 295 for (xmlNodePtr node = m_node; node; node=node->next) 296 { 297 // we are looking for element nodes only 298 if (node->type != XML_ELEMENT_NODE) 299 continue; 300 301 if (callback(XMLNode(node)) == false) 302 return; 303 } 304 } 305 #endif 306 } 307 308 void 309 XMLNode::ForEachSiblingElementWithName (const char *name, NodeCallback const &callback) const 310 { 311 #if defined( LIBXML2_DEFINED ) 312 313 if (IsValid()) 314 { 315 // iterate through all siblings 316 for (xmlNodePtr node = m_node; node; node=node->next) 317 { 318 // we are looking for element nodes only 319 if (node->type != XML_ELEMENT_NODE) 320 continue; 321 322 // If name is nullptr, we take all nodes of type "t", else 323 // just the ones whose name matches 324 if (name) 325 { 326 if (strcmp((const char *)node->name, name) != 0) 327 continue; // Name mismatch, ignore this one 328 } 329 else 330 { 331 if (node->name) 332 continue; // nullptr name specified and this element has a name, ignore this one 333 } 334 335 if (callback(XMLNode(node)) == false) 336 return; 337 } 338 } 339 #endif 340 } 341 342 llvm::StringRef 343 XMLNode::GetName() const 344 { 345 #if defined( LIBXML2_DEFINED ) 346 if (IsValid()) 347 { 348 if (m_node->name) 349 return llvm::StringRef((const char *)m_node->name); 350 } 351 #endif 352 return llvm::StringRef(); 353 } 354 355 bool 356 XMLNode::GetElementText (std::string &text) const 357 { 358 text.clear(); 359 #if defined( LIBXML2_DEFINED ) 360 if (IsValid()) 361 { 362 bool success = false; 363 if (m_node->type == XML_ELEMENT_NODE) 364 { 365 // check child is a text node 366 for (xmlNodePtr node = m_node->children; 367 node != nullptr; 368 node = node->next) 369 { 370 if (node->type == XML_TEXT_NODE) 371 { 372 text.append((const char *)node->content); 373 success = true; 374 } 375 } 376 } 377 return success; 378 } 379 #endif 380 return false; 381 } 382 383 384 bool 385 XMLNode::GetElementTextAsUnsigned (uint64_t &value, uint64_t fail_value, int base) const 386 { 387 bool success = false; 388 #if defined( LIBXML2_DEFINED ) 389 if (IsValid()) 390 { 391 std::string text; 392 if (GetElementText(text)) 393 value = StringConvert::ToUInt64(text.c_str(), fail_value, base, &success); 394 } 395 #endif 396 if (!success) 397 value = fail_value; 398 return success; 399 } 400 401 bool 402 XMLNode::GetElementTextAsFloat (double &value, double fail_value) const 403 { 404 bool success = false; 405 #if defined( LIBXML2_DEFINED ) 406 if (IsValid()) 407 { 408 std::string text; 409 if (GetElementText(text)) 410 { 411 value = atof(text.c_str()); 412 success = true; 413 } 414 } 415 #endif 416 if (!success) 417 value = fail_value; 418 return success; 419 } 420 421 422 423 bool 424 XMLNode::NameIs (const char *name) const 425 { 426 #if defined( LIBXML2_DEFINED ) 427 428 if (IsValid()) 429 { 430 // In case we are looking for a nullptr name or an exact pointer match 431 if (m_node->name == (const xmlChar *)name) 432 return true; 433 if (m_node->name) 434 return strcmp((const char *)m_node->name, name) == 0; 435 } 436 #endif 437 return false; 438 } 439 440 XMLNode 441 XMLNode::FindFirstChildElementWithName (const char *name) const 442 { 443 XMLNode result_node; 444 445 #if defined( LIBXML2_DEFINED ) 446 ForEachChildElementWithName(name, [&result_node, name](const XMLNode& node) -> bool { 447 result_node = node; 448 // Stop iterating, we found the node we wanted 449 return false; 450 }); 451 #endif 452 453 return result_node; 454 } 455 456 bool 457 XMLNode::IsValid() const 458 { 459 return m_node != nullptr; 460 } 461 462 bool 463 XMLNode::IsElement () const 464 { 465 #if defined( LIBXML2_DEFINED ) 466 if (IsValid()) 467 return m_node->type == XML_ELEMENT_NODE; 468 #endif 469 return false; 470 } 471 472 473 XMLNode 474 XMLNode::GetElementForPath (const NamePath &path) 475 { 476 #if defined( LIBXML2_DEFINED ) 477 478 if (IsValid()) 479 { 480 if (path.empty()) 481 return *this; 482 else 483 { 484 XMLNode node = FindFirstChildElementWithName(path[0].c_str()); 485 const size_t n = path.size(); 486 for (size_t i=1; node && i<n; ++i) 487 node = node.FindFirstChildElementWithName(path[i].c_str()); 488 return node; 489 } 490 } 491 #endif 492 493 return XMLNode(); 494 } 495 496 497 #pragma mark -- ApplePropertyList 498 499 ApplePropertyList::ApplePropertyList() : 500 m_xml_doc(), 501 m_dict_node() 502 { 503 504 } 505 506 ApplePropertyList::ApplePropertyList (const char *path) : 507 m_xml_doc(), 508 m_dict_node() 509 { 510 ParseFile(path); 511 } 512 513 ApplePropertyList::~ApplePropertyList() 514 { 515 } 516 517 const std::string & 518 ApplePropertyList::GetErrors() const 519 { 520 return m_xml_doc.GetErrors(); 521 } 522 523 524 bool 525 ApplePropertyList::ParseFile (const char *path) 526 { 527 if (m_xml_doc.ParseFile(path)) 528 { 529 XMLNode plist = m_xml_doc.GetRootElement("plist"); 530 if (plist) 531 { 532 plist.ForEachChildElementWithName("dict", [this](const XMLNode &dict) -> bool { 533 this->m_dict_node = dict; 534 return false; // Stop iterating 535 }); 536 return (bool)m_dict_node; 537 } 538 } 539 return false; 540 } 541 542 bool 543 ApplePropertyList::IsValid() const 544 { 545 return (bool)m_dict_node; 546 } 547 548 bool 549 ApplePropertyList::GetValueAsString (const char *key, std::string &value) const 550 { 551 XMLNode value_node = GetValueNode (key); 552 if (value_node) 553 return ApplePropertyList::ExtractStringFromValueNode(value_node, value); 554 return false; 555 } 556 557 XMLNode 558 ApplePropertyList::GetValueNode (const char *key) const 559 { 560 XMLNode value_node; 561 #if defined( LIBXML2_DEFINED ) 562 563 if (IsValid()) 564 { 565 m_dict_node.ForEachChildElementWithName("key", [key, &value_node](const XMLNode &key_node) -> bool { 566 std::string key_name; 567 if (key_node.GetElementText(key_name)) 568 { 569 if (key_name.compare(key) == 0) 570 { 571 value_node = key_node.GetSibling(); 572 while (value_node && !value_node.IsElement()) 573 value_node = value_node.GetSibling(); 574 return false; // Stop iterating 575 } 576 } 577 return true; // Keep iterating 578 }); 579 } 580 #endif 581 return value_node; 582 } 583 584 bool 585 ApplePropertyList::ExtractStringFromValueNode (const XMLNode &node, std::string &value) 586 { 587 value.clear(); 588 #if defined( LIBXML2_DEFINED ) 589 if (node.IsValid()) 590 { 591 llvm::StringRef element_name = node.GetName(); 592 if (element_name == "true" || element_name == "false") 593 { 594 // The text value _is_ the element name itself... 595 value = element_name.str(); 596 return true; 597 } 598 else if (element_name == "dict" || element_name == "array") 599 return false; // dictionaries and arrays have no text value, so we fail 600 else 601 return node.GetElementText(value); 602 } 603 #endif 604 return false; 605 } 606 607 #if defined( LIBXML2_DEFINED ) 608 609 namespace { 610 611 StructuredData::ObjectSP 612 CreatePlistValue (XMLNode node) 613 { 614 llvm::StringRef element_name = node.GetName(); 615 if (element_name == "array") 616 { 617 std::shared_ptr<StructuredData::Array> array_sp(new StructuredData::Array()); 618 node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool { 619 array_sp->AddItem(CreatePlistValue(node)); 620 return true; // Keep iterating through all child elements of the array 621 }); 622 return array_sp; 623 } 624 else if (element_name == "dict") 625 { 626 XMLNode key_node; 627 std::shared_ptr<StructuredData::Dictionary> dict_sp(new StructuredData::Dictionary()); 628 node.ForEachChildElement([&key_node, &dict_sp](const XMLNode &node) -> bool { 629 if (node.NameIs("key")) 630 { 631 // This is a "key" element node 632 key_node = node; 633 } 634 else 635 { 636 // This is a value node 637 if (key_node) 638 { 639 std::string key_name; 640 key_node.GetElementText(key_name); 641 dict_sp->AddItem(key_name, CreatePlistValue(node)); 642 key_node.Clear(); 643 } 644 } 645 return true; // Keep iterating through all child elements of the dictionary 646 }); 647 return dict_sp; 648 } 649 else if (element_name == "real") 650 { 651 double value = 0.0; 652 node.GetElementTextAsFloat(value); 653 return StructuredData::ObjectSP(new StructuredData::Float(value)); 654 } 655 else if (element_name == "integer") 656 { 657 uint64_t value = 0; 658 node.GetElementTextAsUnsigned(value, 0, 0); 659 return StructuredData::ObjectSP(new StructuredData::Integer(value)); 660 } 661 else if ((element_name == "string") || (element_name == "data") || (element_name == "date")) 662 { 663 std::string text; 664 node.GetElementText(text); 665 return StructuredData::ObjectSP(new StructuredData::String(std::move(text))); 666 } 667 else if (element_name == "true") 668 { 669 return StructuredData::ObjectSP(new StructuredData::Boolean(true)); 670 } 671 else if (element_name == "false") 672 { 673 return StructuredData::ObjectSP(new StructuredData::Boolean(false)); 674 } 675 return StructuredData::ObjectSP(new StructuredData::Null()); 676 } 677 } 678 #endif 679 680 StructuredData::ObjectSP 681 ApplePropertyList::GetStructuredData() 682 { 683 StructuredData::ObjectSP root_sp; 684 #if defined( LIBXML2_DEFINED ) 685 if (IsValid()) 686 { 687 return CreatePlistValue(m_dict_node); 688 } 689 #endif 690 return root_sp; 691 } 692