Commit | Line | Data |
---|---|---|
3719602c PC |
1 | /* Copyright (C) 2010-2020 The RetroArch team |
2 | * | |
3 | * --------------------------------------------------------------------------------------- | |
4 | * The following license statement only applies to this file (rxml.c). | |
5 | * --------------------------------------------------------------------------------------- | |
6 | * | |
7 | * Permission is hereby granted, free of charge, | |
8 | * to any person obtaining a copy of this software and associated documentation files (the "Software"), | |
9 | * to deal in the Software without restriction, including without limitation the rights to | |
10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
11 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
16 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
19 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
21 | */ | |
22 | ||
23 | #include <boolean.h> | |
24 | #include <streams/file_stream.h> | |
25 | #include <compat/posix_string.h> | |
26 | #include <string/stdstring.h> | |
27 | ||
28 | #include <formats/rxml.h> | |
29 | ||
30 | #include "../../deps/yxml/yxml.h" | |
31 | ||
32 | #define BUFSIZE 4096 | |
33 | ||
34 | struct rxml_parse_buffer | |
35 | { | |
36 | rxml_node_t *stack[32]; | |
37 | char xml[BUFSIZE]; | |
38 | char val[BUFSIZE]; | |
39 | }; | |
40 | ||
41 | struct rxml_document | |
42 | { | |
43 | struct rxml_node *root_node; | |
44 | }; | |
45 | ||
46 | struct rxml_node *rxml_root_node(rxml_document_t *doc) | |
47 | { | |
48 | if (doc) | |
49 | return doc->root_node; | |
50 | return NULL; | |
51 | } | |
52 | ||
53 | static void rxml_free_node(struct rxml_node *node) | |
54 | { | |
55 | struct rxml_node *head = NULL; | |
56 | struct rxml_attrib_node *attrib_node_head = NULL; | |
57 | ||
58 | if (!node) | |
59 | return; | |
60 | ||
61 | for (head = node->children; head; ) | |
62 | { | |
63 | struct rxml_node *next_node = (struct rxml_node*)head->next; | |
64 | rxml_free_node(head); | |
65 | head = next_node; | |
66 | } | |
67 | ||
68 | for (attrib_node_head = node->attrib; attrib_node_head; ) | |
69 | { | |
70 | struct rxml_attrib_node *next_attrib = | |
71 | (struct rxml_attrib_node*)attrib_node_head->next; | |
72 | ||
73 | if (attrib_node_head->attrib) | |
74 | free(attrib_node_head->attrib); | |
75 | if (attrib_node_head->value) | |
76 | free(attrib_node_head->value); | |
77 | if (attrib_node_head) | |
78 | free(attrib_node_head); | |
79 | ||
80 | attrib_node_head = next_attrib; | |
81 | } | |
82 | ||
83 | if (node->name) | |
84 | free(node->name); | |
85 | if (node->data) | |
86 | free(node->data); | |
87 | if (node) | |
88 | free(node); | |
89 | } | |
90 | ||
91 | rxml_document_t *rxml_load_document(const char *path) | |
92 | { | |
93 | rxml_document_t *doc = NULL; | |
94 | char *memory_buffer = NULL; | |
95 | int64_t len = 0; | |
96 | RFILE *file = filestream_open(path, | |
97 | RETRO_VFS_FILE_ACCESS_READ, | |
98 | RETRO_VFS_FILE_ACCESS_HINT_NONE); | |
99 | if (!file) | |
100 | return NULL; | |
101 | ||
102 | len = filestream_get_size(file); | |
103 | memory_buffer = (char*)malloc((size_t)(len + 1)); | |
104 | if (!memory_buffer) | |
105 | goto error; | |
106 | ||
107 | memory_buffer[len] = '\0'; | |
108 | if (filestream_read(file, memory_buffer, len) != len) | |
109 | goto error; | |
110 | ||
111 | filestream_close(file); | |
112 | file = NULL; | |
113 | ||
114 | doc = rxml_load_document_string(memory_buffer); | |
115 | ||
116 | free(memory_buffer); | |
117 | return doc; | |
118 | ||
119 | error: | |
120 | free(memory_buffer); | |
121 | if (file) | |
122 | filestream_close(file); | |
123 | return NULL; | |
124 | } | |
125 | ||
126 | rxml_document_t *rxml_load_document_string(const char *str) | |
127 | { | |
128 | yxml_t x; | |
129 | rxml_document_t *doc = NULL; | |
130 | size_t stack_i = 0; | |
131 | size_t level = 0; | |
132 | int i = 0; | |
133 | char *valptr = NULL; | |
134 | rxml_node_t *node = NULL; | |
135 | struct rxml_attrib_node *attr = NULL; | |
136 | struct rxml_parse_buffer *buf = (struct rxml_parse_buffer*) | |
137 | malloc(sizeof(*buf)); | |
138 | if (!buf) | |
139 | return NULL; | |
140 | ||
141 | valptr = buf->val; | |
142 | doc = (rxml_document_t*)malloc(sizeof(*doc)); | |
143 | if (!doc) | |
144 | goto error; | |
145 | ||
146 | yxml_init(&x, buf->xml, BUFSIZE); | |
147 | ||
148 | for (; *str; ++str) | |
149 | { | |
150 | yxml_ret_t r = yxml_parse(&x, *str); | |
151 | ||
152 | if (r < 0) | |
153 | goto error; | |
154 | ||
155 | switch (r) | |
156 | { | |
157 | ||
158 | case YXML_ELEMSTART: | |
159 | if (node) | |
160 | { | |
161 | if (level > stack_i) | |
162 | { | |
163 | buf->stack[stack_i] = node; | |
164 | ++stack_i; | |
165 | ||
166 | node->children = (rxml_node_t*) | |
167 | malloc(sizeof(*node)); | |
168 | ||
169 | node->children->name = NULL; | |
170 | node->children->data = NULL; | |
171 | node->children->attrib = NULL; | |
172 | node->children->children = NULL; | |
173 | node->children->next = NULL; | |
174 | ||
175 | node = node->children; | |
176 | } | |
177 | else | |
178 | { | |
179 | node->next = (rxml_node_t*) | |
180 | malloc(sizeof(*node)); | |
181 | ||
182 | node->next->name = NULL; | |
183 | node->next->data = NULL; | |
184 | node->next->attrib = NULL; | |
185 | node->next->children = NULL; | |
186 | node->next->next = NULL; | |
187 | ||
188 | node = node->next; | |
189 | } | |
190 | } | |
191 | else | |
192 | node = doc->root_node = (rxml_node_t*) | |
193 | calloc(1, sizeof(*node)); | |
194 | ||
195 | if (node->name) | |
196 | free(node->name); | |
197 | node->name = strdup(x.elem); | |
198 | ||
199 | attr = NULL; | |
200 | valptr = buf->val; | |
201 | ||
202 | ++level; | |
203 | break; | |
204 | ||
205 | case YXML_ELEMEND: | |
206 | --level; | |
207 | ||
208 | if (valptr > buf->val) | |
209 | { | |
210 | *valptr = '\0'; | |
211 | ||
212 | /* Original code was broken here: | |
213 | * > If an element ended on two successive | |
214 | * iterations, on the second iteration | |
215 | * the 'data' for the *previous* node would | |
216 | * get overwritten | |
217 | * > This effectively erased the data for the | |
218 | * previous node, *and* caused a memory leak | |
219 | * (due to the double strdup()) | |
220 | * It seems the correct thing to do here is | |
221 | * only copy the data if the current 'level' | |
222 | * and 'stack index' are the same... */ | |
223 | if (level == stack_i) | |
224 | { | |
225 | if (node->data) | |
226 | free(node->data); | |
227 | node->data = strdup(buf->val); | |
228 | } | |
229 | ||
230 | valptr = buf->val; | |
231 | } | |
232 | ||
233 | if (level < stack_i) | |
234 | { | |
235 | --stack_i; | |
236 | node = buf->stack[stack_i]; | |
237 | } | |
238 | break; | |
239 | ||
240 | case YXML_CONTENT: | |
241 | for (i = 0; i < (int)sizeof(x.data) && x.data[i]; i++) | |
242 | { | |
243 | *valptr = x.data[i]; | |
244 | ++valptr; | |
245 | } | |
246 | break; | |
247 | ||
248 | case YXML_ATTRSTART: | |
249 | if (attr) | |
250 | attr = attr->next = (struct rxml_attrib_node*) | |
251 | calloc(1, sizeof(*attr)); | |
252 | else | |
253 | attr = node->attrib = (struct rxml_attrib_node*)calloc(1, sizeof(*attr)); | |
254 | ||
255 | if (attr->attrib) | |
256 | free(attr->attrib); | |
257 | attr->attrib = strdup(x.attr); | |
258 | ||
259 | valptr = buf->val; | |
260 | break; | |
261 | ||
262 | case YXML_ATTRVAL: | |
263 | for (i = 0; i < (int)sizeof(x.data) && x.data[i]; i++) | |
264 | { | |
265 | *valptr = x.data[i]; | |
266 | ++valptr; | |
267 | } | |
268 | break; | |
269 | ||
270 | case YXML_ATTREND: | |
271 | if (valptr > buf->val) | |
272 | { | |
273 | *valptr = '\0'; | |
274 | ||
275 | if (attr) | |
276 | { | |
277 | if (attr->value) | |
278 | free(attr->value); | |
279 | attr->value = strdup(buf->val); | |
280 | } | |
281 | ||
282 | valptr = buf->val; | |
283 | } | |
284 | break; | |
285 | ||
286 | default: | |
287 | break; | |
288 | } | |
289 | } | |
290 | ||
291 | free(buf); | |
292 | return doc; | |
293 | ||
294 | error: | |
295 | rxml_free_document(doc); | |
296 | free(buf); | |
297 | return NULL; | |
298 | } | |
299 | ||
300 | void rxml_free_document(rxml_document_t *doc) | |
301 | { | |
302 | if (!doc) | |
303 | return; | |
304 | ||
305 | if (doc->root_node) | |
306 | rxml_free_node(doc->root_node); | |
307 | ||
308 | free(doc); | |
309 | } | |
310 | ||
311 | const char *rxml_node_attrib(struct rxml_node *node, const char *attrib) | |
312 | { | |
313 | struct rxml_attrib_node *attribs = NULL; | |
314 | for (attribs = node->attrib; attribs; attribs = attribs->next) | |
315 | { | |
316 | if (string_is_equal(attrib, attribs->attrib)) | |
317 | return attribs->value; | |
318 | } | |
319 | ||
320 | return NULL; | |
321 | } |