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