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 void XMLNode::ForEachChildNode(NodeCallback const &callback) const { 155 #if defined(LIBXML2_DEFINED) 156 if (IsValid()) 157 GetChild().ForEachSiblingNode(callback); 158 #endif 159 } 160 161 void 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 169 void 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 178 void 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 202 void 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 215 void 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 232 void 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 261 llvm::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 271 bool 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 292 bool 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 307 bool 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 323 bool 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 337 XMLNode 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 352 bool XMLNode::IsValid() const { return m_node != nullptr; } 353 354 bool 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 362 XMLNode 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 383 ApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {} 384 385 ApplePropertyList::ApplePropertyList(const char *path) 386 : m_xml_doc(), m_dict_node() { 387 ParseFile(path); 388 } 389 390 ApplePropertyList::~ApplePropertyList() {} 391 392 llvm::StringRef ApplePropertyList::GetErrors() const { 393 return m_xml_doc.GetErrors(); 394 } 395 396 bool 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 411 bool ApplePropertyList::IsValid() const { return (bool)m_dict_node; } 412 413 bool 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 421 XMLNode 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 444 bool 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 465 namespace { 466 467 StructuredData::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 523 StructuredData::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