1 //===-- XML.cpp -----------------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Host/Config.h"
10 #include "lldb/Host/XML.h"
12 #include "llvm/ADT/StringExtras.h"
15 using namespace lldb_private
;
17 #pragma mark-- XMLDocument
19 XMLDocument::XMLDocument() = default;
21 XMLDocument::~XMLDocument() { Clear(); }
23 void XMLDocument::Clear() {
24 #if LLDB_ENABLE_LIBXML2
26 xmlDocPtr doc
= m_document
;
33 bool XMLDocument::IsValid() const { return m_document
!= nullptr; }
35 void XMLDocument::ErrorCallback(void *ctx
, const char *format
, ...) {
36 XMLDocument
*document
= (XMLDocument
*)ctx
;
38 va_start(args
, format
);
39 document
->m_errors
.PrintfVarArg(format
, args
);
40 document
->m_errors
.EOL();
44 bool XMLDocument::ParseFile(const char *path
) {
45 #if LLDB_ENABLE_LIBXML2
47 xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback
);
48 m_document
= xmlParseFile(path
);
49 xmlSetGenericErrorFunc(nullptr, nullptr);
54 bool XMLDocument::ParseMemory(const char *xml
, size_t xml_length
,
56 #if LLDB_ENABLE_LIBXML2
58 xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback
);
59 m_document
= xmlReadMemory(xml
, (int)xml_length
, url
, nullptr, 0);
60 xmlSetGenericErrorFunc(nullptr, nullptr);
65 XMLNode
XMLDocument::GetRootElement(const char *required_name
) {
66 #if LLDB_ENABLE_LIBXML2
68 XMLNode
root_node(xmlDocGetRootElement(m_document
));
70 llvm::StringRef actual_name
= root_node
.GetName();
71 if (actual_name
== required_name
)
81 llvm::StringRef
XMLDocument::GetErrors() const { return m_errors
.GetString(); }
83 bool XMLDocument::XMLEnabled() {
84 #if LLDB_ENABLE_LIBXML2
91 #pragma mark-- XMLNode
93 XMLNode::XMLNode() = default;
95 XMLNode::XMLNode(XMLNodeImpl node
) : m_node(node
) {}
97 XMLNode::~XMLNode() = default;
99 void XMLNode::Clear() { m_node
= nullptr; }
101 XMLNode
XMLNode::GetParent() const {
102 #if LLDB_ENABLE_LIBXML2
104 return XMLNode(m_node
->parent
);
112 XMLNode
XMLNode::GetSibling() const {
113 #if LLDB_ENABLE_LIBXML2
115 return XMLNode(m_node
->next
);
123 XMLNode
XMLNode::GetChild() const {
124 #if LLDB_ENABLE_LIBXML2
127 return XMLNode(m_node
->children
);
135 std::string
XMLNode::GetAttributeValue(const char *name
,
136 const char *fail_value
) const {
137 std::string attr_value
;
138 #if LLDB_ENABLE_LIBXML2
140 xmlChar
*value
= xmlGetProp(m_node
, (const xmlChar
*)name
);
142 attr_value
= (const char *)value
;
147 attr_value
= fail_value
;
151 attr_value
= fail_value
;
156 bool XMLNode::GetAttributeValueAsUnsigned(const char *name
, uint64_t &value
,
157 uint64_t fail_value
, int base
) const {
159 return llvm::to_integer(GetAttributeValue(name
, ""), value
, base
);
162 void XMLNode::ForEachChildNode(NodeCallback
const &callback
) const {
163 #if LLDB_ENABLE_LIBXML2
165 GetChild().ForEachSiblingNode(callback
);
169 void XMLNode::ForEachChildElement(NodeCallback
const &callback
) const {
170 #if LLDB_ENABLE_LIBXML2
171 XMLNode child
= GetChild();
173 child
.ForEachSiblingElement(callback
);
177 void XMLNode::ForEachChildElementWithName(const char *name
,
178 NodeCallback
const &callback
) const {
179 #if LLDB_ENABLE_LIBXML2
180 XMLNode child
= GetChild();
182 child
.ForEachSiblingElementWithName(name
, callback
);
186 void XMLNode::ForEachAttribute(AttributeCallback
const &callback
) const {
187 #if LLDB_ENABLE_LIBXML2
190 for (xmlAttrPtr attr
= m_node
->properties
; attr
!= nullptr;
192 // check if name matches
194 // check child is a text node
195 xmlNodePtr child
= attr
->children
;
196 if (child
->type
== XML_TEXT_NODE
) {
197 llvm::StringRef attr_value
;
199 attr_value
= llvm::StringRef((const char *)child
->content
);
200 if (!callback(llvm::StringRef((const char *)attr
->name
), attr_value
))
209 void XMLNode::ForEachSiblingNode(NodeCallback
const &callback
) const {
210 #if LLDB_ENABLE_LIBXML2
213 // iterate through all siblings
214 for (xmlNodePtr node
= m_node
; node
; node
= node
->next
) {
215 if (!callback(XMLNode(node
)))
222 void XMLNode::ForEachSiblingElement(NodeCallback
const &callback
) const {
223 #if LLDB_ENABLE_LIBXML2
226 // iterate through all siblings
227 for (xmlNodePtr node
= m_node
; node
; node
= node
->next
) {
228 // we are looking for element nodes only
229 if (node
->type
!= XML_ELEMENT_NODE
)
232 if (!callback(XMLNode(node
)))
239 void XMLNode::ForEachSiblingElementWithName(
240 const char *name
, NodeCallback
const &callback
) const {
241 #if LLDB_ENABLE_LIBXML2
244 // iterate through all siblings
245 for (xmlNodePtr node
= m_node
; node
; node
= node
->next
) {
246 // we are looking for element nodes only
247 if (node
->type
!= XML_ELEMENT_NODE
)
250 // If name is nullptr, we take all nodes of type "t", else just the ones
251 // whose name matches
253 if (strcmp((const char *)node
->name
, name
) != 0)
254 continue; // Name mismatch, ignore this one
257 continue; // nullptr name specified and this element has a name,
261 if (!callback(XMLNode(node
)))
268 llvm::StringRef
XMLNode::GetName() const {
269 #if LLDB_ENABLE_LIBXML2
272 return llvm::StringRef((const char *)m_node
->name
);
275 return llvm::StringRef();
278 bool XMLNode::GetElementText(std::string
&text
) const {
280 #if LLDB_ENABLE_LIBXML2
282 bool success
= false;
283 if (m_node
->type
== XML_ELEMENT_NODE
) {
284 // check child is a text node
285 for (xmlNodePtr node
= m_node
->children
; node
!= nullptr;
287 if (node
->type
== XML_TEXT_NODE
) {
288 text
.append((const char *)node
->content
);
299 bool XMLNode::GetElementTextAsUnsigned(uint64_t &value
, uint64_t fail_value
,
304 return GetElementText(text
) && llvm::to_integer(text
, value
, base
);
307 bool XMLNode::GetElementTextAsFloat(double &value
, double fail_value
) const {
311 return GetElementText(text
) && llvm::to_float(text
, value
);
314 bool XMLNode::NameIs(const char *name
) const {
315 #if LLDB_ENABLE_LIBXML2
318 // In case we are looking for a nullptr name or an exact pointer match
319 if (m_node
->name
== (const xmlChar
*)name
)
322 return strcmp((const char *)m_node
->name
, name
) == 0;
328 XMLNode
XMLNode::FindFirstChildElementWithName(const char *name
) const {
331 #if LLDB_ENABLE_LIBXML2
332 ForEachChildElementWithName(
333 name
, [&result_node
](const XMLNode
&node
) -> bool {
335 // Stop iterating, we found the node we wanted
343 bool XMLNode::IsValid() const { return m_node
!= nullptr; }
345 bool XMLNode::IsElement() const {
346 #if LLDB_ENABLE_LIBXML2
348 return m_node
->type
== XML_ELEMENT_NODE
;
353 XMLNode
XMLNode::GetElementForPath(const NamePath
&path
) {
354 #if LLDB_ENABLE_LIBXML2
360 XMLNode node
= FindFirstChildElementWithName(path
[0].c_str());
361 const size_t n
= path
.size();
362 for (size_t i
= 1; node
&& i
< n
; ++i
)
363 node
= node
.FindFirstChildElementWithName(path
[i
].c_str());
372 #pragma mark-- ApplePropertyList
374 ApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {}
376 ApplePropertyList::ApplePropertyList(const char *path
)
377 : m_xml_doc(), m_dict_node() {
381 ApplePropertyList::~ApplePropertyList() = default;
383 llvm::StringRef
ApplePropertyList::GetErrors() const {
384 return m_xml_doc
.GetErrors();
387 bool ApplePropertyList::ParseFile(const char *path
) {
388 if (m_xml_doc
.ParseFile(path
)) {
389 XMLNode plist
= m_xml_doc
.GetRootElement("plist");
391 plist
.ForEachChildElementWithName("dict",
392 [this](const XMLNode
&dict
) -> bool {
393 this->m_dict_node
= dict
;
394 return false; // Stop iterating
396 return (bool)m_dict_node
;
402 bool ApplePropertyList::IsValid() const { return (bool)m_dict_node
; }
404 bool ApplePropertyList::GetValueAsString(const char *key
,
405 std::string
&value
) const {
406 XMLNode value_node
= GetValueNode(key
);
408 return ApplePropertyList::ExtractStringFromValueNode(value_node
, value
);
412 XMLNode
ApplePropertyList::GetValueNode(const char *key
) const {
414 #if LLDB_ENABLE_LIBXML2
417 m_dict_node
.ForEachChildElementWithName(
418 "key", [key
, &value_node
](const XMLNode
&key_node
) -> bool {
419 std::string key_name
;
420 if (key_node
.GetElementText(key_name
)) {
421 if (key_name
== key
) {
422 value_node
= key_node
.GetSibling();
423 while (value_node
&& !value_node
.IsElement())
424 value_node
= value_node
.GetSibling();
425 return false; // Stop iterating
428 return true; // Keep iterating
435 bool ApplePropertyList::ExtractStringFromValueNode(const XMLNode
&node
,
436 std::string
&value
) {
438 #if LLDB_ENABLE_LIBXML2
439 if (node
.IsValid()) {
440 llvm::StringRef element_name
= node
.GetName();
441 if (element_name
== "true" || element_name
== "false") {
442 // The text value _is_ the element name itself...
443 value
= element_name
.str();
445 } else if (element_name
== "dict" || element_name
== "array")
446 return false; // dictionaries and arrays have no text value, so we fail
448 return node
.GetElementText(value
);
454 #if LLDB_ENABLE_LIBXML2
456 static StructuredData::ObjectSP
CreatePlistValue(XMLNode node
) {
457 llvm::StringRef element_name
= node
.GetName();
458 if (element_name
== "array") {
459 std::shared_ptr
<StructuredData::Array
> array_sp(
460 new StructuredData::Array());
461 node
.ForEachChildElement([&array_sp
](const XMLNode
&node
) -> bool {
462 array_sp
->AddItem(CreatePlistValue(node
));
463 return true; // Keep iterating through all child elements of the array
466 } else if (element_name
== "dict") {
468 std::shared_ptr
<StructuredData::Dictionary
> dict_sp(
469 new StructuredData::Dictionary());
470 node
.ForEachChildElement(
471 [&key_node
, &dict_sp
](const XMLNode
&node
) -> bool {
472 if (node
.NameIs("key")) {
473 // This is a "key" element node
476 // This is a value node
478 std::string key_name
;
479 key_node
.GetElementText(key_name
);
480 dict_sp
->AddItem(key_name
, CreatePlistValue(node
));
484 return true; // Keep iterating through all child elements of the
488 } else if (element_name
== "real") {
490 node
.GetElementTextAsFloat(value
);
491 return StructuredData::ObjectSP(new StructuredData::Float(value
));
492 } else if (element_name
== "integer") {
494 node
.GetElementTextAsUnsigned(value
, 0, 0);
495 return StructuredData::ObjectSP(new StructuredData::UnsignedInteger(value
));
496 } else if ((element_name
== "string") || (element_name
== "data") ||
497 (element_name
== "date")) {
499 node
.GetElementText(text
);
500 return StructuredData::ObjectSP(
501 new StructuredData::String(std::move(text
)));
502 } else if (element_name
== "true") {
503 return StructuredData::ObjectSP(new StructuredData::Boolean(true));
504 } else if (element_name
== "false") {
505 return StructuredData::ObjectSP(new StructuredData::Boolean(false));
507 return StructuredData::ObjectSP(new StructuredData::Null());
511 StructuredData::ObjectSP
ApplePropertyList::GetStructuredData() {
512 StructuredData::ObjectSP root_sp
;
513 #if LLDB_ENABLE_LIBXML2
515 return CreatePlistValue(m_dict_node
);