diff options
Diffstat (limited to 'xmlconfig.c')
-rw-r--r-- | xmlconfig.c | 922 |
1 files changed, 922 insertions, 0 deletions
diff --git a/xmlconfig.c b/xmlconfig.c new file mode 100644 index 00000000..331cb668 --- /dev/null +++ b/xmlconfig.c @@ -0,0 +1,922 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 Navit Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* see http://library.gnome.org/devel/glib/stable/glib-Simple-XML-Subset-Parser.html + * for details on how the xml file parser works. + */ + +#include <glib.h> +#include <glib/gprintf.h> +#include <string.h> +#include "debug.h" +#include "config.h" +#include "file.h" +#include "coord.h" +#include "layout.h" +#include "mapset.h" +#include "projection.h" +#include "map.h" +#include "navigation.h" +#include "navit.h" +#include "plugin.h" +#include "route.h" +#include "speech.h" +#include "track.h" +#include "vehicle.h" +#include "point.h" +#include "graphics.h" +#include "gui.h" +#include "osd.h" +#include "log.h" +#include "cursor.h" +#include "announcement.h" +#include "vehicleprofile.h" +#include "roadprofile.h" +#include "xmlconfig.h" + +#ifdef HAVE_GLIB +#define ATTR_DISTANCE 1 +#else +#include "ezxml.h" +#define ATTR_DISTANCE 2 +#define G_MARKUP_ERROR 0 +#define G_MARKUP_ERROR_INVALID_CONTENT 0 +#define G_MARKUP_ERROR_PARSE 0 +#define G_MARKUP_ERROR_UNKNOWN_ELEMENT 0 +typedef void * GMarkupParseContext; +#endif + +struct xistate { + struct xistate *parent; + struct xistate *child; + const gchar *element; + const gchar **attribute_names; + const gchar **attribute_values; +}; + +struct xmldocument { + const gchar *href; + const gchar *xpointer; + gpointer user_data; + struct xistate *first; + struct xistate *last; + int active; + int level; +}; + + +struct xmlstate { + const gchar **attribute_names; + const gchar **attribute_values; + struct xmlstate *parent; + struct attr element_attr; + const gchar *element; + xmlerror **error; + struct element_func *func; + struct object_func *object_func; + struct xmldocument *document; +}; + + +struct attr_fixme { + char *element; + char **attr_fixme; +}; + +static struct attr ** convert_to_attrs(struct xmlstate *state, struct attr_fixme *fixme) +{ + const gchar **attribute_name=state->attribute_names; + const gchar **attribute_value=state->attribute_values; + const gchar *name; + int count=0; + struct attr **ret; + static int fixme_count; + + while (*attribute_name) { + count++; + attribute_name++; + } + ret=g_new(struct attr *, count+1); + attribute_name=state->attribute_names; + count=0; + while (*attribute_name) { + name=*attribute_name; + if (fixme) { + char **attr_fixme=fixme->attr_fixme; + while (attr_fixme[0]) { + if (! strcmp(name, attr_fixme[0])) { + name=attr_fixme[1]; + if (fixme_count++ < 10) + dbg(0,"Please change attribute '%s' to '%s' in <%s />\n", attr_fixme[0], attr_fixme[1], fixme->element); + break; + } + attr_fixme+=2; + } + } + ret[count]=attr_new_from_text(name,*attribute_value); + if (ret[count]) + count++; + else if (strcmp(*attribute_name,"enabled")) + dbg(0,"failed to create attribute '%s' with value '%s'\n", *attribute_name,*attribute_value); + attribute_name++; + attribute_value++; + } + ret[count]=NULL; + dbg(1,"ret=%p\n", ret); + return ret; +} + + +static const char * find_attribute(struct xmlstate *state, const char *attribute, int required) +{ + const gchar **attribute_name=state->attribute_names; + const gchar **attribute_value=state->attribute_values; + while(*attribute_name) { + if(! g_ascii_strcasecmp(attribute,*attribute_name)) + return *attribute_value; + attribute_name++; + attribute_value++; + } + if (required) + g_set_error(state->error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "element '%s' is missing attribute '%s'", state->element, attribute); + return NULL; +} + +static int +find_boolean(struct xmlstate *state, const char *attribute, int deflt, int required) +{ + const char *value; + + value=find_attribute(state, attribute, required); + if (! value) + return deflt; + if (g_ascii_strcasecmp(value,"no") && g_ascii_strcasecmp(value,"0") && g_ascii_strcasecmp(value,"false")) + return 1; + return 0; +} + +/** + * * Convert a string number to int + * * + * * @param val the string value to convert + * * @returns int value of converted string + * */ +static int +convert_number(const char *val) +{ + if (val) + return g_ascii_strtoull(val,NULL,0); + else + return 0; +} + +static int +xmlconfig_config(struct xmlstate *state) +{ + state->element_attr.u.data = (void *)1; + return 1; +} + +static int +xmlconfig_announce(struct xmlstate *state) +{ + const char *type,*value; + char key[32]; + int level[3]; + int i; + enum item_type itype; + char *tok, *type_str, *str; + + type=find_attribute(state, "type", 1); + if (! type) + return 0; + for (i = 0 ; i < 3 ; i++) { + sprintf(key,"level%d", i); + value=find_attribute(state, key, 0); + if (value) + level[i]=convert_number(value); + else + level[i]=-1; + } + type_str=g_strdup(type); + str=type_str; + while ((tok=strtok(str, ","))) { + itype=item_from_name(tok); + navigation_set_announce(state->parent->element_attr.u.data, itype, level); + str=NULL; + } + g_free(type_str); + return 1; +} +/** + * * Define the elements in our config + * * + * */ + +#define NEW(x) (void *(*)(struct attr *, struct attr **))(x) +#define GET(x) (int (*)(void *, enum attr_type type, struct attr *attr, struct attr_iter *iter))(x) +#define ITERN(x) (struct attr_iter * (*)(void *))(x) +#define ITERD(x) (void (*)(struct attr_iter *iter))(x) +#define SET(x) (int (*)(void *, struct attr *attr))(x) +#define ADD(x) (int (*)(void *, struct attr *attr))(x) +#define REMOVE(x) (int (*)(void *, struct attr *attr))(x) +#define INIT(x) (int (*)(void *))(x) +#define DESTROY(x) (void (*)(void *))(x) + +static struct object_func object_funcs[] = { + { attr_announcement,NEW(announcement_new), GET(announcement_get_attr), NULL, NULL, SET(announcement_set_attr), ADD(announcement_add_attr) }, + { attr_arrows, NEW(arrows_new)}, + { attr_circle, NEW(circle_new), NULL, NULL, NULL, NULL, ADD(element_add_attr)}, + { attr_coord, NEW(coord_new_from_attrs)}, + { attr_cursor, NEW(cursor_new), NULL, NULL, NULL, NULL, ADD(cursor_add_attr)}, + { attr_debug, NEW(debug_new)}, + { attr_graphics, NEW(graphics_new)}, + { attr_gui, NEW(gui_new), GET(gui_get_attr)}, + { attr_icon, NEW(icon_new), NULL, NULL, NULL, NULL, ADD(element_add_attr)}, + { attr_image, NEW(image_new)}, + { attr_itemgra, NEW(itemgra_new), NULL, NULL, NULL, NULL, ADD(itemgra_add_attr)}, + { attr_layer, NEW(layer_new), NULL, NULL, NULL, NULL, ADD(layer_add_attr)}, + { attr_layout, NEW(layout_new), NULL, NULL, NULL, NULL, ADD(layout_add_attr)}, + { attr_log, NEW(log_new)}, + { attr_map, NEW(map_new)}, + { attr_mapset, NEW(mapset_new), NULL, NULL, NULL, NULL, ADD(mapset_add_attr)}, + { attr_navigation, NEW(navigation_new), GET(navigation_get_attr)}, + { 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)}, + { attr_osd, NEW(osd_new)}, + { attr_plugins, NEW(plugins_new), NULL, NULL, NULL, NULL, NULL, NULL, INIT(plugins_init)}, + { attr_plugin, NEW(plugin_new)}, + { attr_polygon, NEW(polygon_new), NULL, NULL, NULL, NULL, ADD(element_add_attr)}, + { attr_polyline, NEW(polyline_new), NULL, NULL, NULL, NULL, ADD(element_add_attr)}, + { attr_roadprofile,NEW(roadprofile_new), GET(roadprofile_get_attr), NULL, NULL, SET(roadprofile_set_attr), ADD(roadprofile_add_attr) }, + { attr_route, NEW(route_new), GET(route_get_attr)}, + { attr_speech, NEW(speech_new), GET(speech_get_attr), NULL, NULL, SET(speech_set_attr)}, + { attr_text, NEW(text_new)}, + { attr_tracking, NEW(tracking_new)}, + { attr_vehicle, NEW(vehicle_new), GET(vehicle_get_attr), NULL, NULL, NULL, ADD(vehicle_add_attr) }, + { attr_vehicleprofile, NEW(vehicleprofile_new), GET(vehicleprofile_get_attr), NULL, NULL, SET(vehicleprofile_set_attr), ADD(vehicleprofile_add_attr) }, +}; + +struct object_func * +object_func_lookup(enum attr_type type) +{ + int i; + for (i = 0 ; i < sizeof(object_funcs)/sizeof(struct object_func); i++) { + if (object_funcs[i].type == type) + return &object_funcs[i]; + } + return NULL; +} + +struct element_func { + char *name; + char *parent; + int (*func)(struct xmlstate *state); + enum attr_type type; +} elements[] = { + { "config", NULL, xmlconfig_config}, + { "announce", "navigation", xmlconfig_announce}, + { "speech", "navit", NULL, attr_speech}, + { "tracking", "navit", NULL, attr_tracking}, + { "route", "navit", NULL, attr_route}, + { "mapset", "navit", NULL, attr_mapset}, + { "map", "mapset", NULL, attr_map}, + { "debug", "config", NULL, attr_debug}, + { "osd", "navit", NULL, attr_osd}, + { "navigation", "navit", NULL, attr_navigation}, + { "navit", "config", NULL, attr_navit}, + { "graphics", "navit", NULL, attr_graphics}, + { "gui", "navit", NULL, attr_gui}, + { "layout", "navit", NULL, attr_layout}, + { "layer", "layout", NULL, attr_layer}, + { "itemgra", "layer", NULL, attr_itemgra}, + { "circle", "itemgra", NULL, attr_circle}, + { "coord", "circle", NULL, attr_coord}, + { "icon", "itemgra", NULL, attr_icon}, + { "coord", "icon", NULL, attr_coord}, + { "image", "itemgra", NULL, attr_image}, + { "text", "itemgra", NULL, attr_text}, + { "polygon", "itemgra", NULL, attr_polygon}, + { "coord", "polygon", NULL, attr_coord}, + { "polyline", "itemgra", NULL, attr_polyline}, + { "coord", "polyline", NULL, attr_coord}, + { "arrows", "itemgra", NULL, attr_arrows}, + { "vehicle", "navit", NULL, attr_vehicle}, + { "vehicleprofile", "navit", NULL, attr_vehicleprofile}, + { "roadprofile", "vehicleprofile", NULL, attr_roadprofile}, + { "announcement", "roadprofile", NULL, attr_announcement}, + { "cursor", "vehicle", NULL, attr_cursor}, + { "itemgra", "cursor", NULL, attr_itemgra}, + { "log", "vehicle", NULL, attr_log}, + { "log", "navit", NULL, attr_log}, + { "plugins", "config", NULL, attr_plugins}, + { "plugin", "plugins", NULL, attr_plugin}, + {}, +}; + + +static char *attr_fixme_itemgra[]={ + "type","item_types", + NULL,NULL, +}; + +static char *attr_fixme_text[]={ + "label_size","text_size", + NULL,NULL, +}; + +static char *attr_fixme_circle[]={ + "label_size","text_size", + NULL,NULL, +}; + +static struct attr_fixme attr_fixmes[]={ + {"item",attr_fixme_itemgra}, + {"itemgra",attr_fixme_itemgra}, + {"text",attr_fixme_text}, + {"label",attr_fixme_text}, + {"circle",attr_fixme_circle}, + {NULL,NULL}, +}; + + +static char *element_fixmes[]={ + "item","itemgra", + "label","text", + NULL,NULL, +}; +/** + * * Parse the opening tag of a config element + * * + * * @param context document parse context + * * @param element_name the current tag name + * * @param attribute_names ptr to return the set of attribute names + * * @param attribute_values ptr return the set of attribute values + * * @param user_data ptr to xmlstate structure + * * @param error ptr return error context + * * @returns nothing + * */ + +static void +start_element(GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + xmlerror **error) +{ + struct xmlstate *new=NULL, **parent = user_data; + struct element_func *e=elements,*func=NULL; + struct attr_fixme *attr_fixme=attr_fixmes; + char **element_fixme=element_fixmes; + int found=0; + static int fixme_count; + const char *parent_name=NULL; + char *s,*sep="",*possible_parents; + dbg(2,"name='%s' parent='%s'\n", element_name, *parent ? (*parent)->element:NULL); + + /* determine if we have to fix any attributes */ + while (attr_fixme[0].element) { + if (!strcmp(element_name,attr_fixme[0].element)) + break; + attr_fixme++; + } + if (!attr_fixme[0].element) + attr_fixme=NULL; + + /* tell user to fix deprecated element names */ + while (element_fixme[0]) { + if (!strcmp(element_name,element_fixme[0])) { + element_name=element_fixme[1]; + if (fixme_count++ < 10) + dbg(0,"Please change <%s /> to <%s /> in config file\n", element_fixme[0], element_fixme[1]); + } + element_fixme+=2; + } + /* validate that this element is valid + * and that the element has a valid parent */ + possible_parents=g_strdup(""); + if (*parent) + parent_name=(*parent)->element; + while (e->name) { + if (!g_ascii_strcasecmp(element_name, e->name)) { + found=1; + s=g_strconcat(possible_parents,sep,e->parent,NULL); + g_free(possible_parents); + possible_parents=s; + sep=","; + if ((parent_name && e->parent && !g_ascii_strcasecmp(parent_name, e->parent)) || + (!parent_name && !e->parent)) + func=e; + } + e++; + } + if (! found) { + g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_UNKNOWN_ELEMENT, + "Unknown element '%s'", element_name); + g_free(possible_parents); + return; + } + if (! func) { + g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, + "Element '%s' within unexpected context '%s'. Expected '%s'%s", + element_name, parent_name, possible_parents, ! strcmp(possible_parents, "config") ? "\nPlease add <config> </config> tags at the beginning/end of your navit.xml": ""); + g_free(possible_parents); + return; + } + g_free(possible_parents); + + new=g_new(struct xmlstate, 1); + new->attribute_names=attribute_names; + new->attribute_values=attribute_values; + new->parent=*parent; + new->element_attr.u.data=NULL; + new->element=element_name; + new->error=error; + new->func=func; + new->object_func=NULL; + *parent=new; + if (!find_boolean(new, "enabled", 1, 0)) + return; + if (new->parent && !new->parent->element_attr.u.data) + return; + if (func->func) { + if (!func->func(new)) { + return; + } + } else { + struct attr **attrs; + + new->object_func=object_func_lookup(func->type); + if (! new->object_func) + return; + attrs=convert_to_attrs(new,attr_fixme); + new->element_attr.type=attr_none; + new->element_attr.u.data = new->object_func->new(&new->parent->element_attr, attrs); + if (! new->element_attr.u.data) + return; + new->element_attr.type=attr_from_name(element_name); + if (new->element_attr.type == attr_none) + dbg(0,"failed to create object of type '%s'\n", element_name); + if (new->parent->object_func && new->parent->object_func->add_attr) + new->parent->object_func->add_attr(new->parent->element_attr.u.data, &new->element_attr); + } + return; +} + + +/* Called for close tags </foo> */ +static void +end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + xmlerror **error) +{ + struct xmlstate *curr, **state = user_data; + + dbg(2,"name='%s'\n", element_name); + curr=*state; + if (curr->object_func && curr->object_func->init) + curr->object_func->init(curr->element_attr.u.data); + *state=curr->parent; + g_free(curr); +} + +static gboolean parse_file(struct xmldocument *document, xmlerror **error); + +static void +xinclude(GMarkupParseContext *context, const gchar **attribute_names, const gchar **attribute_values, struct xmldocument *doc_old, xmlerror **error) +{ + struct xmldocument doc_new; + struct file_wordexp *we; + int i,count; + const char *href=NULL; + char **we_files; + + if (doc_old->level >= 16) { + g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include recursion too deep"); + return; + } + memset(&doc_new, 0, sizeof(doc_new)); + i=0; + while (attribute_names[i]) { + if(!g_ascii_strcasecmp("href", attribute_names[i])) { + if (!href) + href=attribute_values[i]; + else { + g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include has more than one href"); + return; + } + } else if(!g_ascii_strcasecmp("xpointer", attribute_names[i])) { + if (!doc_new.xpointer) + doc_new.xpointer=attribute_values[i]; + else { + g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include has more than one xpointer"); + return; + } + } else { + g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include has invalid attributes"); + return; + } + i++; + } + if (!doc_new.xpointer && !href) { + g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include has neither href nor xpointer"); + return; + } + doc_new.level=doc_old->level+1; + doc_new.user_data=doc_old->user_data; + if (! href) { + dbg(1,"no href, using '%s'\n", doc_old->href); + doc_new.href=doc_old->href; + parse_file(&doc_new, error); + } else { + dbg(1,"expanding '%s'\n", href); + we=file_wordexp_new(href); + we_files=file_wordexp_get_array(we); + count=file_wordexp_get_count(we); + dbg(1,"%d results\n", count); + if (count != 1 || file_exists(we_files[0])) { + for (i = 0 ; i < count ; i++) { + dbg(1,"result[%d]='%s'\n", i, we_files[i]); + doc_new.href=we_files[i]; + parse_file(&doc_new, error); + } + } + file_wordexp_destroy(we); + + } + +} +static int +strncmp_len(const char *s1, int s1len, const char *s2) +{ + int ret; +#if 0 + char c[s1len+1]; + strncpy(c, s1, s1len); + c[s1len]='\0'; + dbg(0,"'%s' vs '%s'\n", c, s2); +#endif + + ret=strncmp(s1, s2, s1len); + if (ret) + return ret; + return strlen(s2)-s1len; +} + +static int +xpointer_value(const char *test, int len, struct xistate *elem, const char **out, int out_len) +{ + int i,ret=0; + if (len <= 0 || out_len <= 0) { + return 0; + } + if (!(strncmp_len(test,len,"name(.)"))) { + out[0]=elem->element; + return 1; + } + if (test[0] == '@') { + i=0; + while (elem->attribute_names[i] && out_len > 0) { + if (!strncmp_len(test+1,len-1,elem->attribute_names[i])) { + out[ret++]=elem->attribute_values[i]; + out_len--; + } + i++; + } + return ret; + } + return 0; +} + +static int +xpointer_test(const char *test, int len, struct xistate *elem) +{ + int eq,i,count,vlen,cond_req=1,cond=0; + char c; + const char *tmp[16]; +#if 0 + char test2[len+1]; + + strncpy(test2, test, len); + test2[len]='\0'; + dbg(0,"%s\n", test2); +#endif + if (!len) + return 0; + c=test[len-1]; + if (c != '\'' && c != '"') + return 0; + eq=strcspn(test, "="); + if (eq >= len || test[eq+1] != c) + return 0; + vlen=eq; + if (eq > 0 && test[eq-1] == '!') { + cond_req=0; + vlen--; + } + count=xpointer_value(test,vlen,elem,tmp,16); + for (i = 0 ; i < count ; i++) { + if (!strncmp_len(test+eq+2,len-eq-3, tmp[i])) + cond=1; + } + if (cond == cond_req) + return 1; + return 0; +} + +static int +xpointer_element_match(const char *xpointer, int len, struct xistate *elem) +{ + int start,tlen,tlen2; +#if 0 + char test2[len+1]; + + strncpy(test2, xpointer, len); + test2[len]='\0'; + dbg(0,"%s\n", test2); +#endif + start=strcspn(xpointer, "["); + if (start > len) + start=len; + if (strncmp_len(xpointer, start, elem->element) && (start != 1 || xpointer[0] != '*')) + return 0; + if (start == len) + return 1; + if (xpointer[len-1] != ']') + return 0; + tlen=len-start-2; + for (;;) { + start++; + tlen2=strcspn(xpointer+start,"]"); + if (start + tlen2 > len) + return 1; + if (!xpointer_test(xpointer+start, tlen2, elem)) + return 0; + start+=tlen2+1; + } +} + +static int +xpointer_xpointer_match(const char *xpointer, int len, struct xistate *first) +{ + const char *c; + int s; + dbg(2,"%s\n", xpointer); + if (xpointer[0] != '/') + return 0; + c=xpointer+1; + len--; + do { + s=strcspn(c, "/"); + if (s > len) + s=len; + if (! xpointer_element_match(c, s, first)) + return 0; + first=first->child; + c+=s+1; + len-=s+1; + } while (len > 0 && first); + if (len > 0) + return 0; + return 1; +} + +static int +xpointer_match(const char *xpointer, struct xistate *first) +{ + char *prefix="xpointer("; + int len; + if (! xpointer) + return 1; + len=strlen(xpointer); + if (strncmp(xpointer,prefix,strlen(prefix))) + return 0; + if (xpointer[len-1] != ')') + return 0; + return xpointer_xpointer_match(xpointer+strlen(prefix), len-strlen(prefix)-1, first); + +} + +static void +xi_start_element(GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + xmlerror **error) +{ + struct xmldocument *doc=user_data; + struct xistate *xistate; + int i,count=0; + while (attribute_names[count++*ATTR_DISTANCE]); + xistate=g_new0(struct xistate, 1); + xistate->element=element_name; + xistate->attribute_names=g_new0(const char *, count); + xistate->attribute_values=g_new0(const char *, count); + for (i = 0 ; i < count ; i++) { + if (attribute_names[i*ATTR_DISTANCE] && attribute_values[i*ATTR_DISTANCE]) { + xistate->attribute_names[i]=g_strdup(attribute_names[i*ATTR_DISTANCE]); + xistate->attribute_values[i]=g_strdup(attribute_values[i*ATTR_DISTANCE]); + } + } + xistate->parent=doc->last; + + if (doc->last) { + doc->last->child=xistate; + } else + doc->first=xistate; + doc->last=xistate; + if (doc->active > 0 || xpointer_match(doc->xpointer, doc->first)) { + if(!g_ascii_strcasecmp("xi:include", element_name)) { + xinclude(context, xistate->attribute_names, xistate->attribute_values, doc, error); + return; + } + start_element(context, element_name, xistate->attribute_names, xistate->attribute_values, doc->user_data, error); + doc->active++; + } + +} +/** + * * Reached closing tag of a config element + * * + * * @param context + * * @param element name + * * @param user_data ptr to xmldocument + * * @param error ptr to struct for error information + * * @returns nothing + * */ + +static void +xi_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + xmlerror **error) +{ + struct xmldocument *doc=user_data; + struct xistate *xistate=doc->last; + int i=0; + doc->last=doc->last->parent; + if (! doc->last) + doc->first=NULL; + else + doc->last->child=NULL; + if (doc->active > 0) { + if(!g_ascii_strcasecmp("xi:include", element_name)) { + return; + } + end_element(context, element_name, doc->user_data, error); + doc->active--; + } + while (xistate->attribute_names[i]) { + g_free((char *)(xistate->attribute_names[i])); + g_free((char *)(xistate->attribute_values[i])); + i++; + } + g_free(xistate->attribute_names); + g_free(xistate->attribute_values); + g_free(xistate); +} + +/* Called for character data */ +/* text is not nul-terminated */ +static void +xi_text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + xmlerror **error) +{ +} + + +#ifdef HAVE_GLIB + +static const GMarkupParser parser = { + xi_start_element, + xi_end_element, + xi_text, + NULL, + NULL +}; +/** + * * Parse the contents of the configuration file + * * + * * @param document struct holding info about the config file + * * @param error info on any errors detected + * * @returns boolean TRUE or FALSE + * */ + +static gboolean +parse_file(struct xmldocument *document, xmlerror **error) +{ + GMarkupParseContext *context; + gchar *contents, *message; + gsize len; + gint line, chr; + gboolean result; + + dbg(1,"enter filename='%s'\n", document->href); + context = g_markup_parse_context_new (&parser, 0, document, NULL); + + if (!g_file_get_contents (document->href, &contents, &len, error)) { + g_markup_parse_context_free (context); + return FALSE; + } + document->active=document->xpointer ? 0:1; + document->first=NULL; + document->last=NULL; + result = g_markup_parse_context_parse (context, contents, len, error); + if (!result && error && *error) { + g_markup_parse_context_get_position(context, &line, &chr); + message=g_strdup_printf("%s at line %d, char %d\n", (*error)->message, line, chr); + g_free((*error)->message); + (*error)->message=message; + } + g_markup_parse_context_free (context); + g_free (contents); + dbg(1,"return %d\n", result); + + return result; +} +#else +static void +parse_node(struct xmldocument *document, ezxml_t node) +{ + while (node) { + xi_start_element(NULL,node->name, node->attr, node->attr+1, document, NULL); + if (node->txt) + xi_text(NULL,node->txt,strlen(node->txt),document,NULL); + if (node->child) + parse_node(document, node->child); + xi_end_element (NULL,node->name,document,NULL); + node=node->ordered; + } +} + +static gboolean +parse_file(struct xmldocument *document, xmlerror **error) +{ + int fd; + ezxml_t root; + /* BUG workaround: ezxml parse file leaves negative fds unclosed */ + fd = open(document->href, O_RDONLY, 0); + if (fd == -1) + return FALSE; + root = ezxml_parse_fd(fd); + close(fd); + if (!root) + return FALSE; + document->active=document->xpointer ? 0:1; + document->first=NULL; + document->last=NULL; + + parse_node(document, root); + + return TRUE; +} +#endif + +/** + * * Load and parse the master config file + * * + * * @param filename FQFN of the file + * * @param error ptr to error details, if any + * * @returns boolean TRUE or FALSE (if error detected) + * */ + +gboolean config_load(const char *filename, xmlerror **error) +{ + struct xmldocument document; + struct xmlstate *curr=NULL; + gboolean result; + + dbg(1,"enter filename='%s'\n", filename); + memset(&document, 0, sizeof(document)); + document.href=filename; + document.user_data=&curr; + result=parse_file(&document, error); + if (result && curr) { + g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_PARSE, "element '%s' not closed", curr->element); + result=FALSE; + } + dbg(1,"return %d\n", result); + return result; +} + |