diff options
Diffstat (limited to 'libraries/eina/src/lib/eina_simple_xml_parser.c')
-rw-r--r-- | libraries/eina/src/lib/eina_simple_xml_parser.c | 1070 |
1 files changed, 1070 insertions, 0 deletions
diff --git a/libraries/eina/src/lib/eina_simple_xml_parser.c b/libraries/eina/src/lib/eina_simple_xml_parser.c new file mode 100644 index 0000000..08a8259 --- /dev/null +++ b/libraries/eina/src/lib/eina_simple_xml_parser.c | |||
@@ -0,0 +1,1070 @@ | |||
1 | /* EINA - EFL data type library | ||
2 | * Copyright (C) 2011 Gustavo Sverzut Barbieri | ||
3 | * Cedric Bail | ||
4 | * | ||
5 | * This library is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU Lesser General Public | ||
7 | * License as published by the Free Software Foundation; either | ||
8 | * version 2.1 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * This library is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * Lesser General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Lesser General Public | ||
16 | * License along with this library; | ||
17 | * if not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | #ifdef HAVE_CONFIG_H | ||
21 | # include "config.h" | ||
22 | #endif | ||
23 | |||
24 | #ifdef HAVE_ALLOCA_H | ||
25 | # include <alloca.h> | ||
26 | #elif defined __GNUC__ | ||
27 | # define alloca __builtin_alloca | ||
28 | #elif defined _AIX | ||
29 | # define alloca __alloca | ||
30 | #elif defined _MSC_VER | ||
31 | # include <malloc.h> | ||
32 | # define alloca _alloca | ||
33 | #else | ||
34 | # include <stddef.h> | ||
35 | # ifdef __cplusplus | ||
36 | extern "C" | ||
37 | # endif | ||
38 | void *alloca (size_t); | ||
39 | #endif | ||
40 | |||
41 | #include <strings.h> | ||
42 | #include <string.h> | ||
43 | #include <ctype.h> | ||
44 | |||
45 | #ifdef HAVE_EVIL | ||
46 | # include <Evil.h> | ||
47 | #endif | ||
48 | |||
49 | #include "eina_private.h" | ||
50 | #include "eina_log.h" | ||
51 | #include "eina_mempool.h" | ||
52 | #include "eina_stringshare.h" | ||
53 | #include "eina_strbuf.h" | ||
54 | #include "eina_simple_xml_parser.h" | ||
55 | |||
56 | /*============================================================================* | ||
57 | * Local * | ||
58 | *============================================================================*/ | ||
59 | |||
60 | /** | ||
61 | * @cond LOCAL | ||
62 | */ | ||
63 | |||
64 | static Eina_Mempool *_eina_simple_xml_tag_mp = NULL; | ||
65 | static Eina_Mempool *_eina_simple_xml_attribute_mp = NULL; | ||
66 | static int _eina_simple_xml_log_dom = -1; | ||
67 | |||
68 | static const char EINA_MAGIC_SIMPLE_XML_TAG_STR[] = "Eina Simple XML Tag"; | ||
69 | static const char EINA_MAGIC_SIMPLE_XML_DATA_STR[] = "Eina Simple XML Data"; | ||
70 | static const char EINA_MAGIC_SIMPLE_XML_ATTRIBUTE_STR[] = "Eina Simple XML Attribute"; | ||
71 | |||
72 | #define EINA_MAGIC_CHECK_TAG(d, ...) \ | ||
73 | do { \ | ||
74 | if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_SIMPLE_XML_TAG)) \ | ||
75 | { \ | ||
76 | EINA_MAGIC_FAIL(d, EINA_MAGIC_SIMPLE_XML_TAG); \ | ||
77 | return __VA_ARGS__; \ | ||
78 | } \ | ||
79 | } while(0) | ||
80 | |||
81 | #define EINA_MAGIC_CHECK_DATA(d, ...) \ | ||
82 | do { \ | ||
83 | if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_SIMPLE_XML_DATA)) \ | ||
84 | { \ | ||
85 | EINA_MAGIC_FAIL(d, EINA_MAGIC_SIMPLE_XML_DATA); \ | ||
86 | return __VA_ARGS__; \ | ||
87 | } \ | ||
88 | } while(0) | ||
89 | |||
90 | #define EINA_MAGIC_CHECK_ATTRIBUTE(d, ...) \ | ||
91 | do { \ | ||
92 | if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_SIMPLE_XML_ATTRIBUTE)) \ | ||
93 | { \ | ||
94 | EINA_MAGIC_FAIL(d, EINA_MAGIC_SIMPLE_XML_ATTRIBUTE); \ | ||
95 | return __VA_ARGS__; \ | ||
96 | } \ | ||
97 | } while(0) | ||
98 | |||
99 | |||
100 | #ifndef EINA_LOG_COLOR_DEFAULT | ||
101 | #define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN | ||
102 | #endif | ||
103 | |||
104 | #ifdef ERR | ||
105 | #undef ERR | ||
106 | #endif | ||
107 | #define ERR(...) EINA_LOG_DOM_ERR(_eina_simple_xml_log_dom, __VA_ARGS__) | ||
108 | |||
109 | #ifdef WRN | ||
110 | #undef WRN | ||
111 | #endif | ||
112 | #define WRN(...) EINA_LOG_DOM_WARN(_eina_simple_xml_log_dom, __VA_ARGS__) | ||
113 | |||
114 | #ifdef DBG | ||
115 | #undef DBG | ||
116 | #endif | ||
117 | #define DBG(...) EINA_LOG_DOM_DBG(_eina_simple_xml_log_dom, __VA_ARGS__) | ||
118 | |||
119 | |||
120 | static inline const char * | ||
121 | _eina_simple_xml_whitespace_find(const char *itr, const char *itr_end) | ||
122 | { | ||
123 | for (; itr < itr_end; itr++) | ||
124 | if (isspace(*itr)) break; | ||
125 | return itr; | ||
126 | } | ||
127 | |||
128 | static inline const char * | ||
129 | _eina_simple_xml_whitespace_skip(const char *itr, const char *itr_end) | ||
130 | { | ||
131 | for (; itr < itr_end; itr++) | ||
132 | if (!isspace(*itr)) break; | ||
133 | return itr; | ||
134 | } | ||
135 | |||
136 | static inline const char * | ||
137 | _eina_simple_xml_whitespace_unskip(const char *itr, const char *itr_start) | ||
138 | { | ||
139 | for (itr--; itr > itr_start; itr--) | ||
140 | if (!isspace(*itr)) break; | ||
141 | return itr + 1; | ||
142 | } | ||
143 | |||
144 | static inline const char * | ||
145 | _eina_simple_xml_tag_start_find(const char *itr, const char *itr_end) | ||
146 | { | ||
147 | return memchr(itr, '<', itr_end - itr); | ||
148 | } | ||
149 | |||
150 | static inline const char * | ||
151 | _eina_simple_xml_tag_end_find(const char *itr, const char *itr_end) | ||
152 | { | ||
153 | for (; itr < itr_end; itr++) | ||
154 | if ((*itr == '>') || (*itr == '<')) /* consider < also ends a tag */ | ||
155 | return itr; | ||
156 | return NULL; | ||
157 | } | ||
158 | |||
159 | /** | ||
160 | * @endcond | ||
161 | */ | ||
162 | |||
163 | /*============================================================================* | ||
164 | * Global * | ||
165 | *============================================================================*/ | ||
166 | |||
167 | |||
168 | /** | ||
169 | * @internal | ||
170 | * @brief Initialize the simple xml parser module. | ||
171 | * | ||
172 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
173 | * | ||
174 | * This function sets up the simple xml parser module of Eina. It is called by | ||
175 | * eina_init(). | ||
176 | * | ||
177 | * @see eina_init() | ||
178 | */ | ||
179 | Eina_Bool | ||
180 | eina_simple_xml_init(void) | ||
181 | { | ||
182 | const char *choice, *tmp; | ||
183 | |||
184 | _eina_simple_xml_log_dom = eina_log_domain_register("eina_simple_xml", | ||
185 | EINA_LOG_COLOR_DEFAULT); | ||
186 | if (_eina_simple_xml_log_dom < 0) | ||
187 | { | ||
188 | EINA_LOG_ERR("Could not register log domain: eina_simple_xml"); | ||
189 | return EINA_FALSE; | ||
190 | } | ||
191 | |||
192 | #ifdef EINA_DEFAULT_MEMPOOL | ||
193 | choice = "pass_through"; | ||
194 | #else | ||
195 | choice = "chained_mempool"; | ||
196 | #endif | ||
197 | tmp = getenv("EINA_MEMPOOL"); | ||
198 | if (tmp && tmp[0]) | ||
199 | choice = tmp; | ||
200 | |||
201 | _eina_simple_xml_tag_mp = eina_mempool_add | ||
202 | (choice, "simple_xml_tag", NULL, | ||
203 | sizeof(Eina_Simple_XML_Node_Tag), 320); | ||
204 | if (!_eina_simple_xml_tag_mp) | ||
205 | { | ||
206 | ERR("Mempool for simple_xml_tag cannot be allocated in init."); | ||
207 | goto on_init_fail; | ||
208 | } | ||
209 | |||
210 | _eina_simple_xml_attribute_mp = eina_mempool_add | ||
211 | (choice, "simple_xml_attribute", NULL, | ||
212 | sizeof(Eina_Simple_XML_Attribute), 80); | ||
213 | if (!_eina_simple_xml_attribute_mp) | ||
214 | { | ||
215 | ERR("Mempool for simple_xml_attribute cannot be allocated in init."); | ||
216 | eina_mempool_del(_eina_simple_xml_tag_mp); | ||
217 | goto on_init_fail; | ||
218 | } | ||
219 | |||
220 | #define EMS(n) eina_magic_string_static_set(n, n ## _STR) | ||
221 | EMS(EINA_MAGIC_SIMPLE_XML_TAG); | ||
222 | EMS(EINA_MAGIC_SIMPLE_XML_DATA); | ||
223 | EMS(EINA_MAGIC_SIMPLE_XML_ATTRIBUTE); | ||
224 | #undef EMS | ||
225 | |||
226 | return EINA_TRUE; | ||
227 | |||
228 | on_init_fail: | ||
229 | eina_log_domain_unregister(_eina_simple_xml_log_dom); | ||
230 | _eina_simple_xml_log_dom = -1; | ||
231 | return EINA_FALSE; | ||
232 | } | ||
233 | |||
234 | /** | ||
235 | * @internal | ||
236 | * @brief Shut down the simple xml parser module. | ||
237 | * | ||
238 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
239 | * | ||
240 | * This function shuts down the simple xml parser module set | ||
241 | * up by eina_simple_xml_init(). It is called by | ||
242 | * eina_shutdown(). | ||
243 | * | ||
244 | * @see eina_shutdown() | ||
245 | */ | ||
246 | Eina_Bool | ||
247 | eina_simple_xml_shutdown(void) | ||
248 | { | ||
249 | eina_mempool_del(_eina_simple_xml_attribute_mp); | ||
250 | eina_mempool_del(_eina_simple_xml_tag_mp); | ||
251 | |||
252 | eina_log_domain_unregister(_eina_simple_xml_log_dom); | ||
253 | _eina_simple_xml_log_dom = -1; | ||
254 | return EINA_TRUE; | ||
255 | } | ||
256 | |||
257 | |||
258 | /*============================================================================* | ||
259 | * API * | ||
260 | *============================================================================*/ | ||
261 | |||
262 | |||
263 | EAPI Eina_Bool | ||
264 | eina_simple_xml_parse(const char *buf, unsigned buflen, Eina_Bool strip, Eina_Simple_XML_Cb func, const void *data) | ||
265 | { | ||
266 | const char *itr = buf, *itr_end = buf + buflen; | ||
267 | |||
268 | if (!buf) return EINA_FALSE; | ||
269 | if (!func) return EINA_FALSE; | ||
270 | |||
271 | #define CB(type, start, end) \ | ||
272 | do \ | ||
273 | { \ | ||
274 | size_t _sz = end - start; \ | ||
275 | Eina_Bool _ret; \ | ||
276 | _ret = func((void*)data, type, start, start - buf, _sz); \ | ||
277 | if (!_ret) return EINA_FALSE; \ | ||
278 | } \ | ||
279 | while (0) | ||
280 | |||
281 | while (itr < itr_end) | ||
282 | { | ||
283 | if (itr[0] == '<') | ||
284 | { | ||
285 | if (itr + 1 >= itr_end) | ||
286 | { | ||
287 | CB(EINA_SIMPLE_XML_ERROR, itr, itr_end); | ||
288 | return EINA_FALSE; | ||
289 | } | ||
290 | else | ||
291 | { | ||
292 | Eina_Simple_XML_Type type; | ||
293 | size_t toff; | ||
294 | const char *p; | ||
295 | |||
296 | if (itr[1] == '/') | ||
297 | { | ||
298 | type = EINA_SIMPLE_XML_CLOSE; | ||
299 | toff = 1; | ||
300 | } | ||
301 | else if (itr[1] == '?') | ||
302 | { | ||
303 | type = EINA_SIMPLE_XML_PROCESSING; | ||
304 | toff = 1; | ||
305 | } | ||
306 | else if (itr[1] == '!') | ||
307 | { | ||
308 | if ((itr + sizeof("<!DOCTYPE>") - 1 < itr_end) && | ||
309 | (!memcmp(itr + 2, "DOCTYPE", | ||
310 | sizeof("DOCTYPE") - 1)) && | ||
311 | ((itr[2 + sizeof("DOCTYPE") - 1] == '>') || | ||
312 | (isspace(itr[2 + sizeof("DOCTYPE") - 1])))) | ||
313 | { | ||
314 | type = EINA_SIMPLE_XML_DOCTYPE; | ||
315 | toff = sizeof("!DOCTYPE") - 1; | ||
316 | } | ||
317 | else if ((itr + sizeof("<!---->") - 1 < itr_end) && | ||
318 | (!memcmp(itr + 2, "--", sizeof("--") - 1))) | ||
319 | { | ||
320 | type = EINA_SIMPLE_XML_COMMENT; | ||
321 | toff = sizeof("!--") - 1; | ||
322 | } | ||
323 | else if ((itr + sizeof("<![CDATA[]]>") - 1 < itr_end) && | ||
324 | (!memcmp(itr + 2, "[CDATA[", | ||
325 | sizeof("[CDATA[") - 1))) | ||
326 | { | ||
327 | type = EINA_SIMPLE_XML_CDATA; | ||
328 | toff = sizeof("![CDATA[") - 1; | ||
329 | } | ||
330 | else | ||
331 | { | ||
332 | type = EINA_SIMPLE_XML_OPEN; | ||
333 | toff = 0; | ||
334 | } | ||
335 | } | ||
336 | else | ||
337 | { | ||
338 | type = EINA_SIMPLE_XML_OPEN; | ||
339 | toff = 0; | ||
340 | } | ||
341 | |||
342 | p = _eina_simple_xml_tag_end_find(itr + 1 + toff, itr_end); | ||
343 | if (p) | ||
344 | { | ||
345 | if (type == EINA_SIMPLE_XML_CDATA) | ||
346 | { | ||
347 | /* must end with ]]> */ | ||
348 | while ((p) && (memcmp(p - 2, "]]>", 3))) | ||
349 | p = _eina_simple_xml_tag_end_find(p + 1, itr_end); | ||
350 | } | ||
351 | |||
352 | if ((p) && (*p == '<')) | ||
353 | { | ||
354 | type = EINA_SIMPLE_XML_ERROR; | ||
355 | toff = 0; | ||
356 | } | ||
357 | } | ||
358 | |||
359 | if (p) | ||
360 | { | ||
361 | const char *start, *end; | ||
362 | |||
363 | start = itr + 1 + toff; | ||
364 | end = p; | ||
365 | |||
366 | switch (type) | ||
367 | { | ||
368 | case EINA_SIMPLE_XML_OPEN: | ||
369 | if (p[-1] == '/') | ||
370 | { | ||
371 | type = EINA_SIMPLE_XML_OPEN_EMPTY; | ||
372 | end--; | ||
373 | } | ||
374 | break; | ||
375 | case EINA_SIMPLE_XML_CDATA: | ||
376 | if (!memcmp(p - 2, "]]", 2)) end -= 2; | ||
377 | break; | ||
378 | case EINA_SIMPLE_XML_PROCESSING: | ||
379 | if (p[-1] == '?') end--; | ||
380 | break; | ||
381 | case EINA_SIMPLE_XML_COMMENT: | ||
382 | if (!memcmp(p - 2, "--", 2)) end -= 2; | ||
383 | break; | ||
384 | case EINA_SIMPLE_XML_OPEN_EMPTY: | ||
385 | case EINA_SIMPLE_XML_CLOSE: | ||
386 | case EINA_SIMPLE_XML_DATA: | ||
387 | case EINA_SIMPLE_XML_ERROR: | ||
388 | case EINA_SIMPLE_XML_DOCTYPE: | ||
389 | case EINA_SIMPLE_XML_IGNORED: | ||
390 | break; | ||
391 | } | ||
392 | |||
393 | if ((strip) && (type != EINA_SIMPLE_XML_ERROR)) | ||
394 | { | ||
395 | start = _eina_simple_xml_whitespace_skip | ||
396 | (start, end); | ||
397 | end = _eina_simple_xml_whitespace_unskip | ||
398 | (end, start + 1); | ||
399 | } | ||
400 | |||
401 | CB(type, start, end); | ||
402 | |||
403 | if (type != EINA_SIMPLE_XML_ERROR) | ||
404 | itr = p + 1; | ||
405 | else | ||
406 | itr = p; | ||
407 | } | ||
408 | else | ||
409 | { | ||
410 | CB(EINA_SIMPLE_XML_ERROR, itr, itr_end); | ||
411 | return EINA_FALSE; | ||
412 | } | ||
413 | } | ||
414 | } | ||
415 | else | ||
416 | { | ||
417 | const char *p, *end; | ||
418 | |||
419 | if (strip) | ||
420 | { | ||
421 | p = _eina_simple_xml_whitespace_skip(itr, itr_end); | ||
422 | if (p) | ||
423 | { | ||
424 | CB(EINA_SIMPLE_XML_IGNORED, itr, p); | ||
425 | itr = p; | ||
426 | } | ||
427 | } | ||
428 | |||
429 | p = _eina_simple_xml_tag_start_find(itr, itr_end); | ||
430 | if (!p) p = itr_end; | ||
431 | |||
432 | end = p; | ||
433 | if (strip) | ||
434 | end = _eina_simple_xml_whitespace_unskip(end, itr); | ||
435 | |||
436 | if (itr != end) | ||
437 | CB(EINA_SIMPLE_XML_DATA, itr, end); | ||
438 | |||
439 | if ((strip) && (end < p)) | ||
440 | CB(EINA_SIMPLE_XML_IGNORED, end, p); | ||
441 | |||
442 | itr = p; | ||
443 | } | ||
444 | } | ||
445 | |||
446 | #undef CB | ||
447 | |||
448 | return EINA_TRUE; | ||
449 | } | ||
450 | |||
451 | EAPI const char * | ||
452 | eina_simple_xml_tag_attributes_find(const char *buf, unsigned buflen) | ||
453 | { | ||
454 | const char *itr = buf, *itr_end = buf + buflen; | ||
455 | |||
456 | for (; itr < itr_end; itr++) | ||
457 | { | ||
458 | if (!isspace(*itr)) | ||
459 | { | ||
460 | /* user skip tagname and already gave it the attributes */ | ||
461 | if (*itr == '=') | ||
462 | return buf; | ||
463 | } | ||
464 | else | ||
465 | { | ||
466 | itr = _eina_simple_xml_whitespace_skip(itr + 1, itr_end); | ||
467 | if (itr == itr_end) | ||
468 | return NULL; | ||
469 | return itr; | ||
470 | } | ||
471 | } | ||
472 | |||
473 | return NULL; | ||
474 | } | ||
475 | |||
476 | EAPI Eina_Bool | ||
477 | eina_simple_xml_attributes_parse(const char *buf, unsigned buflen, Eina_Simple_XML_Attribute_Cb func, const void *data) | ||
478 | { | ||
479 | const char *itr = buf, *itr_end = buf + buflen; | ||
480 | char *tmpbuf = alloca(buflen + 1); | ||
481 | |||
482 | if (!buf) return EINA_FALSE; | ||
483 | if (!func) return EINA_FALSE; | ||
484 | |||
485 | while (itr < itr_end) | ||
486 | { | ||
487 | const char *p = _eina_simple_xml_whitespace_skip(itr, itr_end); | ||
488 | const char *key, *key_end, *value, *value_end; | ||
489 | char *tval; | ||
490 | |||
491 | if (p == itr_end) return EINA_TRUE; | ||
492 | |||
493 | key = p; | ||
494 | for (key_end = key; key_end < itr_end; key_end++) | ||
495 | if ((*key_end == '=') || (isspace(*key_end))) break; | ||
496 | if (key_end == itr_end) return EINA_FALSE; | ||
497 | if (key_end == key) continue; | ||
498 | |||
499 | if (*key_end == '=') value = key_end + 1; | ||
500 | else | ||
501 | { | ||
502 | value = memchr(key_end, '=', itr_end - key_end); | ||
503 | if (!value) return EINA_FALSE; | ||
504 | value++; | ||
505 | } | ||
506 | for (; value < itr_end; value++) | ||
507 | if (!isspace(*value)) break; | ||
508 | if (value == itr_end) return EINA_FALSE; | ||
509 | |||
510 | if ((*value == '"') || (*value == '\'')) | ||
511 | { | ||
512 | value_end = memchr(value + 1, *value, itr_end - value); | ||
513 | if (!value_end) return EINA_FALSE; | ||
514 | value++; | ||
515 | } | ||
516 | else | ||
517 | { | ||
518 | value_end = _eina_simple_xml_whitespace_find(value, itr_end); | ||
519 | } | ||
520 | |||
521 | memcpy(tmpbuf, key, key_end - key); | ||
522 | tmpbuf[key_end - key] = '\0'; | ||
523 | |||
524 | tval = tmpbuf + (key_end - key) + 1; | ||
525 | memcpy(tval, value, value_end - value); | ||
526 | tval[value_end - value] = '\0'; | ||
527 | |||
528 | if (!func((void*)data, tmpbuf, tval)) | ||
529 | return EINA_FALSE; | ||
530 | |||
531 | itr = value_end + 1; | ||
532 | } | ||
533 | return EINA_TRUE; | ||
534 | } | ||
535 | |||
536 | /* Node loader *************************************************************/ | ||
537 | |||
538 | EAPI Eina_Simple_XML_Attribute * | ||
539 | eina_simple_xml_attribute_new(Eina_Simple_XML_Node_Tag *parent, const char *key, const char *value) | ||
540 | { | ||
541 | Eina_Simple_XML_Attribute *attr; | ||
542 | |||
543 | if (!key) return NULL; | ||
544 | |||
545 | attr = eina_mempool_malloc(_eina_simple_xml_attribute_mp, sizeof(*attr)); | ||
546 | if (!attr) | ||
547 | { | ||
548 | ERR("could not allocate memory for attribute from mempool"); | ||
549 | return NULL; | ||
550 | } | ||
551 | |||
552 | EINA_MAGIC_SET(attr, EINA_MAGIC_SIMPLE_XML_ATTRIBUTE); | ||
553 | attr->parent = parent; | ||
554 | attr->key = eina_stringshare_add(key); | ||
555 | attr->value = eina_stringshare_add(value ? value : ""); | ||
556 | |||
557 | if (parent) | ||
558 | parent->attributes = eina_inlist_append | ||
559 | (parent->attributes, EINA_INLIST_GET(attr)); | ||
560 | |||
561 | return attr; | ||
562 | } | ||
563 | |||
564 | EAPI void | ||
565 | eina_simple_xml_attribute_free(Eina_Simple_XML_Attribute *attr) | ||
566 | { | ||
567 | EINA_MAGIC_CHECK_ATTRIBUTE(attr); | ||
568 | |||
569 | if (attr->parent) | ||
570 | attr->parent->attributes = eina_inlist_remove | ||
571 | (attr->parent->attributes, EINA_INLIST_GET(attr)); | ||
572 | |||
573 | eina_stringshare_del(attr->key); | ||
574 | eina_stringshare_del(attr->value); | ||
575 | EINA_MAGIC_SET(attr, EINA_MAGIC_NONE); | ||
576 | eina_mempool_free(_eina_simple_xml_attribute_mp, attr); | ||
577 | } | ||
578 | |||
579 | static void | ||
580 | _eina_simple_xml_node_data_free(Eina_Simple_XML_Node_Data *node) | ||
581 | { | ||
582 | if (node->base.parent) | ||
583 | node->base.parent->children = eina_inlist_remove | ||
584 | (node->base.parent->children, EINA_INLIST_GET(&node->base)); | ||
585 | |||
586 | EINA_MAGIC_SET(&node->base, EINA_MAGIC_NONE); | ||
587 | free(node); | ||
588 | } | ||
589 | |||
590 | EAPI Eina_Simple_XML_Node_Tag * | ||
591 | eina_simple_xml_node_tag_new(Eina_Simple_XML_Node_Tag *parent, const char *name) | ||
592 | { | ||
593 | Eina_Simple_XML_Node_Tag *n; | ||
594 | |||
595 | if (!name) return NULL; | ||
596 | |||
597 | n = eina_mempool_malloc(_eina_simple_xml_tag_mp, sizeof(*n)); | ||
598 | if (!n) | ||
599 | { | ||
600 | ERR("could not allocate memory for node from mempool"); | ||
601 | return NULL; | ||
602 | } | ||
603 | |||
604 | memset(n, 0, sizeof(*n)); | ||
605 | |||
606 | EINA_MAGIC_SET(&n->base, EINA_MAGIC_SIMPLE_XML_TAG); | ||
607 | |||
608 | n->base.type = EINA_SIMPLE_XML_NODE_TAG; | ||
609 | n->base.parent = parent; | ||
610 | n->name = eina_stringshare_add(name); | ||
611 | |||
612 | if (parent) | ||
613 | parent->children = eina_inlist_append | ||
614 | (parent->children, EINA_INLIST_GET(&n->base)); | ||
615 | |||
616 | return n; | ||
617 | } | ||
618 | |||
619 | void | ||
620 | _eina_simple_xml_node_tag_free(Eina_Simple_XML_Node_Tag *tag) | ||
621 | { | ||
622 | while (tag->children) | ||
623 | { | ||
624 | Eina_Simple_XML_Node *n = EINA_INLIST_CONTAINER_GET | ||
625 | (tag->children, Eina_Simple_XML_Node); | ||
626 | if (n->type == EINA_SIMPLE_XML_NODE_TAG) | ||
627 | _eina_simple_xml_node_tag_free((Eina_Simple_XML_Node_Tag *)n); | ||
628 | else | ||
629 | _eina_simple_xml_node_data_free((Eina_Simple_XML_Node_Data *)n); | ||
630 | } | ||
631 | |||
632 | while (tag->attributes) | ||
633 | { | ||
634 | Eina_Simple_XML_Attribute *a = EINA_INLIST_CONTAINER_GET | ||
635 | (tag->attributes, Eina_Simple_XML_Attribute); | ||
636 | eina_simple_xml_attribute_free(a); | ||
637 | } | ||
638 | |||
639 | if (tag->base.parent) | ||
640 | tag->base.parent->children = eina_inlist_remove | ||
641 | (tag->base.parent->children, EINA_INLIST_GET(&tag->base)); | ||
642 | |||
643 | eina_stringshare_del(tag->name); | ||
644 | EINA_MAGIC_SET(&tag->base, EINA_MAGIC_NONE); | ||
645 | eina_mempool_free(_eina_simple_xml_tag_mp, tag); | ||
646 | } | ||
647 | |||
648 | EAPI void | ||
649 | eina_simple_xml_node_tag_free(Eina_Simple_XML_Node_Tag *tag) | ||
650 | { | ||
651 | EINA_MAGIC_CHECK_TAG(&tag->base); | ||
652 | if (tag->base.type != EINA_SIMPLE_XML_NODE_TAG) | ||
653 | { | ||
654 | ERR("expected tag node!"); | ||
655 | return; | ||
656 | } | ||
657 | _eina_simple_xml_node_tag_free(tag); | ||
658 | } | ||
659 | |||
660 | static Eina_Simple_XML_Node_Data * | ||
661 | _eina_simple_xml_node_data_new(Eina_Simple_XML_Node_Tag *parent, Eina_Simple_XML_Node_Type type, const char *content, unsigned length) | ||
662 | { | ||
663 | Eina_Simple_XML_Node_Data *n = malloc(sizeof(*n) + length + 1); | ||
664 | |||
665 | if (!content) return NULL; | ||
666 | |||
667 | if (!n) | ||
668 | { | ||
669 | ERR("could not allocate memory for node"); | ||
670 | return NULL; | ||
671 | } | ||
672 | |||
673 | EINA_MAGIC_SET(&n->base, EINA_MAGIC_SIMPLE_XML_DATA); | ||
674 | n->base.type = type; | ||
675 | n->base.parent = parent; | ||
676 | |||
677 | n->length = length; | ||
678 | memcpy(n->data, content, length); | ||
679 | n->data[length] = '\0'; | ||
680 | |||
681 | if (parent) | ||
682 | parent->children = eina_inlist_append | ||
683 | (parent->children, EINA_INLIST_GET(&n->base)); | ||
684 | |||
685 | return n; | ||
686 | } | ||
687 | |||
688 | EAPI Eina_Simple_XML_Node_Data * | ||
689 | eina_simple_xml_node_data_new(Eina_Simple_XML_Node_Tag *parent, const char *contents, size_t length) | ||
690 | { | ||
691 | return _eina_simple_xml_node_data_new | ||
692 | (parent, EINA_SIMPLE_XML_NODE_DATA, contents, length); | ||
693 | } | ||
694 | |||
695 | EAPI void | ||
696 | eina_simple_xml_node_data_free(Eina_Simple_XML_Node_Data *node) | ||
697 | { | ||
698 | EINA_MAGIC_CHECK_DATA(&node->base); | ||
699 | if (node->base.type != EINA_SIMPLE_XML_NODE_DATA) | ||
700 | { | ||
701 | ERR("expected node of type: data!"); | ||
702 | return; | ||
703 | } | ||
704 | _eina_simple_xml_node_data_free(node); | ||
705 | } | ||
706 | |||
707 | EAPI Eina_Simple_XML_Node_CData * | ||
708 | eina_simple_xml_node_cdata_new(Eina_Simple_XML_Node_Tag *parent, const char *contents, size_t length) | ||
709 | { | ||
710 | return _eina_simple_xml_node_data_new | ||
711 | (parent, EINA_SIMPLE_XML_NODE_CDATA, contents, length); | ||
712 | } | ||
713 | |||
714 | EAPI void | ||
715 | eina_simple_xml_node_cdata_free(Eina_Simple_XML_Node_Data *node) | ||
716 | { | ||
717 | EINA_MAGIC_CHECK_DATA(&node->base); | ||
718 | if (node->base.type != EINA_SIMPLE_XML_NODE_CDATA) | ||
719 | { | ||
720 | ERR("expected node of type: cdata!"); | ||
721 | return; | ||
722 | } | ||
723 | _eina_simple_xml_node_data_free(node); | ||
724 | } | ||
725 | |||
726 | EAPI Eina_Simple_XML_Node_Processing * | ||
727 | eina_simple_xml_node_processing_new(Eina_Simple_XML_Node_Tag *parent, const char *contents, size_t length) | ||
728 | { | ||
729 | return _eina_simple_xml_node_data_new | ||
730 | (parent, EINA_SIMPLE_XML_NODE_PROCESSING, contents, length); | ||
731 | } | ||
732 | |||
733 | EAPI void | ||
734 | eina_simple_xml_node_processing_free(Eina_Simple_XML_Node_Data *node) | ||
735 | { | ||
736 | EINA_MAGIC_CHECK_DATA(&node->base); | ||
737 | if (node->base.type != EINA_SIMPLE_XML_NODE_PROCESSING) | ||
738 | { | ||
739 | ERR("expected node of type: processing!"); | ||
740 | return; | ||
741 | } | ||
742 | _eina_simple_xml_node_data_free(node); | ||
743 | } | ||
744 | |||
745 | EAPI Eina_Simple_XML_Node_Doctype * | ||
746 | eina_simple_xml_node_doctype_new(Eina_Simple_XML_Node_Tag *parent, const char *contents, size_t length) | ||
747 | { | ||
748 | return _eina_simple_xml_node_data_new | ||
749 | (parent, EINA_SIMPLE_XML_NODE_DOCTYPE, contents, length); | ||
750 | } | ||
751 | |||
752 | EAPI void | ||
753 | eina_simple_xml_node_doctype_free(Eina_Simple_XML_Node_Data *node) | ||
754 | { | ||
755 | EINA_MAGIC_CHECK_DATA(&node->base); | ||
756 | if (node->base.type != EINA_SIMPLE_XML_NODE_DOCTYPE) | ||
757 | { | ||
758 | ERR("expected node of type: doctype!"); | ||
759 | return; | ||
760 | } | ||
761 | _eina_simple_xml_node_data_free(node); | ||
762 | } | ||
763 | |||
764 | EAPI Eina_Simple_XML_Node_Comment * | ||
765 | eina_simple_xml_node_comment_new(Eina_Simple_XML_Node_Tag *parent, const char *contents, size_t length) | ||
766 | { | ||
767 | return _eina_simple_xml_node_data_new | ||
768 | (parent, EINA_SIMPLE_XML_NODE_COMMENT, contents, length); | ||
769 | } | ||
770 | |||
771 | EAPI void | ||
772 | eina_simple_xml_node_comment_free(Eina_Simple_XML_Node_Data *node) | ||
773 | { | ||
774 | EINA_MAGIC_CHECK_DATA(&node->base); | ||
775 | if (node->base.type != EINA_SIMPLE_XML_NODE_COMMENT) | ||
776 | { | ||
777 | ERR("expected node of type: comment!"); | ||
778 | return; | ||
779 | } | ||
780 | _eina_simple_xml_node_data_free(node); | ||
781 | } | ||
782 | |||
783 | struct eina_simple_xml_node_load_ctxt | ||
784 | { | ||
785 | Eina_Simple_XML_Node_Root *root; | ||
786 | Eina_Simple_XML_Node_Tag *current; | ||
787 | }; | ||
788 | |||
789 | static Eina_Bool | ||
790 | _eina_simple_xml_attrs_parse(void *data, const char *key, const char *value) | ||
791 | { | ||
792 | Eina_Simple_XML_Node_Tag *n = data; | ||
793 | Eina_Simple_XML_Attribute *attr; | ||
794 | |||
795 | attr = eina_simple_xml_attribute_new(n, key, value); | ||
796 | return !!attr; | ||
797 | } | ||
798 | |||
799 | static Eina_Bool | ||
800 | _eina_simple_xml_node_parse(void *data, Eina_Simple_XML_Type type, const char *content, unsigned offset, unsigned length) | ||
801 | { | ||
802 | struct eina_simple_xml_node_load_ctxt *ctx = data; | ||
803 | |||
804 | switch (type) | ||
805 | { | ||
806 | case EINA_SIMPLE_XML_OPEN: | ||
807 | case EINA_SIMPLE_XML_OPEN_EMPTY: | ||
808 | { | ||
809 | Eina_Simple_XML_Node_Tag *n; | ||
810 | const char *name, *name_end, *attrs; | ||
811 | |||
812 | attrs = eina_simple_xml_tag_attributes_find(content, length); | ||
813 | if (!attrs) | ||
814 | name_end = content + length; | ||
815 | else | ||
816 | name_end = attrs; | ||
817 | |||
818 | name_end = _eina_simple_xml_whitespace_unskip(name_end, content); | ||
819 | |||
820 | name = eina_stringshare_add_length(content, name_end - content); | ||
821 | n = eina_simple_xml_node_tag_new(ctx->current, name); | ||
822 | eina_stringshare_del(name); | ||
823 | if (!n) return EINA_FALSE; | ||
824 | |||
825 | if (attrs) | ||
826 | eina_simple_xml_attributes_parse | ||
827 | (attrs, length - (attrs - content), | ||
828 | _eina_simple_xml_attrs_parse, n); | ||
829 | |||
830 | if (type == EINA_SIMPLE_XML_OPEN) | ||
831 | ctx->current = n; | ||
832 | } | ||
833 | break; | ||
834 | |||
835 | case EINA_SIMPLE_XML_CLOSE: | ||
836 | if (ctx->current->base.parent) | ||
837 | { | ||
838 | const char *end = _eina_simple_xml_whitespace_unskip | ||
839 | (content + length, content); | ||
840 | int len; | ||
841 | len = end - content; | ||
842 | if ((len == 0) /* </> closes the tag for us. */ || | ||
843 | ((eina_stringshare_strlen(ctx->current->name) == len) && | ||
844 | (memcmp(ctx->current->name, content, len) == 0))) | ||
845 | ctx->current = ctx->current->base.parent; | ||
846 | else | ||
847 | WRN("closed incorrect tag: '%.*s', '%s' was expected!", | ||
848 | len, content, ctx->current->name); | ||
849 | } | ||
850 | else | ||
851 | WRN("closed tag '%.*s' but already at document root!", | ||
852 | length, content); | ||
853 | break; | ||
854 | |||
855 | case EINA_SIMPLE_XML_DATA: | ||
856 | return !!eina_simple_xml_node_data_new | ||
857 | (ctx->current, content, length); | ||
858 | case EINA_SIMPLE_XML_CDATA: | ||
859 | return !!eina_simple_xml_node_cdata_new | ||
860 | (ctx->current, content, length); | ||
861 | case EINA_SIMPLE_XML_PROCESSING: | ||
862 | return !!eina_simple_xml_node_processing_new | ||
863 | (ctx->current, content, length); | ||
864 | case EINA_SIMPLE_XML_DOCTYPE: | ||
865 | return !!eina_simple_xml_node_doctype_new | ||
866 | (ctx->current, content, length); | ||
867 | case EINA_SIMPLE_XML_COMMENT: | ||
868 | return !!eina_simple_xml_node_comment_new | ||
869 | (ctx->current, content, length); | ||
870 | |||
871 | case EINA_SIMPLE_XML_ERROR: | ||
872 | ERR("parser error at offset %u-%u: %.*s", | ||
873 | offset, length, length, content); | ||
874 | break; | ||
875 | case EINA_SIMPLE_XML_IGNORED: | ||
876 | DBG("ignored contents at offset %u-%u: %.*s", | ||
877 | offset, length, length, content); | ||
878 | break; | ||
879 | } | ||
880 | |||
881 | return EINA_TRUE; | ||
882 | } | ||
883 | |||
884 | EAPI Eina_Simple_XML_Node_Root * | ||
885 | eina_simple_xml_node_load(const char *buf, unsigned buflen, Eina_Bool strip) | ||
886 | { | ||
887 | Eina_Simple_XML_Node_Root *root; | ||
888 | struct eina_simple_xml_node_load_ctxt ctx; | ||
889 | |||
890 | if (!buf) return NULL; | ||
891 | |||
892 | root = eina_mempool_malloc(_eina_simple_xml_tag_mp, sizeof(*root)); | ||
893 | if (!root) return NULL; | ||
894 | |||
895 | memset(root, 0, sizeof(*root)); | ||
896 | EINA_MAGIC_SET(&root->base, EINA_MAGIC_SIMPLE_XML_TAG); | ||
897 | root->base.type = EINA_SIMPLE_XML_NODE_ROOT; | ||
898 | |||
899 | ctx.root = root; | ||
900 | ctx.current = root; | ||
901 | eina_simple_xml_parse(buf, buflen, strip, _eina_simple_xml_node_parse, &ctx); | ||
902 | |||
903 | return root; | ||
904 | } | ||
905 | |||
906 | EAPI void | ||
907 | eina_simple_xml_node_root_free(Eina_Simple_XML_Node_Root *root) | ||
908 | { | ||
909 | if (!root) return; | ||
910 | EINA_MAGIC_CHECK_TAG(&root->base); | ||
911 | if (root->base.type != EINA_SIMPLE_XML_NODE_ROOT) | ||
912 | { | ||
913 | ERR("expected root node!"); | ||
914 | return; | ||
915 | } | ||
916 | _eina_simple_xml_node_tag_free(root); | ||
917 | } | ||
918 | |||
919 | static inline void | ||
920 | _eina_simple_xml_node_dump_indent(Eina_Strbuf *buf, const char *indent, unsigned level) | ||
921 | { | ||
922 | unsigned i, indent_len = strlen(indent); | ||
923 | for (i = 0; i < level; i++) | ||
924 | eina_strbuf_append_length(buf, indent, indent_len); | ||
925 | } | ||
926 | |||
927 | static void | ||
928 | _eina_simple_xml_node_tag_attributes_append(Eina_Strbuf *buf, Eina_Simple_XML_Node_Tag *tag) | ||
929 | { | ||
930 | Eina_Simple_XML_Attribute *a; | ||
931 | |||
932 | EINA_INLIST_FOREACH(tag->attributes, a) | ||
933 | eina_strbuf_append_printf(buf, " %s=\"%s\"", a->key, a->value); | ||
934 | } | ||
935 | |||
936 | static void _eina_simple_xml_node_dump(Eina_Strbuf *buf, Eina_Simple_XML_Node *node, const char *indent, unsigned level); | ||
937 | |||
938 | static void | ||
939 | _eina_simple_xml_node_children_dump(Eina_Strbuf *buf, Eina_Simple_XML_Node_Tag *tag, const char *indent, unsigned level) | ||
940 | { | ||
941 | Eina_Simple_XML_Node *node; | ||
942 | |||
943 | EINA_INLIST_FOREACH(tag->children, node) | ||
944 | _eina_simple_xml_node_dump(buf, node, indent, level); | ||
945 | } | ||
946 | |||
947 | static void | ||
948 | _eina_simple_xml_node_dump(Eina_Strbuf *buf, Eina_Simple_XML_Node *node, const char *indent, unsigned level) | ||
949 | { | ||
950 | switch (node->type) | ||
951 | { | ||
952 | case EINA_SIMPLE_XML_NODE_ROOT: | ||
953 | _eina_simple_xml_node_children_dump | ||
954 | (buf, (Eina_Simple_XML_Node_Tag *)node, indent, level); | ||
955 | break; | ||
956 | |||
957 | case EINA_SIMPLE_XML_NODE_TAG: | ||
958 | { | ||
959 | Eina_Simple_XML_Node_Tag *n = (Eina_Simple_XML_Node_Tag *)node; | ||
960 | |||
961 | if (indent) _eina_simple_xml_node_dump_indent(buf, indent, level); | ||
962 | |||
963 | eina_strbuf_append_char(buf, '<'); | ||
964 | eina_strbuf_append_length | ||
965 | (buf, n->name, eina_stringshare_strlen(n->name)); | ||
966 | |||
967 | if (n->attributes) | ||
968 | _eina_simple_xml_node_tag_attributes_append(buf, n); | ||
969 | |||
970 | if (n->children) | ||
971 | eina_strbuf_append_char(buf, '>'); | ||
972 | else | ||
973 | eina_strbuf_append_length(buf, "/>", sizeof("/>") - 1); | ||
974 | |||
975 | if (indent) eina_strbuf_append_char(buf, '\n'); | ||
976 | |||
977 | if (n->children) | ||
978 | { | ||
979 | _eina_simple_xml_node_children_dump(buf, n, indent, level + 1); | ||
980 | |||
981 | if (indent) | ||
982 | _eina_simple_xml_node_dump_indent(buf, indent, level); | ||
983 | |||
984 | eina_strbuf_append_length(buf, "</", sizeof("</") - 1); | ||
985 | eina_strbuf_append_length | ||
986 | (buf, n->name, eina_stringshare_strlen(n->name)); | ||
987 | eina_strbuf_append_char(buf, '>'); | ||
988 | |||
989 | if (indent) eina_strbuf_append_char(buf, '\n'); | ||
990 | } | ||
991 | } | ||
992 | break; | ||
993 | case EINA_SIMPLE_XML_NODE_DATA: | ||
994 | { | ||
995 | Eina_Simple_XML_Node_Data *n = (Eina_Simple_XML_Node_Data *)node; | ||
996 | |||
997 | if (indent) _eina_simple_xml_node_dump_indent(buf, indent, level); | ||
998 | eina_strbuf_append_length(buf, n->data, n->length); | ||
999 | if (indent) eina_strbuf_append_char(buf, '\n'); | ||
1000 | } | ||
1001 | break; | ||
1002 | |||
1003 | case EINA_SIMPLE_XML_NODE_CDATA: | ||
1004 | { | ||
1005 | Eina_Simple_XML_Node_Data *n = (Eina_Simple_XML_Node_Data *)node; | ||
1006 | |||
1007 | if (indent) _eina_simple_xml_node_dump_indent(buf, indent, level); | ||
1008 | eina_strbuf_append_length(buf, "<![CDATA[", sizeof("<![CDATA[") - 1); | ||
1009 | eina_strbuf_append_length(buf, n->data, n->length); | ||
1010 | eina_strbuf_append_length(buf, "]]>", sizeof("]]>") - 1); | ||
1011 | if (indent) eina_strbuf_append_char(buf, '\n'); | ||
1012 | } | ||
1013 | break; | ||
1014 | |||
1015 | case EINA_SIMPLE_XML_NODE_PROCESSING: | ||
1016 | { | ||
1017 | Eina_Simple_XML_Node_Data *n = (Eina_Simple_XML_Node_Data *)node; | ||
1018 | |||
1019 | if (indent) _eina_simple_xml_node_dump_indent(buf, indent, level); | ||
1020 | eina_strbuf_append_length(buf, "<?", sizeof("<?") - 1); | ||
1021 | eina_strbuf_append_length(buf, n->data, n->length); | ||
1022 | eina_strbuf_append_length(buf, " ?>", sizeof(" ?>") - 1); | ||
1023 | if (indent) eina_strbuf_append_char(buf, '\n'); | ||
1024 | } | ||
1025 | break; | ||
1026 | |||
1027 | case EINA_SIMPLE_XML_NODE_DOCTYPE: | ||
1028 | { | ||
1029 | Eina_Simple_XML_Node_Data *n = (Eina_Simple_XML_Node_Data *)node; | ||
1030 | |||
1031 | if (indent) _eina_simple_xml_node_dump_indent(buf, indent, level); | ||
1032 | eina_strbuf_append_length | ||
1033 | (buf, "<!DOCTYPE ", sizeof("<!DOCTYPE ") - 1); | ||
1034 | eina_strbuf_append_length(buf, n->data, n->length); | ||
1035 | eina_strbuf_append_char(buf, '>'); | ||
1036 | if (indent) eina_strbuf_append_char(buf, '\n'); | ||
1037 | } | ||
1038 | break; | ||
1039 | |||
1040 | case EINA_SIMPLE_XML_NODE_COMMENT: | ||
1041 | { | ||
1042 | Eina_Simple_XML_Node_Data *n = (Eina_Simple_XML_Node_Data *)node; | ||
1043 | |||
1044 | if (indent) _eina_simple_xml_node_dump_indent(buf, indent, level); | ||
1045 | eina_strbuf_append_length(buf, "<!-- ", sizeof("<!-- ") - 1); | ||
1046 | eina_strbuf_append_length(buf, n->data, n->length); | ||
1047 | eina_strbuf_append_length(buf, " -->", sizeof(" -->") - 1); | ||
1048 | if (indent) eina_strbuf_append_char(buf, '\n'); | ||
1049 | } | ||
1050 | break; | ||
1051 | } | ||
1052 | } | ||
1053 | |||
1054 | EAPI char * | ||
1055 | eina_simple_xml_node_dump(Eina_Simple_XML_Node *node, const char *indent) | ||
1056 | { | ||
1057 | Eina_Strbuf *buf; | ||
1058 | char *ret; | ||
1059 | |||
1060 | if (!node) return NULL; | ||
1061 | |||
1062 | buf = eina_strbuf_new(); | ||
1063 | if (!buf) return NULL; | ||
1064 | |||
1065 | _eina_simple_xml_node_dump(buf, node, indent, 0); | ||
1066 | |||
1067 | ret = eina_strbuf_string_steal(buf); | ||
1068 | eina_strbuf_free(buf); | ||
1069 | return ret; | ||
1070 | } | ||