Contents of /trunk/src/gpx_dom.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (hide annotations)
Sat Jun 20 11:08:47 2009 UTC (14 years, 11 months ago) by harbaum
File MIME type: text/plain
File size: 30777 byte(s)
Initial import
1 harbaum 1 /*
2     * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3     *
4     * This file is part of GPXView.
5     *
6     * GPXView is free software: you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation, either version 3 of the License, or
9     * (at your option) any later version.
10     *
11     * GPXView is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with GPXView. If not, see <http://www.gnu.org/licenses/>.
18     */
19    
20     #include <stdio.h>
21     #include <stdlib.h>
22     #include <string.h>
23     #include <math.h>
24    
25     #include <libxml/parser.h>
26     #include <libxml/tree.h>
27    
28     #include <libxml/xmlreader.h>
29    
30     #include <glib.h>
31     #include <glib/gstdio.h>
32    
33     #include <zlib.h>
34    
35     #include "gpxview.h"
36     #include "unzip.h"
37    
38     #ifndef LIBXML_TREE_ENABLED
39     #error "Tree not enabled in libxml"
40     #endif
41    
42     void gpx_free_wpt(wpt_t *wpt) {
43     if(wpt->id) xmlFree(wpt->id);
44     if(wpt->cmt) xmlFree(wpt->cmt);
45     if(wpt->desc) xmlFree(wpt->desc);
46     free(wpt);
47     }
48    
49     void gpx_free_log(log_t *log) {
50     if(log->finder) xmlFree(log->finder);
51     if(log->text) xmlFree(log->text);
52     free(log);
53     }
54    
55     void gpx_free_tb(tb_t *tb) {
56     if(tb->name) xmlFree(tb->name);
57     if(tb->ref) xmlFree(tb->ref);
58     free(tb);
59     }
60    
61     void gpx_free_cache(cache_t *cache) {
62     log_t *log = cache->log;
63     wpt_t *wpt = cache->wpt;
64     tb_t *tb = cache->tb;
65    
66     if(cache->id) xmlFree(cache->id);
67     if(cache->name) xmlFree(cache->name);
68     if(cache->owner) xmlFree(cache->owner);
69     if(cache->short_description) xmlFree(cache->short_description);
70     if(cache->long_description) xmlFree(cache->long_description);
71     if(cache->hint) xmlFree(cache->hint);
72     if(cache->url) xmlFree(cache->url);
73    
74     /* free all logs */
75     while(log) { log_t *next = log->next; gpx_free_log(log); log = next; }
76    
77     /* free all waypoints */
78     while(wpt) { wpt_t *next = wpt->next; gpx_free_wpt(wpt); wpt = next; }
79    
80     /* free all tbs */
81     while(tb) { tb_t *next = tb->next; gpx_free_tb(tb); tb = next; }
82    
83     if(cache->notes) notes_free(cache->notes);
84    
85     free(cache);
86     }
87    
88     void gpx_free(gpx_t *gpx) {
89     cache_t *cache = gpx->cache;
90    
91     if(gpx->name) xmlFree(gpx->name);
92     if(gpx->desc) xmlFree(gpx->desc);
93     if(gpx->filename) free(gpx->filename);
94    
95     /* free all caches */
96     while(cache) {
97     cache_t *next = cache->next;
98     gpx_free_cache(cache);
99     cache = next;
100     }
101    
102     free(gpx);
103     }
104    
105     void gpx_free_all(gpx_t *gpx) {
106     while(gpx) {
107     gpx_t *next = gpx->next;
108     gpx_free(gpx);
109     gpx = next;
110     }
111     }
112    
113     static const char *cache_type_str[] = { "<Unknown>",
114     "Traditional Cache|Traditional|Geocache", "Multi-cache|Multi",
115     "Unknown Cache|Other",
116     "Virtual Cache|Virtual", "Webcam Cache|Webcam", "Event Cache|Event",
117     "Letterbox Hybrid", "Earthcache", "Wherigo Cache",
118     "Mega-Event Cache", "Cache In Trash Out Event",
119     ""};
120    
121     static const char *cache_container_str[] = { "<Unknown>",
122     "Regular", "Small", "Micro", "Not chosen|Unknown",
123     "Other", "Large", "Virtual"
124     ""};
125    
126     static const char *log_type_str[] = { "<Unknown>",
127     "Found it|Found", "Didn't find it|Not Found", "Owner Maintenance",
128     "Write Note|Note|Other",
129     "Post Reviewer Note", "Enable Listing", "Publish Listing", "Will Attend",
130     "Attended", "Webcam Photo taken", "Temporarily Disable Listing",
131     "Needs Maintenance", "Update Coordinates", "Unarchive",
132     "Needs Archived", "Archive",
133     ""};
134    
135     static const char *wpt_sym_str[] = { "<Unknown>",
136     "Stages of a Multicache", "Parking Area", "Final Location",
137     "Question to Answer", "Trailhead", "Reference Point",
138     ""};
139    
140     static int str_search(const char *pstr[], char *str, char *type) {
141     int i=0, ret = -1;
142    
143     while(pstr[i+1][0]) {
144     char *p = (char*)pstr[i+1];
145    
146     /* multiple substrings in pattern? */
147     while(strchr(p, '|')) {
148     if(!strncasecmp(p, str, strchr(p, '|')-p))
149     return i;
150    
151     p = strchr(p, '|')+1;
152     }
153    
154     if(!strcasecmp(p, str))
155     return i;
156    
157     i++;
158     }
159    
160     fprintf(stderr, "ERROR parsing \"%s\": Unknown \"%s\"\n", type, str);
161     return -1;
162     }
163    
164     static tb_t *gpx_parse_gpx_wpt_tbs(xmlDocPtr doc, xmlNode *a_node) {
165     tb_t *tb_chain = NULL, *tb;
166     xmlNode *cur_node = NULL;
167    
168     for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
169     if (cur_node->type == XML_ELEMENT_NODE) {
170     if(strcasecmp(cur_node->name, "travelbug") == 0) {
171     xmlNode *sub_node = cur_node->children;
172    
173     /* create a new log entry */
174     tb = malloc(sizeof(tb_t));
175     memset(tb, 0, sizeof(tb_t));
176     tb->ref = xmlGetProp(cur_node, "ref");
177     if(!tb->ref) tb->ref = strdup("<NONE>");
178    
179     while (sub_node != NULL) {
180     if (sub_node->type == XML_ELEMENT_NODE) {
181     if(strcasecmp(sub_node->name, "name") == 0)
182     tb->name = xmlNodeListGetString(doc, sub_node->children, 1);
183     else
184     printf(" tb unused %s\n", sub_node->name);
185     }
186     sub_node = sub_node->next;
187     }
188    
189     /* add tb to chain */
190     tb_t **cur = &tb_chain;
191     while(*cur && (strcmp(tb->ref, (*cur)->ref)<0))
192     cur = &((*cur)->next);
193    
194     tb->next = *cur;
195     *cur = tb;
196     }
197     }
198     }
199     return tb_chain;
200     }
201    
202     static int log_is_older(log_t *a, log_t *b) {
203     if(a->year < b->year) return TRUE;
204     else if(a->year == b->year) {
205     if(a->month < b->month) return TRUE;
206     else if(a->month == b->month) {
207     if(a->day < b->day) return TRUE;
208     }
209     }
210    
211     return FALSE;
212     }
213    
214     /* parse log entry */
215     static log_t *gpx_parse_gpx_wpt_logs(xmlDocPtr doc, xmlNode *a_node) {
216     log_t *log_chain = NULL, *log;
217     xmlNode *cur_node = NULL;
218    
219     for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
220     if (cur_node->type == XML_ELEMENT_NODE) {
221    
222     if(strcasecmp(cur_node->name, "log") == 0) {
223     xmlNode *sub_node = cur_node->children;
224    
225     /* create a new log entry */
226     log = malloc(sizeof(log_t));
227     memset(log, 0, sizeof(log_t));
228    
229     while (sub_node != NULL) {
230     if (sub_node->type == XML_ELEMENT_NODE) {
231     char *str;
232    
233     if((strcasecmp(sub_node->name, "date") == 0) ||
234     (strcasecmp(sub_node->name, "time") == 0)) {
235     if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
236     sscanf(str, "%d-%d-%d", &log->year, &log->month, &log->day);
237     xmlFree(str);
238     }
239     } else if(strcasecmp(sub_node->name, "type") == 0) {
240     if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
241     log->type = str_search(log_type_str, str, "log");
242     xmlFree(str);
243     }
244     } else if((strcasecmp(sub_node->name, "finder") == 0) ||
245     (strcasecmp(sub_node->name, "geocacher") == 0)) {
246     log->finder = xmlNodeListGetString(doc, sub_node->children, 1);
247     } else if(strcasecmp(sub_node->name, "text") == 0) {
248     log->text = xmlNodeListGetString(doc, sub_node->children, 1);
249     } else {
250     // printf(" log unused = %s\n", sub_node->name);
251     }
252     }
253     sub_node = sub_node->next;
254     }
255    
256     /* add log to chain */
257     log_t **cur = &log_chain;
258     while(*cur && log_is_older(log, *cur))
259     cur = &((*cur)->next);
260    
261     log->next = *cur;
262     *cur = log;
263     }
264     }
265     }
266     return log_chain;
267     }
268    
269     int is_white(char c) {
270     return((c==' ')||(c=='\r')||(c=='\n'));
271     }
272    
273     static int all_is_white(char *str) {
274     while(*str) {
275     if(!is_white(*str))
276     return FALSE;
277    
278     str++;
279     }
280     return TRUE;
281     }
282    
283     /* parse waypoint entry */
284     static void gpx_parse_gpx_wpt(gpx_t *gpx, xmlDocPtr doc, xmlNode *a_node,
285     pos_t *pos) {
286     xmlNode *cur_node = NULL;
287     cache_t **cache = &gpx->cache;
288    
289     /* skip to end of list and create a new entry */
290     while(*cache)
291     cache = &(*cache)->next;
292    
293     *cache = malloc(sizeof(cache_t));
294     memset(*cache, 0, sizeof(cache_t));
295    
296     (*cache)->pos.lat = pos->lat;
297     (*cache)->pos.lon = pos->lon;
298    
299     /* set some defaults */
300     (*cache)->type = CACHE_TYPE_UNKNOWN;
301     (*cache)->container = CACHE_CONT_UNKNOWN;
302     (*cache)->available = TRUE;
303    
304     for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
305     if (cur_node->type == XML_ELEMENT_NODE) {
306     char *prop, *str;
307    
308     if(strcasecmp(cur_node->name, "name") == 0) {
309     (*cache)->id = xmlNodeListGetString(doc, cur_node->children, 1);
310     } else if(strcasecmp(cur_node->name, "url") == 0) {
311     (*cache)->url = xmlNodeListGetString(doc, cur_node->children, 1);
312    
313     /* GC names them cache, OC names them geocache */
314     } else if((strcasecmp(cur_node->name, "cache") == 0) ||
315     (strcasecmp(cur_node->name, "geocache") == 0)) {
316    
317     /* try to read "available" and "archived" marker */
318     if((prop = xmlGetProp(cur_node, "available"))) {
319     (*cache)->available = (strcasecmp(prop, "true") == 0);
320     xmlFree(prop);
321     }
322    
323     if((prop = xmlGetProp(cur_node, "archived"))) {
324     (*cache)->archived = (strcasecmp(prop, "true") == 0);
325     xmlFree(prop);
326     }
327    
328     /* think about the children only */
329     xmlNode *sub_node = cur_node->children;
330     while (sub_node != NULL) {
331     if (sub_node->type == XML_ELEMENT_NODE) {
332    
333     if(strcasecmp(sub_node->name, "name") == 0) {
334     (*cache)->name =
335     xmlNodeListGetString(doc, sub_node->children, 1);
336     } else if(strcasecmp(sub_node->name, "owner") == 0) {
337     (*cache)->owner =
338     xmlNodeListGetString(doc, sub_node->children, 1);
339     } else if(strcasecmp(sub_node->name, "type") == 0) {
340     if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
341     (*cache)->type = str_search(cache_type_str, str, "cache type");
342     xmlFree(str);
343     }
344     } else if(strcasecmp(sub_node->name, "container") == 0) {
345     if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
346     (*cache)->container = str_search(cache_container_str, str,
347     "container");
348     xmlFree(str);
349     }
350     } else if((strcasecmp(sub_node->name, "short_description") == 0) ||
351     (strcasecmp(sub_node->name, "summary") == 0)) {
352     (*cache)->short_description =
353     xmlNodeListGetString(doc, sub_node->children, 1);
354     if((prop = xmlGetProp(sub_node, "html"))) {
355     (*cache)->short_is_html = (strcasecmp(prop, "true") == 0);
356     xmlFree(prop);
357     }
358     } else if((strcasecmp(sub_node->name, "long_description") == 0) ||
359     (strcasecmp(sub_node->name, "description") == 0)) {
360     (*cache)->long_description =
361     xmlNodeListGetString(doc, sub_node->children, 1);
362     if((prop = xmlGetProp(sub_node, "html"))) {
363     (*cache)->long_is_html = (strcasecmp(prop, "true") == 0);
364     xmlFree(prop);
365     }
366     } else if((strcasecmp(sub_node->name, "encoded_hints") == 0) ||
367     (strcasecmp(sub_node->name, "hints") == 0)) {
368     if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
369     /* often hints aren't more than just a bunch of blanks ... */
370     if(!all_is_white(str)) {
371     (*cache)->hint = str;
372     if((prop = xmlGetProp(sub_node, "html"))) {
373     (*cache)->hint_is_html = (strcasecmp(prop, "true") == 0);
374     xmlFree(prop);
375     }
376     } else
377     xmlFree(str);
378     }
379     } else if(strcasecmp(sub_node->name, "difficulty") == 0) {
380     if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
381     (*cache)->difficulty = g_ascii_strtod(str, NULL);
382     xmlFree(str);
383     }
384     } else if(strcasecmp(sub_node->name, "terrain") == 0) {
385     if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
386     (*cache)->terrain = g_ascii_strtod(str, NULL);
387     xmlFree(str);
388     }
389     } else if(strcasecmp(sub_node->name, "logs") == 0) {
390     (*cache)->log = gpx_parse_gpx_wpt_logs(doc, sub_node->children);
391     } else if(strcasecmp(sub_node->name, "travelbugs") == 0) {
392     (*cache)->tb = gpx_parse_gpx_wpt_tbs(doc, sub_node->children);
393     } else {
394     if((strcasecmp(sub_node->name, "country") != 0) &&
395     (strcasecmp(sub_node->name, "licence" /* OC */) != 0) &&
396     (strcasecmp(sub_node->name, "state") != 0) &&
397     (strcasecmp(sub_node->name, "locale" /* OC */) != 0) &&
398     (strcasecmp(sub_node->name, "placed_by" /* OC */) != 0)) {
399     if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
400     printf(" cache unused %s: %s\n", sub_node->name, str);
401     xmlFree(str);
402     }
403     }
404     }
405     }
406     sub_node = sub_node->next;
407     }
408     } else {
409     /* ignore some by purpose (some are parsed seperately below) */
410     if((strcasecmp(cur_node->name, "time") != 0) &&
411     (strcasecmp(cur_node->name, "desc") != 0) &&
412     (strcasecmp(cur_node->name, "urlname") != 0) &&
413     (strcasecmp(cur_node->name, "sym") != 0) &&
414     (strcasecmp(cur_node->name, "cmt") != 0) &&
415     (strcasecmp(cur_node->name, "src") != 0) &&
416     (strcasecmp(cur_node->name, "wptExtension") != 0) &&
417     (strcasecmp(cur_node->name, "type") != 0)) {
418    
419     if(str = xmlNodeListGetString(doc, cur_node->children, 1)) {
420     printf(" wpt unused %s: %s\n", cur_node->name, str);
421     xmlFree(str);
422     }
423     }
424     }
425     }
426     }
427    
428     /* special handling for opencaching.de caches */
429     if(strncasecmp((*cache)->id, "OC", 2) == 0) {
430     /* the html attributes are either missing or wrong on OC ... */
431     (*cache)->long_is_html = TRUE;
432     (*cache)->hint_is_html = TRUE;
433     // (*cache)->logs_are_html = TRUE;
434     }
435    
436     /* neither geocaching.com GC* nor opencaching.com OC* nor */
437     /* geocaching australia GA* waypoint */
438     if((strncasecmp((*cache)->id, "GC", 2) != 0) &&
439     (strncasecmp((*cache)->id, "OC", 2) != 0) &&
440     (strncasecmp((*cache)->id, "GA", 2) != 0)) {
441     cache_t *parent = gpx->cache;
442    
443     /* check if the gpx file contains a cache with matching name */
444     while(parent && strcasecmp(parent->id+2, (*cache)->id+2))
445     parent = parent->next;
446    
447     if(parent && parent != *cache) {
448     wpt_t **wpt = &parent->wpt;
449     char *str;
450    
451     /* search end of list */
452     while(*wpt && (strcmp((*wpt)->id, (*cache)->id)<0))
453     wpt = &(*wpt)->next;
454    
455     *wpt = malloc(sizeof(wpt_t));
456     memset(*wpt, 0, sizeof(wpt_t));
457    
458     /* transfer name to waypoint entry */
459     (*wpt)->id = (*cache)->id;
460     (*cache)->id = NULL;
461    
462     (*wpt)->pos.lat = pos->lat;
463     (*wpt)->pos.lon = pos->lon;
464    
465     /* re-parse entry */
466     for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
467     if (cur_node->type == XML_ELEMENT_NODE) {
468     /* pos_t pos; */
469     if(strcasecmp(cur_node->name, "cmt") == 0) {
470     (*wpt)->cmt = xmlNodeListGetString(doc, cur_node->children, 1);
471     } else if(strcasecmp(cur_node->name, "desc") == 0) {
472     (*wpt)->desc = xmlNodeListGetString(doc, cur_node->children, 1);
473     } else if(strcasecmp(cur_node->name, "sym") == 0) {
474     if(str = xmlNodeListGetString(doc, cur_node->children, 1)) {
475     (*wpt)->sym = str_search(wpt_sym_str, str, "wpt sym");
476     xmlFree(str);
477     }
478     }
479     }
480     }
481    
482     /* and just free the current cache entry as we now have used */
483     /* the data for a caches waypoint */
484     gpx_free_cache(*cache);
485     *cache = NULL;
486     } else {
487     /* if it doesn't have a name etc, it's probably not a real */
488     /* cache, so drop it */
489     if(!(*cache)->name || !(*cache)->id) {
490     printf("Orphaned waypoint: %s\n", (*cache)->id);
491     gpx_free_cache(*cache);
492     *cache = NULL;
493     }
494     }
495     } else {
496     /* this is known to be a geocache due to its waypoint name */
497     /* (gc*, oc*, ga*) and is thus forces to be an entry */
498     if(!(*cache)->name) {
499     (*cache)->name = malloc(strlen((*cache)->id) + sizeof("Unnamed ()")+1);
500     snprintf((*cache)->name, strlen((*cache)->id) + sizeof("Unnamed ()")+1,
501     "Unnamed(%s)", (*cache)->id);
502     }
503     }
504     }
505    
506     void gpx_display_log(log_t *log) {
507     printf(" Log:\n");
508     printf(" date: %d.%d.%d\n", log->day, log->month, log->year);
509     printf(" type: %s\n", log_type_str[log->type+1]);
510     printf(" finder: %s\n", log->finder);
511     // printf(" text: %s\n", log->text);
512     }
513    
514     void gpx_display_cache(cache_t *cache) {
515     log_t *log = cache->log;
516    
517     printf("\nCache:\n");
518     printf(" id: %s\n", cache->id);
519     printf(" name: %s\n", cache->name);
520     printf(" latitude: %f\n", cache->pos.lat);
521     printf(" longitude: %f\n", cache->pos.lon);
522     printf(" owner: %s\n", cache->owner);
523     printf(" type: %s\n", cache_type_str[cache->type+1]);
524     printf(" container: %s\n", cache_container_str[cache->container+1]);
525     printf(" difficulty: %.1f\n", cache->difficulty);
526     printf(" terrain: %.1f\n", cache->terrain);
527     // printf(" short: %s\n", cache->short_description);
528     // printf(" long: %s\n", cache->long_description);
529     // printf(" hint: %s\n", cache->hint);
530    
531     while(log) {
532     gpx_display_log(log);
533     log = log->next;
534     }
535     }
536    
537     void gpx_display_all(gpx_t *gpx) {
538     while(gpx) {
539     cache_t *cache = gpx->cache;
540    
541     printf("GPX name: %s\n", gpx->name);
542     printf("GPX desc: %s\n", gpx->desc);
543     printf("GPX date: %d.%d.%d\n", gpx->day, gpx->month, gpx->year);
544     while(cache) {
545     gpx_display_cache(cache);
546     cache = cache->next;
547     }
548     gpx = gpx->next;
549     }
550     }
551    
552     /* parse gpx entry */
553     static void gpx_parse_gpx(gpx_t *gpx, xmlDocPtr doc, xmlNode * a_node) {
554     xmlNode *cur_node = NULL;
555    
556     for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
557     if (cur_node->type == XML_ELEMENT_NODE) {
558     /* some global information */
559     if(!gpx->name && strcasecmp(cur_node->name, "name") == 0) {
560     gpx->name = xmlNodeListGetString(doc, cur_node->children, 1);
561     } else if(!gpx->desc && strcasecmp(cur_node->name, "desc") == 0) {
562     gpx->desc = xmlNodeListGetString(doc, cur_node->children, 1);
563     } else if((strcasecmp(cur_node->name, "date") == 0) ||
564     (strcasecmp(cur_node->name, "time") == 0)) {
565     char *str;
566     if(str = xmlNodeListGetString(doc, cur_node->children, 1)) {
567     sscanf(str, "%d-%d-%d", &gpx->year, &gpx->month, &gpx->day);
568     xmlFree(str);
569     }
570     } else if(strcasecmp(cur_node->name, "wpt") == 0) {
571     char *str;
572     pos_t pos = { 0.0, 0.0 };
573    
574     if((str = xmlGetProp(cur_node, "lat"))) {
575     pos.lat = g_ascii_strtod(str, NULL);
576     xmlFree(str);
577     }
578    
579     if((str = xmlGetProp(cur_node, "lon"))) {
580     pos.lon = g_ascii_strtod(str, NULL);
581     xmlFree(str);
582     }
583    
584     gpx_parse_gpx_wpt(gpx, doc, cur_node->children, &pos);
585     }
586     }
587     }
588     }
589    
590     /* parse loc waypoint entry */
591     static void gpx_parse_loc_wpt(gpx_t *gpx, xmlDocPtr doc, xmlNode *a_node) {
592     xmlNode *cur_node = NULL;
593     cache_t **cache = &gpx->cache;
594    
595     /* skip to end of list and create a new entry */
596     while(*cache)
597     cache = &(*cache)->next;
598    
599     *cache = malloc(sizeof(cache_t));
600     memset(*cache, 0, sizeof(cache_t));
601    
602     /* set some defaults */
603     (*cache)->type = CACHE_TYPE_UNKNOWN;
604     (*cache)->container = CACHE_CONT_UNKNOWN;
605     (*cache)->available = TRUE;
606    
607     for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
608     if (cur_node->type == XML_ELEMENT_NODE) {
609     char *str;
610    
611     if(strcasecmp(cur_node->name, "name") == 0) {
612     (*cache)->name = xmlNodeListGetString(doc, cur_node->children, 1);
613     (*cache)->id = xmlGetProp(cur_node, "id");
614     } else if(strcasecmp(cur_node->name, "link") == 0) {
615     (*cache)->url =
616     xmlNodeListGetString(doc, cur_node->children, 1);
617     } else if(strcasecmp(cur_node->name, "type") == 0) {
618     if(str = xmlNodeListGetString(doc, cur_node->children, 1)) {
619     (*cache)->type = str_search(cache_type_str, str, "cache type");
620     xmlFree(str);
621     }
622     } else if(strcasecmp(cur_node->name, "coord") == 0) {
623     if((str = xmlGetProp(cur_node, "lat"))) {
624     (*cache)->pos.lat = g_ascii_strtod(str, NULL);
625     xmlFree(str);
626     }
627     if((str = xmlGetProp(cur_node, "lon"))) {
628     (*cache)->pos.lon = g_ascii_strtod(str, NULL);
629     xmlFree(str);
630     }
631     }
632     }
633     }
634     }
635    
636     /* parse loc entry */
637     static void gpx_parse_loc(gpx_t *gpx, xmlDocPtr doc, xmlNode * a_node) {
638     xmlNode *cur_node = NULL;
639    
640     for (cur_node = a_node; cur_node; cur_node = cur_node->next)
641     if (cur_node->type == XML_ELEMENT_NODE)
642     if(strcasecmp(cur_node->name, "waypoint") == 0)
643     gpx_parse_loc_wpt(gpx, doc, cur_node->children);
644     }
645    
646     /* parse root element and search for "gpx" */
647     static gpx_t *gpx_parse_root(xmlDocPtr doc, xmlNode * a_node,
648     char *filename, gpx_t *in) {
649     gpx_t *gpx;
650     xmlNode *cur_node = NULL;
651    
652     /* no gpx entry given, create a new one */
653     if(!in) {
654     /* allocate memory to hold gpx file description */
655     gpx = malloc(sizeof(gpx_t));
656     memset(gpx, 0, sizeof(gpx_t));
657     gpx->filename = strdup(filename);
658     } else
659     gpx = in;
660    
661     for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
662     if (cur_node->type == XML_ELEMENT_NODE) {
663     /* parse gpx file ... */
664     if(strcasecmp(cur_node->name, "gpx") == 0)
665     gpx_parse_gpx(gpx, doc, cur_node->children);
666     /* ... or loc file */
667     else if(strcasecmp(cur_node->name, "loc") == 0)
668     gpx_parse_loc(gpx, doc, cur_node->children);
669     }
670     }
671    
672     /* check if a name has been set and use filename if not */
673     if(!in && !gpx->name) {
674     if(!gpx->desc) {
675     char *str = strrchr(filename, '/');
676     if(str) gpx->name = strdup(str+1);
677     else gpx->name = strdup(filename);
678     } else
679     gpx->name = strdup(gpx->desc);
680     }
681    
682     return gpx;
683     }
684    
685     static gpx_t *gpx_parse_doc(xmlDocPtr doc, char *filename, gpx_t *gpx_in) {
686     gpx_t *gpx;
687    
688     /* Get the root element node */
689     xmlNode *root_element = xmlDocGetRootElement(doc);
690    
691     gpx = gpx_parse_root(doc, root_element, filename, gpx_in);
692    
693     /*free the document */
694     xmlFreeDoc(doc);
695    
696     /*
697     * Free the global variables that may
698     * have been allocated by the parser.
699     */
700     xmlCleanupParser();
701    
702     if(!gpx) return gpx_in;
703    
704     return gpx;
705     }
706    
707     static gpx_t *gpx_parse_file(char *filename) {
708     gpx_t *gpx = NULL;
709     xmlDoc *doc = NULL;
710    
711     struct timeval start;
712     gettimeofday(&start, NULL);
713    
714     LIBXML_TEST_VERSION;
715     #if 1
716     /* DOM parser */
717    
718     /* parse the file and get the DOM */
719     if ((doc = xmlReadFile(filename, NULL, 0)) == NULL) {
720     xmlErrorPtr errP = xmlGetLastError();
721     errorf("While parsing \"%s\":\n\n%s", filename, errP->message);
722     return NULL;
723     }
724    
725     gpx = gpx_parse_doc(doc, filename, NULL);
726     #else
727    
728     #endif
729    
730     struct timeval end;
731     gettimeofday(&end, NULL);
732    
733     printf("total parse time for %s: %ldms\n", filename,
734     (end.tv_usec - start.tv_usec)/1000 +
735     (end.tv_sec - start.tv_sec)*1000);
736    
737     /* check of there's a waypoints file (*-wpts.gpx) for this */
738     if(strrchr(filename, '.')) {
739     char *dot = strrchr(filename, '.');
740     char wpts_name[128];
741     *dot = 0;
742     snprintf(wpts_name, sizeof(wpts_name), "%s-wpts.gpx", filename);
743     *dot = '.';
744    
745     if(g_file_test(wpts_name, G_FILE_TEST_EXISTS)) {
746    
747     /* and do the same for this file again */
748     if((doc = xmlReadFile(wpts_name, NULL, 0)) == NULL) {
749     xmlErrorPtr errP = xmlGetLastError();
750     errorf("While parsing \"%s\":\n\n%s", wpts_name, errP->message);
751     } else
752     gpx = gpx_parse_doc(doc, filename, gpx);
753     }
754     }
755    
756     return gpx;
757     }
758    
759     static gpx_t *decompress_file(unzFile file, char *name, char *filename,
760     gpx_t *gpx_in) {
761     unz_file_info info;
762     gpx_t *gpx;
763    
764     if((unzLocateFile(file, name, FALSE) != Z_OK) ||
765     (unzGetCurrentFileInfo(file, &info, NULL,0, NULL,0, NULL,0) != Z_OK) ||
766     (unzOpenCurrentFile(file) != UNZ_OK)) {
767    
768     /* do not complain if we are processing a waypoints file */
769     if(!gpx_in)
770     errorf("Unable to locate/get info/open\n%s\ninside\n%s",
771     name, filename);
772     else
773     printf("Unable to locate/get info/open %s inside %s\n",
774     name, filename);
775    
776     return gpx_in;
777     }
778    
779     printf("file size is %d\n", info.uncompressed_size);
780    
781     char *buffer = malloc(info.uncompressed_size);
782     if(!buffer) {
783     errorf("Out of memory while uncompressing file");
784     unzCloseCurrentFile(file);
785     return gpx_in;
786     }
787    
788     if(unzReadCurrentFile(file, buffer, info.uncompressed_size) < 0) {
789     errorf("Read error on compressed file");
790     free(buffer);
791     unzCloseCurrentFile(file);
792     return gpx_in;
793     }
794    
795     struct timeval start;
796     gettimeofday(&start, NULL);
797    
798     /* fire up libxml */
799     LIBXML_TEST_VERSION;
800    
801     xmlDoc *doc = NULL;
802     /* parse the file and get the DOM */
803     if ((doc = xmlReadMemory(buffer, info.uncompressed_size,
804     NULL, NULL, 0)) == NULL) {
805     xmlErrorPtr errP = xmlGetLastError();
806     errorf("While parsing \"%s\":\n\n%s", filename, errP->message);
807     free(buffer);
808     unzCloseCurrentFile(file);
809     return gpx_in;
810     }
811    
812     printf("loaded successfully, parse ...\n");
813    
814     gpx = gpx_parse_doc(doc, filename, gpx_in);
815    
816     struct timeval end;
817     gettimeofday(&end, NULL);
818    
819     printf("total parse time for %s: %ldms\n", filename,
820     (end.tv_usec - start.tv_usec)/1000 +
821     (end.tv_sec - start.tv_sec)*1000);
822    
823     free(buffer);
824     unzCloseCurrentFile(file);
825     return gpx;
826     }
827    
828     static gpx_t *decompress_zip(char *filename) {
829     char *gpx_name, *fbase;
830     gpx_t *gpx = NULL;
831    
832     /* extract base name and allocate space for file names */
833     fbase = strrchr(filename, '/');
834     if(!fbase) fbase = filename;
835     else fbase++; /* skip '/' */
836     gpx_name = malloc(strlen(fbase)+strlen("-wpts")+1);
837    
838     unzFile file = unzOpen(filename);
839     if(!file) {
840     errorf("Error opening file %s for unzip", filename);
841     free(gpx_name);
842     return NULL;
843     }
844    
845     printf("ZIP file successfully opened\n");
846    
847     /* try to open gpx file inside */
848     strcpy(gpx_name, fbase);
849     strcpy(gpx_name+strlen(gpx_name)-4, ".gpx");
850     printf("gpx file name is %s\n", gpx_name);
851    
852     gpx = decompress_file(file, gpx_name, filename, NULL);
853    
854     /* try to open -wpts.gpx file inside */
855     strcpy(gpx_name, fbase);
856     strcpy(gpx_name+strlen(gpx_name)-4, "-wpts.gpx");
857     printf("gpx wpts file name is %s\n", gpx_name);
858    
859     gpx = decompress_file(file, gpx_name, filename, gpx);
860    
861     unzClose(file);
862     free(gpx_name);
863     return gpx;
864     }
865    
866     gpx_t *gpx_parse(char *filename) {
867     if((strlen(filename) > 4) &&
868     !strcasecmp(filename+strlen(filename)-4, ".zip")) {
869     printf("trying to load a zip file!\n");
870    
871     return decompress_zip(filename);
872     }
873    
874     return gpx_parse_file(filename);
875     }
876    
877     /* scan entire directory */
878     gpx_t *gpx_parse_dir(char *dirname) {
879     GnomeVFSResult result;
880     GnomeVFSDirectoryHandle *handle;
881     GnomeVFSFileInfo *finfo = gnome_vfs_file_info_new();;
882    
883     gpx_t *gpx = NULL;
884     xmlDoc *doc = NULL;
885     xmlNode *root_element = NULL;
886    
887     LIBXML_TEST_VERSION;
888    
889     result = gnome_vfs_directory_open(&handle, dirname,
890     GNOME_VFS_FILE_INFO_DEFAULT);
891    
892     if(result != GNOME_VFS_OK) {
893     errorf("Unable to open directory \"%s\":\n%s",
894     dirname, gnome_vfs_result_to_string(result));
895    
896     return NULL;
897     }
898    
899     while(GNOME_VFS_OK == gnome_vfs_directory_read_next(handle, finfo)) {
900     if(finfo->type == GNOME_VFS_FILE_TYPE_REGULAR) {
901     char *ext = finfo->name+strlen(finfo->name)-4;
902    
903     /* check if file ends with .gpx or .loc */
904     if((strcasecmp(ext, ".gpx") == 0) || (strcasecmp(ext, ".loc") == 0)) {
905     char *filename = malloc(strlen(dirname)+strlen(finfo->name)+2);
906    
907     strcpy(filename, dirname);
908     if(strlastchr(filename) != '/')
909     strcat(filename, "/");
910     strcat(filename, finfo->name);
911    
912     /* parse the file and get the DOM */
913     doc = xmlReadFile(filename, NULL, 0);
914    
915     if (doc == NULL) {
916     xmlErrorPtr errP = xmlGetLastError();
917     errorf("While parsing \"%s\":\n\n%s", filename, errP->message);
918     } else {
919     /*Get the root element node */
920     root_element = xmlDocGetRootElement(doc);
921    
922     gpx = gpx_parse_root(doc, root_element, filename, gpx);
923    
924     xmlFreeDoc(doc);
925     xmlCleanupParser();
926     }
927    
928     free(filename);
929     }
930     }
931     }
932    
933     if(gpx) {
934     /* replace file name with directory name */
935     free(gpx->filename);
936     gpx->filename = strdup(dirname);
937    
938     /* replace gpx name with directory name */
939     free(gpx->name);
940    
941     /* retrieve pure dirname if possible */
942     char *n = strrchr(dirname, '/');
943     if(!n) n = dirname;
944     else n++;
945    
946     // gpx->name = malloc(strlen("<DIR> ")+strlen(n)+1);
947     // strcpy(gpx->name, "<DIR> ");
948     // strcat(gpx->name, n);
949     gpx->name = strdup(n);
950     }
951    
952     gnome_vfs_file_info_unref(finfo);
953     gnome_vfs_directory_close(handle);
954    
955     return gpx;
956     }
957    
958     /* return number of caches in given gpx file */
959     int gpx_total_caches(gpx_t *gpx) {
960     cache_t *cache = gpx->cache;
961     int num = 0;
962    
963     while(cache) {
964     num++;
965     cache = cache->next;
966     }
967    
968     return num;
969     }
970    
971     int gpx_number_of_waypoints(wpt_t *wpt) {
972     int num = 0;
973    
974     while(wpt) {
975     num++;
976     wpt = wpt->next;
977     }
978    
979     return num;
980     }
981    
982     int gpx_number_of_logs(log_t *log) {
983     int num = 0;
984    
985     while(log) {
986     num++;
987     log = log->next;
988     }
989    
990     return num;
991     }
992    
993     int gpx_number_of_tbs(tb_t *tb) {
994     int num = 0;
995    
996     while(tb) {
997     num++;
998     tb = tb->next;
999     }
1000    
1001     return num;
1002     }
1003    
1004     /* http://mathforum.org/library/drmath/view/55417.html */
1005     float gpx_pos_get_bearing(pos_t p1, pos_t p2) {
1006     /* convert to radians */
1007     p1.lat *= (M_PI/180.0); p1.lon *= (M_PI/180.0);
1008     p2.lat *= (M_PI/180.0); p2.lon *= (M_PI/180.0);
1009    
1010     return fmodf(360.0 + (180.0/M_PI) *
1011     (atan2( sin(p2.lon - p1.lon) * cos(p2.lat),
1012     cos(p1.lat) * sin(p2.lat) -
1013     sin(p1.lat) * cos(p2.lat) * cos(p2.lon - p1.lon))),
1014     360.0);
1015     }
1016    
1017     /* http://mathforum.org/library/drmath/view/51722.html */
1018     float gpx_pos_get_distance(pos_t p1, pos_t p2, int miles) {
1019     /* convert to radians */
1020     p1.lat *= (M_PI/180.0); p1.lon *= (M_PI/180.0);
1021     p2.lat *= (M_PI/180.0); p2.lon *= (M_PI/180.0);
1022    
1023     float aob = acos(cos(p1.lat) * cos(p2.lat) * cos(p2.lon - p1.lon) +
1024     sin(p1.lat) * sin(p2.lat));
1025    
1026     if(miles)
1027     return(aob * 3959.0); /* great circle radius in miles */
1028    
1029     return(aob * 6371.0); /* great circle radius in kilometers */
1030     }
1031    
1032     void gpx_pos_get_distance_str(char *str, int len,
1033     pos_t p1, pos_t p2, int mil) {
1034    
1035     float dist = gpx_pos_get_distance(p1, p2, mil);
1036     distance_str(str, len, dist, mil);
1037     }
1038    
1039     /* http://library.gnome.org/devel/gtk/unstable/GtkRadioButton.html */
1040     void gpx_sort(gpx_t *gpx, int by, pos_t *refpos) {
1041     cache_t **new;
1042     cache_t *cur = gpx->cache;
1043     int total = gpx_total_caches(gpx);
1044     float *dist_cache = malloc(total * sizeof(float));
1045    
1046     gpx->cache = NULL; /* detach old chain */
1047     while(cur) {
1048     float cur_dist = gpx_pos_get_distance(*refpos, gpx_cache_pos(cur), 0);
1049     int cur_cnt = 0;
1050    
1051     new = &(gpx->cache);
1052    
1053     /* search for currect insertion point */
1054     while(*new && (dist_cache[cur_cnt] < cur_dist)) {
1055     new = &((*new)->next);
1056     cur_cnt++;
1057     }
1058    
1059     /* save distance for further comparisons */
1060     memmove(dist_cache+cur_cnt+1, dist_cache+cur_cnt,
1061     sizeof(float)*(total-cur_cnt-1));
1062     dist_cache[cur_cnt++] = cur_dist;
1063    
1064     cache_t *next = cur->next;
1065    
1066     /* insert into "new" chain */
1067     cur->next = *new;
1068     *new = cur;
1069    
1070     cur = next;
1071     }
1072    
1073     free(dist_cache);
1074     }
1075    
1076     gpx_t *gpx_cache2gpx(gpx_t *gpx, cache_t *search_cache) {
1077     while(gpx) {
1078     cache_t *cache = gpx->cache;
1079     while(cache) {
1080     if(cache == search_cache)
1081     return gpx;
1082    
1083     cache = cache->next;
1084     }
1085     gpx = gpx->next;
1086     }
1087    
1088     return NULL;
1089     }
1090    
1091     /* since the actual cache position may be overridden, we */
1092     /* always access the position through this function */
1093     pos_t gpx_cache_pos(cache_t *cache) {
1094     if(cache->notes && cache->notes->override)
1095     return cache->notes->pos;
1096    
1097     return cache->pos;
1098     }