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