/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 | } |