]> git.itanic.dy.fi Git - maemo-mapper/blob - src/gpx.c
1ec13549be2b5102ac8ee22be628bd7812622cd2
[maemo-mapper] / src / gpx.c
1 /*
2  * Copyright (C) 2006, 2007 John Costigan.
3  *
4  * POI and GPS-Info code originally written by Cezary Jackiewicz.
5  *
6  * Default map data provided by http://www.openstreetmap.org/
7  *
8  * This file is part of Maemo Mapper.
9  *
10  * Maemo Mapper is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * Maemo Mapper is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Maemo Mapper.  If not, see <http://www.gnu.org/licenses/>.
22  */
23
24 #define _GNU_SOURCE
25
26 #include <string.h>
27 #include <math.h>
28 #include <libxml/parser.h>
29
30 #include "types.h"
31 #include "data.h"
32 #include "defines.h"
33
34 #include "gpx.h"
35 #include "path.h"
36 #include "util.h"
37
38 /**
39  * Handle a start tag in the parsing of a GPX file.
40  */
41 #define MACRO_SET_UNKNOWN() { \
42     data->sax_data.prev_state = data->sax_data.state; \
43     data->sax_data.state = UNKNOWN; \
44     data->sax_data.unknown_depth = 1; \
45 }
46
47 static gchar XML_TZONE[7];
48
49 /**
50  * Handle char data in the parsing of a GPX file.
51  */
52 static void
53 gpx_chars(SaxData *data, const xmlChar *ch, int len)
54 {
55     gint i;
56     vprintf("%s()\n", __PRETTY_FUNCTION__);
57
58     switch(data->state)
59     {
60         case ERROR:
61         case UNKNOWN:
62             break;
63         case INSIDE_WPT_NAME:
64         case INSIDE_WPT_DESC:
65         case INSIDE_PATH_POINT_ELE:
66         case INSIDE_PATH_POINT_TIME:
67         case INSIDE_PATH_POINT_DESC:
68             for(i = 0; i < len; i++)
69                 data->chars = g_string_append_c(data->chars, ch[i]);
70             vprintf("%s\n", data->chars->str);
71             break;
72         default:
73             break;
74     }
75
76     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
77 }
78
79 /**
80  * Handle an entity in the parsing of a GPX file.  We don't do anything
81  * special here.
82  */
83 static xmlEntityPtr
84 gpx_get_entity(SaxData *data, const xmlChar *name)
85 {
86     vprintf("%s()\n", __PRETTY_FUNCTION__);
87     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
88     return xmlGetPredefinedEntity(name);
89 }
90
91 /**
92  * Handle an error in the parsing of a GPX file.
93  */
94 static void
95 gpx_error(SaxData *data, const gchar *msg, ...)
96 {
97     vprintf("%s()\n", __PRETTY_FUNCTION__);
98     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
99     data->state = ERROR;
100 }
101
102 static gboolean
103 gpx_write_string(GnomeVFSHandle *handle, const gchar *str)
104 {
105     GnomeVFSResult vfs_result;
106     GnomeVFSFileSize size;
107     if(GNOME_VFS_OK != (vfs_result = gnome_vfs_write(
108                     handle, str, strlen(str), &size)))
109     {
110         gchar buffer[BUFFER_SIZE];
111         snprintf(buffer, sizeof(buffer),
112                 "%s:\n%s\n%s", _("Error while writing to file"),
113                 _("File is incomplete."),
114                 gnome_vfs_result_to_string(vfs_result));
115         popup_error(_window, buffer);
116         return FALSE;
117     }
118     return TRUE;
119 }
120
121 static gboolean
122 gpx_write_escaped(GnomeVFSHandle *handle, const gchar *str)
123 {
124     const gchar *ptr = str;
125     const gchar *nullchr = ptr + strlen(ptr);
126     while(ptr < nullchr)
127     {
128         gchar *newptr = strpbrk(ptr, "&<>");
129         if(newptr != NULL)
130         {
131             /* First, write out what we have so far. */
132             const gchar *to_write;
133             GnomeVFSResult vfs_result;
134             GnomeVFSFileSize size;
135             if(GNOME_VFS_OK != (vfs_result = gnome_vfs_write(
136                             handle, ptr, newptr - ptr, &size)))
137             {
138                 gchar buffer[BUFFER_SIZE];
139                 snprintf(buffer, sizeof(buffer),
140                         "%s:\n%s\n%s", _("Error while writing to file"),
141                         _("File is incomplete."),
142                         gnome_vfs_result_to_string(vfs_result));
143                 popup_error(_window, buffer);
144                 return FALSE;
145             }
146
147             /* Now, write the XML entity. */
148             switch(*newptr)
149             {
150                 case '&':
151                     to_write = "&amp;";
152                     break;
153                 case '<':
154                     to_write = "&lt;";
155                     break;
156                 case '>':
157                     to_write = "&gt;";
158                     break;
159                 default:
160                     to_write = "";
161             }
162             gpx_write_string(handle, to_write);
163
164             /* Advance the pointer to continue searching for entities. */
165             ptr = newptr + 1;
166         }
167         else
168         {
169             /* No characters need escaping - write the whole thing. */
170             gpx_write_string(handle, ptr);
171             ptr = nullchr;
172         }
173     }
174     return TRUE;
175 }
176
177 /****************************************************************************
178  * BELOW: OPEN PATH *********************************************************
179  ****************************************************************************/
180
181 static void
182 gpx_path_start_element(PathSaxData *data,
183         const xmlChar *name, const xmlChar **attrs)
184 {
185     vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
186
187     switch(data->sax_data.state)
188     {
189         case ERROR:
190             printf("ERROR!\n");
191             break;
192         case START:
193             if(!strcmp((gchar*)name, "gpx"))
194                 data->sax_data.state = INSIDE_GPX;
195             else
196                 MACRO_SET_UNKNOWN();
197             break;
198         case INSIDE_GPX:
199             if(!strcmp((gchar*)name, "trk"))
200                 data->sax_data.state = INSIDE_PATH;
201             else
202                 MACRO_SET_UNKNOWN();
203             break;
204         case INSIDE_PATH:
205             if(!strcmp((gchar*)name, "trkseg"))
206             {
207                 data->sax_data.state = INSIDE_PATH_SEGMENT;
208                 data->sax_data.at_least_one_trkpt = FALSE;
209             }
210             else
211                 MACRO_SET_UNKNOWN();
212             break;
213         case INSIDE_PATH_SEGMENT:
214             if(!strcmp((gchar*)name, "trkpt"))
215             {
216                 const xmlChar **curr_attr;
217                 gchar *error_check;
218                 gdouble lat = 0.0, lon = 0.0;
219                 gboolean has_lat, has_lon;
220                 has_lat = FALSE;
221                 has_lon = FALSE;
222                 for(curr_attr = attrs; *curr_attr != NULL; )
223                 {
224                     const gchar *attr_name = *curr_attr++;
225                     const gchar *attr_val = *curr_attr++;
226                     if(!strcmp(attr_name, "lat"))
227                     {
228                         lat = g_ascii_strtod(attr_val, &error_check);
229                         if(error_check != attr_val)
230                             has_lat = TRUE;
231                     }
232                     else if(!strcmp(attr_name, "lon"))
233                     {
234                         lon = g_ascii_strtod(attr_val, &error_check);
235                         if(error_check != attr_val)
236                             has_lon = TRUE;
237                     }
238                 }
239                 if(has_lat && has_lon)
240                 {
241                     MACRO_PATH_INCREMENT_TAIL(data->path);
242                     latlon2unit(lat, lon,
243                             data->path.tail->unitx,
244                             data->path.tail->unity);
245                     data->path.tail->time = 0;
246                     data->path.tail->altitude = 0;
247                     data->sax_data.state = INSIDE_PATH_POINT;
248                 }
249                 else
250                     data->sax_data.state = ERROR;
251             }
252             else
253                 MACRO_SET_UNKNOWN();
254             break;
255         case INSIDE_PATH_POINT:
256             if(!strcmp((gchar*)name, "time"))
257                 data->sax_data.state = INSIDE_PATH_POINT_TIME;
258             else if(!strcmp((gchar*)name, "ele"))
259                 data->sax_data.state = INSIDE_PATH_POINT_ELE;
260             else if(!strcmp((gchar*)name, "desc"))
261                 data->sax_data.state = INSIDE_PATH_POINT_DESC;
262
263             else
264                 MACRO_SET_UNKNOWN();
265             break;
266         case UNKNOWN:
267             data->sax_data.unknown_depth++;
268             break;
269         default:
270             ;
271     }
272
273     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
274 }
275
276 /**
277  * Handle an end tag in the parsing of a GPX file.
278  */
279 static void
280 gpx_path_end_element(PathSaxData *data, const xmlChar *name)
281 {
282     vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
283
284     switch(data->sax_data.state)
285     {
286         case ERROR:
287             printf("ERROR!\n");
288             break;
289         case START:
290             data->sax_data.state = ERROR;
291             break;
292         case INSIDE_GPX:
293             if(!strcmp((gchar*)name, "gpx"))
294                 data->sax_data.state = FINISH;
295             else
296                 data->sax_data.state = ERROR;
297             break;
298         case INSIDE_PATH:
299             if(!strcmp((gchar*)name, "trk"))
300                 data->sax_data.state = INSIDE_GPX;
301             else
302                 data->sax_data.state = ERROR;
303             break;
304         case INSIDE_PATH_SEGMENT:
305             if(!strcmp((gchar*)name, "trkseg"))
306             {
307                 if(data->sax_data.at_least_one_trkpt)
308                 {
309                     MACRO_PATH_INCREMENT_TAIL(data->path);
310                     *data->path.tail = _point_null;
311                 }
312                 data->sax_data.state = INSIDE_PATH;
313             }
314             else
315                 data->sax_data.state = ERROR;
316             break;
317         case INSIDE_PATH_POINT:
318             if(!strcmp((gchar*)name, "trkpt"))
319             {
320                 data->sax_data.state = INSIDE_PATH_SEGMENT;
321                 data->sax_data.at_least_one_trkpt = TRUE;
322             }
323             else
324                 data->sax_data.state = ERROR;
325             break;
326         case INSIDE_PATH_POINT_ELE:
327             if(!strcmp((gchar*)name, "ele"))
328             {
329                 gchar *error_check;
330                 data->path.tail->altitude
331                     = g_ascii_strtod(data->sax_data.chars->str, &error_check);
332                 if(error_check == data->sax_data.chars->str)
333                     data->path.tail->altitude = 0;
334                 data->sax_data.state = INSIDE_PATH_POINT;
335                 g_string_free(data->sax_data.chars, TRUE);
336                 data->sax_data.chars = g_string_new("");
337             }
338             else
339                 data->sax_data.state = ERROR;
340             break;
341         case INSIDE_PATH_POINT_TIME:
342             if(!strcmp((gchar*)name, "time"))
343             {
344                 struct tm time;
345                 gchar *ptr;
346
347                 if(NULL == (ptr = strptime(data->sax_data.chars->str,
348                             XML_DATE_FORMAT, &time)))
349                     /* Failed to parse dateTime format. */
350                     data->sax_data.state = ERROR;
351                 else
352                 {
353                     /* Parse was successful. Now we have to parse timezone.
354                      * From here on, if there is an error, I just assume local
355                      * timezone.  Yes, this is not proper XML, but I don't
356                      * care. */
357                     gchar *error_check;
358
359                     /* First, set time in "local" time zone. */
360                     data->path.tail->time = (mktime(&time));
361
362                     /* Now, skip inconsequential characters */
363                     while(*ptr && *ptr != 'Z' && *ptr != '-' && *ptr != '+')
364                         ptr++;
365
366                     /* Check if we ran to the end of the string. */
367                     if(*ptr)
368                     {
369                         /* Next character is either 'Z', '-', or '+' */
370                         if(*ptr == 'Z')
371                             /* Zulu (UTC) time. Undo the local time zone's
372                              * offset. */
373                             data->path.tail->time += time.tm_gmtoff;
374                         else
375                         {
376                             /* Not Zulu (UTC). Must parse hours and minutes. */
377                             gint offhours = strtol(ptr, &error_check, 10);
378                             if(error_check != ptr
379                                     && *(ptr = error_check) == ':')
380                             {
381                                 /* Parse of hours worked. Check minutes. */
382                                 gint offmins = strtol(ptr + 1,
383                                         &error_check, 10);
384                                 if(error_check != (ptr + 1))
385                                 {
386                                     /* Parse of minutes worked. Calculate. */
387                                     data->path.tail->time
388                                         += (time.tm_gmtoff
389                                                 - (offhours * 60 * 60
390                                                     + offmins * 60));
391                                 }
392                             }
393                         }
394                     }
395                     /* Successfully parsed dateTime. */
396                     data->sax_data.state = INSIDE_PATH_POINT;
397                 }
398
399                 g_string_free(data->sax_data.chars, TRUE);
400                 data->sax_data.chars = g_string_new("");
401             }
402             else
403                 data->sax_data.state = ERROR;
404             break;
405         case INSIDE_PATH_POINT_DESC:
406             /* only parse description for routes */
407             if(!strcmp((gchar*)name, "desc"))
408             {
409                 MACRO_PATH_INCREMENT_WTAIL(data->path);
410                 data->path.wtail->point = data->path.tail;
411                 data->path.wtail->desc
412                     = g_string_free(data->sax_data.chars, FALSE);
413                 data->sax_data.chars = g_string_new("");
414                 data->sax_data.state = INSIDE_PATH_POINT;
415             }
416             else
417                 data->sax_data.state = ERROR;
418             break;
419         case UNKNOWN:
420             if(!--data->sax_data.unknown_depth)
421                 data->sax_data.state = data->sax_data.prev_state;
422             else
423                 data->sax_data.state = ERROR;
424             break;
425         default:
426             ;
427     }
428
429     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
430 }
431
432 gboolean
433 gpx_path_parse(Path *to_replace, gchar *buffer, gint size, gint policy_old)
434 {
435     PathSaxData data;
436     xmlSAXHandler sax_handler;
437     printf("%s()\n", __PRETTY_FUNCTION__);
438
439     MACRO_PATH_INIT(data.path);
440     data.sax_data.state = START;
441     data.sax_data.chars = g_string_new("");
442
443     memset(&sax_handler, 0, sizeof(sax_handler));
444     sax_handler.characters = (charactersSAXFunc)gpx_chars;
445     sax_handler.startElement = (startElementSAXFunc)gpx_path_start_element;
446     sax_handler.endElement = (endElementSAXFunc)gpx_path_end_element;
447     sax_handler.entityDecl = (entityDeclSAXFunc)gpx_get_entity;
448     sax_handler.warning = (warningSAXFunc)gpx_error;
449     sax_handler.error = (errorSAXFunc)gpx_error;
450     sax_handler.fatalError = (fatalErrorSAXFunc)gpx_error;
451
452     xmlSAXUserParseMemory(&sax_handler, &data, buffer, size);
453     g_string_free(data.sax_data.chars, TRUE);
454
455     if(data.sax_data.state != FINISH)
456     {
457         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
458         return FALSE;
459     }
460
461     if(policy_old && to_replace->head != to_replace->tail)
462     {
463         Point *src_first;
464         Path *src, *dest;
465
466         if(policy_old > 0)
467         {
468             /* Append to current path. Make sure last path point is zero. */
469             if(to_replace->tail->unity != 0)
470             {
471                 MACRO_PATH_INCREMENT_TAIL((*to_replace));
472                 *to_replace->tail = _point_null;
473             }
474             src = &data.path;
475             dest = to_replace;
476         }
477         else
478         {
479             /* Prepend to current route. */
480             src = to_replace;
481             dest = &data.path;
482         }
483
484         /* Find src_first non-zero point. */
485         for(src_first = src->head - 1; src_first++ != src->tail; )
486             if(src_first->unity)
487                 break;
488
489         /* Append route points from src to dest. */
490         if(src->tail >= src_first)
491         {
492             WayPoint *curr;
493             gint num_dest_points = dest->tail - dest->head + 1;
494             gint num_src_points = src->tail - src_first + 1;
495
496             /* Adjust dest->tail to be able to fit src route data
497              * plus room for more route data. */
498             path_resize(dest,
499                     num_dest_points + num_src_points + ARRAY_CHUNK_SIZE);
500
501             memcpy(dest->tail + 1, src_first,
502                     num_src_points * sizeof(Point));
503
504             dest->tail += num_src_points;
505
506             /* Append waypoints from src to dest->. */
507             path_wresize(dest, (dest->wtail - dest->whead)
508                     + (src->wtail - src->whead) + 2 + ARRAY_CHUNK_SIZE);
509             for(curr = src->whead - 1; curr++ != src->wtail; )
510             {
511                 (++(dest->wtail))->point = dest->head + num_dest_points
512                     + (curr->point - src_first);
513                 dest->wtail->desc = curr->desc;
514             }
515
516         }
517
518         /* Kill old route - don't use MACRO_PATH_FREE(), because that
519          * would free the string desc's that we just moved to data.route. */
520         g_free(src->head);
521         g_free(src->whead);
522         if(policy_old < 0)
523             (*to_replace) = *dest;
524     }
525     else
526     {
527         MACRO_PATH_FREE((*to_replace));
528         /* Overwrite with data.route. */
529         (*to_replace) = data.path;
530         path_resize(to_replace,
531                 to_replace->tail - to_replace->head + 1 + ARRAY_CHUNK_SIZE);
532         path_wresize(to_replace,
533                 to_replace->wtail - to_replace->whead + 1 + ARRAY_CHUNK_SIZE);
534     }
535
536     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
537     return TRUE;
538 }
539
540 /****************************************************************************
541  * ABOVE: OPEN PATH *********************************************************
542  ****************************************************************************/
543
544 /****************************************************************************
545  * BELOW: SAVE PATH *********************************************************
546  ****************************************************************************/
547
548 gboolean
549 gpx_path_write(Path *path, GnomeVFSHandle *handle)
550 {
551     Point *curr = NULL;
552     WayPoint *wcurr = NULL;
553     gboolean trkseg_break = FALSE;
554     printf("%s()\n", __PRETTY_FUNCTION__);
555
556     /* Find first non-zero point. */
557     for(curr = path->head - 1, wcurr = path->whead; curr++ != path->tail; )
558     {
559         if(curr->unity)
560             break;
561         else if(wcurr <= path->wtail && curr == wcurr->point)
562             wcurr++;
563     }
564
565     /* Write the header. */
566     gpx_write_string(handle,
567             "<?xml version=\"1.0\"?>\n"
568             "<gpx version=\"1.0\" creator=\"maemo-mapper\" "
569             "xmlns=\"http://www.topografix.com/GPX/1/0\">\n"
570             "  <trk>\n"
571             "    <trkseg>\n");
572
573     /* Curr points to first non-zero point. */
574     for(curr--; curr++ != path->tail; )
575     {
576         gdouble lat, lon;
577         if(curr->unity)
578         {
579             gchar buffer[80];
580             gboolean first_sub = TRUE;
581             if(trkseg_break)
582             {
583                 /* First trkpt of the segment - write trkseg header. */
584                 gpx_write_string(handle, "    </trkseg>\n"
585                              "    <trkseg>\n");
586                 trkseg_break = FALSE;
587             }
588             unit2latlon(curr->unitx, curr->unity, lat, lon);
589             gpx_write_string(handle, "      <trkpt lat=\"");
590             g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lat);
591             gpx_write_string(handle, buffer);
592             gpx_write_string(handle, "\" lon=\"");
593             g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lon);
594             gpx_write_string(handle, buffer);
595             gpx_write_string(handle, "\"");
596
597             /* write the elevation */
598             if(curr->altitude != 0)
599             {
600                 if(first_sub)
601                 {
602                     gpx_write_string(handle, ">\n");
603                     first_sub = FALSE;
604                 }
605                 gpx_write_string(handle, "        <ele>");
606                 {
607                     g_ascii_formatd(buffer, 80, "%.2f", curr->altitude);
608                     gpx_write_string(handle, buffer);
609                 }
610                 gpx_write_string(handle, "</ele>\n");
611             }
612
613             /* write the time */
614             if(curr->time)
615             {
616                 if(first_sub)
617                 {
618                     gpx_write_string(handle, ">\n");
619                     first_sub = FALSE;
620                 }
621                 gpx_write_string(handle, "        <time>");
622                 strftime(buffer, 80, XML_DATE_FORMAT, localtime(&curr->time));
623                 gpx_write_string(handle, buffer);
624                 gpx_write_string(handle, XML_TZONE);
625                 gpx_write_string(handle, "</time>\n");
626             }
627
628             if(wcurr && curr == wcurr->point)
629             {
630                 if(first_sub)
631                 {
632                     gpx_write_string(handle, ">\n");
633                     first_sub = FALSE;
634                 }
635                 gpx_write_string(handle, "        <desc>");
636                 gpx_write_escaped(handle, wcurr->desc);
637                 gpx_write_string(handle, "</desc>\n");
638                 wcurr++;
639             }
640             if(first_sub)
641             {
642                 gpx_write_string(handle, "/>\n");
643             }
644             else
645             {
646                 gpx_write_string(handle, "      </trkpt>\n");
647             }
648         }
649         else
650             trkseg_break = TRUE;
651     }
652
653     /* Write the footer. */
654     gpx_write_string(handle,
655             "    </trkseg>\n"
656             "  </trk>\n"
657             "</gpx>\n");
658
659
660     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
661     return TRUE;
662 }
663
664 /****************************************************************************
665  * ABOVE: SAVE PATH *********************************************************
666  ****************************************************************************/
667
668 /****************************************************************************
669  * BELOW: OPEN POI **********************************************************
670  ****************************************************************************/
671
672 static void
673 gpx_poi_start_element(PoiSaxData *data,
674         const xmlChar *name, const xmlChar **attrs)
675 {
676     vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
677
678     switch(data->sax_data.state)
679     {
680         case ERROR:
681             printf("ERROR!\n");
682             break;
683         case START:
684             if(!strcmp((gchar*)name, "gpx"))
685                 data->sax_data.state = INSIDE_GPX;
686             else
687                 MACRO_SET_UNKNOWN();
688             break;
689         case INSIDE_GPX:
690             if(!strcmp((gchar*)name, "wpt"))
691             {
692                 const xmlChar **curr_attr;
693                 gchar *error_check;
694                 gdouble lat = 0.0, lon = 0.0;
695                 gboolean has_lat, has_lon;
696                 has_lat = FALSE;
697                 has_lon = FALSE;
698
699                 /* Parse the attributes - there should be lat and lon. */
700                 for(curr_attr = attrs; *curr_attr != NULL; )
701                 {
702                     const gchar *attr_name = *curr_attr++;
703                     const gchar *attr_val = *curr_attr++;
704                     if(!strcmp(attr_name, "lat"))
705                     {
706                         lat = g_ascii_strtod(attr_val, &error_check);
707                         if(error_check != attr_val)
708                             has_lat = TRUE;
709                     }
710                     else if(!strcmp(attr_name, "lon"))
711                     {
712                         lon = g_ascii_strtod(attr_val, &error_check);
713                         if(error_check != attr_val)
714                             has_lon = TRUE;
715                     }
716                 }
717                 if(has_lat && has_lon)
718                 {
719                     data->sax_data.state = INSIDE_WPT;
720                     data->curr_poi = g_slice_new0(PoiInfo);
721                     data->curr_poi->lat = lat;
722                     data->curr_poi->lon = lon;
723                     data->poi_list = g_list_append(
724                             data->poi_list, data->curr_poi);
725                 }
726                 else
727                     data->sax_data.state = ERROR;
728             }
729             else
730                 MACRO_SET_UNKNOWN();
731             break;
732         case INSIDE_WPT:
733             if(!strcmp((gchar*)name, "name"))
734                 data->sax_data.state = INSIDE_WPT_NAME;
735             else if(!strcmp((gchar*)name, "desc"))
736                 data->sax_data.state = INSIDE_WPT_DESC;
737             else
738                 MACRO_SET_UNKNOWN();
739             break;
740         case UNKNOWN:
741             printf("UNKNOWN!\n");
742             data->sax_data.unknown_depth++;
743             break;
744         default:
745             ;
746     }
747
748     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
749 }
750
751 /**
752  * Handle an end tag in the parsing of a GPX file.
753  */
754 static void
755 gpx_poi_end_element(PoiSaxData *data, const xmlChar *name)
756 {
757     vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
758
759     switch(data->sax_data.state)
760     {
761         case ERROR:
762             printf("ERROR!\n");
763             break;
764         case START:
765             data->sax_data.state = ERROR;
766             break;
767         case INSIDE_GPX:
768             if(!strcmp((gchar*)name, "gpx"))
769             {
770                 data->sax_data.state = FINISH;
771             }
772             else
773                 data->sax_data.state = ERROR;
774             break;
775         case INSIDE_WPT:
776             if(!strcmp((gchar*)name, "wpt"))
777                 data->sax_data.state = INSIDE_GPX;
778             else
779                 data->sax_data.state = ERROR;
780             break;
781         case INSIDE_WPT_NAME:
782             if(!strcmp((gchar*)name, "name"))
783             {
784                 data->curr_poi->label
785                     = g_string_free(data->sax_data.chars, FALSE);
786                 data->sax_data.chars = g_string_new("");
787                 data->sax_data.state = INSIDE_WPT;
788             }
789             else
790                 data->sax_data.state = ERROR;
791             break;
792         case INSIDE_WPT_DESC:
793             if(!strcmp((gchar*)name, "desc"))
794             {
795                 data->curr_poi->desc
796                     = g_string_free(data->sax_data.chars, FALSE);
797                 data->sax_data.chars = g_string_new("");
798                 data->sax_data.state = INSIDE_WPT;
799             }
800             else
801                 data->sax_data.state = ERROR;
802             break;
803         case UNKNOWN:
804             printf("UNKNOWN!\n");
805             if(!--data->sax_data.unknown_depth)
806                 data->sax_data.state = data->sax_data.prev_state;
807             else
808                 data->sax_data.state = ERROR;
809             break;
810         default:
811             ;
812     }
813
814     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
815 }
816
817 gboolean
818 gpx_poi_parse(gchar *buffer, gint size, GList **poi_list)
819 {
820     PoiSaxData data;
821     xmlSAXHandler sax_handler;
822     printf("%s()\n", __PRETTY_FUNCTION__);
823
824     data.poi_list = *poi_list;
825     data.sax_data.state = START;
826     data.sax_data.chars = g_string_new("");
827
828     memset(&sax_handler, 0, sizeof(sax_handler));
829     sax_handler.characters = (charactersSAXFunc)gpx_chars;
830     sax_handler.startElement = (startElementSAXFunc)gpx_poi_start_element;
831     sax_handler.endElement = (endElementSAXFunc)gpx_poi_end_element;
832     sax_handler.entityDecl = (entityDeclSAXFunc)gpx_get_entity;
833     sax_handler.warning = (warningSAXFunc)gpx_error;
834     sax_handler.error = (errorSAXFunc)gpx_error;
835     sax_handler.fatalError = (fatalErrorSAXFunc)gpx_error;
836
837     xmlSAXUserParseMemory(&sax_handler, &data, buffer, size);
838     g_string_free(data.sax_data.chars, TRUE);
839     *poi_list = data.poi_list;
840
841     if(data.sax_data.state != FINISH)
842     {
843         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
844         return FALSE;
845     }
846
847     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
848     return TRUE;
849 }
850
851 /****************************************************************************
852  * ABOVE: OPEN POI **********************************************************
853  ****************************************************************************/
854
855 /****************************************************************************
856  * BELOW: SAVE POI **********************************************************
857  ****************************************************************************/
858
859 gint
860 gpx_poi_write(GtkTreeModel *model, GnomeVFSHandle *handle)
861 {
862     gint num_written = 0;
863     GtkTreeIter iter;
864     printf("%s()\n", __PRETTY_FUNCTION__);
865
866     /* Write the header. */
867     gpx_write_string(handle,
868             "<?xml version=\"1.0\"?>\n"
869             "<gpx version=\"1.0\" creator=\"maemo-mapper\" "
870             "xmlns=\"http://www.topografix.com/GPX/1/0\">\n");
871
872     /* Iterate through the data model and import as desired. */
873     if(gtk_tree_model_get_iter_first(model, &iter)) do
874     {   
875         PoiInfo poi;
876         gboolean selected;
877         memset(&poi, 0, sizeof(poi));
878
879         gtk_tree_model_get(model, &iter,
880                 POI_SELECTED, &selected,
881                 POI_POIID, &(poi.poi_id),
882                 POI_CATID, &(poi.cat_id),
883                 POI_LAT, &(poi.lat),
884                 POI_LON, &(poi.lon),
885                 POI_LABEL, &(poi.label),
886                 POI_DESC, &(poi.desc),
887                 POI_CLABEL, &(poi.clabel),
888                 -1);
889
890         if(selected)
891         {
892             gchar buffer[80];
893
894             gpx_write_string(handle, "  <wpt lat=\"");
895             g_ascii_formatd(buffer, sizeof(buffer), "%.06f", poi.lat);
896             gpx_write_string(handle, buffer);
897             gpx_write_string(handle, "\" lon=\"");
898             g_ascii_formatd(buffer, sizeof(buffer), "%.06f", poi.lon);
899             gpx_write_string(handle, buffer);
900             gpx_write_string(handle, "\">\n");
901
902             if(poi.label && *poi.label)
903             {
904                 gpx_write_string(handle, "    <name>");
905                 gpx_write_escaped(handle, poi.label);
906                 gpx_write_string(handle, "</name>\n");
907             }
908
909             if(poi.desc && *poi.desc)
910             {
911                 gpx_write_string(handle, "    <desc>");
912                 gpx_write_escaped(handle, poi.desc);
913                 gpx_write_string(handle, "</desc>\n");
914             }
915             gpx_write_string(handle, "  </wpt>\n");
916             ++ num_written;
917         }
918     } while(gtk_tree_model_iter_next(model, &iter));
919
920     /* Write the footer. */
921     gpx_write_string(handle, "</gpx>\n");
922
923     vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, num_written);
924     return num_written;
925 }
926
927 /****************************************************************************
928  * ABOVE: SAVE POI **********************************************************
929  ****************************************************************************/
930
931 void
932 gpx_init()
933 {
934     printf("%s()\n", __PRETTY_FUNCTION__);
935
936     /* set XML_TZONE */
937     {   
938         time_t time1;
939         struct tm time2;
940         time1 = time(NULL);
941         localtime_r(&time1, &time2);
942         snprintf(XML_TZONE, sizeof(XML_TZONE), "%+03ld:%02ld",
943                 (time2.tm_gmtoff / 60 / 60), (time2.tm_gmtoff / 60) % 60);
944     }
945
946     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
947 }