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