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