Coverage Report

Created: 2026-03-31 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pacemaker/lib/common/xml_comment.c
Line
Count
Source
1
/*
2
 * Copyright 2024-2025 the Pacemaker project contributors
3
 *
4
 * The version control history for this file may have further details.
5
 *
6
 * This source code is licensed under the GNU Lesser General Public License
7
 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8
 */
9
10
#include <crm_internal.h>
11
12
#include <stdbool.h>                    // bool, false
13
#include <stdio.h>                      // NULL
14
15
#include <libxml/tree.h>                // xmlDoc, xmlNode, etc.
16
#include <libxml/xmlstring.h>           // xmlChar
17
18
#include "crmcommon_private.h"
19
20
/*!
21
 * \internal
22
 * \brief Create a new XML comment belonging to a given document
23
 *
24
 * \param[in] doc      Document that new comment will belong to
25
 * \param[in] content  Comment content
26
 *
27
 * \return Newly created XML comment (guaranteed not to be \c NULL)
28
 */
29
xmlNode *
30
pcmk__xc_create(xmlDoc *doc, const char *content)
31
0
{
32
0
    xmlNode *node = NULL;
33
34
    // Pacemaker typically assumes every xmlNode has a doc
35
0
    pcmk__assert(doc != NULL);
36
37
0
    node = xmlNewDocComment(doc, (const xmlChar *) content);
38
0
    pcmk__mem_assert(node);
39
0
    pcmk__xml_new_private_data(node);
40
0
    return node;
41
0
}
42
43
/*!
44
 * \internal
45
 * \brief Check whether two comments have matching content (case-insensitive)
46
 *
47
 * \param[in] comment1  First comment node to compare
48
 * \param[in] comment2  Second comment node to compare
49
 *
50
 * \return \c true if \p comment1 and \p comment2 have matching content (by
51
 *         case-insensitive string comparison), or \c false otherwise
52
 */
53
bool
54
pcmk__xc_matches(const xmlNode *comment1, const xmlNode *comment2)
55
0
{
56
0
    pcmk__assert((comment1 != NULL) && (comment1->type == XML_COMMENT_NODE)
57
0
                 && (comment2 != NULL) && (comment2->type == XML_COMMENT_NODE));
58
59
0
    return pcmk__str_eq((const char *) comment1->content,
60
0
                        (const char *) comment2->content, pcmk__str_casei);
61
0
}
62
63
/*!
64
 * \internal
65
 * \brief Find a comment with matching content among children of specified XML
66
 *
67
 * \param[in] parent  XML whose children to search
68
 * \param[in] search  Comment whose content should be searched for
69
 *
70
 * \return Matching comment, or \c NULL if no match is found
71
 */
72
static xmlNode *
73
match_xc_child(const xmlNode *parent, const xmlNode *search)
74
0
{
75
0
    pcmk__assert((search != NULL) && (search->type == XML_COMMENT_NODE));
76
77
0
    for (xmlNode *child = pcmk__xml_first_child(parent); child != NULL;
78
0
         child = pcmk__xml_next(child)) {
79
80
0
        if (child->type != XML_COMMENT_NODE) {
81
0
            continue;
82
0
        }
83
84
0
        if (pcmk__xc_matches(child, search)) {
85
0
            return child;
86
0
        }
87
0
    }
88
89
0
    return NULL;
90
0
}
91
92
/*!
93
 * \internal
94
 * \brief Make one XML comment match another (in content)
95
 *
96
 * \param[in,out] parent   If \p target is NULL and this is not, add or update
97
 *                         comment child of this XML node that matches \p update
98
 * \param[in,out] target   If not NULL, update this XML comment node
99
 * \param[in]     update   Make comment content match this (must not be NULL)
100
 *
101
 * \note At least one of \parent and \target must be non-NULL
102
 */
103
void
104
pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
105
0
{
106
0
    CRM_CHECK(update != NULL, return);
107
0
    CRM_CHECK(update->type == XML_COMMENT_NODE, return);
108
109
0
    if (target == NULL) {
110
0
        target = match_xc_child(parent, update);
111
0
    }
112
113
0
    if (target == NULL) {
114
0
        pcmk__xml_copy(parent, update);
115
116
0
    } else if (!pcmk__str_eq((const char *)target->content, (const char *)update->content, pcmk__str_casei)) {
117
0
        xmlFree(target->content);
118
0
        target->content = xmlStrdup(update->content);
119
0
    }
120
0
}