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