1285101Semaste//===-- XML.cpp -------------------------------------------------*- C++ -*-===//
2285101Semaste//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6285101Semaste//
7285101Semaste//===----------------------------------------------------------------------===//
8285101Semaste
9314564Sdim#include <stdlib.h> /* atof */
10285101Semaste
11360784Sdim#include "lldb/Host/Config.h"
12314564Sdim#include "lldb/Host/StringConvert.h"
13285101Semaste#include "lldb/Host/XML.h"
14285101Semaste
15285101Semasteusing namespace lldb;
16285101Semasteusing namespace lldb_private;
17285101Semaste
18314564Sdim#pragma mark-- XMLDocument
19285101Semaste
20314564SdimXMLDocument::XMLDocument() : m_document(nullptr) {}
21285101Semaste
22314564SdimXMLDocument::~XMLDocument() { Clear(); }
23285101Semaste
24314564Sdimvoid XMLDocument::Clear() {
25360784Sdim#if LLDB_ENABLE_LIBXML2
26314564Sdim  if (m_document) {
27314564Sdim    xmlDocPtr doc = m_document;
28314564Sdim    m_document = nullptr;
29314564Sdim    xmlFreeDoc(doc);
30314564Sdim  }
31285101Semaste#endif
32285101Semaste}
33285101Semaste
34314564Sdimbool XMLDocument::IsValid() const { return m_document != nullptr; }
35285101Semaste
36314564Sdimvoid XMLDocument::ErrorCallback(void *ctx, const char *format, ...) {
37314564Sdim  XMLDocument *document = (XMLDocument *)ctx;
38314564Sdim  va_list args;
39314564Sdim  va_start(args, format);
40314564Sdim  document->m_errors.PrintfVarArg(format, args);
41314564Sdim  document->m_errors.EOL();
42314564Sdim  va_end(args);
43285101Semaste}
44285101Semaste
45314564Sdimbool XMLDocument::ParseFile(const char *path) {
46360784Sdim#if LLDB_ENABLE_LIBXML2
47314564Sdim  Clear();
48314564Sdim  xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);
49314564Sdim  m_document = xmlParseFile(path);
50314564Sdim  xmlSetGenericErrorFunc(nullptr, nullptr);
51285101Semaste#endif
52314564Sdim  return IsValid();
53285101Semaste}
54285101Semaste
55314564Sdimbool XMLDocument::ParseMemory(const char *xml, size_t xml_length,
56314564Sdim                              const char *url) {
57360784Sdim#if LLDB_ENABLE_LIBXML2
58314564Sdim  Clear();
59314564Sdim  xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);
60314564Sdim  m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0);
61314564Sdim  xmlSetGenericErrorFunc(nullptr, nullptr);
62285101Semaste#endif
63314564Sdim  return IsValid();
64285101Semaste}
65285101Semaste
66314564SdimXMLNode XMLDocument::GetRootElement(const char *required_name) {
67360784Sdim#if LLDB_ENABLE_LIBXML2
68314564Sdim  if (IsValid()) {
69314564Sdim    XMLNode root_node(xmlDocGetRootElement(m_document));
70314564Sdim    if (required_name) {
71314564Sdim      llvm::StringRef actual_name = root_node.GetName();
72314564Sdim      if (actual_name == required_name)
73314564Sdim        return root_node;
74314564Sdim    } else {
75314564Sdim      return root_node;
76285101Semaste    }
77314564Sdim  }
78285101Semaste#endif
79314564Sdim  return XMLNode();
80285101Semaste}
81285101Semaste
82314564Sdimllvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); }
83285101Semaste
84314564Sdimbool XMLDocument::XMLEnabled() {
85360784Sdim#if LLDB_ENABLE_LIBXML2
86314564Sdim  return true;
87285101Semaste#else
88314564Sdim  return false;
89285101Semaste#endif
90285101Semaste}
91285101Semaste
92314564Sdim#pragma mark-- XMLNode
93285101Semaste
94314564SdimXMLNode::XMLNode() : m_node(nullptr) {}
95285101Semaste
96314564SdimXMLNode::XMLNode(XMLNodeImpl node) : m_node(node) {}
97285101Semaste
98314564SdimXMLNode::~XMLNode() {}
99285101Semaste
100314564Sdimvoid XMLNode::Clear() { m_node = nullptr; }
101285101Semaste
102314564SdimXMLNode XMLNode::GetParent() const {
103360784Sdim#if LLDB_ENABLE_LIBXML2
104314564Sdim  if (IsValid())
105314564Sdim    return XMLNode(m_node->parent);
106314564Sdim  else
107314564Sdim    return XMLNode();
108285101Semaste#else
109314564Sdim  return XMLNode();
110285101Semaste#endif
111285101Semaste}
112285101Semaste
113314564SdimXMLNode XMLNode::GetSibling() const {
114360784Sdim#if LLDB_ENABLE_LIBXML2
115314564Sdim  if (IsValid())
116314564Sdim    return XMLNode(m_node->next);
117314564Sdim  else
118314564Sdim    return XMLNode();
119285101Semaste#else
120314564Sdim  return XMLNode();
121285101Semaste#endif
122285101Semaste}
123285101Semaste
124314564SdimXMLNode XMLNode::GetChild() const {
125360784Sdim#if LLDB_ENABLE_LIBXML2
126285101Semaste
127314564Sdim  if (IsValid())
128314564Sdim    return XMLNode(m_node->children);
129314564Sdim  else
130314564Sdim    return XMLNode();
131285101Semaste#else
132314564Sdim  return XMLNode();
133285101Semaste#endif
134285101Semaste}
135285101Semaste
136314564Sdimllvm::StringRef XMLNode::GetAttributeValue(const char *name,
137314564Sdim                                           const char *fail_value) const {
138353358Sdim  const char *attr_value = nullptr;
139360784Sdim#if LLDB_ENABLE_LIBXML2
140285101Semaste
141314564Sdim  if (IsValid())
142314564Sdim    attr_value = (const char *)xmlGetProp(m_node, (const xmlChar *)name);
143314564Sdim  else
144314564Sdim    attr_value = fail_value;
145285101Semaste#else
146314564Sdim  attr_value = fail_value;
147285101Semaste#endif
148314564Sdim  if (attr_value)
149314564Sdim    return llvm::StringRef(attr_value);
150314564Sdim  else
151314564Sdim    return llvm::StringRef();
152285101Semaste}
153285101Semaste
154341825Sdimbool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value,
155341825Sdim                                          uint64_t fail_value, int base) const {
156360784Sdim#if LLDB_ENABLE_LIBXML2
157341825Sdim  llvm::StringRef str_value = GetAttributeValue(name, "");
158341825Sdim#else
159341825Sdim  llvm::StringRef str_value;
160341825Sdim#endif
161341825Sdim  bool success = false;
162341825Sdim  value = StringConvert::ToUInt64(str_value.data(), fail_value, base, &success);
163341825Sdim  return success;
164341825Sdim}
165341825Sdim
166314564Sdimvoid XMLNode::ForEachChildNode(NodeCallback const &callback) const {
167360784Sdim#if LLDB_ENABLE_LIBXML2
168314564Sdim  if (IsValid())
169314564Sdim    GetChild().ForEachSiblingNode(callback);
170285101Semaste#endif
171285101Semaste}
172285101Semaste
173314564Sdimvoid XMLNode::ForEachChildElement(NodeCallback const &callback) const {
174360784Sdim#if LLDB_ENABLE_LIBXML2
175314564Sdim  XMLNode child = GetChild();
176314564Sdim  if (child)
177314564Sdim    child.ForEachSiblingElement(callback);
178285101Semaste#endif
179285101Semaste}
180285101Semaste
181314564Sdimvoid XMLNode::ForEachChildElementWithName(const char *name,
182314564Sdim                                          NodeCallback const &callback) const {
183360784Sdim#if LLDB_ENABLE_LIBXML2
184314564Sdim  XMLNode child = GetChild();
185314564Sdim  if (child)
186314564Sdim    child.ForEachSiblingElementWithName(name, callback);
187285101Semaste#endif
188285101Semaste}
189285101Semaste
190314564Sdimvoid XMLNode::ForEachAttribute(AttributeCallback const &callback) const {
191360784Sdim#if LLDB_ENABLE_LIBXML2
192285101Semaste
193314564Sdim  if (IsValid()) {
194314564Sdim    for (xmlAttrPtr attr = m_node->properties; attr != nullptr;
195314564Sdim         attr = attr->next) {
196314564Sdim      // check if name matches
197314564Sdim      if (attr->name) {
198314564Sdim        // check child is a text node
199314564Sdim        xmlNodePtr child = attr->children;
200314564Sdim        if (child->type == XML_TEXT_NODE) {
201314564Sdim          llvm::StringRef attr_value;
202314564Sdim          if (child->content)
203314564Sdim            attr_value = llvm::StringRef((const char *)child->content);
204344779Sdim          if (!callback(llvm::StringRef((const char *)attr->name), attr_value))
205314564Sdim            return;
206285101Semaste        }
207314564Sdim      }
208285101Semaste    }
209314564Sdim  }
210285101Semaste#endif
211285101Semaste}
212285101Semaste
213314564Sdimvoid XMLNode::ForEachSiblingNode(NodeCallback const &callback) const {
214360784Sdim#if LLDB_ENABLE_LIBXML2
215285101Semaste
216314564Sdim  if (IsValid()) {
217314564Sdim    // iterate through all siblings
218314564Sdim    for (xmlNodePtr node = m_node; node; node = node->next) {
219344779Sdim      if (!callback(XMLNode(node)))
220314564Sdim        return;
221285101Semaste    }
222314564Sdim  }
223285101Semaste#endif
224285101Semaste}
225285101Semaste
226314564Sdimvoid XMLNode::ForEachSiblingElement(NodeCallback const &callback) const {
227360784Sdim#if LLDB_ENABLE_LIBXML2
228314564Sdim
229314564Sdim  if (IsValid()) {
230314564Sdim    // iterate through all siblings
231314564Sdim    for (xmlNodePtr node = m_node; node; node = node->next) {
232314564Sdim      // we are looking for element nodes only
233314564Sdim      if (node->type != XML_ELEMENT_NODE)
234314564Sdim        continue;
235314564Sdim
236344779Sdim      if (!callback(XMLNode(node)))
237314564Sdim        return;
238285101Semaste    }
239314564Sdim  }
240285101Semaste#endif
241285101Semaste}
242285101Semaste
243314564Sdimvoid XMLNode::ForEachSiblingElementWithName(
244314564Sdim    const char *name, NodeCallback const &callback) const {
245360784Sdim#if LLDB_ENABLE_LIBXML2
246314564Sdim
247314564Sdim  if (IsValid()) {
248314564Sdim    // iterate through all siblings
249314564Sdim    for (xmlNodePtr node = m_node; node; node = node->next) {
250314564Sdim      // we are looking for element nodes only
251314564Sdim      if (node->type != XML_ELEMENT_NODE)
252314564Sdim        continue;
253314564Sdim
254341825Sdim      // If name is nullptr, we take all nodes of type "t", else just the ones
255341825Sdim      // whose name matches
256314564Sdim      if (name) {
257314564Sdim        if (strcmp((const char *)node->name, name) != 0)
258314564Sdim          continue; // Name mismatch, ignore this one
259314564Sdim      } else {
260314564Sdim        if (node->name)
261314564Sdim          continue; // nullptr name specified and this element has a name,
262314564Sdim                    // ignore this one
263314564Sdim      }
264314564Sdim
265344779Sdim      if (!callback(XMLNode(node)))
266314564Sdim        return;
267285101Semaste    }
268314564Sdim  }
269285101Semaste#endif
270285101Semaste}
271285101Semaste
272314564Sdimllvm::StringRef XMLNode::GetName() const {
273360784Sdim#if LLDB_ENABLE_LIBXML2
274314564Sdim  if (IsValid()) {
275314564Sdim    if (m_node->name)
276314564Sdim      return llvm::StringRef((const char *)m_node->name);
277314564Sdim  }
278285101Semaste#endif
279314564Sdim  return llvm::StringRef();
280285101Semaste}
281285101Semaste
282314564Sdimbool XMLNode::GetElementText(std::string &text) const {
283314564Sdim  text.clear();
284360784Sdim#if LLDB_ENABLE_LIBXML2
285314564Sdim  if (IsValid()) {
286314564Sdim    bool success = false;
287314564Sdim    if (m_node->type == XML_ELEMENT_NODE) {
288314564Sdim      // check child is a text node
289314564Sdim      for (xmlNodePtr node = m_node->children; node != nullptr;
290314564Sdim           node = node->next) {
291314564Sdim        if (node->type == XML_TEXT_NODE) {
292314564Sdim          text.append((const char *)node->content);
293314564Sdim          success = true;
294285101Semaste        }
295314564Sdim      }
296285101Semaste    }
297314564Sdim    return success;
298314564Sdim  }
299285101Semaste#endif
300314564Sdim  return false;
301285101Semaste}
302285101Semaste
303314564Sdimbool XMLNode::GetElementTextAsUnsigned(uint64_t &value, uint64_t fail_value,
304314564Sdim                                       int base) const {
305314564Sdim  bool success = false;
306360784Sdim#if LLDB_ENABLE_LIBXML2
307314564Sdim  if (IsValid()) {
308314564Sdim    std::string text;
309314564Sdim    if (GetElementText(text))
310314564Sdim      value = StringConvert::ToUInt64(text.c_str(), fail_value, base, &success);
311314564Sdim  }
312285101Semaste#endif
313314564Sdim  if (!success)
314314564Sdim    value = fail_value;
315314564Sdim  return success;
316285101Semaste}
317285101Semaste
318314564Sdimbool XMLNode::GetElementTextAsFloat(double &value, double fail_value) const {
319314564Sdim  bool success = false;
320360784Sdim#if LLDB_ENABLE_LIBXML2
321314564Sdim  if (IsValid()) {
322314564Sdim    std::string text;
323314564Sdim    if (GetElementText(text)) {
324314564Sdim      value = atof(text.c_str());
325314564Sdim      success = true;
326285101Semaste    }
327314564Sdim  }
328285101Semaste#endif
329314564Sdim  if (!success)
330314564Sdim    value = fail_value;
331314564Sdim  return success;
332285101Semaste}
333285101Semaste
334314564Sdimbool XMLNode::NameIs(const char *name) const {
335360784Sdim#if LLDB_ENABLE_LIBXML2
336285101Semaste
337314564Sdim  if (IsValid()) {
338314564Sdim    // In case we are looking for a nullptr name or an exact pointer match
339314564Sdim    if (m_node->name == (const xmlChar *)name)
340314564Sdim      return true;
341314564Sdim    if (m_node->name)
342314564Sdim      return strcmp((const char *)m_node->name, name) == 0;
343314564Sdim  }
344285101Semaste#endif
345314564Sdim  return false;
346285101Semaste}
347285101Semaste
348314564SdimXMLNode XMLNode::FindFirstChildElementWithName(const char *name) const {
349314564Sdim  XMLNode result_node;
350285101Semaste
351360784Sdim#if LLDB_ENABLE_LIBXML2
352314564Sdim  ForEachChildElementWithName(
353327952Sdim      name, [&result_node](const XMLNode &node) -> bool {
354285101Semaste        result_node = node;
355285101Semaste        // Stop iterating, we found the node we wanted
356285101Semaste        return false;
357314564Sdim      });
358285101Semaste#endif
359285101Semaste
360314564Sdim  return result_node;
361285101Semaste}
362285101Semaste
363314564Sdimbool XMLNode::IsValid() const { return m_node != nullptr; }
364285101Semaste
365314564Sdimbool XMLNode::IsElement() const {
366360784Sdim#if LLDB_ENABLE_LIBXML2
367314564Sdim  if (IsValid())
368314564Sdim    return m_node->type == XML_ELEMENT_NODE;
369285101Semaste#endif
370314564Sdim  return false;
371285101Semaste}
372285101Semaste
373314564SdimXMLNode XMLNode::GetElementForPath(const NamePath &path) {
374360784Sdim#if LLDB_ENABLE_LIBXML2
375285101Semaste
376314564Sdim  if (IsValid()) {
377314564Sdim    if (path.empty())
378314564Sdim      return *this;
379314564Sdim    else {
380314564Sdim      XMLNode node = FindFirstChildElementWithName(path[0].c_str());
381314564Sdim      const size_t n = path.size();
382314564Sdim      for (size_t i = 1; node && i < n; ++i)
383314564Sdim        node = node.FindFirstChildElementWithName(path[i].c_str());
384314564Sdim      return node;
385285101Semaste    }
386314564Sdim  }
387285101Semaste#endif
388285101Semaste
389314564Sdim  return XMLNode();
390285101Semaste}
391285101Semaste
392314564Sdim#pragma mark-- ApplePropertyList
393285101Semaste
394314564SdimApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {}
395285101Semaste
396314564SdimApplePropertyList::ApplePropertyList(const char *path)
397314564Sdim    : m_xml_doc(), m_dict_node() {
398314564Sdim  ParseFile(path);
399285101Semaste}
400285101Semaste
401314564SdimApplePropertyList::~ApplePropertyList() {}
402285101Semaste
403314564Sdimllvm::StringRef ApplePropertyList::GetErrors() const {
404314564Sdim  return m_xml_doc.GetErrors();
405285101Semaste}
406285101Semaste
407314564Sdimbool ApplePropertyList::ParseFile(const char *path) {
408314564Sdim  if (m_xml_doc.ParseFile(path)) {
409314564Sdim    XMLNode plist = m_xml_doc.GetRootElement("plist");
410314564Sdim    if (plist) {
411314564Sdim      plist.ForEachChildElementWithName("dict",
412314564Sdim                                        [this](const XMLNode &dict) -> bool {
413314564Sdim                                          this->m_dict_node = dict;
414314564Sdim                                          return false; // Stop iterating
415314564Sdim                                        });
416314564Sdim      return (bool)m_dict_node;
417314564Sdim    }
418314564Sdim  }
419314564Sdim  return false;
420285101Semaste}
421285101Semaste
422314564Sdimbool ApplePropertyList::IsValid() const { return (bool)m_dict_node; }
423285101Semaste
424314564Sdimbool ApplePropertyList::GetValueAsString(const char *key,
425314564Sdim                                         std::string &value) const {
426314564Sdim  XMLNode value_node = GetValueNode(key);
427314564Sdim  if (value_node)
428314564Sdim    return ApplePropertyList::ExtractStringFromValueNode(value_node, value);
429314564Sdim  return false;
430285101Semaste}
431285101Semaste
432314564SdimXMLNode ApplePropertyList::GetValueNode(const char *key) const {
433314564Sdim  XMLNode value_node;
434360784Sdim#if LLDB_ENABLE_LIBXML2
435285101Semaste
436314564Sdim  if (IsValid()) {
437314564Sdim    m_dict_node.ForEachChildElementWithName(
438314564Sdim        "key", [key, &value_node](const XMLNode &key_node) -> bool {
439314564Sdim          std::string key_name;
440314564Sdim          if (key_node.GetElementText(key_name)) {
441344779Sdim            if (key_name == key) {
442314564Sdim              value_node = key_node.GetSibling();
443314564Sdim              while (value_node && !value_node.IsElement())
444314564Sdim                value_node = value_node.GetSibling();
445314564Sdim              return false; // Stop iterating
446285101Semaste            }
447314564Sdim          }
448314564Sdim          return true; // Keep iterating
449285101Semaste        });
450314564Sdim  }
451285101Semaste#endif
452314564Sdim  return value_node;
453285101Semaste}
454285101Semaste
455314564Sdimbool ApplePropertyList::ExtractStringFromValueNode(const XMLNode &node,
456314564Sdim                                                   std::string &value) {
457314564Sdim  value.clear();
458360784Sdim#if LLDB_ENABLE_LIBXML2
459314564Sdim  if (node.IsValid()) {
460314564Sdim    llvm::StringRef element_name = node.GetName();
461314564Sdim    if (element_name == "true" || element_name == "false") {
462314564Sdim      // The text value _is_ the element name itself...
463314564Sdim      value = element_name.str();
464314564Sdim      return true;
465314564Sdim    } else if (element_name == "dict" || element_name == "array")
466314564Sdim      return false; // dictionaries and arrays have no text value, so we fail
467314564Sdim    else
468314564Sdim      return node.GetElementText(value);
469314564Sdim  }
470285101Semaste#endif
471314564Sdim  return false;
472285101Semaste}
473285101Semaste
474360784Sdim#if LLDB_ENABLE_LIBXML2
475285101Semaste
476285101Semastenamespace {
477285101Semaste
478314564SdimStructuredData::ObjectSP CreatePlistValue(XMLNode node) {
479314564Sdim  llvm::StringRef element_name = node.GetName();
480314564Sdim  if (element_name == "array") {
481314564Sdim    std::shared_ptr<StructuredData::Array> array_sp(
482314564Sdim        new StructuredData::Array());
483314564Sdim    node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool {
484314564Sdim      array_sp->AddItem(CreatePlistValue(node));
485314564Sdim      return true; // Keep iterating through all child elements of the array
486314564Sdim    });
487314564Sdim    return array_sp;
488314564Sdim  } else if (element_name == "dict") {
489314564Sdim    XMLNode key_node;
490314564Sdim    std::shared_ptr<StructuredData::Dictionary> dict_sp(
491314564Sdim        new StructuredData::Dictionary());
492314564Sdim    node.ForEachChildElement(
493314564Sdim        [&key_node, &dict_sp](const XMLNode &node) -> bool {
494314564Sdim          if (node.NameIs("key")) {
495314564Sdim            // This is a "key" element node
496314564Sdim            key_node = node;
497314564Sdim          } else {
498314564Sdim            // This is a value node
499314564Sdim            if (key_node) {
500314564Sdim              std::string key_name;
501314564Sdim              key_node.GetElementText(key_name);
502314564Sdim              dict_sp->AddItem(key_name, CreatePlistValue(node));
503314564Sdim              key_node.Clear();
504314564Sdim            }
505314564Sdim          }
506314564Sdim          return true; // Keep iterating through all child elements of the
507314564Sdim                       // dictionary
508314564Sdim        });
509314564Sdim    return dict_sp;
510314564Sdim  } else if (element_name == "real") {
511314564Sdim    double value = 0.0;
512314564Sdim    node.GetElementTextAsFloat(value);
513314564Sdim    return StructuredData::ObjectSP(new StructuredData::Float(value));
514314564Sdim  } else if (element_name == "integer") {
515314564Sdim    uint64_t value = 0;
516314564Sdim    node.GetElementTextAsUnsigned(value, 0, 0);
517314564Sdim    return StructuredData::ObjectSP(new StructuredData::Integer(value));
518314564Sdim  } else if ((element_name == "string") || (element_name == "data") ||
519314564Sdim             (element_name == "date")) {
520314564Sdim    std::string text;
521314564Sdim    node.GetElementText(text);
522314564Sdim    return StructuredData::ObjectSP(
523314564Sdim        new StructuredData::String(std::move(text)));
524314564Sdim  } else if (element_name == "true") {
525314564Sdim    return StructuredData::ObjectSP(new StructuredData::Boolean(true));
526314564Sdim  } else if (element_name == "false") {
527314564Sdim    return StructuredData::ObjectSP(new StructuredData::Boolean(false));
528314564Sdim  }
529314564Sdim  return StructuredData::ObjectSP(new StructuredData::Null());
530285101Semaste}
531314564Sdim}
532285101Semaste#endif
533285101Semaste
534314564SdimStructuredData::ObjectSP ApplePropertyList::GetStructuredData() {
535314564Sdim  StructuredData::ObjectSP root_sp;
536360784Sdim#if LLDB_ENABLE_LIBXML2
537314564Sdim  if (IsValid()) {
538314564Sdim    return CreatePlistValue(m_dict_node);
539314564Sdim  }
540285101Semaste#endif
541314564Sdim  return root_sp;
542285101Semaste}
543