summaryrefslogtreecommitdiff
path: root/xmlconfig.c
diff options
context:
space:
mode:
Diffstat (limited to 'xmlconfig.c')
-rw-r--r--xmlconfig.c922
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;
+}
+