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