EFL 1.7 svn doobies
[profile/ivi/efreet.git] / src / lib / efreet_xml.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 #include <ctype.h>
6 #include <sys/stat.h>
7 #include <fcntl.h>
8 #include <unistd.h>
9 #include <sys/mman.h>
10
11 #include <Ecore_File.h>
12
13 /* define macros and variable for using the eina logging system  */
14 #define EFREET_MODULE_LOG_DOM _efreet_xml_log_dom
15 static int _efreet_xml_log_dom = -1;
16
17 #include "Efreet.h"
18 #include "efreet_private.h"
19 #include "efreet_xml.h"
20
21 #if 0
22 static void efreet_xml_dump(Efreet_Xml *xml, int level);
23 #endif
24
25 static Efreet_Xml *efreet_xml_parse(char **data, int *size);
26 static int efreet_xml_tag_parse(char **data, int *size, const char **tag);
27 static void efreet_xml_attributes_parse(char **data, int *size,
28                                         Efreet_Xml_Attribute ***attributes);
29 static void efreet_xml_text_parse(char **data, int *size, const char **text);
30
31 static int efreet_xml_tag_empty(char **data, int *size);
32 static int efreet_xml_tag_close(char **data, int *size, const char *tag);
33
34 static void efreet_xml_cb_attribute_free(void *data);
35 static void efreet_xml_comment_skip(char **data, int *size);
36
37 static int error = 0;
38
39 static int _efreet_xml_init_count = 0;
40
41 /**
42  * @internal
43  * @return Returns > 0 on success or 0 on failure
44  * @brief Initialize the XML parser subsystem
45  */
46 int
47 efreet_xml_init(void)
48 {
49     _efreet_xml_init_count++;
50     if (_efreet_xml_init_count > 1) return _efreet_xml_init_count;
51     _efreet_xml_log_dom = eina_log_domain_register
52       ("efreet_xml", EFREET_DEFAULT_LOG_COLOR);
53     if (_efreet_xml_log_dom < 0)
54     {
55         _efreet_xml_init_count--;
56         EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_xml.");
57         return _efreet_xml_init_count;
58     }
59     return _efreet_xml_init_count;
60 }
61
62 /**
63  * @internal
64  * @returns the number of initializations left for this system
65  * @brief Attempts to shut down the subsystem if nothing else is using it
66  */
67 void
68 efreet_xml_shutdown(void)
69 {
70     _efreet_xml_init_count--;
71     if (_efreet_xml_init_count > 0) return;
72     eina_log_domain_unregister(_efreet_xml_log_dom);
73     _efreet_xml_log_dom = -1;
74 }
75
76 /**
77  * @internal
78  * @param file The file to parse
79  * @return Returns an Efreet_Xml structure for the given file @a file or
80  * NULL on failure
81  * @brief Parses the given file into an Efreet_Xml structure.
82  */
83 Efreet_Xml *
84 efreet_xml_new(const char *file)
85 {
86     Efreet_Xml *xml = NULL;
87     int size, fd = -1;
88     char *data = MAP_FAILED;
89
90     if (!file) return NULL;
91     if (!ecore_file_exists(file)) return NULL;
92
93     size = ecore_file_size(file);
94     if (size <= 0) goto efreet_error;
95
96     fd = open(file, O_RDONLY);
97     if (fd == -1) goto efreet_error;
98
99     /* let's make mmap safe and just get 0 pages for IO erro */
100     eina_mmap_safety_enabled_set(EINA_TRUE);
101    
102     data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
103     if (data == MAP_FAILED) goto efreet_error;
104
105     error = 0;
106     xml = efreet_xml_parse(&data, &size);
107     if (!xml || error) goto efreet_error;
108
109     munmap(data, size);
110     close(fd);
111     return xml;
112
113 efreet_error:
114     ERR("could not parse xml file");
115     if (data != MAP_FAILED) munmap(data, size);
116     if (fd != -1) close(fd);
117     if (xml) efreet_xml_del(xml);
118     return NULL;
119 }
120
121 /**
122  * @internal
123  * @param xml The Efree_Xml to free
124  * @return Returns no value
125  * @brief Frees up the given Efreet_Xml structure
126  */
127 void
128 efreet_xml_del(Efreet_Xml *xml)
129 {
130     IF_FREE_LIST(xml->children, efreet_xml_cb_attribute_free);
131
132     if (xml->tag) eina_stringshare_del(xml->tag);
133     if (xml->attributes)
134     {
135         Efreet_Xml_Attribute **curr;
136
137         curr = xml->attributes;
138         while (*curr)
139         {
140             eina_stringshare_del((*curr)->key);
141             eina_stringshare_del((*curr)->value);
142
143             FREE(*curr);
144             curr++;
145         }
146         FREE(xml->attributes);
147     }
148     IF_RELEASE(xml->text);
149     FREE(xml);
150 }
151
152 /**
153  * @param xml The xml struct to work with
154  * @param key The attribute key to look for
155  * @return Returns the value for the given key, or NULL if none found
156  * @brief Retrieves the value for the given attribute key
157  */
158 const char *
159 efreet_xml_attribute_get(Efreet_Xml *xml, const char *key)
160 {
161     Efreet_Xml_Attribute **curr;
162
163     if (!xml || !key || !xml->attributes) return NULL;
164
165     for (curr = xml->attributes; *curr; curr++)
166     {
167         if (!strcmp((*curr)->key, key))
168             return (*curr)->value;
169     }
170     return NULL;
171 }
172
173 static void
174 efreet_xml_cb_attribute_free(void *data)
175 {
176     efreet_xml_del(data);
177 }
178
179 #if 0
180 static void
181 efreet_xml_dump(Efreet_Xml *xml, int level)
182 {
183     int i;
184
185     for (i = 0; i < level; i++)
186         printf("\t");
187     printf("<%s", xml->tag);
188     if (xml->attributes)
189     {
190         Efreet_Xml_Attribute **curr;
191         for (curr = xml->attributes; *curr; curr++)
192             printf(" %s=\"%s\"", (*curr)->key, (*curr)->value);
193     }
194
195     if (xml->children)
196     {
197         Efreet_Xml *child;
198         Eina_List *l;
199
200         printf(">");
201
202         EINA_LIST_FOREACH(xml->children, l, child)
203             efreet_xml_dump(child, level + 1);
204
205         for (i = 0; i < level; i++)
206             printf("\t");
207         printf("</%s>", xml->tag);
208     }
209     else if (xml->text)
210         printf(">%s</%s>\n", xml->text, xml->tag);
211     else
212         printf("/>\n");
213 }
214 #endif
215
216 static Efreet_Xml *
217 efreet_xml_parse(char **data, int *size)
218 {
219     Efreet_Xml *xml, *sub_xml;
220     const char *tag = NULL;
221
222     /* parse this tag */
223     if (!efreet_xml_tag_parse(data, size, &(tag))) return NULL;
224     xml = NEW(Efreet_Xml, 1);
225     if (!xml)
226     {
227         eina_stringshare_del(tag);
228         return NULL;
229     }
230
231     xml->children = NULL;
232
233     xml->tag = tag;
234     efreet_xml_attributes_parse(data, size, &(xml->attributes));
235
236     /* Check wether element is empty */
237     if (efreet_xml_tag_empty(data, size)) return xml;
238     efreet_xml_text_parse(data, size, &(xml->text));
239
240     /* Check wether element is closed */
241     if (efreet_xml_tag_close(data, size, xml->tag)) return xml;
242
243     while ((sub_xml = efreet_xml_parse(data, size)))
244         xml->children = eina_list_append(xml->children, sub_xml);
245
246     efreet_xml_tag_close(data, size, xml->tag);
247
248     return xml;
249 }
250
251 static int
252 efreet_xml_tag_parse(char **data, int *size, const char **tag)
253 {
254     const char *start = NULL, *end = NULL;
255     char buf[256];
256     int buf_size;
257
258     /* Search for tag */
259     while (*size > 1)
260     {
261         /* Check for tag start */
262         if (**data == '<')
263         {
264             /* Check for end tag */
265             if (*(*data + 1) == '/') return 0;
266
267             /* skip comments */
268             if (*size > 3 && *(*data + 1) == '!' && *(*data + 2) == '-' && *(*data + 3) == '-')
269             {
270                 (*data) += 3;
271                 (*size) -= 3;
272                 efreet_xml_comment_skip(data, size);
273                 continue;
274             }
275
276             /* Check for xml directives (and ignore them) */
277             else if ((*(*data + 1) != '!') && (*(*data + 1) != '?'))
278             {
279                 (*size)--;
280                 (*data)++;
281                 start = *data;
282                 break;
283             }
284         }
285         (*size)--;
286         (*data)++;
287     }
288
289     if (!start)
290     {
291         ERR("missing start tag");
292         error = 1;
293         return 0;
294     }
295
296     while (*size > 0)
297     {
298         if (!isalpha(**data))
299         {
300             end = *data;
301             break;
302         }
303         (*size)--;
304         (*data)++;
305     }
306
307     if (!end)
308     {
309         ERR("no end of tag");
310         error = 1;
311         return 0;
312     }
313
314     buf_size = end - start + 1;
315     if (buf_size <= 1)
316     {
317         ERR("no tag name");
318         error = 1;
319         return 0;
320     }
321
322     if (buf_size > 256) buf_size = 256;
323     memcpy(buf, start, buf_size - 1);
324     buf[buf_size - 1] = '\0';
325     *tag = eina_stringshare_add(buf);
326
327     return 1;
328 }
329
330 static void
331 efreet_xml_attributes_parse(char **data, int *size,
332         Efreet_Xml_Attribute ***attributes)
333 {
334     Efreet_Xml_Attribute attr[10];
335     int i, count = 0;
336
337     while (*size > 0)
338     {
339         if (**data == '>')
340         {
341             (*size)++;
342             (*data)--;
343             break;
344         }
345         else if ((count < 10) && (isalpha(**data)))
346         {
347             /* beginning of key */
348             const char *start = NULL, *end = NULL;
349             char buf[256];
350             int buf_size;
351
352             attr[count].key = NULL;
353             attr[count].value = NULL;
354
355             start = *data;
356             while ((*size > 0) && ((isalpha(**data)) || (**data == '_')))
357             {
358                 (*size)--;
359                 (*data)++;
360             }
361
362             end = *data;
363             buf_size = end - start + 1;
364             if (buf_size <= 1)
365             {
366                 ERR("zero length key");
367                 goto efreet_error;
368             }
369
370             if (buf_size > 256) buf_size = 256;
371             memcpy(buf, start, buf_size - 1);
372             buf[buf_size - 1] = '\0';
373             attr[count].key = eina_stringshare_add(buf);
374
375             /* search for '=', key/value seperator */
376             start = NULL;
377             while (*size > 0)
378             {
379                 if (**data == '=')
380                 {
381                     start = *data;
382                     break;
383                 }
384                 (*size)--;
385                 (*data)++;
386             }
387
388             if (!start)
389             {
390                 ERR("missing value for attribute!");
391                 goto efreet_error;
392             }
393
394             /* search for '"', beginning of value */
395             start = NULL;
396             while (*size > 0)
397             {
398                 if (**data == '"')
399                 {
400                     start = *data;
401                     break;
402                 }
403                 (*size)--;
404                 (*data)++;
405             }
406
407             if (!start)
408             {
409                 ERR("erroneous value for attribute!");
410                 goto efreet_error;
411             }
412
413             /* skip '"' */
414             start++;
415             (*size)--;
416             (*data)++;
417
418             /* search for '"', end of value */
419             end = NULL;
420             while (*size > 0)
421             {
422                 if (**data == '"')
423                 {
424                     end = *data;
425                     break;
426                 }
427                 (*size)--;
428                 (*data)++;
429             }
430
431             if (!end)
432             {
433                 ERR("erroneous value for attribute!");
434                 goto efreet_error;
435             }
436
437             buf_size = end - start + 1;
438             if (buf_size <= 1)
439             {
440                 ERR("zero length value");
441                 goto efreet_error;
442             }
443
444             if (buf_size > 256) buf_size = 256;
445             memcpy(buf, start, buf_size - 1);
446             buf[buf_size - 1] = '\0';
447             attr[count].value = eina_stringshare_add(buf);
448
449             count++;
450         }
451
452         (*size)--;
453         (*data)++;
454     }
455
456     *attributes = NEW(Efreet_Xml_Attribute *, count + 1);
457     if (!*attributes) goto efreet_error;
458     for (i = 0; i < count; i++)
459     {
460         (*attributes)[i] = malloc(sizeof(Efreet_Xml_Attribute));
461         (*attributes)[i]->key = attr[i].key;
462         (*attributes)[i]->value = attr[i].value;
463     }
464     return;
465
466 efreet_error:
467     while (count >= 0)
468     {
469         if (attr[count].key) eina_stringshare_del(attr[count].key);
470         if (attr[count].value) eina_stringshare_del(attr[count].value);
471         count--;
472     }
473     error = 1;
474     return;
475 }
476
477 static void
478 efreet_xml_text_parse(char **data, int *size, const char **text)
479 {
480     const char *start = NULL, *end = NULL;
481     int buf_size;
482
483     /* skip leading whitespace */
484     while (*size > 0)
485     {
486         if (!isspace(**data))
487         {
488             start = *data;
489             break;
490         }
491         (*size)--;
492         (*data)++;
493     }
494
495     if (!start) return;
496
497     /* find next tag */
498     while (*size > 0)
499     {
500         if (**data == '<')
501         {
502             end = *data;
503             break;
504         }
505         (*size)--;
506         (*data)++;
507     }
508     if (!end) return;
509
510     /* skip trailing whitespace */
511     while (isspace(*(end - 1))) end--;
512
513     /* copy text */
514     buf_size = end - start + 1;
515     if (buf_size <= 1) return;
516
517     *text = eina_stringshare_add_length(start, buf_size - 1);
518 }
519
520 static int
521 efreet_xml_tag_empty(char **data, int *size)
522 {
523     while (*size > 1)
524     {
525         if (**data == '/')
526         {
527             (*size)--;
528             (*data)++;
529             if (**data == '>')
530             {
531                 (*size)--;
532                 (*data)++;
533                 return 1;
534             }
535         }
536         else if (**data == '>')
537         {
538             (*size)--;
539             (*data)++;
540             return 0;
541         }
542         (*size)--;
543         (*data)++;
544     }
545     ERR("missing end of tag");
546     error = 1;
547
548     return 1;
549 }
550
551 static int
552 efreet_xml_tag_close(char **data, int *size, const char *tag)
553 {
554     while (*size > 1)
555     {
556         if (**data == '<')
557         {
558             if (*(*data + 1) == '/')
559             {
560                 (*size) -= 2;
561                 (*data) += 2;
562                 if ((int)strlen(tag) > *size)
563                 {
564                     ERR("wrong end tag");
565                     error = 1;
566                     return 1;
567                 }
568                 else
569                 {
570                     char *tmp;
571                     tmp = *data;
572                     while ((*tag) && (*tmp == *tag))
573                     {
574                         tmp++;
575                         tag++;
576                     }
577
578                     if (*tag)
579                     {
580                         ERR("wrong end tag");
581                         error = 1;
582                         return 1;
583                     }
584                 }
585                 return 1;
586             }
587             else return 0;
588         }
589         (*size)--;
590         (*data)++;
591     }
592     return 0;
593 }
594
595 static void
596 efreet_xml_comment_skip(char **data, int *size)
597 {
598     while (*size > 2)
599     {
600         if (**data == '-' && *(*data + 1) == '-' && *(*data + 2) == '>')
601         {
602             (*data) += 3;
603             (*size) -= 3;
604             return;
605         }
606         (*data)++;
607         (*size)--;
608     }
609 }