/* * Copyright (C) 2006, 2007 John Costigan. * * POI and GPS-Info code originally written by Cezary Jackiewicz. * * Default map data provided by http://www.openstreetmap.org/ * * This file is part of Maemo Mapper. * * Maemo Mapper is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Maemo Mapper is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Maemo Mapper. If not, see . */ #define _GNU_SOURCE #include #include #include #include "types.h" #include "data.h" #include "defines.h" #include "gpx.h" #include "path.h" #include "util.h" /** This enum defines the states of the SAX parsing state machine. */ typedef enum { START, INSIDE_GPX, INSIDE_WPT, INSIDE_WPT_NAME, INSIDE_WPT_DESC, INSIDE_WPT_CMT, INSIDE_PATH, INSIDE_PATH_SEGMENT, INSIDE_PATH_POINT, INSIDE_PATH_POINT_ELE, INSIDE_PATH_POINT_TIME, INSIDE_PATH_POINT_DESC, INSIDE_PATH_POINT_CMT, FINISH, UNKNOWN, ERROR, } SaxState; /** Data used during the SAX parsing operation. */ typedef struct _SaxData SaxData; struct _SaxData { SaxState state; SaxState prev_state; gint unknown_depth; gboolean at_least_one_trkpt; GString *chars; }; typedef struct _PathSaxData PathSaxData; struct _PathSaxData { SaxData sax_data; Path path; }; typedef struct _PoiSaxData PoiSaxData; struct _PoiSaxData { SaxData sax_data; GList *poi_list; PoiInfo *curr_poi; }; /** * Handle a start tag in the parsing of a GPX file. */ #define MACRO_SET_UNKNOWN() { \ data->sax_data.prev_state = data->sax_data.state; \ data->sax_data.state = UNKNOWN; \ data->sax_data.unknown_depth = 1; \ } static gchar XML_TZONE[7]; /** * Handle char data in the parsing of a GPX file. */ static void gpx_chars(SaxData *data, const xmlChar *ch, int len) { gint i; vprintf("%s()\n", __PRETTY_FUNCTION__); switch(data->state) { case ERROR: case UNKNOWN: break; case INSIDE_WPT_NAME: case INSIDE_WPT_DESC: case INSIDE_PATH_POINT_ELE: case INSIDE_PATH_POINT_TIME: case INSIDE_PATH_POINT_DESC: case INSIDE_PATH_POINT_CMT: for(i = 0; i < len; i++) data->chars = g_string_append_c(data->chars, ch[i]); vprintf("%s\n", data->chars->str); break; default: break; } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Handle an entity in the parsing of a GPX file. We don't do anything * special here. */ static xmlEntityPtr gpx_get_entity(SaxData *data, const xmlChar *name) { vprintf("%s()\n", __PRETTY_FUNCTION__); vprintf("%s(): return\n", __PRETTY_FUNCTION__); return xmlGetPredefinedEntity(name); } /** * Handle an error in the parsing of a GPX file. */ static void gpx_error(SaxData *data, const gchar *msg, ...) { vprintf("%s()\n", __PRETTY_FUNCTION__); vprintf("%s(): return\n", __PRETTY_FUNCTION__); data->state = ERROR; } static gboolean gpx_write_string(GnomeVFSHandle *handle, const gchar *str) { GnomeVFSResult vfs_result; GnomeVFSFileSize size; if(GNOME_VFS_OK != (vfs_result = gnome_vfs_write( handle, str, strlen(str), &size))) { gchar buffer[BUFFER_SIZE]; snprintf(buffer, sizeof(buffer), "%s:\n%s\n%s", _("Error while writing to file"), _("File is incomplete."), gnome_vfs_result_to_string(vfs_result)); popup_error(_window, buffer); return FALSE; } return TRUE; } static gboolean gpx_write_escaped(GnomeVFSHandle *handle, const gchar *str) { const gchar *ptr = str; const gchar *nullchr = ptr + strlen(ptr); while(ptr < nullchr) { gchar *newptr = strpbrk(ptr, "&<>"); if(newptr != NULL) { /* First, write out what we have so far. */ const gchar *to_write; GnomeVFSResult vfs_result; GnomeVFSFileSize size; if(GNOME_VFS_OK != (vfs_result = gnome_vfs_write( handle, ptr, newptr - ptr, &size))) { gchar buffer[BUFFER_SIZE]; snprintf(buffer, sizeof(buffer), "%s:\n%s\n%s", _("Error while writing to file"), _("File is incomplete."), gnome_vfs_result_to_string(vfs_result)); popup_error(_window, buffer); return FALSE; } /* Now, write the XML entity. */ switch(*newptr) { case '&': to_write = "&"; break; case '<': to_write = "<"; break; case '>': to_write = ">"; break; default: to_write = ""; } gpx_write_string(handle, to_write); /* Advance the pointer to continue searching for entities. */ ptr = newptr + 1; } else { /* No characters need escaping - write the whole thing. */ gpx_write_string(handle, ptr); ptr = nullchr; } } return TRUE; } /**************************************************************************** * BELOW: OPEN PATH ********************************************************* ****************************************************************************/ static void gpx_path_start_element(PathSaxData *data, const xmlChar *name, const xmlChar **attrs) { vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name); switch(data->sax_data.state) { case ERROR: printf("ERROR!\n"); break; case START: if(!strcmp((gchar*)name, "gpx")) data->sax_data.state = INSIDE_GPX; else { MACRO_SET_UNKNOWN(); } break; case INSIDE_GPX: if(!strcmp((gchar*)name, "trk")) data->sax_data.state = INSIDE_PATH; else { MACRO_SET_UNKNOWN(); } break; case INSIDE_PATH: if(!strcmp((gchar*)name, "trkseg")) { data->sax_data.state = INSIDE_PATH_SEGMENT; data->sax_data.at_least_one_trkpt = FALSE; } else { MACRO_SET_UNKNOWN(); } break; case INSIDE_PATH_SEGMENT: if(!strcmp((gchar*)name, "trkpt")) { const xmlChar **curr_attr; gchar *error_check; gdouble lat = 0.0, lon = 0.0; gboolean has_lat, has_lon; has_lat = FALSE; has_lon = FALSE; for(curr_attr = attrs; *curr_attr != NULL; ) { const gchar *attr_name = *curr_attr++; const gchar *attr_val = *curr_attr++; if(!strcmp(attr_name, "lat")) { lat = g_ascii_strtod(attr_val, &error_check); if(error_check != attr_val) has_lat = TRUE; } else if(!strcmp(attr_name, "lon")) { lon = g_ascii_strtod(attr_val, &error_check); if(error_check != attr_val) has_lon = TRUE; } } if(has_lat && has_lon) { MACRO_PATH_INCREMENT_TAIL(data->path); latlon2unit(lat, lon, data->path.tail->unitx, data->path.tail->unity); data->path.tail->time = 0; data->path.tail->altitude = 0; data->sax_data.state = INSIDE_PATH_POINT; } else data->sax_data.state = ERROR; } else { MACRO_SET_UNKNOWN(); } break; case INSIDE_PATH_POINT: if(!strcmp((gchar*)name, "time")) data->sax_data.state = INSIDE_PATH_POINT_TIME; else if(!strcmp((gchar*)name, "ele")) data->sax_data.state = INSIDE_PATH_POINT_ELE; else if(!strcmp((gchar*)name, "desc")) data->sax_data.state = INSIDE_PATH_POINT_DESC; else if(!strcmp((gchar*)name, "cmt")) data->sax_data.state = INSIDE_PATH_POINT_CMT; else { MACRO_SET_UNKNOWN(); } break; case UNKNOWN: data->sax_data.unknown_depth++; break; default: ; } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Handle an end tag in the parsing of a GPX file. */ static void gpx_path_end_element(PathSaxData *data, const xmlChar *name) { vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name); switch(data->sax_data.state) { case ERROR: printf("ERROR!\n"); break; case START: data->sax_data.state = ERROR; break; case INSIDE_GPX: if(!strcmp((gchar*)name, "gpx")) data->sax_data.state = FINISH; else data->sax_data.state = ERROR; break; case INSIDE_PATH: if(!strcmp((gchar*)name, "trk")) data->sax_data.state = INSIDE_GPX; else data->sax_data.state = ERROR; break; case INSIDE_PATH_SEGMENT: if(!strcmp((gchar*)name, "trkseg")) { if(data->sax_data.at_least_one_trkpt) { MACRO_PATH_INCREMENT_TAIL(data->path); *data->path.tail = _point_null; } data->sax_data.state = INSIDE_PATH; } else data->sax_data.state = ERROR; break; case INSIDE_PATH_POINT: if(!strcmp((gchar*)name, "trkpt")) { data->sax_data.state = INSIDE_PATH_SEGMENT; data->sax_data.at_least_one_trkpt = TRUE; } else data->sax_data.state = ERROR; break; case INSIDE_PATH_POINT_ELE: if(!strcmp((gchar*)name, "ele")) { gchar *error_check; data->path.tail->altitude = g_ascii_strtod(data->sax_data.chars->str, &error_check); if(error_check == data->sax_data.chars->str) data->path.tail->altitude = 0; data->sax_data.state = INSIDE_PATH_POINT; g_string_free(data->sax_data.chars, TRUE); data->sax_data.chars = g_string_new(""); } else data->sax_data.state = ERROR; break; case INSIDE_PATH_POINT_TIME: if(!strcmp((gchar*)name, "time")) { struct tm time; gchar *ptr; if(NULL == (ptr = strptime(data->sax_data.chars->str, XML_DATE_FORMAT, &time))) /* Failed to parse dateTime format. */ data->sax_data.state = ERROR; else { /* Parse was successful. Now we have to parse timezone. * From here on, if there is an error, I just assume local * timezone. Yes, this is not proper XML, but I don't * care. */ gchar *error_check; /* First, set time in "local" time zone. */ data->path.tail->time = (mktime(&time)); /* Now, skip inconsequential characters */ while(*ptr && *ptr != 'Z' && *ptr != '-' && *ptr != '+') ptr++; /* Check if we ran to the end of the string. */ if(*ptr) { /* Next character is either 'Z', '-', or '+' */ if(*ptr == 'Z') /* Zulu (UTC) time. Undo the local time zone's * offset. */ data->path.tail->time += time.tm_gmtoff; else { /* Not Zulu (UTC). Must parse hours and minutes. */ gint offhours = strtol(ptr, &error_check, 10); if(error_check != ptr && *(ptr = error_check) == ':') { /* Parse of hours worked. Check minutes. */ gint offmins = strtol(ptr + 1, &error_check, 10); if(error_check != (ptr + 1)) { /* Parse of minutes worked. Calculate. */ data->path.tail->time += (time.tm_gmtoff - (offhours * 60 * 60 + offmins * 60)); } } } } /* Successfully parsed dateTime. */ data->sax_data.state = INSIDE_PATH_POINT; } g_string_free(data->sax_data.chars, TRUE); data->sax_data.chars = g_string_new(""); } else data->sax_data.state = ERROR; break; case INSIDE_PATH_POINT_CMT: /* only parse description for routes */ if(!strcmp((gchar*)name, "cmt")) { if(data->path.wtail < data->path.whead || data->path.wtail->point != data->path.tail) { MACRO_PATH_INCREMENT_WTAIL(data->path); data->path.wtail->point = data->path.tail; data->path.wtail->desc = g_string_free(data->sax_data.chars, FALSE); } else g_string_free(data->sax_data.chars, TRUE); data->sax_data.chars = g_string_new(""); data->sax_data.state = INSIDE_PATH_POINT; } else data->sax_data.state = ERROR; break; case INSIDE_PATH_POINT_DESC: /* only parse description for routes */ if(!strcmp((gchar*)name, "desc")) { /* If we already have a desc (e.g. from cmt), then overwrite */ if(data->path.wtail >= data->path.whead && data->path.wtail->point == data->path.tail) g_free(data->path.wtail->desc); else { MACRO_PATH_INCREMENT_WTAIL(data->path); } data->path.wtail->point = data->path.tail; data->path.wtail->desc = g_string_free(data->sax_data.chars, FALSE); data->sax_data.chars = g_string_new(""); data->sax_data.state = INSIDE_PATH_POINT; } else data->sax_data.state = ERROR; break; case UNKNOWN: if(!--data->sax_data.unknown_depth) data->sax_data.state = data->sax_data.prev_state; break; default: ; } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } gboolean gpx_path_parse(Path *to_replace, gchar *buffer, gint size, gint policy_old) { PathSaxData data; xmlSAXHandler sax_handler; printf("%s()\n", __PRETTY_FUNCTION__); MACRO_PATH_INIT(data.path); data.sax_data.state = START; data.sax_data.chars = g_string_new(""); memset(&sax_handler, 0, sizeof(sax_handler)); sax_handler.characters = (charactersSAXFunc)gpx_chars; sax_handler.startElement = (startElementSAXFunc)gpx_path_start_element; sax_handler.endElement = (endElementSAXFunc)gpx_path_end_element; sax_handler.entityDecl = (entityDeclSAXFunc)gpx_get_entity; sax_handler.warning = (warningSAXFunc)gpx_error; sax_handler.error = (errorSAXFunc)gpx_error; sax_handler.fatalError = (fatalErrorSAXFunc)gpx_error; xmlSAXUserParseMemory(&sax_handler, &data, buffer, size); g_string_free(data.sax_data.chars, TRUE); if(data.sax_data.state != FINISH) { vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } if(policy_old && to_replace->head != to_replace->tail) { Point *src_first; Path *src, *dest; if(policy_old > 0) { /* Append to current path. Make sure last path point is zero. */ if(to_replace->tail->unity != 0) { MACRO_PATH_INCREMENT_TAIL((*to_replace)); *to_replace->tail = _point_null; } src = &data.path; dest = to_replace; } else { /* Prepend to current route. */ src = to_replace; dest = &data.path; } /* Find src_first non-zero point. */ for(src_first = src->head - 1; src_first++ != src->tail; ) if(src_first->unity) break; /* Append route points from src to dest. */ if(src->tail >= src_first) { WayPoint *curr; gint num_dest_points = dest->tail - dest->head + 1; gint num_src_points = src->tail - src_first + 1; /* Adjust dest->tail to be able to fit src route data * plus room for more route data. */ path_resize(dest, num_dest_points + num_src_points + ARRAY_CHUNK_SIZE); memcpy(dest->tail + 1, src_first, num_src_points * sizeof(Point)); dest->tail += num_src_points; /* Append waypoints from src to dest->. */ path_wresize(dest, (dest->wtail - dest->whead) + (src->wtail - src->whead) + 2 + ARRAY_CHUNK_SIZE); for(curr = src->whead - 1; curr++ != src->wtail; ) { (++(dest->wtail))->point = dest->head + num_dest_points + (curr->point - src_first); dest->wtail->desc = curr->desc; } } /* Kill old route - don't use MACRO_PATH_FREE(), because that * would free the string desc's that we just moved to data.route. */ g_free(src->head); g_free(src->whead); if(policy_old < 0) (*to_replace) = *dest; } else { MACRO_PATH_FREE((*to_replace)); /* Overwrite with data.route. */ (*to_replace) = data.path; path_resize(to_replace, to_replace->tail - to_replace->head + 1 + ARRAY_CHUNK_SIZE); path_wresize(to_replace, to_replace->wtail - to_replace->whead + 1 + ARRAY_CHUNK_SIZE); } vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } /**************************************************************************** * ABOVE: OPEN PATH ********************************************************* ****************************************************************************/ /**************************************************************************** * BELOW: SAVE PATH ********************************************************* ****************************************************************************/ gboolean gpx_path_write(Path *path, GnomeVFSHandle *handle) { Point *curr = NULL; WayPoint *wcurr = NULL; gboolean trkseg_break = FALSE; printf("%s()\n", __PRETTY_FUNCTION__); /* Find first non-zero point. */ for(curr = path->head - 1, wcurr = path->whead; curr++ != path->tail; ) { if(curr->unity) break; else if(wcurr <= path->wtail && curr == wcurr->point) wcurr++; } /* Write the header. */ gpx_write_string(handle, "\n" "\n" " \n" " \n"); /* Curr points to first non-zero point. */ for(curr--; curr++ != path->tail; ) { gdouble lat, lon; if(curr->unity) { gchar buffer[80]; gboolean first_sub = TRUE; if(trkseg_break) { /* First trkpt of the segment - write trkseg header. */ gpx_write_string(handle, " \n" " \n"); trkseg_break = FALSE; } unit2latlon(curr->unitx, curr->unity, lat, lon); gpx_write_string(handle, " altitude != 0) { if(first_sub) { gpx_write_string(handle, ">\n"); first_sub = FALSE; } gpx_write_string(handle, " "); { g_ascii_formatd(buffer, 80, "%.2f", curr->altitude); gpx_write_string(handle, buffer); } gpx_write_string(handle, "\n"); } /* write the time */ if(curr->time) { if(first_sub) { gpx_write_string(handle, ">\n"); first_sub = FALSE; } gpx_write_string(handle, " \n"); } if(wcurr && curr == wcurr->point) { if(first_sub) { gpx_write_string(handle, ">\n"); first_sub = FALSE; } gpx_write_string(handle, " "); gpx_write_escaped(handle, wcurr->desc); gpx_write_string(handle, "\n"); wcurr++; } if(first_sub) { gpx_write_string(handle, "/>\n"); } else { gpx_write_string(handle, " \n"); } } else trkseg_break = TRUE; } /* Write the footer. */ gpx_write_string(handle, " \n" " \n" "\n"); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } /**************************************************************************** * ABOVE: SAVE PATH ********************************************************* ****************************************************************************/ /**************************************************************************** * BELOW: OPEN POI ********************************************************** ****************************************************************************/ static void gpx_poi_start_element(PoiSaxData *data, const xmlChar *name, const xmlChar **attrs) { vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name); switch(data->sax_data.state) { case ERROR: printf("ERROR!\n"); break; case START: if(!strcmp((gchar*)name, "gpx")) data->sax_data.state = INSIDE_GPX; else { MACRO_SET_UNKNOWN(); } break; case INSIDE_GPX: if(!strcmp((gchar*)name, "wpt")) { const xmlChar **curr_attr; gchar *error_check; gdouble lat = 0.0, lon = 0.0; gboolean has_lat, has_lon; has_lat = FALSE; has_lon = FALSE; /* Parse the attributes - there should be lat and lon. */ for(curr_attr = attrs; *curr_attr != NULL; ) { const gchar *attr_name = *curr_attr++; const gchar *attr_val = *curr_attr++; if(!strcmp(attr_name, "lat")) { lat = g_ascii_strtod(attr_val, &error_check); if(error_check != attr_val) has_lat = TRUE; } else if(!strcmp(attr_name, "lon")) { lon = g_ascii_strtod(attr_val, &error_check); if(error_check != attr_val) has_lon = TRUE; } } if(has_lat && has_lon) { data->sax_data.state = INSIDE_WPT; data->curr_poi = g_slice_new0(PoiInfo); data->curr_poi->lat = lat; data->curr_poi->lon = lon; data->poi_list = g_list_append( data->poi_list, data->curr_poi); } else data->sax_data.state = ERROR; } else { MACRO_SET_UNKNOWN(); } break; case INSIDE_WPT: if(!strcmp((gchar*)name, "name")) data->sax_data.state = INSIDE_WPT_NAME; else if(!strcmp((gchar*)name, "desc")) data->sax_data.state = INSIDE_WPT_DESC; else if(!strcmp((gchar*)name, "cmt")) data->sax_data.state = INSIDE_WPT_CMT; else { MACRO_SET_UNKNOWN(); } break; case UNKNOWN: data->sax_data.unknown_depth++; break; default: ; } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Handle an end tag in the parsing of a GPX file. */ static void gpx_poi_end_element(PoiSaxData *data, const xmlChar *name) { vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name); switch(data->sax_data.state) { case ERROR: printf("ERROR!\n"); break; case START: data->sax_data.state = ERROR; break; case INSIDE_GPX: if(!strcmp((gchar*)name, "gpx")) { data->sax_data.state = FINISH; } else data->sax_data.state = ERROR; break; case INSIDE_WPT: if(!strcmp((gchar*)name, "wpt")) data->sax_data.state = INSIDE_GPX; else data->sax_data.state = ERROR; break; case INSIDE_WPT_NAME: if(!strcmp((gchar*)name, "name")) { data->curr_poi->label = g_string_free(data->sax_data.chars, FALSE); data->sax_data.chars = g_string_new(""); data->sax_data.state = INSIDE_WPT; } else data->sax_data.state = ERROR; break; case INSIDE_WPT_CMT: if(!strcmp((gchar*)name, "cmt")) { /* Only use if we don't already have a desc */ if(!data->curr_poi->desc) { data->curr_poi->desc = g_string_free(data->sax_data.chars, FALSE); data->sax_data.chars = g_string_new(""); data->sax_data.state = INSIDE_WPT; } } else data->sax_data.state = ERROR; break; case INSIDE_WPT_DESC: if(!strcmp((gchar*)name, "desc")) { /* If we already have a desc (e.g. from cmt), then overwrite */ if(data->curr_poi->desc) g_free(data->curr_poi->desc); data->curr_poi->desc = g_string_free(data->sax_data.chars, FALSE); data->sax_data.chars = g_string_new(""); data->sax_data.state = INSIDE_WPT; } else data->sax_data.state = ERROR; break; case UNKNOWN: if(!--data->sax_data.unknown_depth) data->sax_data.state = data->sax_data.prev_state; break; default: ; } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } gboolean gpx_poi_parse(gchar *buffer, gint size, GList **poi_list) { PoiSaxData data; xmlSAXHandler sax_handler; printf("%s()\n", __PRETTY_FUNCTION__); data.poi_list = *poi_list; data.sax_data.state = START; data.sax_data.chars = g_string_new(""); memset(&sax_handler, 0, sizeof(sax_handler)); sax_handler.characters = (charactersSAXFunc)gpx_chars; sax_handler.startElement = (startElementSAXFunc)gpx_poi_start_element; sax_handler.endElement = (endElementSAXFunc)gpx_poi_end_element; sax_handler.entityDecl = (entityDeclSAXFunc)gpx_get_entity; sax_handler.warning = (warningSAXFunc)gpx_error; sax_handler.error = (errorSAXFunc)gpx_error; sax_handler.fatalError = (fatalErrorSAXFunc)gpx_error; xmlSAXUserParseMemory(&sax_handler, &data, buffer, size); g_string_free(data.sax_data.chars, TRUE); *poi_list = data.poi_list; if(data.sax_data.state != FINISH) { vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } /**************************************************************************** * ABOVE: OPEN POI ********************************************************** ****************************************************************************/ /**************************************************************************** * BELOW: SAVE POI ********************************************************** ****************************************************************************/ gint gpx_poi_write(GtkTreeModel *model, GnomeVFSHandle *handle) { gint num_written = 0; GtkTreeIter iter; printf("%s()\n", __PRETTY_FUNCTION__); /* Write the header. */ gpx_write_string(handle, "\n" "\n"); /* Iterate through the data model and import as desired. */ if(gtk_tree_model_get_iter_first(model, &iter)) do { PoiInfo poi; gboolean selected; memset(&poi, 0, sizeof(poi)); gtk_tree_model_get(model, &iter, POI_SELECTED, &selected, POI_POIID, &(poi.poi_id), POI_CATID, &(poi.cat_id), POI_LAT, &(poi.lat), POI_LON, &(poi.lon), POI_LABEL, &(poi.label), POI_DESC, &(poi.desc), POI_CLABEL, &(poi.clabel), -1); if(selected) { gchar buffer[80]; gpx_write_string(handle, " \n"); if(poi.label && *poi.label) { gpx_write_string(handle, " "); gpx_write_escaped(handle, poi.label); gpx_write_string(handle, "\n"); } if(poi.desc && *poi.desc) { gpx_write_string(handle, " "); gpx_write_escaped(handle, poi.desc); gpx_write_string(handle, "\n"); } gpx_write_string(handle, " \n"); ++ num_written; } } while(gtk_tree_model_iter_next(model, &iter)); /* Write the footer. */ gpx_write_string(handle, "\n"); vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, num_written); return num_written; } /**************************************************************************** * ABOVE: SAVE POI ********************************************************** ****************************************************************************/ void gpx_init() { printf("%s()\n", __PRETTY_FUNCTION__); /* set XML_TZONE */ { time_t time1; struct tm time2; time1 = time(NULL); localtime_r(&time1, &time2); snprintf(XML_TZONE, sizeof(XML_TZONE), "%+03ld:%02ld", (time2.tm_gmtoff / 60 / 60), (time2.tm_gmtoff / 60) % 60); } vprintf("%s(): return\n", __PRETTY_FUNCTION__); }