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