XML.cpp revision 285101
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 15using namespace lldb; 16using namespace lldb_private; 17 18 19#pragma mark -- XMLDocument 20 21XMLDocument::XMLDocument () : 22 m_document (nullptr) 23{ 24} 25 26XMLDocument::~XMLDocument () 27{ 28 Clear(); 29} 30 31void 32XMLDocument::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 44bool 45XMLDocument::IsValid() const 46{ 47 return m_document != nullptr; 48} 49 50void 51XMLDocument::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 61bool 62XMLDocument::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 73bool 74XMLDocument::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 86XMLNode 87XMLDocument::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 108const std::string & 109XMLDocument::GetErrors() const 110{ 111 return m_errors.GetString(); 112} 113 114bool 115XMLDocument::XMLEnabled () 116{ 117#if defined( LIBXML2_DEFINED ) 118 return true; 119#else 120 return false; 121#endif 122} 123 124#pragma mark -- XMLNode 125 126XMLNode::XMLNode() : 127 m_node(nullptr) 128{ 129} 130 131XMLNode::XMLNode(XMLNodeImpl node) : 132 m_node(node) 133{ 134} 135 136XMLNode::~XMLNode() 137{ 138 139} 140 141void 142XMLNode::Clear() 143{ 144 m_node = nullptr; 145} 146 147XMLNode 148XMLNode::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 161XMLNode 162XMLNode::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 175XMLNode 176XMLNode::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 190llvm::StringRef 191XMLNode::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 212void 213XMLNode::ForEachChildNode (NodeCallback const &callback) const 214{ 215#if defined( LIBXML2_DEFINED ) 216 if (IsValid()) 217 GetChild().ForEachSiblingNode(callback); 218#endif 219} 220 221void 222XMLNode::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 231void 232XMLNode::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 241void 242XMLNode::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 270void 271XMLNode::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 287void 288XMLNode::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 308void 309XMLNode::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 elemnt has a name, ignore this one 333 } 334 335 if (callback(XMLNode(node)) == false) 336 return; 337 } 338 } 339#endif 340} 341 342llvm::StringRef 343XMLNode::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 355bool 356XMLNode::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 384bool 385XMLNode::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 401bool 402XMLNode::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 423bool 424XMLNode::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 440XMLNode 441XMLNode::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 456bool 457XMLNode::IsValid() const 458{ 459 return m_node != nullptr; 460} 461 462bool 463XMLNode::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 473XMLNode 474XMLNode::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 499ApplePropertyList::ApplePropertyList() : 500 m_xml_doc(), 501 m_dict_node() 502{ 503 504} 505 506ApplePropertyList::ApplePropertyList (const char *path) : 507 m_xml_doc(), 508 m_dict_node() 509{ 510 ParseFile(path); 511} 512 513ApplePropertyList::~ApplePropertyList() 514{ 515} 516 517const std::string & 518ApplePropertyList::GetErrors() const 519{ 520 return m_xml_doc.GetErrors(); 521} 522 523 524bool 525ApplePropertyList::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 542bool 543ApplePropertyList::IsValid() const 544{ 545 return (bool)m_dict_node; 546} 547 548bool 549ApplePropertyList::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 557XMLNode 558ApplePropertyList::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 584bool 585ApplePropertyList::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 = std::move(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 609namespace { 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 680StructuredData::ObjectSP 681ApplePropertyList::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 693 694