Coverage Report

Created: 2022-01-15 10:30

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