Add:Core:Support for FRA FRA NLS Setting
[navit-package] / navit / xmlconfig.c
1 /**
2  * Navit, a modular navigation system.
3  * Copyright (C) 2005-2009 Navit Team
4  *
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.
8  *
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.
13  *
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.
18  */
19
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.
22  */
23
24 #include <glib.h>
25 #include <glib/gprintf.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include "debug.h"
29 #include "config.h"
30 #include "file.h"
31 #include "coord.h"
32 #include "layout.h"
33 #include "mapset.h"
34 #include "projection.h"
35 #include "map.h"
36 #include "navigation.h"
37 #include "navit.h"
38 #include "plugin.h"
39 #include "route.h"
40 #include "speech.h"
41 #include "track.h"
42 #include "vehicle.h"
43 #include "point.h"
44 #include "graphics.h"
45 #include "gui.h"
46 #include "osd.h"
47 #include "log.h"
48 #include "announcement.h"
49 #include "vehicleprofile.h"
50 #include "roadprofile.h"
51 #include "config_.h"
52 #include "xmlconfig.h"
53
54 #ifdef HAVE_GLIB
55 #define ATTR_DISTANCE 1
56 const int xml_attr_distance=1;
57 #else
58 #include "ezxml.h"
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;
66 #endif
67
68 struct xistate {
69         struct xistate *parent;
70         struct xistate *child;
71         const gchar *element;
72         const gchar **attribute_names;
73         const gchar **attribute_values;
74 };
75
76 struct xmldocument {
77         const gchar *href;
78         const gchar *xpointer;  
79         gpointer user_data;
80         struct xistate *first;
81         struct xistate *last;
82         int active;
83         int level;
84 };
85
86
87 struct xmlstate {
88         const gchar **attribute_names;
89         const gchar **attribute_values;
90         struct xmlstate *parent;
91         struct attr element_attr;
92         const gchar *element;
93         xmlerror **error;
94         struct element_func *func;
95         struct object_func *object_func;
96         struct xmldocument *document;
97 };
98
99
100 struct attr_fixme {
101         char *element;
102         char **attr_fixme;
103 };
104
105 static struct attr ** convert_to_attrs(struct xmlstate *state, struct attr_fixme *fixme)
106 {
107         const gchar **attribute_name=state->attribute_names;
108         const gchar **attribute_value=state->attribute_values;
109         const gchar *name;
110         int count=0;
111         struct attr **ret;
112         static int fixme_count;
113
114         while (*attribute_name) {
115                 count++;
116                 attribute_name++;
117         }
118         ret=g_new(struct attr *, count+1);
119         attribute_name=state->attribute_names;
120         count=0;
121         while (*attribute_name) {
122                 name=*attribute_name;
123                 if (fixme) {
124                         char **attr_fixme=fixme->attr_fixme;
125                         while (attr_fixme[0]) {
126                                 if (! strcmp(name, attr_fixme[0])) {
127                                         name=attr_fixme[1];
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);
130                                         break;
131                                 }
132                                 attr_fixme+=2;
133                         }
134                 }
135                 ret[count]=attr_new_from_text(name,*attribute_value);
136                 if (ret[count])
137                         count++;
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);
140                 attribute_name++;
141                 attribute_value++;
142         }       
143         ret[count]=NULL;
144         dbg(1,"ret=%p\n", ret);
145         return ret;
146 }
147
148
149 static const char * find_attribute(struct xmlstate *state, const char *attribute, int required)
150 {
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;
156                 attribute_name++;
157                 attribute_value++;
158         }
159         if (required) 
160                 g_set_error(state->error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "element '%s' is missing attribute '%s'", state->element, attribute);
161         return NULL;
162 }
163
164 static int
165 find_boolean(struct xmlstate *state, const char *attribute, int deflt, int required)
166 {
167         const char *value;
168
169         value=find_attribute(state, attribute, required);
170         if (! value)
171                 return deflt;
172         if (g_ascii_strcasecmp(value,"no") && g_ascii_strcasecmp(value,"0") && g_ascii_strcasecmp(value,"false")) 
173                 return 1;
174         return 0;
175 }
176
177 /**
178  * * Convert a string number to int
179  * *
180  * * @param val the string value to convert
181  * * @returns int value of converted string
182  * */
183 static int
184 convert_number(const char *val)
185 {
186         if (val)
187                 return g_ascii_strtoull(val,NULL,0);
188         else
189                 return 0;
190 }
191
192 static int
193 xmlconfig_announce(struct xmlstate *state)
194 {
195         const char *type,*value;
196         char key[32];
197         int level[3];
198         int i;
199         enum item_type itype;
200         char *tok, *type_str, *str;
201
202         type=find_attribute(state, "type", 1);
203         if (! type)
204                 return 0;
205         for (i = 0 ; i < 3 ; i++) {
206                 sprintf(key,"level%d", i);
207                 value=find_attribute(state, key, 0);
208                 if (value) 
209                         level[i]=convert_number(value);
210                 else
211                         level[i]=-1;
212         }
213         type_str=g_strdup(type);
214         str=type_str;
215         while ((tok=strtok(str, ","))) {
216                 itype=item_from_name(tok);
217                 navigation_set_announce(state->parent->element_attr.u.data, itype, level);
218                 str=NULL;
219         }
220         g_free(type_str);
221         return 1;
222 }
223 /**
224  * * Define the elements in our config
225  * *
226  * */
227
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)
237
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) },
270 };
271
272 struct object_func *
273 object_func_lookup(enum attr_type type)
274 {
275         int i;
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];
279         }
280         return NULL;
281 }
282
283 struct element_func {
284         char *name;
285         char *parent;
286         int (*func)(struct xmlstate *state);
287         enum attr_type type;
288 } elements[] = {
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},
327         {},
328 };
329
330
331 static char *attr_fixme_itemgra[]={
332         "type","item_types",
333         NULL,NULL,
334 };
335
336 static char *attr_fixme_text[]={
337         "label_size","text_size",
338         NULL,NULL,
339 };
340
341 static char *attr_fixme_circle[]={
342         "label_size","text_size",
343         NULL,NULL,
344 };
345
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},
352         {NULL,NULL},
353 };
354
355
356 static char *element_fixmes[]={
357         "item","itemgra",
358         "label","text",
359         NULL,NULL,
360 };
361 /**
362  * * Parse the opening tag of a config element
363  * *
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
370  * * @returns nothing
371  * */
372
373 static void
374 start_element(GMarkupParseContext *context,
375                 const gchar         *element_name,
376                 const gchar        **attribute_names,
377                 const gchar        **attribute_values,
378                 gpointer             user_data,
379                 xmlerror             **error)
380 {
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;
385         int found=0;
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);
391
392         /* determine if we have to fix any attributes */
393         while (attr_fixme[0].element) {
394                 if (!strcmp(element_name,attr_fixme[0].element))
395                         break;
396                 attr_fixme++;
397         }
398         if (!attr_fixme[0].element)
399                 attr_fixme=NULL;
400         
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]);
407                 }
408                 element_fixme+=2;
409         }
410         /* validate that this element is valid
411          * and that the element has a valid parent */
412         possible_parents=g_strdup("");
413         if (*parent)
414                 parent_name=(*parent)->element;
415         while (e->name) {
416                 if (!g_ascii_strcasecmp(element_name, e->name)) {
417                         found=1;
418                         s=g_strconcat(possible_parents,sep,e->parent,NULL);
419                         g_free(possible_parents);
420                         possible_parents=s;
421                         sep=",";
422                         if ((parent_name && e->parent && !g_ascii_strcasecmp(parent_name, e->parent)) ||
423                             (!parent_name && !e->parent))
424                                 func=e;
425                 }
426                 e++;
427         }
428         if (! found) {
429                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_UNKNOWN_ELEMENT,
430                                 "Unknown element '%s'", element_name);
431                 g_free(possible_parents);
432                 return;
433         }
434         if (! func) {
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);
439                 return;
440         }
441         g_free(possible_parents);
442
443         new=g_new(struct xmlstate, 1);
444         new->attribute_names=attribute_names;
445         new->attribute_values=attribute_values;
446         new->parent=*parent;
447         new->element_attr.u.data=NULL;
448         new->element=element_name;
449         new->error=error;
450         new->func=func;
451         new->object_func=NULL;
452         *parent=new;
453         if (!find_boolean(new, "enabled", 1, 0))
454                 return;
455         if (new->parent && !new->parent->element_attr.u.data)
456                 return;
457         if (func->func) {
458                 if (!func->func(new)) {
459                         return;
460                 }
461         } else {
462                 struct attr **attrs;
463
464                 new->object_func=object_func_lookup(func->type);
465                 if (! new->object_func)
466                         return;
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)
470                         parent_attr=NULL;
471                 else
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)
475                         return;
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);
481         }
482         return;
483 }
484
485
486 /* Called for close tags </foo> */
487 static void
488 end_element (GMarkupParseContext *context,
489                 const gchar         *element_name,
490                 gpointer             user_data,
491                 xmlerror             **error)
492 {
493         struct xmlstate *curr, **state = user_data;
494
495         dbg(2,"name='%s'\n", element_name);
496         curr=*state;
497         if (curr->object_func && curr->object_func->init)
498                 curr->object_func->init(curr->element_attr.u.data);
499         *state=curr->parent;
500         g_free(curr);
501 }
502
503 static gboolean parse_file(struct xmldocument *document, xmlerror **error);
504
505 static void
506 xinclude(GMarkupParseContext *context, const gchar **attribute_names, const gchar **attribute_values, struct xmldocument *doc_old, xmlerror **error)
507 {
508         struct xmldocument doc_new;
509         struct file_wordexp *we;
510         int i,count;
511         const char *href=NULL;
512         char **we_files;
513
514         if (doc_old->level >= 16) {
515                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include recursion too deep");
516                 return;
517         }
518         memset(&doc_new, 0, sizeof(doc_new));
519         i=0;
520         while (attribute_names[i]) {
521                 if(!g_ascii_strcasecmp("href", attribute_names[i])) {
522                         if (!href) 
523                                 href=attribute_values[i];
524                         else {
525                                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include has more than one href");
526                                 return;
527                         }
528                 } else if(!g_ascii_strcasecmp("xpointer", attribute_names[i])) {
529                         if (!doc_new.xpointer) 
530                                 doc_new.xpointer=attribute_values[i];
531                         else {
532                                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include has more than one xpointer");
533                                 return;
534                         }
535                 } else {
536                         g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include has invalid attributes");
537                         return;
538                 }
539                 i++;
540         }
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");
543                 return;
544         }
545         doc_new.level=doc_old->level+1;
546         doc_new.user_data=doc_old->user_data;
547         if (! href) {
548                 dbg(1,"no href, using '%s'\n", doc_old->href);
549                 doc_new.href=doc_old->href;
550                 parse_file(&doc_new, error);
551         } else {
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);
562                         }
563                 }
564                 file_wordexp_destroy(we);       
565                 
566         }
567         
568 }
569 static int
570 strncmp_len(const char *s1, int s1len, const char *s2)
571 {
572         int ret;
573 #if 0
574         char c[s1len+1];
575         strncpy(c, s1, s1len);
576         c[s1len]='\0';
577         dbg(0,"'%s' vs '%s'\n", c, s2);
578 #endif
579
580         ret=strncmp(s1, s2, s1len);
581         if (ret)
582                 return ret;
583         return strlen(s2)-s1len;
584 }
585
586 static int 
587 xpointer_value(const char *test, int len, struct xistate *elem, const char **out, int out_len)
588 {
589         int i,ret=0;
590         if (len <= 0 || out_len <= 0) {
591                 return 0;
592         }
593         if (!(strncmp_len(test,len,"name(.)"))) {
594                 out[0]=elem->element;
595                 return 1;
596         }
597         if (test[0] == '@') {
598                 i=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];
602                                 out_len--;
603                         }
604                         i++;
605                 }
606                 return ret;
607         }
608         return 0;
609 }
610
611 static int
612 xpointer_test(const char *test, int len, struct xistate *elem)
613 {
614         int eq,i,count,vlen,cond_req=1,cond=0;
615         char c;
616         const char *tmp[16];
617 #if 0
618         char test2[len+1];
619
620         strncpy(test2, test, len);
621         test2[len]='\0';
622         dbg(0,"%s\n", test2);
623 #endif
624         if (!len)
625                 return 0;
626         c=test[len-1];
627         if (c != '\'' && c != '"')
628                 return 0;
629         eq=strcspn(test, "=");
630         if (eq >= len || test[eq+1] != c)
631                 return 0;
632         vlen=eq;
633         if (eq > 0 && test[eq-1] == '!') {
634                 cond_req=0;
635                 vlen--;
636         }
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]))
640                         cond=1;
641         }
642         if (cond == cond_req)
643                 return 1;
644         return 0;
645 }
646
647 static int
648 xpointer_element_match(const char *xpointer, int len, struct xistate *elem)
649 {
650         int start,tlen,tlen2;
651 #if 0
652         char test2[len+1];
653
654         strncpy(test2, xpointer, len);
655         test2[len]='\0';
656         dbg(0,"%s\n", test2);
657 #endif
658         start=strcspn(xpointer, "[");
659         if (start > len)
660                 start=len;
661         if (strncmp_len(xpointer, start, elem->element) && (start != 1 || xpointer[0] != '*'))
662                 return 0;
663         if (start == len)
664                 return 1;
665         if (xpointer[len-1] != ']')
666                 return 0;
667         tlen=len-start-2;
668         for (;;) {
669                 start++;
670                 tlen2=strcspn(xpointer+start,"]");
671                 if (start + tlen2 > len)
672                         return 1;
673                 if (!xpointer_test(xpointer+start, tlen2, elem))
674                         return 0;
675                 start+=tlen2+1;
676         }
677 }
678
679 static int
680 xpointer_xpointer_match(const char *xpointer, int len, struct xistate *first)
681 {
682         const char *c;
683         int s;
684         dbg(2,"%s\n", xpointer);
685         if (xpointer[0] != '/')
686                 return 0;
687         c=xpointer+1;
688         len--;
689         do {
690                 s=strcspn(c, "/");
691                 if (s > len)
692                         s=len;
693                 if (! xpointer_element_match(c, s, first))
694                         return 0;
695                 first=first->child;
696                 c+=s+1;
697                 len-=s+1;
698         } while (len > 0 && first);
699         if (len > 0)
700                 return 0;
701         return 1;
702 }
703
704 static int
705 xpointer_match(const char *xpointer, struct xistate *first)
706 {
707         char *prefix="xpointer(";
708         int len;
709         if (! xpointer)
710                 return 1;
711         len=strlen(xpointer);
712         if (strncmp(xpointer,prefix,strlen(prefix)))
713                 return 0;
714         if (xpointer[len-1] != ')')
715                 return 0;
716         return xpointer_xpointer_match(xpointer+strlen(prefix), len-strlen(prefix)-1, first);
717
718 }
719
720 static void
721 xi_start_element(GMarkupParseContext *context,
722                 const gchar         *element_name,
723                 const gchar        **attribute_names,
724                 const gchar        **attribute_values,
725                 gpointer             user_data,
726                 xmlerror             **error)
727 {
728         struct xmldocument *doc=user_data;
729         struct xistate *xistate;
730         int i,count=0;
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]);
740                 }
741         }
742         xistate->parent=doc->last;
743         
744         if (doc->last) {
745                 doc->last->child=xistate;
746         } else
747                 doc->first=xistate;
748         doc->last=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);
752                         return;
753                 }
754                 start_element(context, element_name, xistate->attribute_names, xistate->attribute_values, doc->user_data, error);
755                 doc->active++;
756         }
757                 
758 }
759 /**
760  * * Reached closing tag of a config element
761  * *
762  * * @param context
763  * * @param element name
764  * * @param user_data ptr to xmldocument
765  * * @param error ptr to struct for error information
766  * * @returns nothing
767  * */
768
769 static void
770 xi_end_element (GMarkupParseContext *context,
771                 const gchar         *element_name,
772                 gpointer             user_data,
773                 xmlerror             **error)
774 {
775         struct xmldocument *doc=user_data;
776         struct xistate *xistate=doc->last;
777         int i=0;
778         doc->last=doc->last->parent;
779         if (! doc->last)
780                 doc->first=NULL;
781         else
782                 doc->last->child=NULL;
783         if (doc->active > 0) {
784                 if(!g_ascii_strcasecmp("xi:include", element_name)) {
785                         return;
786                 }
787                 end_element(context, element_name, doc->user_data, error);
788                 doc->active--;
789         }
790         while (xistate->attribute_names[i]) {
791                 g_free((char *)(xistate->attribute_names[i]));
792                 g_free((char *)(xistate->attribute_values[i]));
793                 i++;
794         }
795         g_free(xistate->attribute_names);
796         g_free(xistate->attribute_values);
797         g_free(xistate);
798 }
799
800 /* Called for character data */
801 /* text is not nul-terminated */
802 static void
803 xi_text (GMarkupParseContext *context,
804                 const gchar            *text,
805                 gsize                   text_len,
806                 gpointer                user_data,
807                 xmlerror               **error)
808 {
809         struct xmldocument *doc=user_data;
810         int i;
811         if (doc->active) {
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;
816                                 struct attr attr;
817                                 char *text_dup=malloc(text_len+1);
818
819                                 curr=*state;
820                                 strncpy(text_dup, text, text_len);
821                                 text_dup[text_len]='\0';
822                                 attr.type=attr_xml_text;
823                                 attr.u.str=text_dup;
824                                 if (curr->object_func && curr->object_func->add_attr)
825                                         curr->object_func->add_attr(curr->element_attr.u.data, &attr);
826                                 g_free(text_dup);
827                                 return;
828                         }
829                 }
830         }
831 }
832
833 #ifndef HAVE_GLIB
834 static void
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 *))
838 {
839         while (node) {
840                 if (start)
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);
844                 if (node->child)
845                         parse_node_text(node->child, data, start, end, text);
846                 if (end)
847                         end(NULL, node->name, data, NULL);
848                 node=node->ordered;
849         }
850 }
851 #endif
852
853 void
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 *))
857 {
858 #ifdef HAVE_GLIB
859         GMarkupParser parser = { start, end, text, NULL, NULL};
860         GMarkupParseContext *context;
861         gboolean result;
862
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);
866 #else
867         char *str=g_strdup(document);
868         ezxml_t root = ezxml_parse_str(str, strlen(str));
869         if (!root)
870                 return;
871         parse_node_text(root, data, start, end, text);
872         ezxml_free(root);
873         g_free(str);
874 #endif
875 }
876
877
878 #ifdef HAVE_GLIB
879
880 static const GMarkupParser parser = {
881         xi_start_element,
882         xi_end_element,
883         xi_text,
884         NULL,
885         NULL
886 };
887 /**
888  * * Parse the contents of the configuration file
889  * *
890  * * @param document struct holding info  about the config file
891  * * @param error info on any errors detected
892  * * @returns boolean TRUE or FALSE
893  * */
894
895 static gboolean
896 parse_file(struct xmldocument *document, xmlerror **error)
897 {
898         GMarkupParseContext *context;
899         gchar *contents, *message;
900         gsize len;
901         gint line, chr;
902         gboolean result;
903
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);
906
907         if (!g_file_get_contents (document->href, &contents, &len, error)) {
908                 g_markup_parse_context_free (context);
909                 return FALSE;
910         }
911         document->active=document->xpointer ? 0:1;
912         document->first=NULL;
913         document->last=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;
920         }
921         g_markup_parse_context_free (context);
922         g_free (contents);
923         dbg(1,"return %d\n", result);
924
925         return result;
926 }
927 #else
928 static void
929 parse_node(struct xmldocument *document, ezxml_t node)
930 {
931         while (node) {
932                 xi_start_element(NULL,node->name, node->attr, node->attr+1, document, NULL);
933                 if (node->txt)
934                         xi_text(NULL,node->txt,strlen(node->txt),document,NULL);
935                 if (node->child)
936                         parse_node(document, node->child);
937                 xi_end_element (NULL,node->name,document,NULL);
938                 node=node->ordered;
939         }
940 }
941
942 static gboolean
943 parse_file(struct xmldocument *document, xmlerror **error)
944 {
945         int fd;
946         ezxml_t root;
947         /* BUG workaround: ezxml parse file leaves negative fds unclosed */
948         fd = open(document->href, O_RDONLY, 0);
949         if (fd == -1)
950                 return FALSE;
951         root = ezxml_parse_fd(fd);
952         close(fd);
953         if (!root)
954                 return FALSE;
955         document->active=document->xpointer ? 0:1;
956         document->first=NULL;
957         document->last=NULL;
958
959         parse_node(document, root);
960
961         return TRUE;
962 }
963 #endif
964
965 /**
966  * * Load and parse the master config file
967  * *
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)
971  * */
972
973 gboolean config_load(const char *filename, xmlerror **error)
974 {
975         struct xmldocument document;
976         struct xmlstate *curr=NULL;
977         gboolean result;
978
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);
986                 result=FALSE;
987         }
988         dbg(1,"return %d\n", result);
989         return result;
990 }
991