Updated Turkish translation
[navit-package] / navit / xmlconfig.c
1 /**
2  * Navit, a modular navigation system.
3  * Copyright (C) 2005-2008 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 "debug.h"
28 #include "config.h"
29 #include "file.h"
30 #include "coord.h"
31 #include "layout.h"
32 #include "mapset.h"
33 #include "projection.h"
34 #include "map.h"
35 #include "navigation.h"
36 #include "navit.h"
37 #include "plugin.h"
38 #include "route.h"
39 #include "speech.h"
40 #include "track.h"
41 #include "vehicle.h"
42 #include "point.h"
43 #include "graphics.h"
44 #include "gui.h"
45 #include "osd.h"
46 #include "log.h"
47 #include "cursor.h"
48 #include "announcement.h"
49 #include "vehicleprofile.h"
50 #include "roadprofile.h"
51 #include "xmlconfig.h"
52
53 #ifdef HAVE_GLIB
54 #define ATTR_DISTANCE 1
55 #else
56 #include "ezxml.h"
57 #define ATTR_DISTANCE 2
58 #define G_MARKUP_ERROR 0
59 #define G_MARKUP_ERROR_INVALID_CONTENT 0
60 #define G_MARKUP_ERROR_PARSE 0
61 #define G_MARKUP_ERROR_UNKNOWN_ELEMENT 0
62 typedef void * GMarkupParseContext;
63 #endif
64
65 struct xistate {
66         struct xistate *parent;
67         struct xistate *child;
68         const gchar *element;
69         const gchar **attribute_names;
70         const gchar **attribute_values;
71 };
72
73 struct xmldocument {
74         const gchar *href;
75         const gchar *xpointer;  
76         gpointer user_data;
77         struct xistate *first;
78         struct xistate *last;
79         int active;
80         int level;
81 };
82
83
84 struct xmlstate {
85         const gchar **attribute_names;
86         const gchar **attribute_values;
87         struct xmlstate *parent;
88         struct attr element_attr;
89         const gchar *element;
90         xmlerror **error;
91         struct element_func *func;
92         struct object_func *object_func;
93         struct xmldocument *document;
94 };
95
96
97 struct attr_fixme {
98         char *element;
99         char **attr_fixme;
100 };
101
102 static struct attr ** convert_to_attrs(struct xmlstate *state, struct attr_fixme *fixme)
103 {
104         const gchar **attribute_name=state->attribute_names;
105         const gchar **attribute_value=state->attribute_values;
106         const gchar *name;
107         int count=0;
108         struct attr **ret;
109         static int fixme_count;
110
111         while (*attribute_name) {
112                 count++;
113                 attribute_name++;
114         }
115         ret=g_new(struct attr *, count+1);
116         attribute_name=state->attribute_names;
117         count=0;
118         while (*attribute_name) {
119                 name=*attribute_name;
120                 if (fixme) {
121                         char **attr_fixme=fixme->attr_fixme;
122                         while (attr_fixme[0]) {
123                                 if (! strcmp(name, attr_fixme[0])) {
124                                         name=attr_fixme[1];
125                                         if (fixme_count++ < 10)
126                                                 dbg(0,"Please change attribute '%s' to '%s' in <%s />\n", attr_fixme[0], attr_fixme[1], fixme->element);
127                                         break;
128                                 }
129                                 attr_fixme+=2;
130                         }
131                 }
132                 ret[count]=attr_new_from_text(name,*attribute_value);
133                 if (ret[count])
134                         count++;
135                 else if (strcmp(*attribute_name,"enabled"))
136                         dbg(0,"failed to create attribute '%s' with value '%s'\n", *attribute_name,*attribute_value);
137                 attribute_name++;
138                 attribute_value++;
139         }       
140         ret[count]=NULL;
141         dbg(1,"ret=%p\n", ret);
142         return ret;
143 }
144
145
146 static const char * find_attribute(struct xmlstate *state, const char *attribute, int required)
147 {
148         const gchar **attribute_name=state->attribute_names;
149         const gchar **attribute_value=state->attribute_values;
150         while(*attribute_name) {
151                 if(! g_ascii_strcasecmp(attribute,*attribute_name))
152                         return *attribute_value;
153                 attribute_name++;
154                 attribute_value++;
155         }
156         if (required) 
157                 g_set_error(state->error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "element '%s' is missing attribute '%s'", state->element, attribute);
158         return NULL;
159 }
160
161 static int
162 find_boolean(struct xmlstate *state, const char *attribute, int deflt, int required)
163 {
164         const char *value;
165
166         value=find_attribute(state, attribute, required);
167         if (! value)
168                 return deflt;
169         if (g_ascii_strcasecmp(value,"no") && g_ascii_strcasecmp(value,"0") && g_ascii_strcasecmp(value,"false")) 
170                 return 1;
171         return 0;
172 }
173
174 /**
175  * * Convert a string number to int
176  * *
177  * * @param val the string value to convert
178  * * @returns int value of converted string
179  * */
180 static int
181 convert_number(const char *val)
182 {
183         if (val)
184                 return g_ascii_strtoull(val,NULL,0);
185         else
186                 return 0;
187 }
188
189 static int
190 xmlconfig_config(struct xmlstate *state)
191 {
192         state->element_attr.u.data = (void *)1;
193         return 1;
194 }
195
196 static int
197 xmlconfig_announce(struct xmlstate *state)
198 {
199         const char *type,*value;
200         char key[32];
201         int level[3];
202         int i;
203         enum item_type itype;
204         char *tok, *type_str, *str;
205
206         type=find_attribute(state, "type", 1);
207         if (! type)
208                 return 0;
209         for (i = 0 ; i < 3 ; i++) {
210                 sprintf(key,"level%d", i);
211                 value=find_attribute(state, key, 0);
212                 if (value) 
213                         level[i]=convert_number(value);
214                 else
215                         level[i]=-1;
216         }
217         type_str=g_strdup(type);
218         str=type_str;
219         while ((tok=strtok(str, ","))) {
220                 itype=item_from_name(tok);
221                 navigation_set_announce(state->parent->element_attr.u.data, itype, level);
222                 str=NULL;
223         }
224         g_free(type_str);
225         return 1;
226 }
227 /**
228  * * Define the elements in our config
229  * *
230  * */
231
232 #define NEW(x) (void *(*)(struct attr *, struct attr **))(x)
233 #define GET(x) (int (*)(void *, enum attr_type type, struct attr *attr, struct attr_iter *iter))(x)
234 #define ITERN(x) (struct attr_iter * (*)(void *))(x) 
235 #define ITERD(x) (void (*)(struct attr_iter *iter))(x) 
236 #define SET(x) (int (*)(void *, struct attr *attr))(x)
237 #define ADD(x) (int (*)(void *, struct attr *attr))(x)
238 #define REMOVE(x) (int (*)(void *, struct attr *attr))(x)
239 #define INIT(x) (int (*)(void *))(x)
240 #define DESTROY(x) (void (*)(void *))(x)
241
242 static struct object_func object_funcs[] = {
243         { attr_announcement,NEW(announcement_new),  GET(announcement_get_attr), NULL, NULL, SET(announcement_set_attr), ADD(announcement_add_attr) },
244         { attr_arrows,     NEW(arrows_new)},
245         { attr_circle,     NEW(circle_new),   NULL, NULL, NULL, NULL, ADD(element_add_attr)},
246         { attr_coord,      NEW(coord_new_from_attrs)},
247         { attr_cursor,     NEW(cursor_new),   NULL, NULL, NULL, NULL, ADD(cursor_add_attr)},
248         { attr_debug,      NEW(debug_new)},
249         { attr_graphics,   NEW(graphics_new)},
250         { attr_gui,        NEW(gui_new), GET(gui_get_attr)},
251         { attr_icon,       NEW(icon_new),     NULL, NULL, NULL, NULL, ADD(element_add_attr)},
252         { attr_image,      NEW(image_new)},
253         { attr_itemgra,    NEW(itemgra_new),  NULL, NULL, NULL, NULL, ADD(itemgra_add_attr)},
254         { attr_layer,      NEW(layer_new),    NULL, NULL, NULL, NULL, ADD(layer_add_attr)},
255         { attr_layout,     NEW(layout_new),   NULL, NULL, NULL, NULL, ADD(layout_add_attr)},
256         { attr_log,        NEW(log_new)},
257         { attr_map,        NEW(map_new)},
258         { attr_mapset,     NEW(mapset_new),   NULL, NULL, NULL, NULL, ADD(mapset_add_attr)},
259         { attr_navigation, NEW(navigation_new), GET(navigation_get_attr)},
260         { 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)},
261         { attr_osd,        NEW(osd_new)},
262         { attr_plugins,    NEW(plugins_new),  NULL, NULL, NULL, NULL, NULL, NULL, INIT(plugins_init)},
263         { attr_plugin,     NEW(plugin_new)},
264         { attr_polygon,    NEW(polygon_new),  NULL, NULL, NULL, NULL, ADD(element_add_attr)},
265         { attr_polyline,   NEW(polyline_new), NULL, NULL, NULL, NULL, ADD(element_add_attr)},
266         { attr_roadprofile,NEW(roadprofile_new),  GET(roadprofile_get_attr), NULL, NULL, SET(roadprofile_set_attr), ADD(roadprofile_add_attr) },
267         { attr_route,      NEW(route_new), GET(route_get_attr)},
268         { attr_speech,     NEW(speech_new), GET(speech_get_attr), NULL, NULL, SET(speech_set_attr)},
269         { attr_text,       NEW(text_new)},
270         { attr_tracking,   NEW(tracking_new)},
271         { attr_vehicle,    NEW(vehicle_new),  GET(vehicle_get_attr), NULL, NULL, NULL, ADD(vehicle_add_attr) },
272         { attr_vehicleprofile, NEW(vehicleprofile_new),  GET(vehicleprofile_get_attr), NULL, NULL, SET(vehicleprofile_set_attr), ADD(vehicleprofile_add_attr) },
273 };
274
275 struct object_func *
276 object_func_lookup(enum attr_type type)
277 {
278         int i;
279         for (i = 0 ; i < sizeof(object_funcs)/sizeof(struct object_func); i++) {
280                 if (object_funcs[i].type == type)
281                         return &object_funcs[i];
282         }
283         return NULL;
284 }
285
286 struct element_func {
287         char *name;
288         char *parent;
289         int (*func)(struct xmlstate *state);
290         enum attr_type type;
291 } elements[] = {
292         { "config", NULL, xmlconfig_config},
293         { "announce", "navigation", xmlconfig_announce},
294         { "speech", "navit", NULL, attr_speech},
295         { "tracking", "navit", NULL, attr_tracking},
296         { "route", "navit", NULL, attr_route},
297         { "mapset", "navit", NULL, attr_mapset},
298         { "map",  "mapset", NULL, attr_map},
299         { "debug", "config", NULL, attr_debug},
300         { "osd", "navit", NULL, attr_osd},
301         { "navigation", "navit", NULL, attr_navigation},
302         { "navit", "config", NULL, attr_navit},
303         { "graphics", "navit", NULL, attr_graphics},
304         { "gui", "navit", NULL, attr_gui},
305         { "layout", "navit", NULL, attr_layout},
306         { "layer", "layout", NULL, attr_layer},
307         { "itemgra", "layer", NULL, attr_itemgra},
308         { "circle", "itemgra", NULL, attr_circle},
309         { "coord", "circle", NULL, attr_coord},
310         { "icon", "itemgra", NULL, attr_icon},
311         { "coord", "icon", NULL, attr_coord},
312         { "image", "itemgra", NULL, attr_image},
313         { "text", "itemgra", NULL, attr_text},
314         { "polygon", "itemgra", NULL, attr_polygon},
315         { "coord", "polygon", NULL, attr_coord},
316         { "polyline", "itemgra", NULL, attr_polyline},
317         { "coord", "polyline", NULL, attr_coord},
318         { "arrows", "itemgra", NULL, attr_arrows},
319         { "vehicle", "navit", NULL, attr_vehicle},
320         { "vehicleprofile", "navit", NULL, attr_vehicleprofile},
321         { "roadprofile", "vehicleprofile", NULL, attr_roadprofile},
322         { "announcement", "roadprofile", NULL, attr_announcement},
323         { "cursor", "vehicle", NULL, attr_cursor},
324         { "itemgra", "cursor", NULL, attr_itemgra},
325         { "log", "vehicle", NULL, attr_log},
326         { "log", "navit", NULL, attr_log},
327         { "plugins", "config", NULL, attr_plugins},
328         { "plugin", "plugins", NULL, attr_plugin},
329         {},
330 };
331
332
333 static char *attr_fixme_itemgra[]={
334         "type","item_types",
335         NULL,NULL,
336 };
337
338 static char *attr_fixme_text[]={
339         "label_size","text_size",
340         NULL,NULL,
341 };
342
343 static char *attr_fixme_circle[]={
344         "label_size","text_size",
345         NULL,NULL,
346 };
347
348 static struct attr_fixme attr_fixmes[]={
349         {"item",attr_fixme_itemgra},
350         {"itemgra",attr_fixme_itemgra},
351         {"text",attr_fixme_text},
352         {"label",attr_fixme_text},
353         {"circle",attr_fixme_circle},
354         {NULL,NULL},
355 };
356
357
358 static char *element_fixmes[]={
359         "item","itemgra",
360         "label","text",
361         NULL,NULL,
362 };
363 /**
364  * * Parse the opening tag of a config element
365  * *
366  * * @param context document parse context
367  * * @param element_name the current tag name
368  * * @param attribute_names ptr to return the set of attribute names
369  * * @param attribute_values ptr return the set of attribute values
370  * * @param user_data ptr to xmlstate structure
371  * * @param error ptr return error context
372  * * @returns nothing
373  * */
374
375 static void
376 start_element(GMarkupParseContext *context,
377                 const gchar         *element_name,
378                 const gchar        **attribute_names,
379                 const gchar        **attribute_values,
380                 gpointer             user_data,
381                 xmlerror             **error)
382 {
383         struct xmlstate *new=NULL, **parent = user_data;
384         struct element_func *e=elements,*func=NULL;
385         struct attr_fixme *attr_fixme=attr_fixmes;
386         char **element_fixme=element_fixmes;
387         int found=0;
388         static int fixme_count;
389         const char *parent_name=NULL;
390         char *s,*sep="",*possible_parents;
391         dbg(2,"name='%s' parent='%s'\n", element_name, *parent ? (*parent)->element:NULL);
392
393         /* determine if we have to fix any attributes */
394         while (attr_fixme[0].element) {
395                 if (!strcmp(element_name,attr_fixme[0].element))
396                         break;
397                 attr_fixme++;
398         }
399         if (!attr_fixme[0].element)
400                 attr_fixme=NULL;
401         
402         /* tell user to fix  deprecated element names */
403         while (element_fixme[0]) {
404                 if (!strcmp(element_name,element_fixme[0])) {
405                         element_name=element_fixme[1];
406                         if (fixme_count++ < 10)
407                                 dbg(0,"Please change <%s /> to <%s /> in config file\n", element_fixme[0], element_fixme[1]);
408                 }
409                 element_fixme+=2;
410         }
411         /* validate that this element is valid
412          * and that the element has a valid parent */
413         possible_parents=g_strdup("");
414         if (*parent)
415                 parent_name=(*parent)->element;
416         while (e->name) {
417                 if (!g_ascii_strcasecmp(element_name, e->name)) {
418                         found=1;
419                         s=g_strconcat(possible_parents,sep,e->parent,NULL);
420                         g_free(possible_parents);
421                         possible_parents=s;
422                         sep=",";
423                         if ((parent_name && e->parent && !g_ascii_strcasecmp(parent_name, e->parent)) ||
424                             (!parent_name && !e->parent))
425                                 func=e;
426                 }
427                 e++;
428         }
429         if (! found) {
430                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_UNKNOWN_ELEMENT,
431                                 "Unknown element '%s'", element_name);
432                 g_free(possible_parents);
433                 return;
434         }
435         if (! func) {
436                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT,
437                                 "Element '%s' within unexpected context '%s'. Expected '%s'%s",
438                                 element_name, parent_name, possible_parents, ! strcmp(possible_parents, "config") ? "\nPlease add <config> </config> tags at the beginning/end of your navit.xml": "");
439                 g_free(possible_parents);
440                 return;
441         }
442         g_free(possible_parents);
443
444         new=g_new(struct xmlstate, 1);
445         new->attribute_names=attribute_names;
446         new->attribute_values=attribute_values;
447         new->parent=*parent;
448         new->element_attr.u.data=NULL;
449         new->element=element_name;
450         new->error=error;
451         new->func=func;
452         new->object_func=NULL;
453         *parent=new;
454         if (!find_boolean(new, "enabled", 1, 0))
455                 return;
456         if (new->parent && !new->parent->element_attr.u.data)
457                 return;
458         if (func->func) {
459                 if (!func->func(new)) {
460                         return;
461                 }
462         } else {
463                 struct attr **attrs;
464
465                 new->object_func=object_func_lookup(func->type);
466                 if (! new->object_func)
467                         return;
468                 attrs=convert_to_attrs(new,attr_fixme);
469                 new->element_attr.type=attr_none;
470                 new->element_attr.u.data = new->object_func->new(&new->parent->element_attr, attrs);
471                 if (! new->element_attr.u.data)
472                         return;
473                 new->element_attr.type=attr_from_name(element_name);
474                 if (new->element_attr.type == attr_none) 
475                         dbg(0,"failed to create object of type '%s'\n", element_name);
476                 if (new->parent->object_func && new->parent->object_func->add_attr) 
477                         new->parent->object_func->add_attr(new->parent->element_attr.u.data, &new->element_attr);
478         }
479         return;
480 }
481
482
483 /* Called for close tags </foo> */
484 static void
485 end_element (GMarkupParseContext *context,
486                 const gchar         *element_name,
487                 gpointer             user_data,
488                 xmlerror             **error)
489 {
490         struct xmlstate *curr, **state = user_data;
491
492         dbg(2,"name='%s'\n", element_name);
493         curr=*state;
494         if (curr->object_func && curr->object_func->init)
495                 curr->object_func->init(curr->element_attr.u.data);
496         *state=curr->parent;
497         g_free(curr);
498 }
499
500 static gboolean parse_file(struct xmldocument *document, xmlerror **error);
501
502 static void
503 xinclude(GMarkupParseContext *context, const gchar **attribute_names, const gchar **attribute_values, struct xmldocument *doc_old, xmlerror **error)
504 {
505         struct xmldocument doc_new;
506         struct file_wordexp *we;
507         int i,count;
508         const char *href=NULL;
509         char **we_files;
510
511         if (doc_old->level >= 16) {
512                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include recursion too deep");
513                 return;
514         }
515         memset(&doc_new, 0, sizeof(doc_new));
516         i=0;
517         while (attribute_names[i]) {
518                 if(!g_ascii_strcasecmp("href", attribute_names[i])) {
519                         if (!href) 
520                                 href=attribute_values[i];
521                         else {
522                                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include has more than one href");
523                                 return;
524                         }
525                 } else if(!g_ascii_strcasecmp("xpointer", attribute_names[i])) {
526                         if (!doc_new.xpointer) 
527                                 doc_new.xpointer=attribute_values[i];
528                         else {
529                                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include has more than one xpointer");
530                                 return;
531                         }
532                 } else {
533                         g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include has invalid attributes");
534                         return;
535                 }
536                 i++;
537         }
538         if (!doc_new.xpointer && !href) {
539                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "xi:include has neither href nor xpointer");
540                 return;
541         }
542         doc_new.level=doc_old->level+1;
543         doc_new.user_data=doc_old->user_data;
544         if (! href) {
545                 dbg(1,"no href, using '%s'\n", doc_old->href);
546                 doc_new.href=doc_old->href;
547                 parse_file(&doc_new, error);
548         } else {
549                 dbg(1,"expanding '%s'\n", href);
550                 we=file_wordexp_new(href);
551                 we_files=file_wordexp_get_array(we);
552                 count=file_wordexp_get_count(we);
553                 dbg(1,"%d results\n", count);
554                 if (count != 1 || file_exists(we_files[0])) {
555                         for (i = 0 ; i < count ; i++) {
556                                 dbg(1,"result[%d]='%s'\n", i, we_files[i]);
557                                 doc_new.href=we_files[i];
558                                 parse_file(&doc_new, error);
559                         }
560                 }
561                 file_wordexp_destroy(we);       
562                 
563         }
564         
565 }
566 static int
567 strncmp_len(const char *s1, int s1len, const char *s2)
568 {
569         int ret;
570 #if 0
571         char c[s1len+1];
572         strncpy(c, s1, s1len);
573         c[s1len]='\0';
574         dbg(0,"'%s' vs '%s'\n", c, s2);
575 #endif
576
577         ret=strncmp(s1, s2, s1len);
578         if (ret)
579                 return ret;
580         return strlen(s2)-s1len;
581 }
582
583 static int 
584 xpointer_value(const char *test, int len, struct xistate *elem, const char **out, int out_len)
585 {
586         int i,ret=0;
587         if (len <= 0 || out_len <= 0) {
588                 return 0;
589         }
590         if (!(strncmp_len(test,len,"name(.)"))) {
591                 out[0]=elem->element;
592                 return 1;
593         }
594         if (test[0] == '@') {
595                 i=0;
596                 while (elem->attribute_names[i] && out_len > 0) {
597                         if (!strncmp_len(test+1,len-1,elem->attribute_names[i])) {
598                                 out[ret++]=elem->attribute_values[i];
599                                 out_len--;
600                         }
601                         i++;
602                 }
603                 return ret;
604         }
605         return 0;
606 }
607
608 static int
609 xpointer_test(const char *test, int len, struct xistate *elem)
610 {
611         int eq,i,count,vlen,cond_req=1,cond=0;
612         char c;
613         const char *tmp[16];
614 #if 0
615         char test2[len+1];
616
617         strncpy(test2, test, len);
618         test2[len]='\0';
619         dbg(0,"%s\n", test2);
620 #endif
621         if (!len)
622                 return 0;
623         c=test[len-1];
624         if (c != '\'' && c != '"')
625                 return 0;
626         eq=strcspn(test, "=");
627         if (eq >= len || test[eq+1] != c)
628                 return 0;
629         vlen=eq;
630         if (eq > 0 && test[eq-1] == '!') {
631                 cond_req=0;
632                 vlen--;
633         }
634         count=xpointer_value(test,vlen,elem,tmp,16);
635         for (i = 0 ; i < count ; i++) {
636                 if (!strncmp_len(test+eq+2,len-eq-3, tmp[i]))
637                         cond=1;
638         }
639         if (cond == cond_req)
640                 return 1;
641         return 0;
642 }
643
644 static int
645 xpointer_element_match(const char *xpointer, int len, struct xistate *elem)
646 {
647         int start,tlen,tlen2;
648 #if 0
649         char test2[len+1];
650
651         strncpy(test2, xpointer, len);
652         test2[len]='\0';
653         dbg(0,"%s\n", test2);
654 #endif
655         start=strcspn(xpointer, "[");
656         if (start > len)
657                 start=len;
658         if (strncmp_len(xpointer, start, elem->element) && (start != 1 || xpointer[0] != '*'))
659                 return 0;
660         if (start == len)
661                 return 1;
662         if (xpointer[len-1] != ']')
663                 return 0;
664         tlen=len-start-2;
665         for (;;) {
666                 start++;
667                 tlen2=strcspn(xpointer+start,"]");
668                 if (start + tlen2 > len)
669                         return 1;
670                 if (!xpointer_test(xpointer+start, tlen2, elem))
671                         return 0;
672                 start+=tlen2+1;
673         }
674 }
675
676 static int
677 xpointer_xpointer_match(const char *xpointer, int len, struct xistate *first)
678 {
679         const char *c;
680         int s;
681         dbg(2,"%s\n", xpointer);
682         if (xpointer[0] != '/')
683                 return 0;
684         c=xpointer+1;
685         len--;
686         do {
687                 s=strcspn(c, "/");
688                 if (s > len)
689                         s=len;
690                 if (! xpointer_element_match(c, s, first))
691                         return 0;
692                 first=first->child;
693                 c+=s+1;
694                 len-=s+1;
695         } while (len > 0 && first);
696         if (len > 0)
697                 return 0;
698         return 1;
699 }
700
701 static int
702 xpointer_match(const char *xpointer, struct xistate *first)
703 {
704         char *prefix="xpointer(";
705         int len;
706         if (! xpointer)
707                 return 1;
708         len=strlen(xpointer);
709         if (strncmp(xpointer,prefix,strlen(prefix)))
710                 return 0;
711         if (xpointer[len-1] != ')')
712                 return 0;
713         return xpointer_xpointer_match(xpointer+strlen(prefix), len-strlen(prefix)-1, first);
714
715 }
716
717 static void
718 xi_start_element(GMarkupParseContext *context,
719                 const gchar         *element_name,
720                 const gchar        **attribute_names,
721                 const gchar        **attribute_values,
722                 gpointer             user_data,
723                 xmlerror             **error)
724 {
725         struct xmldocument *doc=user_data;
726         struct xistate *xistate;
727         int i,count=0;
728         while (attribute_names[count++*ATTR_DISTANCE]);
729         xistate=g_new0(struct xistate, 1);
730         xistate->element=element_name;
731         xistate->attribute_names=g_new0(const char *, count);
732         xistate->attribute_values=g_new0(const char *, count);
733         for (i = 0 ; i < count ; i++) {
734                 if (attribute_names[i*ATTR_DISTANCE] && attribute_values[i*ATTR_DISTANCE]) {
735                         xistate->attribute_names[i]=g_strdup(attribute_names[i*ATTR_DISTANCE]);
736                         xistate->attribute_values[i]=g_strdup(attribute_values[i*ATTR_DISTANCE]);
737                 }
738         }
739         xistate->parent=doc->last;
740         
741         if (doc->last) {
742                 doc->last->child=xistate;
743         } else
744                 doc->first=xistate;
745         doc->last=xistate;
746         if (doc->active > 0 || xpointer_match(doc->xpointer, doc->first)) {
747                 if(!g_ascii_strcasecmp("xi:include", element_name)) {
748                         xinclude(context, xistate->attribute_names, xistate->attribute_values, doc, error);
749                         return;
750                 }
751                 start_element(context, element_name, xistate->attribute_names, xistate->attribute_values, doc->user_data, error);
752                 doc->active++;
753         }
754                 
755 }
756 /**
757  * * Reached closing tag of a config element
758  * *
759  * * @param context
760  * * @param element name
761  * * @param user_data ptr to xmldocument
762  * * @param error ptr to struct for error information
763  * * @returns nothing
764  * */
765
766 static void
767 xi_end_element (GMarkupParseContext *context,
768                 const gchar         *element_name,
769                 gpointer             user_data,
770                 xmlerror             **error)
771 {
772         struct xmldocument *doc=user_data;
773         struct xistate *xistate=doc->last;
774         int i=0;
775         doc->last=doc->last->parent;
776         if (! doc->last)
777                 doc->first=NULL;
778         else
779                 doc->last->child=NULL;
780         if (doc->active > 0) {
781                 if(!g_ascii_strcasecmp("xi:include", element_name)) {
782                         return;
783                 }
784                 end_element(context, element_name, doc->user_data, error);
785                 doc->active--;
786         }
787         while (xistate->attribute_names[i]) {
788                 g_free((char *)(xistate->attribute_names[i]));
789                 g_free((char *)(xistate->attribute_values[i]));
790                 i++;
791         }
792         g_free(xistate->attribute_names);
793         g_free(xistate->attribute_values);
794         g_free(xistate);
795 }
796
797 /* Called for character data */
798 /* text is not nul-terminated */
799 static void
800 xi_text (GMarkupParseContext *context,
801                 const gchar            *text,
802                 gsize                   text_len,
803                 gpointer                user_data,
804                 xmlerror               **error)
805 {
806 }
807
808
809 #ifdef HAVE_GLIB
810
811 static const GMarkupParser parser = {
812         xi_start_element,
813         xi_end_element,
814         xi_text,
815         NULL,
816         NULL
817 };
818 /**
819  * * Parse the contents of the configuration file
820  * *
821  * * @param document struct holding info  about the config file
822  * * @param error info on any errors detected
823  * * @returns boolean TRUE or FALSE
824  * */
825
826 static gboolean
827 parse_file(struct xmldocument *document, xmlerror **error)
828 {
829         GMarkupParseContext *context;
830         gchar *contents, *message;
831         gsize len;
832         gint line, chr;
833         gboolean result;
834
835         dbg(1,"enter filename='%s'\n", document->href);
836         context = g_markup_parse_context_new (&parser, 0, document, NULL);
837
838         if (!g_file_get_contents (document->href, &contents, &len, error)) {
839                 g_markup_parse_context_free (context);
840                 return FALSE;
841         }
842         document->active=document->xpointer ? 0:1;
843         document->first=NULL;
844         document->last=NULL;
845         result = g_markup_parse_context_parse (context, contents, len, error);
846         if (!result && error && *error) {
847                 g_markup_parse_context_get_position(context, &line, &chr);
848                 message=g_strdup_printf("%s at line %d, char %d\n", (*error)->message, line, chr);
849                 g_free((*error)->message);
850                 (*error)->message=message;
851         }
852         g_markup_parse_context_free (context);
853         g_free (contents);
854         dbg(1,"return %d\n", result);
855
856         return result;
857 }
858 #else
859 static void
860 parse_node(struct xmldocument *document, ezxml_t node)
861 {
862         while (node) {
863                 xi_start_element(NULL,node->name, node->attr, node->attr+1, document, NULL);
864                 if (node->txt)
865                         xi_text(NULL,node->txt,strlen(node->txt),document,NULL);
866                 if (node->child)
867                         parse_node(document, node->child);
868                 xi_end_element (NULL,node->name,document,NULL);
869                 node=node->ordered;
870         }
871 }
872
873 static gboolean
874 parse_file(struct xmldocument *document, xmlerror **error)
875 {
876         int fd;
877         ezxml_t root;
878         /* BUG workaround: ezxml parse file leaves negative fds unclosed */
879         fd = open(document->href, O_RDONLY, 0);
880         if (fd == -1)
881                 return FALSE;
882         root = ezxml_parse_fd(fd);
883         close(fd);
884         if (!root)
885                 return FALSE;
886         document->active=document->xpointer ? 0:1;
887         document->first=NULL;
888         document->last=NULL;
889
890         parse_node(document, root);
891
892         return TRUE;
893 }
894 #endif
895
896 /**
897  * * Load and parse the master config file
898  * *
899  * * @param filename FQFN of the file
900  * * @param error ptr to error details, if any
901  * * @returns boolean TRUE or FALSE (if error detected)
902  * */
903
904 gboolean config_load(const char *filename, xmlerror **error)
905 {
906         struct xmldocument document;
907         struct xmlstate *curr=NULL;
908         gboolean result;
909
910         dbg(1,"enter filename='%s'\n", filename);
911         memset(&document, 0, sizeof(document));
912         document.href=filename;
913         document.user_data=&curr;
914         result=parse_file(&document, error);
915         if (result && curr) {
916                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_PARSE, "element '%s' not closed", curr->element);
917                 result=FALSE;
918         }
919         dbg(1,"return %d\n", result);
920         return result;
921 }
922