50784fc046c1a57eb1a247bfdd1d110439adb01c
[navit-package] / src / xmlconfig.c
1 #include <glib.h>
2 #include <glib/gprintf.h>
3 #include <string.h>
4 #include "debug.h"
5 #include "coord.h"
6 #include "layout.h"
7 #include "mapset.h"
8 #include "projection.h"
9 #include "map.h"
10 #include "navigation.h"
11 #include "navit.h"
12 #include "plugin.h"
13 #include "route.h"
14 #include "speech.h"
15 #include "track.h"
16 #include "vehicle.h"
17 #include "point.h"
18 #include "graphics.h"
19 #include "gui.h"
20 #include "osd.h"
21 #include "log.h"
22 #include "xmlconfig.h"
23
24
25 struct xmlstate {
26         const gchar **attribute_names;
27         const gchar **attribute_values;
28         struct xmlstate *parent;
29         void *element_object;
30         const gchar *element;
31         GError **error;
32         struct element_func *func;
33 } *xmlstate_root;
34
35
36 static struct attr ** convert_to_attrs(struct xmlstate *state)
37 {
38         const gchar **attribute_name=state->attribute_names;
39         const gchar **attribute_value=state->attribute_values;
40         int count=0;
41         struct attr **ret;
42
43         while (*attribute_name) {
44                 count++;
45                 attribute_name++;
46         }
47         ret=g_new(struct attr *, count+1);
48         attribute_name=state->attribute_names;
49         count=0;
50         while (*attribute_name) {
51                 ret[count]=attr_new_from_text(*attribute_name,*attribute_value);
52                 if (ret[count])
53                         count++;
54                 attribute_name++;
55                 attribute_value++;
56         }       
57         ret[count]=NULL;
58         dbg(1,"ret=%p\n", ret);
59         return ret;
60 }
61
62
63 static const char * find_attribute(struct xmlstate *state, const char *attribute, int required)
64 {
65         const gchar **attribute_name=state->attribute_names;
66         const gchar **attribute_value=state->attribute_values;
67         while(*attribute_name) {
68                 if(! g_ascii_strcasecmp(attribute,*attribute_name))
69                         return *attribute_value;
70                 attribute_name++;
71                 attribute_value++;
72         }
73         if (required) 
74                 g_set_error(state->error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "element '%s' is missing attribute '%s'", state->element, attribute);
75         return NULL;
76 }
77
78 static int
79 find_color(struct xmlstate *state, int required, struct color *color)
80 {
81         const char *value;
82         int r,g,b,a;
83
84         value=find_attribute(state, "color", required);
85         if (! value)
86                 return 0;
87         if(strlen(value)==7){
88                 sscanf(value,"#%02x%02x%02x", &r, &g, &b);
89                 color->r = (r << 8) | r;
90                 color->g = (g << 8) | g;
91                 color->b = (b << 8) | b;
92                 color->a = (65535);
93         } else if(strlen(value)==9){
94                 sscanf(value,"#%02x%02x%02x%02x", &r, &g, &b, &a);
95                 color->r = (r << 8) | r;
96                 color->g = (g << 8) | g;
97                 color->b = (b << 8) | b;
98                 color->a = (a << 8) | a;
99         } else {
100                 dbg(0,"color %i has unknown format\n",value);
101         }
102         return 1;
103 }
104
105 static int
106 find_order(struct xmlstate *state, int required, int *min, int *max)
107 {
108         const char *value, *pos;
109         int ret;
110
111         *min=0;
112         *max=18;
113         value=find_attribute(state, "order", required);
114         if (! value)
115                 return 0;
116         pos=index(value, '-');
117         if (! pos) {
118                 ret=sscanf(value,"%d",min);
119                 *max=*min;
120         } else if (pos == value) 
121                 ret=sscanf(value,"-%d",max);
122         else
123                 ret=sscanf(value,"%d-%d", min, max);
124         return ret;
125 }
126
127 static int
128 find_boolean(struct xmlstate *state, const char *attribute, int deflt, int required)
129 {
130         const char *value;
131
132         value=find_attribute(state, attribute, required);
133         if (! value)
134                 return deflt;
135         if (g_ascii_strcasecmp(value,"no") && g_ascii_strcasecmp(value,"0") && g_ascii_strcasecmp(value,"false")) 
136                 return 1;
137         return 0;
138 }
139
140 static int
141 convert_number(const char *val)
142 {
143         return g_ascii_strtoull(val,NULL,0);
144 }
145
146 static int
147 xmlconfig_plugins(struct xmlstate *state)
148 {
149         state->element_object = plugins_new();
150         if (! state->element_object)
151                 return 0;
152         return 1;
153 }
154
155 static int
156 xmlconfig_plugin(struct xmlstate *state)
157 {
158         const char *path;
159         int active,lazy;
160
161         state->element_object=state->parent->element_object;
162         path=find_attribute(state, "path", 1);
163         if (! path)
164                 return 0;
165         active=find_boolean(state, "active", 1, 0);
166         lazy=find_boolean(state, "lazy", 1, 0);
167         plugins_add_path(state->parent->element_object, path, active, lazy);
168         return 1;
169 }
170
171 static int
172 xmlconfig_speech(struct xmlstate *state)
173 {
174         const char *type;
175         const char *data;
176         type=find_attribute(state, "type", 1);
177         if (! type)
178                 return 0;
179         data=find_attribute(state, "data", 0);
180         state->element_object = speech_new(type, data);
181         if (! state->element_object)
182                 return 0;
183         navit_set_speech(state->parent->element_object, state->element_object);
184         return 1;
185 }
186
187 static int
188 xmlconfig_debug(struct xmlstate *state)
189 {
190         const char *name,*level;
191         name=find_attribute(state, "name", 1);
192         if (! name)
193                 return 0;
194         level=find_attribute(state, "level", 1);
195         if (! level)
196                 return 0;
197         debug_level_set(name, convert_number(level));
198         return 1;
199 }
200
201 static int
202 xmlconfig_navit(struct xmlstate *state)
203 {
204         const char *value;
205         int zoom=0;
206         struct pcoord c;
207         struct coord co;
208         enum projection pro=projection_mg;
209
210         value=find_attribute(state, "zoom", 0);
211         if (value) 
212                 zoom=convert_number(value);
213         if (! zoom)
214                 zoom=256;
215         value=find_attribute(state, "center", 0);
216         if (! value || ! coord_parse(value, pro, &co)) {
217                 c.x=1300000;
218                 c.y=7000000;
219         }
220         c.x = co.x;
221         c.y = co.y;
222         c.pro = pro;
223         state->element_object = navit_new(&c, zoom);
224         if (! state->element_object)
225                 return 0;
226         return 1;
227 }
228
229 static int
230 xmlconfig_graphics(struct xmlstate *state)
231 {
232         struct attr **attrs;
233         const char *type=find_attribute(state, "type", 1);
234         if (! type)
235                 return 0;
236         attrs=convert_to_attrs(state);
237         state->element_object = graphics_new(type, attrs);
238         if (! state->element_object)
239                 return 0;
240         navit_set_graphics(state->parent->element_object, state->element_object, type);
241         return 1;
242 }
243
244 static int
245 xmlconfig_gui(struct xmlstate *state)
246 {
247         struct attr **attrs;
248         const char *type=find_attribute(state, "type", 1);
249         if (! type)
250                 return 0;
251         attrs=convert_to_attrs(state);
252         state->element_object = gui_new(state->parent->element_object, type, attrs);
253         if (! state->element_object)
254                 return 0;
255         navit_set_gui(state->parent->element_object, state->element_object, type);
256         return 1;
257 }
258
259 static int
260 xmlconfig_vehicle(struct xmlstate *state)
261 {
262         const char *s=find_attribute(state, "source", 1);
263         const char *value,*name;
264         struct color color;
265         int update=1, follow=0, active;
266         struct navit_vehicle *nv;
267
268         if (! s)
269                 return 0;
270         if (! find_color(state, 1, &color))
271                 return 0;
272         state->element_object = vehicle_new(s);
273         if (! state->element_object)
274                 return 0;
275         if ((value=find_attribute(state, "update", 0)))
276                 update=convert_number(value);
277         if ((value=find_attribute(state, "follow", 0)))
278                 follow=convert_number(value);
279         active=find_boolean(state, "active", 1, 0);
280         name=find_attribute(state, "name", 0);
281         nv=navit_add_vehicle(state->parent->element_object, state->element_object, name, &color, update, follow);
282         if (active)
283                 navit_set_vehicle(state->parent->element_object, nv);
284         return 1;
285 }
286
287 static int
288 xmlconfig_log(struct xmlstate *state)
289 {
290         struct attr **attrs;
291         attrs=convert_to_attrs(state);
292         state->element_object = log_new(attrs);
293         if (! state->element_object)
294                 return 0;
295         if (vehicle_add_log(state->parent->element_object, state->element_object, attrs))
296                 return 0;
297         return 1;
298 }
299
300
301 static int
302 xmlconfig_window_items(struct xmlstate *state)
303 {
304         int distance=-1;
305         enum item_type itype;
306         const char *name=find_attribute(state, "name", 1);
307         const char *value=find_attribute(state, "distance", 0);
308         const char *type=find_attribute(state, "type", 1);
309         char *tok,*str,*type_str,*saveptr=NULL;
310         if (! name || !type)
311                 return 0;
312         if (value) 
313                 distance=convert_number(value);
314         state->element_object = navit_window_items_new(name, distance);
315         type_str=g_strdup(type);
316         str=type_str;
317         while ((tok=strtok_r(str, ",", &saveptr))) {
318                 itype=item_from_name(tok);
319                 navit_window_items_add_item(state->element_object, itype);
320                 str=NULL;
321         }
322         g_free(type_str);
323
324         navit_add_window_items(state->parent->element_object, state->element_object);
325
326         return 1;
327 }
328
329
330 static int
331 xmlconfig_tracking(struct xmlstate *state)
332 {
333         state->element_object = tracking_new(NULL);
334         navit_tracking_add(state->parent->element_object, state->element_object);
335         return 1;
336 }
337
338 static int
339 xmlconfig_route(struct xmlstate *state)
340 {
341         state->element_object = route_new(NULL);
342         navit_route_add(state->parent->element_object, state->element_object);
343         return 1;
344 }
345
346 static int
347 xmlconfig_speed(struct xmlstate *state)
348 {
349         const char *type;
350         const char *value;
351         int v;
352         enum item_type itype;
353         char *saveptr=NULL, *tok, *type_str, *str;
354
355         type=find_attribute(state, "type", 1);
356         if (! type)
357                 return 0;
358         value=find_attribute(state, "value", 1);
359         if (! value)
360                 return 0;
361         v=convert_number(value);
362         type_str=g_strdup(type);
363         str=type_str;
364         while ((tok=strtok_r(str, ",", &saveptr))) {
365                 itype=item_from_name(tok);
366                 route_set_speed(state->parent->element_object, itype, v);
367                 str=NULL;
368         }
369         g_free(type_str);
370
371         return 1;
372 }
373
374
375 static int
376 xmlconfig_navigation(struct xmlstate *state)
377 {
378         state->element_object = navigation_new(NULL);
379         navit_navigation_add(state->parent->element_object, state->element_object);
380         return 1;
381 }
382
383 static int
384 xmlconfig_osd(struct xmlstate *state)
385 {
386         struct attr **attrs;
387         const char *type=find_attribute(state, "type", 1);
388         if (! type)
389                 return 0;
390         attrs=convert_to_attrs(state);
391         state->element_object = osd_new(state->parent->element_object, type, attrs);
392         return 1;
393 }
394
395 static int
396 xmlconfig_announce(struct xmlstate *state)
397 {
398         const char *type,*value;
399         char key[32];
400         int level[3];
401         int i;
402         enum item_type itype;
403         char *saveptr=NULL, *tok, *type_str, *str;
404
405         type=find_attribute(state, "type", 1);
406         if (! type)
407                 return 0;
408         for (i = 0 ; i < 3 ; i++) {
409                 sprintf(key,"level%d", i);
410                 value=find_attribute(state, key, 0);
411                 if (value) 
412                         level[i]=convert_number(value);
413                 else
414                         level[i]=-1;
415         }
416         type_str=g_strdup(type);
417         str=type_str;
418         while ((tok=strtok_r(str, ",", &saveptr))) {
419                 itype=item_from_name(tok);
420                 navigation_set_announce(state->parent->element_object, itype, level);
421                 str=NULL;
422         }
423         g_free(type_str);
424         return 1;
425 }
426
427 static int
428 xmlconfig_mapset(struct xmlstate *state)
429 {
430         state->element_object = mapset_new();
431         if (! state->element_object)
432                 return 0;
433         navit_add_mapset(state->parent->element_object, state->element_object);
434
435         return 1;
436 }
437
438 static int
439 xmlconfig_map(struct xmlstate *state)
440 {
441         struct attr **attrs;
442         const char *type=find_attribute(state, "type", 1);
443         if (! type)
444                 return 0;
445         attrs=convert_to_attrs(state);
446         state->element_object = map_new(type, attrs);
447         if (! state->element_object)
448                 return 0;
449         if (!find_boolean(state, "active", 1, 0))
450                 map_set_active(state->element_object, 0);
451         mapset_add(state->parent->element_object, state->element_object);
452
453         return 1;
454 }
455
456 static int
457 xmlconfig_layout(struct xmlstate *state)
458 {
459         const char *name=find_attribute(state, "name", 1);
460
461         if (! name)
462                 return 0;
463         state->element_object = layout_new(name);
464         if (! state->element_object)
465                 return 0;
466         navit_add_layout(state->parent->element_object, state->element_object);
467         return 1;
468 }
469
470 static int
471 xmlconfig_layer(struct xmlstate *state)
472 {
473         const char *name=find_attribute(state, "name", 1);
474         if (! name)
475                 return 0;
476         state->element_object = layer_new(name, convert_number(find_attribute(state, "details", 0)));
477         if (! state->element_object)
478                 return 0;
479         layout_add_layer(state->parent->element_object, state->element_object);
480         return 1;
481 }
482
483 static int
484 xmlconfig_item(struct xmlstate *state)
485 {
486         const char *type=find_attribute(state, "type", 1);
487         int min, max;
488         enum item_type itype;
489         char *saveptr=NULL, *tok, *type_str, *str;
490
491         if (! type)
492                 return 0;
493         if (! find_order(state, 1, &min, &max))
494                 return 0;
495         state->element_object=itemtype_new(min, max);
496         if (! state->element_object)
497                 return 0;
498         type_str=g_strdup(type);
499         str=type_str;
500         layer_add_itemtype(state->parent->element_object, state->element_object);
501         while ((tok=strtok_r(str, ",", &saveptr))) {
502                 itype=item_from_name(tok);
503                 itemtype_add_type(state->element_object, itype);
504                 str=NULL;
505         }
506         g_free(type_str);
507
508         return 1;
509 }
510
511 static int
512 xmlconfig_polygon(struct xmlstate *state)
513 {
514         struct color color;
515
516         if (! find_color(state, 1, &color))
517                 return 0;
518         state->element_object=polygon_new(&color);
519         if (! state->element_object)
520                 return 0;
521         itemtype_add_element(state->parent->element_object, state->element_object);
522
523         return 1;
524 }
525
526 static int
527 xmlconfig_polyline(struct xmlstate *state)
528 {
529         struct color color;
530         const char *width;
531         int w=0;
532
533         if (! find_color(state, 1, &color))
534                 return 0;
535         width=find_attribute(state, "width", 0);
536         if (width) 
537                 w=convert_number(width);
538         state->element_object=polyline_new(&color, w);
539         if (! state->element_object)
540                 return 0;
541         itemtype_add_element(state->parent->element_object, state->element_object);
542
543         return 1;
544 }
545
546 static int
547 xmlconfig_circle(struct xmlstate *state)
548 {
549         struct color color;
550         const char *width, *radius, *label_size;
551         int w=0,r=0,ls=0;
552
553         if (! find_color(state, 1, &color))
554                 return 0;
555         width=find_attribute(state, "width", 0);
556         if (width) 
557                 w=convert_number(width);
558         radius=find_attribute(state, "radius", 0);
559         if (radius) 
560                 r=convert_number(radius);
561         label_size=find_attribute(state, "label_size", 0);
562         if (label_size) 
563                 ls=convert_number(label_size);
564         state->element_object=circle_new(&color, r, w, ls);
565         if (! state->element_object)
566                 return 0;
567         itemtype_add_element(state->parent->element_object, state->element_object);
568
569         return 1;
570 }
571
572 static int
573 xmlconfig_label(struct xmlstate *state)
574 {
575         const char *label_size;
576         int ls=0;
577
578         label_size=find_attribute(state, "label_size", 0);
579         if (label_size) 
580                 ls=convert_number(label_size);
581         state->element_object=label_new(ls);
582         if (! state->element_object)
583                 return 0;
584         itemtype_add_element(state->parent->element_object, state->element_object);
585
586         return 1;
587 }
588
589 static int
590 xmlconfig_icon(struct xmlstate *state)
591 {
592         const char *src=find_attribute(state, "src", 1);
593
594         if (! src)
595                 return 0;
596         state->element_object=icon_new(src);
597         if (! state->element_object)
598                 return 0;
599         itemtype_add_element(state->parent->element_object, state->element_object);
600
601         return 1;
602 }
603
604 static int
605 xmlconfig_image(struct xmlstate *state)
606 {
607         state->element_object=image_new();
608         if (! state->element_object)
609                 return 0;
610         itemtype_add_element(state->parent->element_object, state->element_object);
611
612         return 1;
613 }
614
615 struct element_func {
616         char *name;
617         char *parent;
618         int (*func)(struct xmlstate *state);
619 } elements[] = {
620         { "debug", NULL, xmlconfig_debug},
621         { "navit", NULL, xmlconfig_navit},
622         { "graphics", "navit", xmlconfig_graphics},
623         { "gui", "navit", xmlconfig_gui},
624         { "layout", "navit", xmlconfig_layout},
625         { "layer", "layout", xmlconfig_layer},
626         { "item", "layer", xmlconfig_item},
627         { "circle", "item", xmlconfig_circle},
628         { "icon", "item", xmlconfig_icon},
629         { "image", "item", xmlconfig_image},
630         { "label", "item", xmlconfig_label},
631         { "polygon", "item", xmlconfig_polygon},
632         { "polyline", "item", xmlconfig_polyline},
633         { "mapset", "navit", xmlconfig_mapset},
634         { "map",  "mapset", xmlconfig_map},
635         { "navigation", "navit", xmlconfig_navigation},
636         { "osd", "navit", xmlconfig_osd},
637         { "announce", "navigation", xmlconfig_announce},
638         { "speech", "navit", xmlconfig_speech},
639         { "tracking", "navit", xmlconfig_tracking},
640         { "route", "navit", xmlconfig_route},
641         { "speed", "route", xmlconfig_speed},
642         { "vehicle", "navit", xmlconfig_vehicle},
643         { "log", "vehicle", xmlconfig_log},
644         { "window_items", "navit", xmlconfig_window_items},
645         { "plugins", NULL, xmlconfig_plugins},
646         { "plugin", "plugins", xmlconfig_plugin},
647         {},
648 };
649
650 static void
651 start_element (GMarkupParseContext *context,
652                 const gchar         *element_name,
653                 const gchar        **attribute_names,
654                 const gchar        **attribute_values,
655                 gpointer             user_data,
656                 GError             **error)
657 {
658         struct xmlstate *new=NULL, **parent = user_data;
659         struct element_func *e=elements,*func=NULL;
660         const char *parent_name=NULL;
661         while (e->name) {
662                 if (!g_ascii_strcasecmp(element_name, e->name)) {
663                         func=e;
664                 }
665                 e++;
666         }
667         if (! func) {
668                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_UNKNOWN_ELEMENT,
669                                 "Unknown element '%s'", element_name);
670                 return;
671         }
672         if (*parent)
673                 parent_name=(*parent)->element;
674         if ((parent_name && func->parent && g_ascii_strcasecmp(parent_name, func->parent)) || 
675             (!parent_name && func->parent) || (parent_name && !func->parent)) {
676                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT,
677                                 "Element '%s' within unexpected context '%s'. Expected '%s'",
678                                 element_name, parent_name, func->parent);
679                 return;
680         }
681
682         new=g_new(struct xmlstate, 1);
683         new->attribute_names=attribute_names;
684         new->attribute_values=attribute_values;
685         new->parent=*parent;
686         new->element_object=NULL;
687         new->element=element_name;
688         new->error=error;
689         new->func=func;
690         *parent=new;
691         if (!find_boolean(new, "enabled", 1, 0))
692                 return;
693         if (new->parent && !new->parent->element_object)
694                 return;
695         if (!func->func(new)) {
696                 return;
697         }
698         return;
699 #if 0
700         struct elem_data *data = user_data;
701         void *elem=NULL;
702         void *parent_object;
703         char *parent_token;
704         parent_object=data->elem_stack ? data->elem_stack->data : NULL;
705         parent_token=data->token_stack ? data->token_stack->data : NULL;
706
707         /* g_printf("start_element: %s AN: %s AV: %s\n",element_name,*attribute_names,*attribute_values); */
708
709
710         printf("Unknown element '%s'\n", element_name);
711 #if 0
712         data->elem_stack = g_list_prepend(data->elem_stack, elem);
713         data->token_stack = g_list_prepend(data->token_stack, (gpointer)element_name);
714 #endif
715 #endif
716 }
717
718
719 /* Called for close tags </foo> */
720 static void
721 end_element (GMarkupParseContext *context,
722                 const gchar         *element_name,
723                 gpointer             user_data,
724                 GError             **error)
725 {
726         struct xmlstate *curr, **state = user_data;
727
728         curr=*state;
729         if(!g_ascii_strcasecmp("plugins", element_name) && curr->element_object) 
730                 plugins_init(curr->element_object);
731         if(!g_ascii_strcasecmp("navit", element_name) && curr->element_object) 
732                 navit_init(curr->element_object);
733         *state=curr->parent;
734         g_free(curr);
735 }
736
737
738 /* Called for character data */
739 /* text is not nul-terminated */
740 static void
741 text (GMarkupParseContext *context,
742                 const gchar            *text,
743                 gsize                   text_len,
744                 gpointer                user_data,
745                 GError               **error)
746 {
747         struct xmlstate **state = user_data;
748
749         (void) state;
750 }
751
752
753
754 static const GMarkupParser parser = {
755         start_element,
756         end_element,
757         text,
758         NULL,
759         NULL
760 };
761
762
763 gboolean config_load(char *filename, GError **error)
764 {
765         GMarkupParseContext *context;
766         char *contents;
767         gsize len;
768         gboolean result;
769         gint line;
770         gint chr;
771         gchar *message;
772
773         struct xmlstate *curr=NULL;
774         
775         context = g_markup_parse_context_new (&parser, 0, &curr, NULL);
776
777         if (!g_file_get_contents (filename, &contents, &len, error))
778                 return FALSE;
779
780         result = g_markup_parse_context_parse (context, contents, len, error);
781         if (result && curr) {
782                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_PARSE, "element '%s' not closed", curr->element);
783                 result=FALSE;   
784         }
785         if (!result && error && *error) {
786                 g_markup_parse_context_get_position(context, &line, &chr);
787                 message=g_strdup_printf("%s at line %d, char %d\n", (*error)->message, line, chr);
788                 g_free((*error)->message);
789                 (*error)->message=message;
790         }
791         g_markup_parse_context_free (context);
792         g_free (contents);
793
794         return result;
795 }
796