180814287SRaphael Isemann //===-- XML.cpp -----------------------------------------------------------===//
2d04f0edaSGreg Clayton //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d04f0edaSGreg Clayton //
7d04f0edaSGreg Clayton //===----------------------------------------------------------------------===//
8d04f0edaSGreg Clayton 
9bf68bcb9SJonas Devlieghere #include "lldb/Host/Config.h"
10b9c1b51eSKate Stone #include "lldb/Host/XML.h"
11d04f0edaSGreg Clayton 
12d04f0edaSGreg Clayton using namespace lldb;
13d04f0edaSGreg Clayton using namespace lldb_private;
14d04f0edaSGreg Clayton 
15d04f0edaSGreg Clayton #pragma mark-- XMLDocument
16d04f0edaSGreg Clayton 
17fd2433e1SJonas Devlieghere XMLDocument::XMLDocument() = default;
18d04f0edaSGreg Clayton 
~XMLDocument()19b9c1b51eSKate Stone XMLDocument::~XMLDocument() { Clear(); }
20d04f0edaSGreg Clayton 
Clear()21b9c1b51eSKate Stone void XMLDocument::Clear() {
224b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
23b9c1b51eSKate Stone   if (m_document) {
24d04f0edaSGreg Clayton     xmlDocPtr doc = m_document;
25d04f0edaSGreg Clayton     m_document = nullptr;
26d04f0edaSGreg Clayton     xmlFreeDoc(doc);
27d04f0edaSGreg Clayton   }
28d04f0edaSGreg Clayton #endif
29d04f0edaSGreg Clayton }
30d04f0edaSGreg Clayton 
IsValid() const31b9c1b51eSKate Stone bool XMLDocument::IsValid() const { return m_document != nullptr; }
32d04f0edaSGreg Clayton 
ErrorCallback(void * ctx,const char * format,...)33b9c1b51eSKate Stone void XMLDocument::ErrorCallback(void *ctx, const char *format, ...) {
34d04f0edaSGreg Clayton   XMLDocument *document = (XMLDocument *)ctx;
35d04f0edaSGreg Clayton   va_list args;
36d04f0edaSGreg Clayton   va_start(args, format);
37d04f0edaSGreg Clayton   document->m_errors.PrintfVarArg(format, args);
38d04f0edaSGreg Clayton   document->m_errors.EOL();
39d04f0edaSGreg Clayton   va_end(args);
40d04f0edaSGreg Clayton }
41d04f0edaSGreg Clayton 
ParseFile(const char * path)42b9c1b51eSKate Stone bool XMLDocument::ParseFile(const char *path) {
434b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
44d04f0edaSGreg Clayton   Clear();
45d04f0edaSGreg Clayton   xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);
46d04f0edaSGreg Clayton   m_document = xmlParseFile(path);
47d04f0edaSGreg Clayton   xmlSetGenericErrorFunc(nullptr, nullptr);
48d04f0edaSGreg Clayton #endif
49d04f0edaSGreg Clayton   return IsValid();
50d04f0edaSGreg Clayton }
51d04f0edaSGreg Clayton 
ParseMemory(const char * xml,size_t xml_length,const char * url)52b9c1b51eSKate Stone bool XMLDocument::ParseMemory(const char *xml, size_t xml_length,
53b9c1b51eSKate Stone                               const char *url) {
544b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
55d04f0edaSGreg Clayton   Clear();
56d04f0edaSGreg Clayton   xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);
57d04f0edaSGreg Clayton   m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0);
58d04f0edaSGreg Clayton   xmlSetGenericErrorFunc(nullptr, nullptr);
59d04f0edaSGreg Clayton #endif
60d04f0edaSGreg Clayton   return IsValid();
61d04f0edaSGreg Clayton }
62d04f0edaSGreg Clayton 
GetRootElement(const char * required_name)63b9c1b51eSKate Stone XMLNode XMLDocument::GetRootElement(const char *required_name) {
644b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
65b9c1b51eSKate Stone   if (IsValid()) {
66d04f0edaSGreg Clayton     XMLNode root_node(xmlDocGetRootElement(m_document));
67b9c1b51eSKate Stone     if (required_name) {
68d04f0edaSGreg Clayton       llvm::StringRef actual_name = root_node.GetName();
69d04f0edaSGreg Clayton       if (actual_name == required_name)
70d04f0edaSGreg Clayton         return root_node;
71b9c1b51eSKate Stone     } else {
72d04f0edaSGreg Clayton       return root_node;
73d04f0edaSGreg Clayton     }
74d04f0edaSGreg Clayton   }
75d04f0edaSGreg Clayton #endif
76d04f0edaSGreg Clayton   return XMLNode();
77d04f0edaSGreg Clayton }
78d04f0edaSGreg Clayton 
GetErrors() const798927f23aSZachary Turner llvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); }
802a2949cfSGreg Clayton 
XMLEnabled()81b9c1b51eSKate Stone bool XMLDocument::XMLEnabled() {
824b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
83d04f0edaSGreg Clayton   return true;
84d04f0edaSGreg Clayton #else
85d04f0edaSGreg Clayton   return false;
86d04f0edaSGreg Clayton #endif
87d04f0edaSGreg Clayton }
88d04f0edaSGreg Clayton 
89d04f0edaSGreg Clayton #pragma mark-- XMLNode
90d04f0edaSGreg Clayton 
91fd2433e1SJonas Devlieghere XMLNode::XMLNode() = default;
92d04f0edaSGreg Clayton 
XMLNode(XMLNodeImpl node)93b9c1b51eSKate Stone XMLNode::XMLNode(XMLNodeImpl node) : m_node(node) {}
94d04f0edaSGreg Clayton 
95fd2433e1SJonas Devlieghere XMLNode::~XMLNode() = default;
96d04f0edaSGreg Clayton 
Clear()97b9c1b51eSKate Stone void XMLNode::Clear() { m_node = nullptr; }
98d04f0edaSGreg Clayton 
GetParent() const99b9c1b51eSKate Stone XMLNode XMLNode::GetParent() const {
1004b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
101d04f0edaSGreg Clayton   if (IsValid())
102d04f0edaSGreg Clayton     return XMLNode(m_node->parent);
103d04f0edaSGreg Clayton   else
104d04f0edaSGreg Clayton     return XMLNode();
105d04f0edaSGreg Clayton #else
106d04f0edaSGreg Clayton   return XMLNode();
107d04f0edaSGreg Clayton #endif
108d04f0edaSGreg Clayton }
109d04f0edaSGreg Clayton 
GetSibling() const110b9c1b51eSKate Stone XMLNode XMLNode::GetSibling() const {
1114b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
112d04f0edaSGreg Clayton   if (IsValid())
113d04f0edaSGreg Clayton     return XMLNode(m_node->next);
114d04f0edaSGreg Clayton   else
115d04f0edaSGreg Clayton     return XMLNode();
116d04f0edaSGreg Clayton #else
117d04f0edaSGreg Clayton   return XMLNode();
118d04f0edaSGreg Clayton #endif
119d04f0edaSGreg Clayton }
120d04f0edaSGreg Clayton 
GetChild() const121b9c1b51eSKate Stone XMLNode XMLNode::GetChild() const {
1224b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
123d04f0edaSGreg Clayton 
124d04f0edaSGreg Clayton   if (IsValid())
125d04f0edaSGreg Clayton     return XMLNode(m_node->children);
126d04f0edaSGreg Clayton   else
127d04f0edaSGreg Clayton     return XMLNode();
128d04f0edaSGreg Clayton #else
129d04f0edaSGreg Clayton   return XMLNode();
130d04f0edaSGreg Clayton #endif
131d04f0edaSGreg Clayton }
132d04f0edaSGreg Clayton 
GetAttributeValue(const char * name,const char * fail_value) const133*1267506eSLirong Yuan std::string XMLNode::GetAttributeValue(const char *name,
134b9c1b51eSKate Stone                                        const char *fail_value) const {
135*1267506eSLirong Yuan   std::string attr_value;
1364b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
137*1267506eSLirong Yuan   if (IsValid()) {
138*1267506eSLirong Yuan     xmlChar *value = xmlGetProp(m_node, (const xmlChar *)name);
139*1267506eSLirong Yuan     if (value) {
140*1267506eSLirong Yuan       attr_value = (const char *)value;
141*1267506eSLirong Yuan       xmlFree(value);
142*1267506eSLirong Yuan     }
143*1267506eSLirong Yuan   } else {
144*1267506eSLirong Yuan     if (fail_value)
145d04f0edaSGreg Clayton       attr_value = fail_value;
146*1267506eSLirong Yuan   }
147d04f0edaSGreg Clayton #else
148*1267506eSLirong Yuan   if (fail_value)
149d04f0edaSGreg Clayton     attr_value = fail_value;
150d04f0edaSGreg Clayton #endif
151*1267506eSLirong Yuan   return attr_value;
152d04f0edaSGreg Clayton }
153d04f0edaSGreg Clayton 
GetAttributeValueAsUnsigned(const char * name,uint64_t & value,uint64_t fail_value,int base) const15416064d35SPavel Labath bool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value,
15516064d35SPavel Labath                                           uint64_t fail_value, int base) const {
15693b82f45SMichał Górny   value = fail_value;
15793b82f45SMichał Górny   return llvm::to_integer(GetAttributeValue(name, ""), value, base);
15816064d35SPavel Labath }
15916064d35SPavel Labath 
ForEachChildNode(NodeCallback const & callback) const160b9c1b51eSKate Stone void XMLNode::ForEachChildNode(NodeCallback const &callback) const {
1614b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
162d04f0edaSGreg Clayton   if (IsValid())
163d04f0edaSGreg Clayton     GetChild().ForEachSiblingNode(callback);
164d04f0edaSGreg Clayton #endif
165d04f0edaSGreg Clayton }
166d04f0edaSGreg Clayton 
ForEachChildElement(NodeCallback const & callback) const167b9c1b51eSKate Stone void XMLNode::ForEachChildElement(NodeCallback const &callback) const {
1684b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
169d04f0edaSGreg Clayton   XMLNode child = GetChild();
170d04f0edaSGreg Clayton   if (child)
171d04f0edaSGreg Clayton     child.ForEachSiblingElement(callback);
172d04f0edaSGreg Clayton #endif
173d04f0edaSGreg Clayton }
174d04f0edaSGreg Clayton 
ForEachChildElementWithName(const char * name,NodeCallback const & callback) const175b9c1b51eSKate Stone void XMLNode::ForEachChildElementWithName(const char *name,
176b9c1b51eSKate Stone                                           NodeCallback const &callback) const {
1774b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
178d04f0edaSGreg Clayton   XMLNode child = GetChild();
179d04f0edaSGreg Clayton   if (child)
180d04f0edaSGreg Clayton     child.ForEachSiblingElementWithName(name, callback);
181d04f0edaSGreg Clayton #endif
182d04f0edaSGreg Clayton }
183d04f0edaSGreg Clayton 
ForEachAttribute(AttributeCallback const & callback) const184b9c1b51eSKate Stone void XMLNode::ForEachAttribute(AttributeCallback const &callback) const {
1854b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
186d04f0edaSGreg Clayton 
187b9c1b51eSKate Stone   if (IsValid()) {
188b9c1b51eSKate Stone     for (xmlAttrPtr attr = m_node->properties; attr != nullptr;
189b9c1b51eSKate Stone          attr = attr->next) {
190d04f0edaSGreg Clayton       // check if name matches
191b9c1b51eSKate Stone       if (attr->name) {
192d04f0edaSGreg Clayton         // check child is a text node
193d04f0edaSGreg Clayton         xmlNodePtr child = attr->children;
194b9c1b51eSKate Stone         if (child->type == XML_TEXT_NODE) {
195d04f0edaSGreg Clayton           llvm::StringRef attr_value;
196d04f0edaSGreg Clayton           if (child->content)
197d04f0edaSGreg Clayton             attr_value = llvm::StringRef((const char *)child->content);
198a6682a41SJonas Devlieghere           if (!callback(llvm::StringRef((const char *)attr->name), attr_value))
199d04f0edaSGreg Clayton             return;
200d04f0edaSGreg Clayton         }
201d04f0edaSGreg Clayton       }
202d04f0edaSGreg Clayton     }
203d04f0edaSGreg Clayton   }
204d04f0edaSGreg Clayton #endif
205d04f0edaSGreg Clayton }
206d04f0edaSGreg Clayton 
ForEachSiblingNode(NodeCallback const & callback) const207b9c1b51eSKate Stone void XMLNode::ForEachSiblingNode(NodeCallback const &callback) const {
2084b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
209d04f0edaSGreg Clayton 
210b9c1b51eSKate Stone   if (IsValid()) {
211d04f0edaSGreg Clayton     // iterate through all siblings
212b9c1b51eSKate Stone     for (xmlNodePtr node = m_node; node; node = node->next) {
213a6682a41SJonas Devlieghere       if (!callback(XMLNode(node)))
214d04f0edaSGreg Clayton         return;
215d04f0edaSGreg Clayton     }
216d04f0edaSGreg Clayton   }
217d04f0edaSGreg Clayton #endif
218d04f0edaSGreg Clayton }
219d04f0edaSGreg Clayton 
ForEachSiblingElement(NodeCallback const & callback) const220b9c1b51eSKate Stone void XMLNode::ForEachSiblingElement(NodeCallback const &callback) const {
2214b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
222d04f0edaSGreg Clayton 
223b9c1b51eSKate Stone   if (IsValid()) {
224d04f0edaSGreg Clayton     // iterate through all siblings
225b9c1b51eSKate Stone     for (xmlNodePtr node = m_node; node; node = node->next) {
226d04f0edaSGreg Clayton       // we are looking for element nodes only
227d04f0edaSGreg Clayton       if (node->type != XML_ELEMENT_NODE)
228d04f0edaSGreg Clayton         continue;
229d04f0edaSGreg Clayton 
230a6682a41SJonas Devlieghere       if (!callback(XMLNode(node)))
231d04f0edaSGreg Clayton         return;
232d04f0edaSGreg Clayton     }
233d04f0edaSGreg Clayton   }
234d04f0edaSGreg Clayton #endif
235d04f0edaSGreg Clayton }
236d04f0edaSGreg Clayton 
ForEachSiblingElementWithName(const char * name,NodeCallback const & callback) const237b9c1b51eSKate Stone void XMLNode::ForEachSiblingElementWithName(
238b9c1b51eSKate Stone     const char *name, NodeCallback const &callback) const {
2394b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
240d04f0edaSGreg Clayton 
241b9c1b51eSKate Stone   if (IsValid()) {
242d04f0edaSGreg Clayton     // iterate through all siblings
243b9c1b51eSKate Stone     for (xmlNodePtr node = m_node; node; node = node->next) {
244d04f0edaSGreg Clayton       // we are looking for element nodes only
245d04f0edaSGreg Clayton       if (node->type != XML_ELEMENT_NODE)
246d04f0edaSGreg Clayton         continue;
247d04f0edaSGreg Clayton 
24805097246SAdrian Prantl       // If name is nullptr, we take all nodes of type "t", else just the ones
24905097246SAdrian Prantl       // whose name matches
250b9c1b51eSKate Stone       if (name) {
251d04f0edaSGreg Clayton         if (strcmp((const char *)node->name, name) != 0)
252d04f0edaSGreg Clayton           continue; // Name mismatch, ignore this one
253b9c1b51eSKate Stone       } else {
254d04f0edaSGreg Clayton         if (node->name)
255b9c1b51eSKate Stone           continue; // nullptr name specified and this element has a name,
256b9c1b51eSKate Stone                     // ignore this one
257d04f0edaSGreg Clayton       }
258d04f0edaSGreg Clayton 
259a6682a41SJonas Devlieghere       if (!callback(XMLNode(node)))
260d04f0edaSGreg Clayton         return;
261d04f0edaSGreg Clayton     }
262d04f0edaSGreg Clayton   }
263d04f0edaSGreg Clayton #endif
264d04f0edaSGreg Clayton }
265d04f0edaSGreg Clayton 
GetName() const266b9c1b51eSKate Stone llvm::StringRef XMLNode::GetName() const {
2674b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
268b9c1b51eSKate Stone   if (IsValid()) {
269d04f0edaSGreg Clayton     if (m_node->name)
270d04f0edaSGreg Clayton       return llvm::StringRef((const char *)m_node->name);
271d04f0edaSGreg Clayton   }
272d04f0edaSGreg Clayton #endif
273d04f0edaSGreg Clayton   return llvm::StringRef();
274d04f0edaSGreg Clayton }
275d04f0edaSGreg Clayton 
GetElementText(std::string & text) const276b9c1b51eSKate Stone bool XMLNode::GetElementText(std::string &text) const {
277d04f0edaSGreg Clayton   text.clear();
2784b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
279b9c1b51eSKate Stone   if (IsValid()) {
280d04f0edaSGreg Clayton     bool success = false;
281b9c1b51eSKate Stone     if (m_node->type == XML_ELEMENT_NODE) {
282d04f0edaSGreg Clayton       // check child is a text node
283b9c1b51eSKate Stone       for (xmlNodePtr node = m_node->children; node != nullptr;
284b9c1b51eSKate Stone            node = node->next) {
285b9c1b51eSKate Stone         if (node->type == XML_TEXT_NODE) {
286d04f0edaSGreg Clayton           text.append((const char *)node->content);
287d04f0edaSGreg Clayton           success = true;
288d04f0edaSGreg Clayton         }
289d04f0edaSGreg Clayton       }
290d04f0edaSGreg Clayton     }
291d04f0edaSGreg Clayton     return success;
292d04f0edaSGreg Clayton   }
293d04f0edaSGreg Clayton #endif
294d04f0edaSGreg Clayton   return false;
295d04f0edaSGreg Clayton }
296d04f0edaSGreg Clayton 
GetElementTextAsUnsigned(uint64_t & value,uint64_t fail_value,int base) const297b9c1b51eSKate Stone bool XMLNode::GetElementTextAsUnsigned(uint64_t &value, uint64_t fail_value,
298b9c1b51eSKate Stone                                        int base) const {
299ee8f9940SGreg Clayton   std::string text;
30093b82f45SMichał Górny 
301ee8f9940SGreg Clayton   value = fail_value;
30293b82f45SMichał Górny   return GetElementText(text) && llvm::to_integer(text, value, base);
303ee8f9940SGreg Clayton }
304ee8f9940SGreg Clayton 
GetElementTextAsFloat(double & value,double fail_value) const305b9c1b51eSKate Stone bool XMLNode::GetElementTextAsFloat(double &value, double fail_value) const {
306ee8f9940SGreg Clayton   std::string text;
30793b82f45SMichał Górny 
308ee8f9940SGreg Clayton   value = fail_value;
30993b82f45SMichał Górny   return GetElementText(text) && llvm::to_float(text, value);
310ee8f9940SGreg Clayton }
311ee8f9940SGreg Clayton 
NameIs(const char * name) const312b9c1b51eSKate Stone bool XMLNode::NameIs(const char *name) const {
3134b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
314d04f0edaSGreg Clayton 
315b9c1b51eSKate Stone   if (IsValid()) {
316d04f0edaSGreg Clayton     // In case we are looking for a nullptr name or an exact pointer match
317d04f0edaSGreg Clayton     if (m_node->name == (const xmlChar *)name)
318d04f0edaSGreg Clayton       return true;
319d04f0edaSGreg Clayton     if (m_node->name)
320d04f0edaSGreg Clayton       return strcmp((const char *)m_node->name, name) == 0;
321d04f0edaSGreg Clayton   }
322d04f0edaSGreg Clayton #endif
323d04f0edaSGreg Clayton   return false;
324d04f0edaSGreg Clayton }
325d04f0edaSGreg Clayton 
FindFirstChildElementWithName(const char * name) const326b9c1b51eSKate Stone XMLNode XMLNode::FindFirstChildElementWithName(const char *name) const {
327d04f0edaSGreg Clayton   XMLNode result_node;
328d04f0edaSGreg Clayton 
3294b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
330b9c1b51eSKate Stone   ForEachChildElementWithName(
331014ef593SDavide Italiano       name, [&result_node](const XMLNode &node) -> bool {
332d04f0edaSGreg Clayton         result_node = node;
333d04f0edaSGreg Clayton         // Stop iterating, we found the node we wanted
334d04f0edaSGreg Clayton         return false;
335d04f0edaSGreg Clayton       });
336d04f0edaSGreg Clayton #endif
337d04f0edaSGreg Clayton 
338d04f0edaSGreg Clayton   return result_node;
339d04f0edaSGreg Clayton }
340d04f0edaSGreg Clayton 
IsValid() const341b9c1b51eSKate Stone bool XMLNode::IsValid() const { return m_node != nullptr; }
342d04f0edaSGreg Clayton 
IsElement() const343b9c1b51eSKate Stone bool XMLNode::IsElement() const {
3444b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
345d04f0edaSGreg Clayton   if (IsValid())
346d04f0edaSGreg Clayton     return m_node->type == XML_ELEMENT_NODE;
347d04f0edaSGreg Clayton #endif
348d04f0edaSGreg Clayton   return false;
349d04f0edaSGreg Clayton }
350d04f0edaSGreg Clayton 
GetElementForPath(const NamePath & path)351b9c1b51eSKate Stone XMLNode XMLNode::GetElementForPath(const NamePath &path) {
3524b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
353d04f0edaSGreg Clayton 
354b9c1b51eSKate Stone   if (IsValid()) {
355d04f0edaSGreg Clayton     if (path.empty())
356d04f0edaSGreg Clayton       return *this;
357b9c1b51eSKate Stone     else {
358d04f0edaSGreg Clayton       XMLNode node = FindFirstChildElementWithName(path[0].c_str());
359d04f0edaSGreg Clayton       const size_t n = path.size();
360d04f0edaSGreg Clayton       for (size_t i = 1; node && i < n; ++i)
361d04f0edaSGreg Clayton         node = node.FindFirstChildElementWithName(path[i].c_str());
362d04f0edaSGreg Clayton       return node;
363d04f0edaSGreg Clayton     }
364d04f0edaSGreg Clayton   }
365d04f0edaSGreg Clayton #endif
366d04f0edaSGreg Clayton 
367d04f0edaSGreg Clayton   return XMLNode();
368d04f0edaSGreg Clayton }
369d04f0edaSGreg Clayton 
370d04f0edaSGreg Clayton #pragma mark-- ApplePropertyList
371d04f0edaSGreg Clayton 
ApplePropertyList()372b9c1b51eSKate Stone ApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {}
373d04f0edaSGreg Clayton 
ApplePropertyList(const char * path)374b9c1b51eSKate Stone ApplePropertyList::ApplePropertyList(const char *path)
375b9c1b51eSKate Stone     : m_xml_doc(), m_dict_node() {
376d04f0edaSGreg Clayton   ParseFile(path);
377d04f0edaSGreg Clayton }
378d04f0edaSGreg Clayton 
379fd2433e1SJonas Devlieghere ApplePropertyList::~ApplePropertyList() = default;
3802a2949cfSGreg Clayton 
GetErrors() const3818927f23aSZachary Turner llvm::StringRef ApplePropertyList::GetErrors() const {
3822a2949cfSGreg Clayton   return m_xml_doc.GetErrors();
3832a2949cfSGreg Clayton }
3842a2949cfSGreg Clayton 
ParseFile(const char * path)385b9c1b51eSKate Stone bool ApplePropertyList::ParseFile(const char *path) {
386b9c1b51eSKate Stone   if (m_xml_doc.ParseFile(path)) {
387d04f0edaSGreg Clayton     XMLNode plist = m_xml_doc.GetRootElement("plist");
388b9c1b51eSKate Stone     if (plist) {
389b9c1b51eSKate Stone       plist.ForEachChildElementWithName("dict",
390b9c1b51eSKate Stone                                         [this](const XMLNode &dict) -> bool {
391d04f0edaSGreg Clayton                                           this->m_dict_node = dict;
392d04f0edaSGreg Clayton                                           return false; // Stop iterating
393d04f0edaSGreg Clayton                                         });
394d04f0edaSGreg Clayton       return (bool)m_dict_node;
395d04f0edaSGreg Clayton     }
396d04f0edaSGreg Clayton   }
397d04f0edaSGreg Clayton   return false;
398d04f0edaSGreg Clayton }
399d04f0edaSGreg Clayton 
IsValid() const400b9c1b51eSKate Stone bool ApplePropertyList::IsValid() const { return (bool)m_dict_node; }
401d04f0edaSGreg Clayton 
GetValueAsString(const char * key,std::string & value) const402b9c1b51eSKate Stone bool ApplePropertyList::GetValueAsString(const char *key,
403b9c1b51eSKate Stone                                          std::string &value) const {
404d04f0edaSGreg Clayton   XMLNode value_node = GetValueNode(key);
405d04f0edaSGreg Clayton   if (value_node)
406d04f0edaSGreg Clayton     return ApplePropertyList::ExtractStringFromValueNode(value_node, value);
407d04f0edaSGreg Clayton   return false;
408d04f0edaSGreg Clayton }
409d04f0edaSGreg Clayton 
GetValueNode(const char * key) const410b9c1b51eSKate Stone XMLNode ApplePropertyList::GetValueNode(const char *key) const {
411d04f0edaSGreg Clayton   XMLNode value_node;
4124b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
413d04f0edaSGreg Clayton 
414b9c1b51eSKate Stone   if (IsValid()) {
415b9c1b51eSKate Stone     m_dict_node.ForEachChildElementWithName(
416b9c1b51eSKate Stone         "key", [key, &value_node](const XMLNode &key_node) -> bool {
417d04f0edaSGreg Clayton           std::string key_name;
418b9c1b51eSKate Stone           if (key_node.GetElementText(key_name)) {
4198d20cfdfSJonas Devlieghere             if (key_name == key) {
420d04f0edaSGreg Clayton               value_node = key_node.GetSibling();
421d04f0edaSGreg Clayton               while (value_node && !value_node.IsElement())
422d04f0edaSGreg Clayton                 value_node = value_node.GetSibling();
423d04f0edaSGreg Clayton               return false; // Stop iterating
424d04f0edaSGreg Clayton             }
425d04f0edaSGreg Clayton           }
426d04f0edaSGreg Clayton           return true; // Keep iterating
427d04f0edaSGreg Clayton         });
428d04f0edaSGreg Clayton   }
429d04f0edaSGreg Clayton #endif
430d04f0edaSGreg Clayton   return value_node;
431d04f0edaSGreg Clayton }
432d04f0edaSGreg Clayton 
ExtractStringFromValueNode(const XMLNode & node,std::string & value)433b9c1b51eSKate Stone bool ApplePropertyList::ExtractStringFromValueNode(const XMLNode &node,
434b9c1b51eSKate Stone                                                    std::string &value) {
435d04f0edaSGreg Clayton   value.clear();
4364b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
437b9c1b51eSKate Stone   if (node.IsValid()) {
438d04f0edaSGreg Clayton     llvm::StringRef element_name = node.GetName();
439b9c1b51eSKate Stone     if (element_name == "true" || element_name == "false") {
440d04f0edaSGreg Clayton       // The text value _is_ the element name itself...
4413a29f8b9SPavel Labath       value = element_name.str();
442d04f0edaSGreg Clayton       return true;
443b9c1b51eSKate Stone     } else if (element_name == "dict" || element_name == "array")
444d04f0edaSGreg Clayton       return false; // dictionaries and arrays have no text value, so we fail
445d04f0edaSGreg Clayton     else
446d04f0edaSGreg Clayton       return node.GetElementText(value);
447d04f0edaSGreg Clayton   }
448d04f0edaSGreg Clayton #endif
449d04f0edaSGreg Clayton   return false;
450d04f0edaSGreg Clayton }
451d04f0edaSGreg Clayton 
4524b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
453ee8f9940SGreg Clayton 
CreatePlistValue(XMLNode node)45493c1b3caSPavel Labath static StructuredData::ObjectSP CreatePlistValue(XMLNode node) {
455ee8f9940SGreg Clayton   llvm::StringRef element_name = node.GetName();
456b9c1b51eSKate Stone   if (element_name == "array") {
457b9c1b51eSKate Stone     std::shared_ptr<StructuredData::Array> array_sp(
458b9c1b51eSKate Stone         new StructuredData::Array());
459ee8f9940SGreg Clayton     node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool {
460ee8f9940SGreg Clayton       array_sp->AddItem(CreatePlistValue(node));
461ee8f9940SGreg Clayton       return true; // Keep iterating through all child elements of the array
462ee8f9940SGreg Clayton     });
463ee8f9940SGreg Clayton     return array_sp;
464b9c1b51eSKate Stone   } else if (element_name == "dict") {
465ee8f9940SGreg Clayton     XMLNode key_node;
466b9c1b51eSKate Stone     std::shared_ptr<StructuredData::Dictionary> dict_sp(
467b9c1b51eSKate Stone         new StructuredData::Dictionary());
468b9c1b51eSKate Stone     node.ForEachChildElement(
469b9c1b51eSKate Stone         [&key_node, &dict_sp](const XMLNode &node) -> bool {
470b9c1b51eSKate Stone           if (node.NameIs("key")) {
471ee8f9940SGreg Clayton             // This is a "key" element node
472ee8f9940SGreg Clayton             key_node = node;
473b9c1b51eSKate Stone           } else {
474ee8f9940SGreg Clayton             // This is a value node
475b9c1b51eSKate Stone             if (key_node) {
4762a2949cfSGreg Clayton               std::string key_name;
4772a2949cfSGreg Clayton               key_node.GetElementText(key_name);
4782a2949cfSGreg Clayton               dict_sp->AddItem(key_name, CreatePlistValue(node));
479ee8f9940SGreg Clayton               key_node.Clear();
480ee8f9940SGreg Clayton             }
481ee8f9940SGreg Clayton           }
482b9c1b51eSKate Stone           return true; // Keep iterating through all child elements of the
483b9c1b51eSKate Stone                        // dictionary
484ee8f9940SGreg Clayton         });
485ee8f9940SGreg Clayton     return dict_sp;
486b9c1b51eSKate Stone   } else if (element_name == "real") {
487ee8f9940SGreg Clayton     double value = 0.0;
488ee8f9940SGreg Clayton     node.GetElementTextAsFloat(value);
489ee8f9940SGreg Clayton     return StructuredData::ObjectSP(new StructuredData::Float(value));
490b9c1b51eSKate Stone   } else if (element_name == "integer") {
491ee8f9940SGreg Clayton     uint64_t value = 0;
492ee8f9940SGreg Clayton     node.GetElementTextAsUnsigned(value, 0, 0);
493ee8f9940SGreg Clayton     return StructuredData::ObjectSP(new StructuredData::Integer(value));
494b9c1b51eSKate Stone   } else if ((element_name == "string") || (element_name == "data") ||
495b9c1b51eSKate Stone              (element_name == "date")) {
496ee8f9940SGreg Clayton     std::string text;
497ee8f9940SGreg Clayton     node.GetElementText(text);
498b9c1b51eSKate Stone     return StructuredData::ObjectSP(
499b9c1b51eSKate Stone         new StructuredData::String(std::move(text)));
500b9c1b51eSKate Stone   } else if (element_name == "true") {
501ee8f9940SGreg Clayton     return StructuredData::ObjectSP(new StructuredData::Boolean(true));
502b9c1b51eSKate Stone   } else if (element_name == "false") {
503ee8f9940SGreg Clayton     return StructuredData::ObjectSP(new StructuredData::Boolean(false));
504ee8f9940SGreg Clayton   }
505ee8f9940SGreg Clayton   return StructuredData::ObjectSP(new StructuredData::Null());
506ee8f9940SGreg Clayton }
507ee8f9940SGreg Clayton #endif
508ee8f9940SGreg Clayton 
GetStructuredData()509b9c1b51eSKate Stone StructuredData::ObjectSP ApplePropertyList::GetStructuredData() {
510ee8f9940SGreg Clayton   StructuredData::ObjectSP root_sp;
5114b15c6e2SJonas Devlieghere #if LLDB_ENABLE_LIBXML2
512b9c1b51eSKate Stone   if (IsValid()) {
513ee8f9940SGreg Clayton     return CreatePlistValue(m_dict_node);
514ee8f9940SGreg Clayton   }
515ee8f9940SGreg Clayton #endif
516ee8f9940SGreg Clayton   return root_sp;
517ee8f9940SGreg Clayton }
518