Merge with modular_map
[navit-package] / src / xmlconfig.c
1 #include <glib.h>
2 #include <glib/gprintf.h>
3 #include <string.h>
4 #include "xmlconfig.h"
5 #include "navit.h"
6 #include "vehicle.h"
7 #include "mapset.h"
8 #include "map.h"
9 #include "layout.h"
10 #include "projection.h"
11 #include "coord.h"
12 #include "plugin.h"
13
14
15 struct xmlstate {
16         const gchar **attribute_names;
17         const gchar **attribute_values;
18         struct xmlstate *parent;
19         void *element_object;
20         const gchar *element;
21         GError **error;
22         struct element_func *func;
23 } *xmlstate_root;
24
25
26 static const char * find_attribute(struct xmlstate *state, const char *attribute, int required)
27 {
28         const gchar **attribute_name=state->attribute_names;
29         const gchar **attribute_value=state->attribute_values;
30         while(*attribute_name) {
31                 if(! g_ascii_strcasecmp(attribute,*attribute_name))
32                         return *attribute_value;
33                 attribute_name++;
34                 attribute_value++;
35         }
36         if (required) 
37                 g_set_error(state->error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT, "element '%s' is missing attribute '%s'", state->element, attribute);
38         return NULL;
39 }
40
41 static int
42 find_color(struct xmlstate *state, int required, struct color *color)
43 {
44         const char *value;
45         int r,g,b;
46
47         value=find_attribute(state, "color", required);
48         if (! value)
49                 return 0;
50
51         sscanf(value,"#%02x%02x%02x", &r, &g, &b);
52         color->r = (r << 8) | r;
53         color->g = (g << 8) | g;
54         color->b = (b << 8) | b;
55         return 1;
56 }
57
58 static int
59 find_zoom(struct xmlstate *state, int required, int *min, int *max)
60 {
61         const char *value, *pos;
62         int ret;
63
64         *min=0;
65         *max=18;
66         value=find_attribute(state, "zoom", required);
67         if (! value)
68                 return 0;
69         pos=index(value, '-');
70         if (! pos) {
71                 ret=sscanf(value,"%d",min);
72                 *max=*min;
73         } else if (pos == value) 
74                 ret=sscanf(value,"-%d",max);
75         else
76                 ret=sscanf(value,"%d-%d", min, max);
77         return ret;
78 }
79
80 static int
81 find_boolean(struct xmlstate *state, const char *attribute, int deflt, int required)
82 {
83         const char *value;
84
85         value=find_attribute(state, attribute, required);
86         if (! value)
87                 return deflt;
88         if (g_ascii_strcasecmp(value,"no") && g_ascii_strcasecmp(value,"0") && g_ascii_strcasecmp(value,"false")) 
89                 return 1;
90         return 0;
91 }
92
93 static int
94 convert_number(const char *val)
95 {
96         return g_ascii_strtoull(val,NULL,0);
97 }
98
99 static int
100 xmlconfig_plugins(struct xmlstate *state)
101 {
102         state->element_object = plugins_new();
103         if (! state->element_object)
104                 return 0;
105         return 1;
106 }
107
108 static int
109 xmlconfig_plugin(struct xmlstate *state)
110 {
111         const char *path;
112         int active,lazy;
113
114         state->element_object=state->parent->element_object;
115         path=find_attribute(state, "path", 1);
116         if (! path)
117                 return 0;
118         active=find_boolean(state, "active", 1, 0);
119         lazy=find_boolean(state, "lazy", 1, 0);
120         plugins_add_path(state->parent->element_object, path, active, lazy);
121         return 1;
122 }
123
124 static int
125 xmlconfig_navit(struct xmlstate *state)
126 {
127         const char *value,*gui,*graphics;
128         int zoom=0;
129         struct coord c;
130         enum projection pro=projection_mg;
131
132         value=find_attribute(state, "zoom", 0);
133         if (value) 
134                 zoom=convert_number(value);
135         if (! zoom)
136                 zoom=256;
137         value=find_attribute(state, "center", 0);
138         if (! value || ! coord_parse(value, pro, &c)) {
139                 c.x=1300000;
140                 c.y=7000000;
141         }
142         gui=find_attribute(state, "gui", 0);
143         if (! gui)
144                 gui="gtk";
145         graphics=find_attribute(state, "graphics", 0);
146         if (! graphics)
147                 graphics="gtk_drawing_area";
148         state->element_object = navit_new(gui, graphics, &c, pro, zoom);
149         if (! state->element_object)
150                 return 0;
151         return 1;
152 }
153
154 static int
155 xmlconfig_vehicle(struct xmlstate *state)
156 {
157         const char *s=find_attribute(state, "source", 1);
158         const char *value;
159         struct color color;
160         int update=1, follow=0;
161
162         if (! s)
163                 return 0;
164         if (! find_color(state, 1, &color))
165                 return 0;
166         state->element_object = vehicle_new(s);
167         if (! state->element_object)
168                 return 0;
169         if ((value=find_attribute(state, "update", 0)))
170                 update=convert_number(value);
171         if ((value=find_attribute(state, "follow", 0)))
172                 follow=convert_number(value);
173         
174         navit_vehicle_add(state->parent->element_object, state->element_object, &color, update, follow);
175         return 1;
176 }
177
178 static int
179 xmlconfig_mapset(struct xmlstate *state)
180 {
181         state->element_object = mapset_new();
182         if (! state->element_object)
183                 return 0;
184         navit_add_mapset(state->parent->element_object, state->element_object);
185
186         return 1;
187 }
188
189 static int
190 xmlconfig_map(struct xmlstate *state)
191 {
192         const char *type=find_attribute(state, "type", 1);
193         const char *data=find_attribute(state, "data", 1);
194         if (! type || ! data)
195                 return 0;
196         state->element_object = map_new(type, data);
197         if (! state->element_object)
198                 return 0;
199         if (!find_boolean(state, "active", 1, 0))
200                 map_set_active(state->element_object, 0);
201         mapset_add(state->parent->element_object, state->element_object);
202
203         return 1;
204 }
205
206 static int
207 xmlconfig_layout(struct xmlstate *state)
208 {
209         const char *name=find_attribute(state, "name", 1);
210
211         if (! name)
212                 return 0;
213         state->element_object = layout_new(name);
214         if (! state->element_object)
215                 return 0;
216         navit_add_layout(state->parent->element_object, state->element_object);
217         return 1;
218 }
219
220 static int
221 xmlconfig_layer(struct xmlstate *state)
222 {
223         const char *name=find_attribute(state, "name", 1);
224         if (! name)
225                 return 0;
226         state->element_object = layer_new(name, convert_number(find_attribute(state, "details", 0)));
227         if (! state->element_object)
228                 return 0;
229         layout_add_layer(state->parent->element_object, state->element_object);
230         return 1;
231 }
232
233 static int
234 xmlconfig_item(struct xmlstate *state)
235 {
236         const char *type=find_attribute(state, "type", 1);
237         int min, max;
238         enum item_type itype;
239         char *saveptr, *tok, *type_str, *str;
240
241         if (! type)
242                 return 0;
243         if (! find_zoom(state, 1, &min, &max))
244                 return 0;
245         state->element_object=itemtype_new(min, max);
246         if (! state->element_object)
247                 return 0;
248         type_str=g_strdup(type);
249         str=type_str;
250         layer_add_itemtype(state->parent->element_object, state->element_object);
251         while ((tok=strtok_r(str, ",", &saveptr))) {
252                 itype=item_from_name(tok);
253                 itemtype_add_type(state->element_object, itype);
254                 str=NULL;
255         }
256         g_free(type_str);
257
258         return 1;
259 }
260
261 static int
262 xmlconfig_polygon(struct xmlstate *state)
263 {
264         struct color color;
265
266         if (! find_color(state, 1, &color))
267                 return 0;
268         state->element_object=polygon_new(&color);
269         if (! state->element_object)
270                 return 0;
271         itemtype_add_element(state->parent->element_object, state->element_object);
272
273         return 1;
274 }
275
276 static int
277 xmlconfig_polyline(struct xmlstate *state)
278 {
279         struct color color;
280         const char *width;
281         int w=0;
282
283         if (! find_color(state, 1, &color))
284                 return 0;
285         width=find_attribute(state, "width", 0);
286         if (width) 
287                 w=convert_number(width);
288         state->element_object=polyline_new(&color, w);
289         if (! state->element_object)
290                 return 0;
291         itemtype_add_element(state->parent->element_object, state->element_object);
292
293         return 1;
294 }
295
296 static int
297 xmlconfig_circle(struct xmlstate *state)
298 {
299         struct color color;
300         const char *width, *radius, *label_size;
301         int w=0,r=0,ls=0;
302
303         if (! find_color(state, 1, &color))
304                 return 0;
305         width=find_attribute(state, "width", 0);
306         if (width) 
307                 w=convert_number(width);
308         radius=find_attribute(state, "radius", 0);
309         if (radius) 
310                 r=convert_number(radius);
311         label_size=find_attribute(state, "label_size", 0);
312         if (label_size) 
313                 ls=convert_number(label_size);
314         state->element_object=circle_new(&color, r, w, ls);
315         if (! state->element_object)
316                 return 0;
317         itemtype_add_element(state->parent->element_object, state->element_object);
318
319         return 1;
320 }
321
322 static int
323 xmlconfig_label(struct xmlstate *state)
324 {
325         const char *label_size;
326         int ls=0;
327
328         label_size=find_attribute(state, "label_size", 0);
329         if (label_size) 
330                 ls=convert_number(label_size);
331         state->element_object=label_new(ls);
332         if (! state->element_object)
333                 return 0;
334         itemtype_add_element(state->parent->element_object, state->element_object);
335
336         return 1;
337 }
338
339 static int
340 xmlconfig_icon(struct xmlstate *state)
341 {
342         const char *src=find_attribute(state, "src", 1);
343
344         if (! src)
345                 return 0;
346         state->element_object=icon_new(src);
347         if (! state->element_object)
348                 return 0;
349         itemtype_add_element(state->parent->element_object, state->element_object);
350
351         return 1;
352 }
353
354 static int
355 xmlconfig_image(struct xmlstate *state)
356 {
357         state->element_object=image_new();
358         if (! state->element_object)
359                 return 0;
360         itemtype_add_element(state->parent->element_object, state->element_object);
361
362         return 1;
363 }
364
365 struct element_func {
366         char *name;
367         char *parent;
368         int (*func)(struct xmlstate *state);
369 } elements[] = {
370         { "plugins", NULL, xmlconfig_plugins},
371         { "plugin", "plugins", xmlconfig_plugin},
372         { "navit", NULL, xmlconfig_navit},
373         { "vehicle", "navit", xmlconfig_vehicle},
374         { "mapset", "navit", xmlconfig_mapset},
375         { "map",  "mapset", xmlconfig_map},
376         { "layout", "navit", xmlconfig_layout},
377         { "layer", "layout", xmlconfig_layer},
378         { "item", "layer", xmlconfig_item},
379         { "polygon", "item", xmlconfig_polygon},
380         { "polyline", "item", xmlconfig_polyline},
381         { "circle", "item", xmlconfig_circle},
382         { "label", "item", xmlconfig_label},
383         { "icon", "item", xmlconfig_icon},
384         { "image", "item", xmlconfig_image},
385         {},
386 };
387
388 static void
389 start_element (GMarkupParseContext *context,
390                 const gchar         *element_name,
391                 const gchar        **attribute_names,
392                 const gchar        **attribute_values,
393                 gpointer             user_data,
394                 GError             **error)
395 {
396         struct xmlstate *new=NULL, **parent = user_data;
397         struct element_func *e=elements,*func=NULL;
398         const char *parent_name=NULL;
399         while (e->name) {
400                 if (!g_ascii_strcasecmp(element_name, e->name)) {
401                         func=e;
402                 }
403                 e++;
404         }
405         if (! func) {
406                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_UNKNOWN_ELEMENT,
407                                 "Unknown element '%s'", element_name);
408                 return;
409         }
410         if (*parent)
411                 parent_name=(*parent)->element;
412         if ((parent_name && func->parent && g_ascii_strcasecmp(parent_name, func->parent)) || 
413             (!parent_name && func->parent) || (parent_name && !func->parent)) {
414                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT,
415                                 "Element '%s' within unexpected context '%s'. Expected '%s'",
416                                 element_name, parent_name, func->parent);
417                 return;
418         }
419
420         new=g_new(struct xmlstate, 1);
421         new->attribute_names=attribute_names;
422         new->attribute_values=attribute_values;
423         new->parent=*parent;
424         new->element_object=NULL;
425         new->element=element_name;
426         new->error=error;
427         new->func=func;
428         *parent=new;
429         if (!find_boolean(new, "enabled", 1, 0))
430                 return;
431         if (new->parent && !new->parent->element_object)
432                 return;
433         if (!func->func(new)) {
434                 return;
435         }
436         return;
437 #if 0
438         struct elem_data *data = user_data;
439         void *elem=NULL;
440         void *parent_object;
441         char *parent_token;
442         parent_object=data->elem_stack ? data->elem_stack->data : NULL;
443         parent_token=data->token_stack ? data->token_stack->data : NULL;
444
445         /* g_printf("start_element: %s AN: %s AV: %s\n",element_name,*attribute_names,*attribute_values); */
446
447
448         printf("Unknown element '%s'\n", element_name);
449 #if 0
450         data->elem_stack = g_list_prepend(data->elem_stack, elem);
451         data->token_stack = g_list_prepend(data->token_stack, (gpointer)element_name);
452 #endif
453 #endif
454 }
455
456
457 /* Called for close tags </foo> */
458 static void
459 end_element (GMarkupParseContext *context,
460                 const gchar         *element_name,
461                 gpointer             user_data,
462                 GError             **error)
463 {
464         struct xmlstate *curr, **state = user_data;
465
466         curr=*state;
467         if(!g_ascii_strcasecmp("plugins", element_name) && curr->element_object) 
468                 plugins_init(curr->element_object);
469         if(!g_ascii_strcasecmp("navit", element_name) && curr->element_object) 
470                 navit_init(curr->element_object);
471         *state=curr->parent;
472         g_free(curr);
473 }
474
475
476 /* Called for character data */
477 /* text is not nul-terminated */
478 static void
479 text (GMarkupParseContext *context,
480                 const gchar            *text,
481                 gsize                   text_len,
482                 gpointer                user_data,
483                 GError               **error)
484 {
485         struct xmlstate **state = user_data;
486
487         (void) state;
488 }
489
490
491
492 static const GMarkupParser parser = {
493         start_element,
494         end_element,
495         text,
496         NULL,
497         NULL
498 };
499
500
501 gboolean config_load(char *filename, GError **error)
502 {
503         GMarkupParseContext *context;
504         char *contents;
505         gsize len;
506         gboolean result;
507         gint line;
508         gint chr;
509         gchar *message;
510
511         struct xmlstate *curr=NULL;
512         
513         context = g_markup_parse_context_new (&parser, 0, &curr, NULL);
514
515         if (!g_file_get_contents (filename, &contents, &len, error))
516                 return FALSE;
517
518         result = g_markup_parse_context_parse (context, contents, len, error);
519         if (result && curr) {
520                 g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_PARSE, "element '%s' not closed", curr->element);
521                 result=FALSE;   
522         }
523         if (!result && error && *error) {
524                 g_markup_parse_context_get_position(context, &line, &chr);
525                 message=g_strdup_printf("%s at line %d, char %d\n", (*error)->message, line, chr);
526                 g_free((*error)->message);
527                 (*error)->message=message;
528         }
529         g_markup_parse_context_free (context);
530         g_free (contents);
531
532         return result;
533 }
534