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