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