2 * Navit, a modular navigation system.
3 * Copyright (C) 2005-2009 Navit Team
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * version 2 as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
20 /* see http://library.gnome.org/devel/glib/stable/glib-Simple-XML-Subset-Parser.html
21 * for details on how the xml file parser works.
25 #include <glib/gprintf.h>
34 #include "projection.h"
36 #include "navigation.h"
48 #include "announcement.h"
49 #include "vehicleprofile.h"
50 #include "roadprofile.h"
52 #include "xmlconfig.h"
55 #define ATTR_DISTANCE 1
56 const int xml_attr_distance=1;
59 const int xml_attr_distance=2;
60 #define ATTR_DISTANCE 2
61 #define G_MARKUP_ERROR 0
62 #define G_MARKUP_ERROR_INVALID_CONTENT 0
63 #define G_MARKUP_ERROR_PARSE 0
64 #define G_MARKUP_ERROR_UNKNOWN_ELEMENT 0
65 typedef void * GMarkupParseContext;
69 struct xistate *parent;
70 struct xistate *child;
72 const gchar **attribute_names;
73 const gchar **attribute_values;
78 const gchar *xpointer;
80 struct xistate *first;
88 const gchar **attribute_names;
89 const gchar **attribute_values;
90 struct xmlstate *parent;
91 struct attr element_attr;
94 struct element_func *func;
95 struct object_func *object_func;
96 struct xmldocument *document;
105 static struct attr ** convert_to_attrs(struct xmlstate *state, struct attr_fixme *fixme)
107 const gchar **attribute_name=state->attribute_names;
108 const gchar **attribute_value=state->attribute_values;
112 static int fixme_count;
114 while (*attribute_name) {
118 ret=g_new(struct attr *, count+1);
119 attribute_name=state->attribute_names;
121 while (*attribute_name) {
122 name=*attribute_name;
124 char **attr_fixme=fixme->attr_fixme;
125 while (attr_fixme[0]) {
126 if (! strcmp(name, attr_fixme[0])) {
128 if (fixme_count++ < 10)
129 dbg(0,"Please change attribute '%s' to '%s' in <%s />\n", attr_fixme[0], attr_fixme[1], fixme->element);
135 ret[count]=attr_new_from_text(name,*attribute_value);
138 else if (strcmp(*attribute_name,"enabled") && strcmp(*attribute_name,"xmlns:xi"))
139 dbg(0,"failed to create attribute '%s' with value '%s'\n", *attribute_name,*attribute_value);
144 dbg(1,"ret=%p\n", ret);
149 static const char * find_attribute(struct xmlstate *state, const char *attribute, int required)
151 const gchar **attribute_name=state->attribute_names;
152 const gchar **attribute_value=state->attribute_values;
153 while(*attribute_name) {
154 if(! g_ascii_strcasecmp(attribute,*attribute_name))
155 return *attribute_value;
160 g_set_error(state->error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "element '%s' is missing attribute '%s'", state->element, attribute);
165 find_boolean(struct xmlstate *state, const char *attribute, int deflt, int required)
169 value=find_attribute(state, attribute, required);
172 if (g_ascii_strcasecmp(value,"no") && g_ascii_strcasecmp(value,"0") && g_ascii_strcasecmp(value,"false"))
178 * * Convert a string number to int
180 * * @param val the string value to convert
181 * * @returns int value of converted string
184 convert_number(const char *val)
187 return g_ascii_strtoull(val,NULL,0);
193 xmlconfig_announce(struct xmlstate *state)
195 const char *type,*value;
199 enum item_type itype;
200 char *tok, *type_str, *str;
202 type=find_attribute(state, "type", 1);
205 for (i = 0 ; i < 3 ; i++) {
206 sprintf(key,"level%d", i);
207 value=find_attribute(state, key, 0);
209 level[i]=convert_number(value);
213 type_str=g_strdup(type);
215 while ((tok=strtok(str, ","))) {
216 itype=item_from_name(tok);
217 navigation_set_announce(state->parent->element_attr.u.data, itype, level);
224 * * Define the elements in our config
228 #define NEW(x) (void *(*)(struct attr *, struct attr **))(x)
229 #define GET(x) (int (*)(void *, enum attr_type type, struct attr *attr, struct attr_iter *iter))(x)
230 #define ITERN(x) (struct attr_iter * (*)(void *))(x)
231 #define ITERD(x) (void (*)(struct attr_iter *iter))(x)
232 #define SET(x) (int (*)(void *, struct attr *attr))(x)
233 #define ADD(x) (int (*)(void *, struct attr *attr))(x)
234 #define REMOVE(x) (int (*)(void *, struct attr *attr))(x)
235 #define INIT(x) (int (*)(void *))(x)
236 #define DESTROY(x) (void (*)(void *))(x)
238 static struct object_func object_funcs[] = {
239 { attr_announcement,NEW(announcement_new), GET(announcement_get_attr), NULL, NULL, SET(announcement_set_attr), ADD(announcement_add_attr) },
240 { attr_arrows, NEW(arrows_new)},
241 { attr_circle, NEW(circle_new), NULL, NULL, NULL, NULL, ADD(element_add_attr)},
242 { attr_config, NEW(config_new), GET(config_get_attr), ITERN(config_attr_iter_new), ITERD(config_attr_iter_destroy), SET(config_set_attr), ADD(config_add_attr), REMOVE(config_remove_attr), NULL, DESTROY(config_destroy)},
243 { attr_coord, NEW(coord_new_from_attrs)},
244 { attr_cursor, NEW(cursor_new), NULL, NULL, NULL, NULL, ADD(cursor_add_attr)},
245 { attr_debug, NEW(debug_new)},
246 { attr_graphics, NEW(graphics_new)},
247 { attr_gui, NEW(gui_new), GET(gui_get_attr), NULL, NULL, SET(gui_set_attr), ADD(gui_add_attr)},
248 { attr_icon, NEW(icon_new), NULL, NULL, NULL, NULL, ADD(element_add_attr)},
249 { attr_image, NEW(image_new)},
250 { attr_itemgra, NEW(itemgra_new), NULL, NULL, NULL, NULL, ADD(itemgra_add_attr)},
251 { attr_layer, NEW(layer_new), NULL, NULL, NULL, NULL, ADD(layer_add_attr)},
252 { attr_layout, NEW(layout_new), NULL, NULL, NULL, NULL, ADD(layout_add_attr)},
253 { attr_log, NEW(log_new)},
254 { attr_map, NEW(map_new)},
255 { attr_mapset, NEW(mapset_new), NULL, NULL, NULL, NULL, ADD(mapset_add_attr)},
256 { attr_navigation, NEW(navigation_new), GET(navigation_get_attr)},
257 { attr_navit, NEW(navit_new), GET(navit_get_attr), ITERN(navit_attr_iter_new), ITERD(navit_attr_iter_destroy), SET(navit_set_attr), ADD(navit_add_attr), REMOVE(navit_remove_attr), INIT(navit_init), DESTROY(navit_destroy)},
258 { attr_osd, NEW(osd_new)},
259 { attr_plugins, NEW(plugins_new), NULL, NULL, NULL, NULL, NULL, NULL, INIT(plugins_init)},
260 { attr_plugin, NEW(plugin_new)},
261 { attr_polygon, NEW(polygon_new), NULL, NULL, NULL, NULL, ADD(element_add_attr)},
262 { attr_polyline, NEW(polyline_new), NULL, NULL, NULL, NULL, ADD(element_add_attr)},
263 { attr_roadprofile,NEW(roadprofile_new), GET(roadprofile_get_attr), NULL, NULL, SET(roadprofile_set_attr), ADD(roadprofile_add_attr) },
264 { attr_route, NEW(route_new), GET(route_get_attr)},
265 { attr_speech, NEW(speech_new), GET(speech_get_attr), NULL, NULL, SET(speech_set_attr)},
266 { attr_text, NEW(text_new)},
267 { attr_tracking, NEW(tracking_new)},
268 { attr_vehicle, NEW(vehicle_new), GET(vehicle_get_attr), NULL, NULL, NULL, ADD(vehicle_add_attr) },
269 { attr_vehicleprofile, NEW(vehicleprofile_new), GET(vehicleprofile_get_attr), NULL, NULL, SET(vehicleprofile_set_attr), ADD(vehicleprofile_add_attr) },
273 object_func_lookup(enum attr_type type)
276 for (i = 0 ; i < sizeof(object_funcs)/sizeof(struct object_func); i++) {
277 if (object_funcs[i].type == type)
278 return &object_funcs[i];
283 struct element_func {
286 int (*func)(struct xmlstate *state);
289 { "config", NULL, NULL, attr_config},
290 { "announce", "navigation", xmlconfig_announce},
291 { "speech", "navit", NULL, attr_speech},
292 { "tracking", "navit", NULL, attr_tracking},
293 { "route", "navit", NULL, attr_route},
294 { "mapset", "navit", NULL, attr_mapset},
295 { "map", "mapset", NULL, attr_map},
296 { "debug", "config", NULL, attr_debug},
297 { "osd", "navit", NULL, attr_osd},
298 { "navigation", "navit", NULL, attr_navigation},
299 { "navit", "config", NULL, attr_navit},
300 { "graphics", "navit", NULL, attr_graphics},
301 { "gui", "navit", NULL, attr_gui},
302 { "layout", "navit", NULL, attr_layout},
303 { "cursor", "layout", NULL, attr_cursor},
304 { "layer", "layout", NULL, attr_layer},
305 { "itemgra", "layer", NULL, attr_itemgra},
306 { "circle", "itemgra", NULL, attr_circle},
307 { "coord", "circle", NULL, attr_coord},
308 { "icon", "itemgra", NULL, attr_icon},
309 { "coord", "icon", NULL, attr_coord},
310 { "image", "itemgra", NULL, attr_image},
311 { "text", "itemgra", NULL, attr_text},
312 { "polygon", "itemgra", NULL, attr_polygon},
313 { "coord", "polygon", NULL, attr_coord},
314 { "polyline", "itemgra", NULL, attr_polyline},
315 { "coord", "polyline", NULL, attr_coord},
316 { "arrows", "itemgra", NULL, attr_arrows},
317 { "vehicle", "navit", NULL, attr_vehicle},
318 { "vehicleprofile", "navit", NULL, attr_vehicleprofile},
319 { "roadprofile", "vehicleprofile", NULL, attr_roadprofile},
320 { "announcement", "roadprofile", NULL, attr_announcement},
321 { "cursor", "vehicle", NULL, attr_cursor},
322 { "itemgra", "cursor", NULL, attr_itemgra},
323 { "log", "vehicle", NULL, attr_log},
324 { "log", "navit", NULL, attr_log},
325 { "plugins", "config", NULL, attr_plugins},
326 { "plugin", "plugins", NULL, attr_plugin},
331 static char *attr_fixme_itemgra[]={
336 static char *attr_fixme_text[]={
337 "label_size","text_size",
341 static char *attr_fixme_circle[]={
342 "label_size","text_size",
346 static struct attr_fixme attr_fixmes[]={
347 {"item",attr_fixme_itemgra},
348 {"itemgra",attr_fixme_itemgra},
349 {"text",attr_fixme_text},
350 {"label",attr_fixme_text},
351 {"circle",attr_fixme_circle},
356 static char *element_fixmes[]={
362 * * Parse the opening tag of a config element
364 * * @param context document parse context
365 * * @param element_name the current tag name
366 * * @param attribute_names ptr to return the set of attribute names
367 * * @param attribute_values ptr return the set of attribute values
368 * * @param user_data ptr to xmlstate structure
369 * * @param error ptr return error context
374 start_element(GMarkupParseContext *context,
375 const gchar *element_name,
376 const gchar **attribute_names,
377 const gchar **attribute_values,
381 struct xmlstate *new=NULL, **parent = user_data;
382 struct element_func *e=elements,*func=NULL;
383 struct attr_fixme *attr_fixme=attr_fixmes;
384 char **element_fixme=element_fixmes;
386 static int fixme_count;
387 const char *parent_name=NULL;
388 char *s,*sep="",*possible_parents;
389 struct attr *parent_attr;
390 dbg(2,"name='%s' parent='%s'\n", element_name, *parent ? (*parent)->element:NULL);
392 /* determine if we have to fix any attributes */
393 while (attr_fixme[0].element) {
394 if (!strcmp(element_name,attr_fixme[0].element))
398 if (!attr_fixme[0].element)
401 /* tell user to fix deprecated element names */
402 while (element_fixme[0]) {
403 if (!strcmp(element_name,element_fixme[0])) {
404 element_name=element_fixme[1];
405 if (fixme_count++ < 10)
406 dbg(0,"Please change <%s /> to <%s /> in config file\n", element_fixme[0], element_fixme[1]);
410 /* validate that this element is valid
411 * and that the element has a valid parent */
412 possible_parents=g_strdup("");
414 parent_name=(*parent)->element;
416 if (!g_ascii_strcasecmp(element_name, e->name)) {
418 s=g_strconcat(possible_parents,sep,e->parent,NULL);
419 g_free(possible_parents);
422 if ((parent_name && e->parent && !g_ascii_strcasecmp(parent_name, e->parent)) ||
423 (!parent_name && !e->parent))
429 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_UNKNOWN_ELEMENT,
430 "Unknown element '%s'", element_name);
431 g_free(possible_parents);
435 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT,
436 "Element '%s' within unexpected context '%s'. Expected '%s'%s",
437 element_name, parent_name, possible_parents, ! strcmp(possible_parents, "config") ? "\nPlease add <config> </config> tags at the beginning/end of your navit.xml": "");
438 g_free(possible_parents);
441 g_free(possible_parents);
443 new=g_new(struct xmlstate, 1);
444 new->attribute_names=attribute_names;
445 new->attribute_values=attribute_values;
447 new->element_attr.u.data=NULL;
448 new->element=element_name;
451 new->object_func=NULL;
453 if (!find_boolean(new, "enabled", 1, 0))
455 if (new->parent && !new->parent->element_attr.u.data)
458 if (!func->func(new)) {
464 new->object_func=object_func_lookup(func->type);
465 if (! new->object_func)
467 attrs=convert_to_attrs(new,attr_fixme);
468 new->element_attr.type=attr_none;
469 if (!new->parent || new->parent->element_attr.type == attr_none)
472 parent_attr=&new->parent->element_attr;
473 new->element_attr.u.data = new->object_func->new(parent_attr, attrs);
474 if (! new->element_attr.u.data)
476 new->element_attr.type=attr_from_name(element_name);
477 if (new->element_attr.type == attr_none)
478 dbg(0,"failed to create object of type '%s'\n", element_name);
479 if (new->parent && new->parent->object_func && new->parent->object_func->add_attr)
480 new->parent->object_func->add_attr(new->parent->element_attr.u.data, &new->element_attr);
486 /* Called for close tags </foo> */
488 end_element (GMarkupParseContext *context,
489 const gchar *element_name,
493 struct xmlstate *curr, **state = user_data;
495 dbg(2,"name='%s'\n", element_name);
497 if (curr->object_func && curr->object_func->init)
498 curr->object_func->init(curr->element_attr.u.data);
503 static gboolean parse_file(struct xmldocument *document, xmlerror **error);
506 xinclude(GMarkupParseContext *context, const gchar **attribute_names, const gchar **attribute_values, struct xmldocument *doc_old, xmlerror **error)
508 struct xmldocument doc_new;
509 struct file_wordexp *we;
511 const char *href=NULL;
514 if (doc_old->level >= 16) {
515 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include recursion too deep");
518 memset(&doc_new, 0, sizeof(doc_new));
520 while (attribute_names[i]) {
521 if(!g_ascii_strcasecmp("href", attribute_names[i])) {
523 href=attribute_values[i];
525 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include has more than one href");
528 } else if(!g_ascii_strcasecmp("xpointer", attribute_names[i])) {
529 if (!doc_new.xpointer)
530 doc_new.xpointer=attribute_values[i];
532 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include has more than one xpointer");
536 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include has invalid attributes");
541 if (!doc_new.xpointer && !href) {
542 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include has neither href nor xpointer");
545 doc_new.level=doc_old->level+1;
546 doc_new.user_data=doc_old->user_data;
548 dbg(1,"no href, using '%s'\n", doc_old->href);
549 doc_new.href=doc_old->href;
550 parse_file(&doc_new, error);
552 dbg(1,"expanding '%s'\n", href);
553 we=file_wordexp_new(href);
554 we_files=file_wordexp_get_array(we);
555 count=file_wordexp_get_count(we);
556 dbg(1,"%d results\n", count);
557 if (count != 1 || file_exists(we_files[0])) {
558 for (i = 0 ; i < count ; i++) {
559 dbg(1,"result[%d]='%s'\n", i, we_files[i]);
560 doc_new.href=we_files[i];
561 parse_file(&doc_new, error);
564 file_wordexp_destroy(we);
570 strncmp_len(const char *s1, int s1len, const char *s2)
575 strncpy(c, s1, s1len);
577 dbg(0,"'%s' vs '%s'\n", c, s2);
580 ret=strncmp(s1, s2, s1len);
583 return strlen(s2)-s1len;
587 xpointer_value(const char *test, int len, struct xistate *elem, const char **out, int out_len)
590 if (len <= 0 || out_len <= 0) {
593 if (!(strncmp_len(test,len,"name(.)"))) {
594 out[0]=elem->element;
597 if (test[0] == '@') {
599 while (elem->attribute_names[i] && out_len > 0) {
600 if (!strncmp_len(test+1,len-1,elem->attribute_names[i])) {
601 out[ret++]=elem->attribute_values[i];
612 xpointer_test(const char *test, int len, struct xistate *elem)
614 int eq,i,count,vlen,cond_req=1,cond=0;
620 strncpy(test2, test, len);
622 dbg(0,"%s\n", test2);
627 if (c != '\'' && c != '"')
629 eq=strcspn(test, "=");
630 if (eq >= len || test[eq+1] != c)
633 if (eq > 0 && test[eq-1] == '!') {
637 count=xpointer_value(test,vlen,elem,tmp,16);
638 for (i = 0 ; i < count ; i++) {
639 if (!strncmp_len(test+eq+2,len-eq-3, tmp[i]))
642 if (cond == cond_req)
648 xpointer_element_match(const char *xpointer, int len, struct xistate *elem)
650 int start,tlen,tlen2;
654 strncpy(test2, xpointer, len);
656 dbg(0,"%s\n", test2);
658 start=strcspn(xpointer, "[");
661 if (strncmp_len(xpointer, start, elem->element) && (start != 1 || xpointer[0] != '*'))
665 if (xpointer[len-1] != ']')
670 tlen2=strcspn(xpointer+start,"]");
671 if (start + tlen2 > len)
673 if (!xpointer_test(xpointer+start, tlen2, elem))
680 xpointer_xpointer_match(const char *xpointer, int len, struct xistate *first)
684 dbg(2,"%s\n", xpointer);
685 if (xpointer[0] != '/')
693 if (! xpointer_element_match(c, s, first))
698 } while (len > 0 && first);
705 xpointer_match(const char *xpointer, struct xistate *first)
707 char *prefix="xpointer(";
711 len=strlen(xpointer);
712 if (strncmp(xpointer,prefix,strlen(prefix)))
714 if (xpointer[len-1] != ')')
716 return xpointer_xpointer_match(xpointer+strlen(prefix), len-strlen(prefix)-1, first);
721 xi_start_element(GMarkupParseContext *context,
722 const gchar *element_name,
723 const gchar **attribute_names,
724 const gchar **attribute_values,
728 struct xmldocument *doc=user_data;
729 struct xistate *xistate;
731 while (attribute_names[count++*ATTR_DISTANCE]);
732 xistate=g_new0(struct xistate, 1);
733 xistate->element=element_name;
734 xistate->attribute_names=g_new0(const char *, count);
735 xistate->attribute_values=g_new0(const char *, count);
736 for (i = 0 ; i < count ; i++) {
737 if (attribute_names[i*ATTR_DISTANCE] && attribute_values[i*ATTR_DISTANCE]) {
738 xistate->attribute_names[i]=g_strdup(attribute_names[i*ATTR_DISTANCE]);
739 xistate->attribute_values[i]=g_strdup(attribute_values[i*ATTR_DISTANCE]);
742 xistate->parent=doc->last;
745 doc->last->child=xistate;
749 if (doc->active > 0 || xpointer_match(doc->xpointer, doc->first)) {
750 if(!g_ascii_strcasecmp("xi:include", element_name)) {
751 xinclude(context, xistate->attribute_names, xistate->attribute_values, doc, error);
754 start_element(context, element_name, xistate->attribute_names, xistate->attribute_values, doc->user_data, error);
760 * * Reached closing tag of a config element
763 * * @param element name
764 * * @param user_data ptr to xmldocument
765 * * @param error ptr to struct for error information
770 xi_end_element (GMarkupParseContext *context,
771 const gchar *element_name,
775 struct xmldocument *doc=user_data;
776 struct xistate *xistate=doc->last;
778 doc->last=doc->last->parent;
782 doc->last->child=NULL;
783 if (doc->active > 0) {
784 if(!g_ascii_strcasecmp("xi:include", element_name)) {
787 end_element(context, element_name, doc->user_data, error);
790 while (xistate->attribute_names[i]) {
791 g_free((char *)(xistate->attribute_names[i]));
792 g_free((char *)(xistate->attribute_values[i]));
795 g_free(xistate->attribute_names);
796 g_free(xistate->attribute_values);
800 /* Called for character data */
801 /* text is not nul-terminated */
803 xi_text (GMarkupParseContext *context,
809 struct xmldocument *doc=user_data;
812 for (i = 0 ; i < text_len ; i++) {
813 if (!isspace(text[i])) {
814 struct xmldocument *doc=user_data;
815 struct xmlstate *curr, **state = doc->user_data;
817 char *text_dup=malloc(text_len+1);
820 strncpy(text_dup, text, text_len);
821 text_dup[text_len]='\0';
822 attr.type=attr_xml_text;
824 if (curr->object_func && curr->object_func->add_attr)
825 curr->object_func->add_attr(curr->element_attr.u.data, &attr);
835 parse_node_text(ezxml_t node, void *data, void (*start)(void *, const char *, const char **, const char **, void *, void *),
836 void (*end)(void *, const char *, void *, void *),
837 void (*text)(void *, const char *, int, void *, void *))
841 start(NULL, node->name, (const char **)node->attr, (const char **)(node->attr+1), data, NULL);
842 if (text && node->txt)
843 text(NULL, node->txt, strlen(node->txt), data, NULL);
845 parse_node_text(node->child, data, start, end, text);
847 end(NULL, node->name, data, NULL);
854 xml_parse_text(const char *document, void *data, void (*start)(void *, const char *, const char **, const char **, void *, void *),
855 void (*end)(void *, const char *, void *, void *),
856 void (*text)(void *, const char *, int, void *, void *))
859 GMarkupParser parser = { start, end, text, NULL, NULL};
860 GMarkupParseContext *context;
863 context = g_markup_parse_context_new (&parser, 0, data, NULL);
864 result = g_markup_parse_context_parse (context, document, strlen(document), NULL);
865 g_markup_parse_context_free (context);
867 char *str=g_strdup(document);
868 ezxml_t root = ezxml_parse_str(str, strlen(str));
871 parse_node_text(root, data, start, end, text);
880 static const GMarkupParser parser = {
888 * * Parse the contents of the configuration file
890 * * @param document struct holding info about the config file
891 * * @param error info on any errors detected
892 * * @returns boolean TRUE or FALSE
896 parse_file(struct xmldocument *document, xmlerror **error)
898 GMarkupParseContext *context;
899 gchar *contents, *message;
904 dbg(1,"enter filename='%s'\n", document->href);
905 context = g_markup_parse_context_new (&parser, G_MARKUP_TREAT_CDATA_AS_TEXT, document, NULL);
907 if (!g_file_get_contents (document->href, &contents, &len, error)) {
908 g_markup_parse_context_free (context);
911 document->active=document->xpointer ? 0:1;
912 document->first=NULL;
914 result = g_markup_parse_context_parse (context, contents, len, error);
915 if (!result && error && *error) {
916 g_markup_parse_context_get_position(context, &line, &chr);
917 message=g_strdup_printf("%s at line %d, char %d\n", (*error)->message, line, chr);
918 g_free((*error)->message);
919 (*error)->message=message;
921 g_markup_parse_context_free (context);
923 dbg(1,"return %d\n", result);
929 parse_node(struct xmldocument *document, ezxml_t node)
932 xi_start_element(NULL,node->name, node->attr, node->attr+1, document, NULL);
934 xi_text(NULL,node->txt,strlen(node->txt),document,NULL);
936 parse_node(document, node->child);
937 xi_end_element (NULL,node->name,document,NULL);
943 parse_file(struct xmldocument *document, xmlerror **error)
947 /* BUG workaround: ezxml parse file leaves negative fds unclosed */
948 fd = open(document->href, O_RDONLY, 0);
951 root = ezxml_parse_fd(fd);
955 document->active=document->xpointer ? 0:1;
956 document->first=NULL;
959 parse_node(document, root);
966 * * Load and parse the master config file
968 * * @param filename FQFN of the file
969 * * @param error ptr to error details, if any
970 * * @returns boolean TRUE or FALSE (if error detected)
973 gboolean config_load(const char *filename, xmlerror **error)
975 struct xmldocument document;
976 struct xmlstate *curr=NULL;
979 dbg(1,"enter filename='%s'\n", filename);
980 memset(&document, 0, sizeof(document));
981 document.href=filename;
982 document.user_data=&curr;
983 result=parse_file(&document, error);
984 if (result && curr) {
985 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_PARSE, "element '%s' not closed", curr->element);
988 dbg(1,"return %d\n", result);