XML.cpp revision 314564
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
15using namespace lldb;
16using namespace lldb_private;
17
18#pragma mark-- XMLDocument
19
20XMLDocument::XMLDocument() : m_document(nullptr) {}
21
22XMLDocument::~XMLDocument() { Clear(); }
23
24void 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
34bool XMLDocument::IsValid() const { return m_document != nullptr; }
35
36void 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
45bool 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
55bool 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
66XMLNode 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
82llvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); }
83
84bool XMLDocument::XMLEnabled() {
85#if defined(LIBXML2_DEFINED)
86  return true;
87#else
88  return false;
89#endif
90}
91
92#pragma mark-- XMLNode
93
94XMLNode::XMLNode() : m_node(nullptr) {}
95
96XMLNode::XMLNode(XMLNodeImpl node) : m_node(node) {}
97
98XMLNode::~XMLNode() {}
99
100void XMLNode::Clear() { m_node = nullptr; }
101
102XMLNode 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
113XMLNode 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
124XMLNode 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
136llvm::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
154void XMLNode::ForEachChildNode(NodeCallback const &callback) const {
155#if defined(LIBXML2_DEFINED)
156  if (IsValid())
157    GetChild().ForEachSiblingNode(callback);
158#endif
159}
160
161void 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
169void 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
178void 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
202void 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
215void 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
232void 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
261llvm::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
271bool 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
292bool 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
307bool 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
323bool 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
337XMLNode 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
352bool XMLNode::IsValid() const { return m_node != nullptr; }
353
354bool 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
362XMLNode 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
383ApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {}
384
385ApplePropertyList::ApplePropertyList(const char *path)
386    : m_xml_doc(), m_dict_node() {
387  ParseFile(path);
388}
389
390ApplePropertyList::~ApplePropertyList() {}
391
392llvm::StringRef ApplePropertyList::GetErrors() const {
393  return m_xml_doc.GetErrors();
394}
395
396bool 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
411bool ApplePropertyList::IsValid() const { return (bool)m_dict_node; }
412
413bool 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
421XMLNode 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
444bool 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
465namespace {
466
467StructuredData::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
523StructuredData::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