Coverage Report

Created: 2025-11-11 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libxlsxwriter/src/relationships.c
Line
Count
Source
1
/*****************************************************************************
2
 * relationships - A library for creating Excel XLSX relationships files.
3
 *
4
 * Used in conjunction with the libxlsxwriter library.
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
7
 * Copyright 2014-2025, John McNamara, jmcnamara@cpan.org.
8
 *
9
 */
10
11
#include <string.h>
12
#include "xlsxwriter/xmlwriter.h"
13
#include "xlsxwriter/relationships.h"
14
#include "xlsxwriter/utility.h"
15
16
/*
17
 * Forward declarations.
18
 */
19
20
/*****************************************************************************
21
 *
22
 * Private functions.
23
 *
24
 ****************************************************************************/
25
26
/*
27
 * Create a new relationships object.
28
 */
29
lxw_relationships *
30
lxw_relationships_new(void)
31
2.63k
{
32
2.63k
    lxw_relationships *rels = calloc(1, sizeof(lxw_relationships));
33
2.63k
    GOTO_LABEL_ON_MEM_ERROR(rels, mem_error);
34
35
2.63k
    rels->relationships = calloc(1, sizeof(struct lxw_rel_tuples));
36
2.63k
    GOTO_LABEL_ON_MEM_ERROR(rels->relationships, mem_error);
37
2.63k
    STAILQ_INIT(rels->relationships);
38
39
2.63k
    return rels;
40
41
0
mem_error:
42
0
    lxw_free_relationships(rels);
43
0
    return NULL;
44
2.63k
}
45
46
/*
47
 * Free a relationships object.
48
 */
49
void
50
lxw_free_relationships(lxw_relationships *rels)
51
2.63k
{
52
2.63k
    lxw_rel_tuple *relationship;
53
54
2.63k
    if (!rels)
55
0
        return;
56
57
2.63k
    if (rels->relationships) {
58
11.7k
        while (!STAILQ_EMPTY(rels->relationships)) {
59
9.10k
            relationship = STAILQ_FIRST(rels->relationships);
60
9.10k
            STAILQ_REMOVE_HEAD(rels->relationships, list_pointers);
61
9.10k
            free(relationship->type);
62
9.10k
            free(relationship->target);
63
9.10k
            free(relationship->target_mode);
64
9.10k
            free(relationship);
65
9.10k
        }
66
67
2.63k
        free(rels->relationships);
68
2.63k
    }
69
70
2.63k
    free(rels);
71
2.63k
}
72
73
/*****************************************************************************
74
 *
75
 * XML functions.
76
 *
77
 ****************************************************************************/
78
79
/*
80
 * Write the XML declaration.
81
 */
82
STATIC void
83
_relationships_xml_declaration(lxw_relationships *self)
84
2.63k
{
85
2.63k
    lxw_xml_declaration(self->file);
86
2.63k
}
87
88
/*
89
 * Write the <Relationship> element.
90
 */
91
STATIC void
92
_write_relationship(lxw_relationships *self, const char *type,
93
                    const char *target, const char *target_mode)
94
9.10k
{
95
9.10k
    struct xml_attribute_list attributes;
96
9.10k
    struct xml_attribute *attribute;
97
9.10k
    char r_id[LXW_MAX_ATTRIBUTE_LENGTH] = { 0 };
98
99
9.10k
    self->rel_id++;
100
9.10k
    lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", self->rel_id);
101
102
9.10k
    LXW_INIT_ATTRIBUTES();
103
9.10k
    LXW_PUSH_ATTRIBUTES_STR("Id", r_id);
104
9.10k
    LXW_PUSH_ATTRIBUTES_STR("Type", type);
105
9.10k
    LXW_PUSH_ATTRIBUTES_STR("Target", target);
106
107
9.10k
    if (target_mode)
108
0
        LXW_PUSH_ATTRIBUTES_STR("TargetMode", target_mode);
109
110
9.10k
    lxw_xml_empty_tag(self->file, "Relationship", &attributes);
111
112
9.10k
    LXW_FREE_ATTRIBUTES();
113
9.10k
}
114
115
/*
116
 * Write the <Relationships> element.
117
 */
118
STATIC void
119
_write_relationships(lxw_relationships *self)
120
2.63k
{
121
2.63k
    struct xml_attribute_list attributes;
122
2.63k
    struct xml_attribute *attribute;
123
2.63k
    lxw_rel_tuple *rel;
124
125
2.63k
    LXW_INIT_ATTRIBUTES();
126
2.63k
    LXW_PUSH_ATTRIBUTES_STR("xmlns", LXW_SCHEMA_PACKAGE);
127
128
2.63k
    lxw_xml_start_tag(self->file, "Relationships", &attributes);
129
130
9.10k
    STAILQ_FOREACH(rel, self->relationships, list_pointers) {
131
9.10k
        _write_relationship(self, rel->type, rel->target, rel->target_mode);
132
9.10k
    }
133
134
2.63k
    LXW_FREE_ATTRIBUTES();
135
2.63k
}
136
137
/*****************************************************************************
138
 *
139
 * XML file assembly functions.
140
 *
141
 ****************************************************************************/
142
143
/*
144
 * Assemble and write the XML file.
145
 */
146
void
147
lxw_relationships_assemble_xml_file(lxw_relationships *self)
148
2.63k
{
149
    /* Write the XML declaration. */
150
2.63k
    _relationships_xml_declaration(self);
151
152
2.63k
    _write_relationships(self);
153
154
    /* Close the relationships tag. */
155
2.63k
    lxw_xml_end_tag(self->file, "Relationships");
156
2.63k
}
157
158
/*
159
 * Add a generic container relationship to XLSX .rels xml files.
160
 */
161
STATIC void
162
_add_relationship(lxw_relationships *self, const char *schema,
163
                  const char *type, const char *target,
164
                  const char *target_mode)
165
9.10k
{
166
9.10k
    lxw_rel_tuple *relationship;
167
168
9.10k
    if (!schema || !type || !target)
169
0
        return;
170
171
9.10k
    relationship = calloc(1, sizeof(lxw_rel_tuple));
172
9.10k
    GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error);
173
174
9.10k
    relationship->type = calloc(1, LXW_MAX_ATTRIBUTE_LENGTH);
175
9.10k
    GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error);
176
177
    /* Add the schema to the relationship type. */
178
9.10k
    lxw_snprintf(relationship->type, LXW_MAX_ATTRIBUTE_LENGTH, "%s%s",
179
9.10k
                 schema, type);
180
181
9.10k
    relationship->target = lxw_strdup(target);
182
9.10k
    GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error);
183
184
9.10k
    if (target_mode) {
185
0
        relationship->target_mode = lxw_strdup(target_mode);
186
0
        GOTO_LABEL_ON_MEM_ERROR(relationship->target_mode, mem_error);
187
0
    }
188
189
9.10k
    STAILQ_INSERT_TAIL(self->relationships, relationship, list_pointers);
190
191
9.10k
    return;
192
193
0
mem_error:
194
0
    if (relationship) {
195
0
        free(relationship->type);
196
0
        free(relationship->target);
197
0
        free(relationship->target_mode);
198
0
        free(relationship);
199
0
    }
200
0
}
201
202
/*****************************************************************************
203
 *
204
 * Public functions.
205
 *
206
 ****************************************************************************/
207
208
/*
209
 * Add a document relationship to XLSX .rels xml files.
210
 */
211
void
212
lxw_add_document_relationship(lxw_relationships *self, const char *type,
213
                              const char *target)
214
7.78k
{
215
7.78k
    _add_relationship(self, LXW_SCHEMA_DOCUMENT, type, target, NULL);
216
7.78k
}
217
218
/*
219
 * Add a package relationship to XLSX .rels xml files.
220
 */
221
void
222
lxw_add_package_relationship(lxw_relationships *self, const char *type,
223
                             const char *target)
224
1.31k
{
225
1.31k
    _add_relationship(self, LXW_SCHEMA_PACKAGE, type, target, NULL);
226
1.31k
}
227
228
/*
229
 * Add a MS schema package relationship to XLSX .rels xml files.
230
 */
231
void
232
lxw_add_ms_package_relationship(lxw_relationships *self, const char *type,
233
                                const char *target)
234
0
{
235
0
    _add_relationship(self, LXW_SCHEMA_MS, type, target, NULL);
236
0
}
237
238
/*
239
 * Add a worksheet relationship to sheet .rels xml files.
240
 */
241
void
242
lxw_add_worksheet_relationship(lxw_relationships *self, const char *type,
243
                               const char *target, const char *target_mode)
244
0
{
245
0
    _add_relationship(self, LXW_SCHEMA_DOCUMENT, type, target, target_mode);
246
0
}
247
248
/*
249
 * Add a richValue relationship to sheet .rels xml files.
250
 */
251
void
252
lxw_add_rich_value_relationship(lxw_relationships *self)
253
0
{
254
0
    _add_relationship(self,
255
0
                      "http://schemas.microsoft.com/office/2022/10/relationships/",
256
0
                      "richValueRel", "richData/richValueRel.xml", NULL);
257
0
    _add_relationship(self,
258
0
                      "http://schemas.microsoft.com/office/2017/06/relationships/",
259
0
                      "rdRichValue", "richData/rdrichvalue.xml", NULL);
260
0
    _add_relationship(self,
261
0
                      "http://schemas.microsoft.com/office/2017/06/relationships/",
262
0
                      "rdRichValueStructure",
263
0
                      "richData/rdrichvaluestructure.xml", NULL);
264
0
    _add_relationship(self,
265
0
                      "http://schemas.microsoft.com/office/2017/06/relationships/",
266
0
                      "rdRichValueTypes", "richData/rdRichValueTypes.xml",
267
0
                      NULL);
268
269
0
}