Contents of /trunk/src/gpx.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 205 - (hide annotations)
Mon Nov 23 20:12:22 2009 UTC (14 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 39882 byte(s)
Map/cache icon handling
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     void gpx_free_wpt(wpt_t *wpt) {
39     if(wpt->id) xmlFree(wpt->id);
40     if(wpt->cmt) xmlFree(wpt->cmt);
41     if(wpt->desc) xmlFree(wpt->desc);
42     free(wpt);
43     }
44    
45 harbaum 134 void gpx_free_user(user_t *user) {
46     if(user->name) xmlFree(user->name);
47     free(user);
48     }
49    
50 harbaum 1 void gpx_free_log(log_t *log) {
51 harbaum 137 if(log->finder) gpx_free_user(log->finder);
52 harbaum 1 if(log->text) xmlFree(log->text);
53     free(log);
54     }
55    
56     void gpx_free_tb(tb_t *tb) {
57     if(tb->name) xmlFree(tb->name);
58     if(tb->ref) xmlFree(tb->ref);
59     free(tb);
60     }
61    
62     void gpx_free_cache(cache_t *cache) {
63     log_t *log = cache->log;
64     wpt_t *wpt = cache->wpt;
65     tb_t *tb = cache->tb;
66    
67     if(cache->id) xmlFree(cache->id);
68     if(cache->name) xmlFree(cache->name);
69 harbaum 134 if(cache->owner) gpx_free_user(cache->owner);
70 harbaum 1 if(cache->short_description) xmlFree(cache->short_description);
71     if(cache->long_description) xmlFree(cache->long_description);
72     if(cache->hint) xmlFree(cache->hint);
73     if(cache->url) xmlFree(cache->url);
74    
75     /* free all logs */
76     while(log) { log_t *next = log->next; gpx_free_log(log); log = next; }
77    
78     /* free all waypoints */
79     while(wpt) { wpt_t *next = wpt->next; gpx_free_wpt(wpt); wpt = next; }
80    
81     /* free all tbs */
82     while(tb) { tb_t *next = tb->next; gpx_free_tb(tb); tb = next; }
83    
84     if(cache->notes) notes_free(cache->notes);
85    
86     free(cache);
87     }
88    
89     void gpx_free_caches(gpx_t *gpx) {
90     cache_t *cache = gpx->cache;
91    
92     /* free all caches */
93     while(cache) {
94     cache_t *next = cache->next;
95     gpx_free_cache(cache);
96     cache = next;
97     }
98    
99     gpx->cache = NULL;
100     }
101    
102     void gpx_free(gpx_t *gpx) {
103    
104     if(gpx->name) xmlFree(gpx->name);
105     if(gpx->desc) xmlFree(gpx->desc);
106     if(gpx->filename) free(gpx->filename);
107    
108     gpx_free_caches(gpx);
109    
110     free(gpx);
111     }
112    
113     void gpx_free_all(gpx_t *gpx) {
114     while(gpx) {
115     gpx_t *next = gpx->next;
116     gpx_free(gpx);
117     gpx = next;
118     }
119     }
120    
121     static const char *cache_type_str[] = { "<Unknown>",
122 harbaum 144 "Traditional Cache|Traditional|Geocache", "Multi-cache|Multi|Multicache",
123     "Unknown Cache|Other|Unknown",
124 harbaum 1 "Virtual Cache|Virtual", "Webcam Cache|Webcam", "Event Cache|Event|Geocoins:",
125     "Letterbox Hybrid|Letterbox", "Earthcache", "Wherigo Cache",
126     "Mega-Event Cache", "Cache In Trash Out Event",
127     ""};
128    
129     static const char *cache_container_str[] = { "<Unknown>",
130     "Regular", "Small", "Micro", "Not chosen|Unknown",
131     "Other", "Large", "Virtual"
132     ""};
133    
134     static const char *log_type_str[] = { "<Unknown>",
135     "Found it|Found", "Didn't find it|Not Found", "Owner Maintenance",
136     "Write Note|Note|Other",
137     "Post Reviewer Note", "Enable Listing", "Publish Listing", "Will Attend",
138     "Attended", "Webcam Photo taken",
139     "Temporarily Disable Listing|Cache Disabled!",
140     "Needs Maintenance", "Update Coordinates", "Unarchive|Archive (show)",
141     "Needs Archived", "Archive",
142     ""};
143    
144     static const char *wpt_sym_str[] = { "<Unknown>",
145     "Stages of a Multicache", "Parking Area", "Final Location",
146     "Question to Answer", "Trailhead", "Reference Point",
147     ""};
148    
149     #define DLG_DIV 10
150    
151     /* create the dialog box shown while loading in progress */
152     gpx_dialog_t *gpx_busy_dialog_new(GtkWidget *parent) {
153     #ifdef USE_MAEMO
154     gpx_dialog_t *dialog = malloc(sizeof(gpx_dialog_t));
155     memset(dialog, 0, sizeof(gpx_dialog_t));
156    
157     dialog->dialog = gtk_dialog_new();
158    
159     gtk_dialog_set_has_separator(GTK_DIALOG(dialog->dialog), FALSE);
160 harbaum 7 gtk_window_set_title(GTK_WINDOW(dialog->dialog), _("Loading"));
161 harbaum 1 gtk_window_set_default_size(GTK_WINDOW(dialog->dialog), 300, 10);
162    
163 harbaum 30 gtk_window_set_modal(GTK_WINDOW(dialog->dialog), TRUE);
164 harbaum 1 gtk_window_set_transient_for(GTK_WINDOW(dialog->dialog), GTK_WINDOW(parent));
165    
166     dialog->label = gtk_label_new("---");
167     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog->dialog)->vbox),
168     dialog->label);
169    
170     dialog->pbar = gtk_progress_bar_new();
171     gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(dialog->pbar),
172     0.0025 * DLG_DIV);
173    
174     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog->dialog)->vbox),
175     dialog->pbar);
176    
177     gtk_widget_show_all(dialog->dialog);
178    
179     return dialog;
180     #else
181     return NULL;
182     #endif
183     }
184    
185     void gpx_busy_dialog_destroy(gpx_dialog_t *dialog) {
186     if(!dialog) return;
187    
188     gtk_widget_destroy(dialog->dialog);
189     free(dialog);
190     }
191    
192     static void gpx_busy_dialog_progress(gpx_dialog_t *dialog) {
193     static int sub_dlg = 0;
194    
195     if(!dialog) return;
196    
197     if(sub_dlg++ >= DLG_DIV) {
198     sub_dlg = 0;
199    
200     gtk_progress_bar_pulse(GTK_PROGRESS_BAR(dialog->pbar));
201    
202     /* wait for main gui to appear */
203     while(gtk_events_pending())
204     gtk_main_iteration();
205     }
206     }
207    
208     static void gpx_busy_dialog_set(gpx_dialog_t *dialog, char *name) {
209     if(!dialog) return;
210    
211     if(strrchr(name, '/'))
212     name = strrchr(name, '/')+1;
213    
214     gtk_label_set_text(GTK_LABEL(dialog->label), name);
215    
216     /* wait for main gui to appear */
217     while(gtk_events_pending())
218     gtk_main_iteration();
219     }
220    
221     static int str_search(const char *pstr[], char *str, char *type) {
222     int i=0;
223    
224     while(pstr[i+1][0]) {
225     char *p = (char*)pstr[i+1];
226    
227     /* multiple substrings in pattern? */
228     while(strchr(p, '|')) {
229     if(!strncasecmp(p, str, strchr(p, '|')-p))
230     return i;
231    
232     p = strchr(p, '|')+1;
233     }
234    
235     if(!strcasecmp(p, str))
236     return i;
237    
238     i++;
239     }
240    
241     fprintf(stderr, "ERROR parsing \"%s\": Unknown \"%s\"\n", type, str);
242     return -1;
243     }
244    
245     static int log_is_older(log_t *a, log_t *b) {
246     if(a->year < b->year) return TRUE;
247     else if(a->year == b->year) {
248     if(a->month < b->month) return TRUE;
249     else if(a->month == b->month) {
250     if(a->day < b->day) return TRUE;
251     }
252     }
253    
254     return FALSE;
255     }
256    
257     int is_white(char c) {
258     return((c==' ')||(c=='\r')||(c=='\n'));
259     }
260    
261     static int all_is_white(char *str) {
262     while(*str) {
263     if(!is_white(*str))
264     return FALSE;
265    
266     str++;
267     }
268     return TRUE;
269     }
270    
271     void gpx_display_log(log_t *log) {
272     printf(" Log:\n");
273     printf(" date: %d.%d.%d\n", log->day, log->month, log->year);
274     printf(" type: %s\n", log_type_str[log->type+1]);
275 harbaum 137 printf(" finder: %s\n", log->finder->name);
276 harbaum 138 printf(" id: #%u\n", log->id);
277 harbaum 1 // printf(" text: %s\n", log->text);
278     }
279    
280     void gpx_display_cache(cache_t *cache) {
281     log_t *log = cache->log;
282    
283     printf("\nCache:\n");
284     printf(" id: %s\n", cache->id);
285     printf(" name: %s\n", cache->name);
286     printf(" latitude: %f\n", cache->pos.lat);
287     printf(" longitude: %f\n", cache->pos.lon);
288 harbaum 134 printf(" owner: %s\n", cache->owner->name);
289 harbaum 1 printf(" type: %s\n", cache_type_str[cache->type+1]);
290     printf(" container: %s\n", cache_container_str[cache->container+1]);
291     printf(" difficulty: %.1f\n", cache->difficulty);
292     printf(" terrain: %.1f\n", cache->terrain);
293     // printf(" short: %s\n", cache->short_description);
294     // printf(" long: %s\n", cache->long_description);
295     // printf(" hint: %s\n", cache->hint);
296    
297     while(log) {
298     gpx_display_log(log);
299     log = log->next;
300     }
301     }
302    
303     void gpx_display_all(gpx_t *gpx) {
304     while(gpx) {
305     cache_t *cache = gpx->cache;
306    
307     printf("GPX name: %s\n", gpx->name);
308     printf("GPX desc: %s\n", gpx->desc);
309     printf("GPX date: %d.%d.%d\n", gpx->day, gpx->month, gpx->year);
310     while(cache) {
311     gpx_display_cache(cache);
312     cache = cache->next;
313     }
314     gpx = gpx->next;
315     }
316     }
317    
318     static gint my_strcmp(const xmlChar *a, const xmlChar *b) {
319     if(!a && !b) return 0;
320     if(!a) return -1;
321     if(!b) return +1;
322     return strcmp((char*)a,(char*)b);
323     }
324    
325     static float xml_get_prop_float(xmlTextReaderPtr reader, char *name) {
326     float ret = NAN;
327     char *prop;
328     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST name))) {
329     ret = g_ascii_strtod(prop, NULL);
330     xmlFree(prop);
331     }
332     return ret;
333     }
334    
335 harbaum 134 static unsigned int xml_get_prop_id(xmlTextReaderPtr reader) {
336     unsigned int ret = 0;
337     char *prop;
338     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
339     ret = atoi(prop);
340     xmlFree(prop);
341     }
342     return ret;
343     }
344    
345 harbaum 1 static int xml_prop_is(xmlTextReaderPtr reader, char *name, char *value,
346     int def_value) {
347     int match = def_value;
348     char *prop;
349     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST name))) {
350     match = (strcasecmp(prop, value) == 0);
351     xmlFree(prop);
352     }
353     return match;
354     }
355    
356     /* skip current element incl. everything below (mainly for testing) */
357     /* returns FALSE if something failed */
358     static gboolean skip_element(xmlTextReaderPtr reader) {
359     g_assert(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT);
360     const xmlChar *name = xmlTextReaderConstName(reader);
361     g_assert(name);
362     int depth = xmlTextReaderDepth(reader);
363    
364     if(xmlTextReaderIsEmptyElement(reader))
365     return TRUE;
366    
367     int ret = xmlTextReaderRead(reader);
368     while((ret == 1) &&
369     ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
370     (xmlTextReaderDepth(reader) > depth) ||
371     (my_strcmp(xmlTextReaderConstName(reader), name) != 0))) {
372     ret = xmlTextReaderRead(reader);
373     }
374     return(ret == 1);
375     }
376    
377     static char *process_text(xmlTextReaderPtr reader) {
378     char *text = NULL;
379    
380 harbaum 101 if(!xmlTextReaderIsEmptyElement(reader)) {
381 harbaum 1
382 harbaum 101 int depth = xmlTextReaderDepth(reader);
383     int ret = xmlTextReaderRead(reader);
384     while((ret == 1) &&
385     ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
386     (xmlTextReaderDepth(reader) != depth))) {
387    
388     /* found a text fragment */
389     if((xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT) ||
390     (xmlTextReaderNodeType(reader) == XML_READER_TYPE_CDATA)) {
391     char *frag = (char*)xmlTextReaderConstValue(reader);
392    
393     if(!text) text = strdup(frag);
394     else {
395     char *old = text;
396     text = malloc(strlen(old) + strlen(frag) + 1);
397     strcpy(text, old);
398     strcat(text, frag);
399     free(old);
400     }
401 harbaum 1 }
402 harbaum 101 ret = xmlTextReaderRead(reader);
403 harbaum 1 }
404     }
405    
406     return text;
407     }
408    
409     static int xml_str_search(xmlTextReaderPtr reader,
410     const char *pstr[], char *type, int def) {
411     char *text = process_text(reader);
412     int result = def;
413     if(text) {
414     result = str_search(pstr, text, type);
415     free(text);
416     }
417     return result;
418     }
419    
420     static float xml_float(xmlTextReaderPtr reader, float def) {
421     char *text = process_text(reader);
422     float result = def;
423     if(text) {
424     result = g_ascii_strtod(text, NULL);
425     free(text);
426     }
427     return result;
428     }
429    
430     static void xml_get_date(xmlTextReaderPtr reader, int *year, int *month, int *day) {
431     char *str = process_text(reader);
432     if(str) {
433     sscanf(str, "%d-%d-%d", year, month, day);
434     free(str);
435     }
436     }
437    
438 harbaum 204 static log_t *process_gpx_wpt_gc_logs_log(xmlTextReaderPtr reader,
439     char *username, cache_t *cache) {
440 harbaum 1
441 harbaum 204 gboolean by_user = FALSE;
442    
443 harbaum 1 if(xmlTextReaderIsEmptyElement(reader))
444     return NULL;
445    
446     /* create a new log entry */
447 harbaum 138 log_t *log = g_new0(log_t, 1);
448 harbaum 1
449 harbaum 138 char *prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id");
450     if(prop)
451     log->id = atoi(prop);
452    
453 harbaum 1 /* process all sub-nodes */
454     int depth = xmlTextReaderDepth(reader);
455     int ret = xmlTextReaderRead(reader);
456     while(ret == 1) {
457    
458     switch(xmlTextReaderNodeType(reader)) {
459     case XML_READER_TYPE_ELEMENT:
460     g_assert(xmlTextReaderDepth(reader) == depth+1);
461     char *name = (char*)xmlTextReaderConstName(reader);
462     if(strrchr(name, ':')) name = strrchr(name, ':')+1;
463     if(name) {
464     if((strcasecmp(name, "date") == 0) ||
465     (strcasecmp(name, "time") == 0)) {
466     xml_get_date(reader, &log->year, &log->month, &log->day);
467     } else if(strcasecmp(name, "type") == 0) {
468     log->type = xml_str_search(reader, log_type_str, "log", 0);
469     } else if((strcasecmp(name, "finder") == 0) ||
470     (strcasecmp(name, "geocacher") == 0)) {
471 harbaum 137 if(!log->finder) {
472     log->finder = g_new0(user_t, 1);
473     log->finder->name = process_text(reader);
474     log->finder->id = xml_get_prop_id(reader);
475 harbaum 204
476     /* is this a log by us? */
477     if(username && !strcasecmp(username, log->finder->name))
478     by_user = TRUE;
479 harbaum 137 }
480 harbaum 1 } else if(strcasecmp(name, "text") == 0) {
481     if(!log->text) log->text = process_text(reader);
482     } else
483     skip_element(reader);
484     } else
485     skip_element(reader);
486     break;
487    
488     case XML_READER_TYPE_END_ELEMENT:
489 harbaum 204 if(by_user && log->type == LOG_TYPE_FOUND)
490     cache->found = TRUE;
491    
492 harbaum 1 /* end element must be for the current element */
493     g_assert(xmlTextReaderDepth(reader) == depth);
494     return log;
495     break;
496    
497     default:
498     break;
499     }
500     ret = xmlTextReaderRead(reader);
501     }
502    
503 harbaum 74 gpx_free_log(log);
504     return NULL;
505 harbaum 1 }
506    
507 harbaum 204 static log_t *process_gpx_wpt_gc_logs(xmlTextReaderPtr reader, char *username,
508     cache_t *cache) {
509 harbaum 1 log_t *log_chain = NULL;
510    
511     if(xmlTextReaderIsEmptyElement(reader))
512     return NULL;
513    
514     /* process all sub-nodes */
515     int depth = xmlTextReaderDepth(reader);
516     int ret = xmlTextReaderRead(reader);
517     while(ret == 1) {
518    
519     switch(xmlTextReaderNodeType(reader)) {
520     case XML_READER_TYPE_ELEMENT:
521     g_assert(xmlTextReaderDepth(reader) == depth+1);
522     char *name = (char*)xmlTextReaderConstName(reader);
523     if(strrchr(name, ':')) name = strrchr(name, ':')+1;
524     if(name) {
525     if(strcasecmp(name, "log") == 0) {
526 harbaum 204 log_t *log = process_gpx_wpt_gc_logs_log(reader, username, cache);
527 harbaum 1 if(log) {
528     /* add log to chain */
529     log_t **cur = &log_chain;
530     while(*cur && log_is_older(log, *cur))
531     cur = &((*cur)->next);
532    
533     log->next = *cur;
534     *cur = log;
535     }
536     } else
537     skip_element(reader);
538     } else
539     skip_element(reader);
540     break;
541    
542     case XML_READER_TYPE_END_ELEMENT:
543     /* end element must be for the current element */
544     g_assert(xmlTextReaderDepth(reader) == depth);
545     return log_chain;
546     break;
547    
548     default:
549     break;
550     }
551     ret = xmlTextReaderRead(reader);
552     }
553    
554 harbaum 74 /* free the entire log chain */
555     while(log_chain) {
556     log_t *next = log_chain->next;
557     gpx_free_log(log_chain);
558     log_chain = next;
559     }
560    
561     return NULL;
562 harbaum 1 }
563    
564     static tb_t *process_gpx_wpt_gc_tbs_travelbug(xmlTextReaderPtr reader) {
565    
566     if(xmlTextReaderIsEmptyElement(reader))
567     return NULL;
568    
569     /* create a new tb entry */
570     tb_t *tb = malloc(sizeof(tb_t));
571     memset(tb, 0, sizeof(tb_t));
572    
573     char *prop;
574     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref")))
575     tb->ref = strdup(prop);
576     else
577     tb->ref = strdup("<NONE>");
578    
579 harbaum 133 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id")))
580     tb->id = atoi(prop);
581    
582 harbaum 1 /* process all sub-nodes */
583     int depth = xmlTextReaderDepth(reader);
584     int ret = xmlTextReaderRead(reader);
585     while(ret == 1) {
586    
587     switch(xmlTextReaderNodeType(reader)) {
588     case XML_READER_TYPE_ELEMENT:
589     g_assert(xmlTextReaderDepth(reader) == depth+1);
590     char *name = (char*)xmlTextReaderConstName(reader);
591     if(strrchr(name, ':')) name = strrchr(name, ':')+1;
592     if(name) {
593     if(strcasecmp(name, "name") == 0) {
594     if(!tb->name) tb->name = process_text(reader);
595     } else
596     skip_element(reader);
597     } else
598     skip_element(reader);
599     break;
600    
601     case XML_READER_TYPE_END_ELEMENT:
602     /* end element must be for the current element */
603     g_assert(xmlTextReaderDepth(reader) == depth);
604     return tb;
605     break;
606    
607     default:
608     break;
609     }
610     ret = xmlTextReaderRead(reader);
611     }
612    
613 harbaum 74 gpx_free_tb(tb);
614     return NULL;
615 harbaum 1 }
616    
617     static tb_t *process_gpx_wpt_gc_tbs(xmlTextReaderPtr reader) {
618     tb_t *tb = NULL, **tbP = &tb;
619    
620     if(xmlTextReaderIsEmptyElement(reader))
621     return tb;
622    
623     /* process all sub-nodes */
624     int depth = xmlTextReaderDepth(reader);
625     int ret = xmlTextReaderRead(reader);
626     while(ret == 1) {
627    
628     switch(xmlTextReaderNodeType(reader)) {
629     case XML_READER_TYPE_ELEMENT:
630     g_assert(xmlTextReaderDepth(reader) == depth+1);
631     char *name = (char*)xmlTextReaderConstName(reader);
632     if(strrchr(name, ':')) name = strrchr(name, ':')+1;
633     if(name) {
634     if(strcasecmp(name, "travelbug") == 0) {
635     *tbP = process_gpx_wpt_gc_tbs_travelbug(reader);
636     if(*tbP) tbP = &(*tbP)->next;
637     } else
638     skip_element(reader);
639     } else
640     skip_element(reader);
641     break;
642    
643     case XML_READER_TYPE_END_ELEMENT:
644     /* end element must be for the current element */
645     g_assert(xmlTextReaderDepth(reader) == depth);
646     return tb;
647     break;
648    
649     default:
650     break;
651     }
652     ret = xmlTextReaderRead(reader);
653     }
654    
655 harbaum 74 while(tb) {
656     tb_t *next = tb;
657     gpx_free_tb(tb);
658     tb = next;
659     }
660    
661     return NULL;
662 harbaum 1 }
663    
664 harbaum 204 static void process_gpx_wpt_gc(xmlTextReaderPtr reader,
665     cache_t *cache, char *username) {
666 harbaum 1 cache->available = xml_prop_is(reader, "available", "true", TRUE);
667     cache->archived = xml_prop_is(reader, "archived", "true", FALSE);
668    
669     if(xmlTextReaderIsEmptyElement(reader))
670     return;
671    
672     /* process all sub-nodes */
673     int depth = xmlTextReaderDepth(reader);
674     int ret = xmlTextReaderRead(reader);
675     while(ret == 1) {
676    
677     switch(xmlTextReaderNodeType(reader)) {
678     case XML_READER_TYPE_ELEMENT:
679     g_assert(xmlTextReaderDepth(reader) == depth+1);
680     char *name = (char*)xmlTextReaderConstName(reader);
681     if(strrchr(name, ':')) name = strrchr(name, ':')+1;
682     if(name) {
683     if(strcasecmp(name, "name") == 0) {
684     if(!cache->name) cache->name = process_text(reader);
685     } else if(strcasecmp(name, "owner") == 0) {
686 harbaum 134 if(!cache->owner) {
687     cache->owner = g_new0(user_t, 1);
688     cache->owner->name = process_text(reader);
689     cache->owner->id = xml_get_prop_id(reader);
690 harbaum 205
691     if(username && !strcasecmp(cache->owner->name, username))
692     cache->mine = TRUE;
693 harbaum 134 }
694 harbaum 1 } else if(strcasecmp(name, "type") == 0) {
695     cache->type = xml_str_search(reader, cache_type_str,
696     "cache type", CACHE_TYPE_UNKNOWN);
697     } else if(strcasecmp(name, "container") == 0) {
698     cache->container = xml_str_search(reader, cache_container_str,
699     "container", CACHE_CONT_UNKNOWN);
700     } else if((strcasecmp(name, "short_description") == 0) ||
701     (strcasecmp(name, "summary") == 0)) {
702     if(!cache->short_description) {
703     cache->short_description = process_text(reader);
704     cache->short_is_html = xml_prop_is(reader, "html", "true", FALSE);
705     }
706     } else if((strcasecmp(name, "long_description") == 0) ||
707     (strcasecmp(name, "description") == 0)) {
708     if(!cache->long_description) {
709     cache->long_description = process_text(reader);
710     cache->long_is_html = xml_prop_is(reader, "html", "true", FALSE);
711     }
712     } else if((strcasecmp(name, "encoded_hints") == 0) ||
713     (strcasecmp(name, "hints") == 0)) {
714     if(!cache->hint) {
715     cache->hint = process_text(reader);
716 harbaum 101
717 harbaum 1 /* often hints aren't more than just a bunch of blanks ... */
718     if(cache->hint && all_is_white(cache->hint)) {
719     free(cache->hint);
720     cache->hint = NULL;
721     } else
722     cache->hint_is_html = xml_prop_is(reader, "html", "true", FALSE);
723     }
724     } else if(strcasecmp(name, "difficulty") == 0) {
725     cache->difficulty = xml_float(reader, 0.0);
726     } else if(strcasecmp(name, "terrain") == 0) {
727     cache->terrain = xml_float(reader, 0.0);
728     } else if(strcasecmp(name, "logs") == 0) {
729 harbaum 204 if(!cache->log) cache->log =
730     process_gpx_wpt_gc_logs(reader, username, cache);
731 harbaum 1 } else if(strcasecmp(name, "travelbugs") == 0) {
732     if(!cache->tb) cache->tb = process_gpx_wpt_gc_tbs(reader);
733     } else {
734     // printf("unhandled item found: gpx/wpt/cache/%s\n", name);
735     skip_element(reader);
736     }
737     } else
738     skip_element(reader);
739     break;
740    
741     case XML_READER_TYPE_END_ELEMENT:
742     /* end element must be for the current element */
743     g_assert(xmlTextReaderDepth(reader) == depth);
744     return;
745     break;
746    
747     default:
748     break;
749     }
750     ret = xmlTextReaderRead(reader);
751     }
752     }
753    
754     /* parse waypoint entry */
755     static cache_t *process_gpx_wpt(xmlTextReaderPtr reader, gpx_dialog_t *dialog,
756 harbaum 204 gpx_t *gpx, char *username) {
757 harbaum 1 char *cmt = NULL, *desc = NULL;
758     char *sym = NULL;
759    
760     gpx_busy_dialog_progress(dialog);
761    
762     if(xmlTextReaderIsEmptyElement(reader))
763     return NULL;
764    
765     cache_t *cache = malloc(sizeof(cache_t));
766     memset(cache, 0, sizeof(cache_t));
767    
768     /* set some defaults */
769     cache->type = CACHE_TYPE_UNKNOWN;
770     cache->container = CACHE_CONT_UNKNOWN;
771     cache->available = TRUE;
772    
773     /* parse attributes */
774     cache->pos.lat = xml_get_prop_float(reader, "lat");
775     cache->pos.lon = xml_get_prop_float(reader, "lon");
776    
777     /* process all sub-nodes */
778     int depth = xmlTextReaderDepth(reader);
779     int ret = xmlTextReaderRead(reader);
780     while(ret == 1) {
781    
782     switch(xmlTextReaderNodeType(reader)) {
783     case XML_READER_TYPE_ELEMENT:
784     g_assert(xmlTextReaderDepth(reader) == depth+1);
785     char *name = (char*)xmlTextReaderConstName(reader);
786    
787     if(strrchr(name, ':')) name = strrchr(name, ':')+1;
788    
789     if(name) {
790     if(strcasecmp(name, "name") == 0) {
791     if(!cache->id) cache->id = process_text(reader);
792     } else if(strcasecmp(name, "url") == 0) {
793     if(!cache->url) cache->url = process_text(reader);
794     } else if((strcasecmp(name, "cache") == 0) ||
795     (strcasecmp(name, "geocache") == 0)) {
796 harbaum 204 process_gpx_wpt_gc(reader, cache, username);
797 harbaum 1
798     /* the following are used if the current entry is a waypoint */
799     } else if(strcasecmp(name, "cmt") == 0) {
800     if(!cmt) cmt = process_text(reader);
801     } else if(strcasecmp(name, "desc") == 0) {
802     if(!desc) desc = process_text(reader);
803     } else if(strcasecmp(name, "sym") == 0) {
804     if(!sym) sym = process_text(reader);
805     } else {
806     skip_element(reader);
807     }
808     } else
809     skip_element(reader);
810     break;
811    
812     case XML_READER_TYPE_END_ELEMENT:
813     /* end element must be for the current element */
814     g_assert(xmlTextReaderDepth(reader) == depth);
815    
816     /* ------------ cleanup -------------- */
817    
818     /* special handling for opencaching.de caches */
819     if(cache->id && strncasecmp(cache->id, "OC", 2) == 0) {
820     /* the html attributes are either missing or wrong on OC ... */
821     cache->long_is_html = TRUE;
822     cache->hint_is_html = TRUE;
823     cache->logs_are_html = TRUE;
824     }
825    
826     /* neither geocaching.com GC* nor opencaching.com OC* nor */
827     /* geocaching australia GA* waypoint */
828     if(cache->id &&
829 harbaum 14 (strncasecmp(cache->id, "__", 2) != 0) &&
830 harbaum 1 (strncasecmp(cache->id, "GC", 2) != 0) &&
831     (strncasecmp(cache->id, "OC", 2) != 0) &&
832     (strncasecmp(cache->id, "GA", 2) != 0)) {
833     cache_t *parent = gpx->cache;
834    
835     /* check if the gpx file contains a cache with matching name */
836     while(parent && strcasecmp(parent->id+2, cache->id+2))
837     parent = parent->next;
838    
839     if(parent && parent != cache) {
840     wpt_t **wpt = &parent->wpt;
841    
842     /* search end of list */
843     while(*wpt && (strcmp((*wpt)->id, cache->id)<0))
844     wpt = &(*wpt)->next;
845    
846     *wpt = malloc(sizeof(wpt_t));
847     memset(*wpt, 0, sizeof(wpt_t));
848    
849     /* transfer name to waypoint entry */
850     (*wpt)->id = cache->id;
851     cache->id = NULL;
852    
853     (*wpt)->pos.lat = cache->pos.lat;
854     (*wpt)->pos.lon = cache->pos.lon;
855    
856     (*wpt)->cmt = cmt; cmt = NULL;
857     (*wpt)->desc = desc; desc = NULL;
858     (*wpt)->sym = str_search(wpt_sym_str, sym, "wpt sym");
859    
860     /* and just free the current cache entry as we now have used */
861     /* the data for a caches waypoint */
862     gpx_free_cache(cache);
863     cache = NULL;
864     } else {
865     /* if it doesn't have a name etc, it's probably not a real */
866     /* cache, so drop it */
867     if(!cache->name || !cache->id) {
868     printf("Orphaned waypoint: %s\n", cache->id);
869     gpx_free_cache(cache);
870     cache = NULL;
871     }
872     }
873     } else {
874     if(!cache->id)
875     cache->id = g_strdup_printf("NO ID");
876    
877     /* this is known to be a geocache due to its waypoint name */
878     /* (gc*, oc*, ga*) and is thus forces to be an entry */
879     if(!cache->name)
880     cache->name = g_strdup_printf("Unnamed(%s)", cache->id);
881     }
882    
883     if(desc) free(desc);
884     if(cmt) free(cmt);
885     if(sym) free(sym);
886    
887     return cache;
888     break;
889    
890     default:
891     break;
892     }
893     ret = xmlTextReaderRead(reader);
894     }
895    
896 harbaum 74 gpx_free_cache(cache);
897     return NULL;
898 harbaum 1 }
899    
900 harbaum 74 static gboolean process_gpx(xmlTextReaderPtr reader, gpx_dialog_t *dialog,
901 harbaum 204 gpx_t *gpx, char *username) {
902 harbaum 1
903     /* no attributes of interest */
904    
905     /* the following might be optimized for speed reasons! */
906    
907     /* find end of cache chain */
908     cache_t **cache = &gpx->cache;
909     while(*cache) cache = &(*cache)->next;
910    
911     const xmlChar *name = xmlTextReaderConstName(reader);
912 harbaum 74 if(!name) return FALSE;
913 harbaum 1
914     /* read next node */
915     int ret = xmlTextReaderRead(reader);
916     while(ret == 1) {
917    
918     switch(xmlTextReaderNodeType(reader)) {
919     case XML_READER_TYPE_ELEMENT:
920    
921     g_assert(xmlTextReaderDepth(reader) == 1);
922     char *name = (char*)xmlTextReaderConstName(reader);
923     if(name && !gpx->name && strcasecmp(name, "name") == 0) {
924     gpx->name = process_text(reader);
925     } else if(name && !gpx->desc && strcasecmp(name, "desc") == 0) {
926     gpx->desc = process_text(reader);
927     } else if(name && ((strcasecmp(name, "time") == 0) ||
928     (strcasecmp(name, "date") == 0))) {
929     xml_get_date(reader, &gpx->year, &gpx->month, &gpx->day);
930     } else if(name && strcasecmp(name, "wpt") == 0) {
931 harbaum 204 *cache = process_gpx_wpt(reader, dialog, gpx, username);
932 harbaum 1 if(*cache) cache = &(*cache)->next;
933     } else {
934     // printf("something unknown (%s) found\n", name);
935     skip_element(reader);
936     }
937     break;
938    
939     case XML_READER_TYPE_END_ELEMENT:
940     /* end element must be for the current element */
941     g_assert(xmlTextReaderDepth(reader) == 0);
942 harbaum 74 return TRUE;
943 harbaum 1 break;
944    
945     default:
946     break;
947     }
948     ret = xmlTextReaderRead(reader);
949     }
950    
951 harbaum 74 return FALSE;
952 harbaum 1 }
953    
954     /* parse loc waypoint entry */
955     static cache_t *process_loc_waypoint(xmlTextReaderPtr reader,
956     gpx_dialog_t *dialog) {
957    
958     gpx_busy_dialog_progress(dialog);
959    
960     if(xmlTextReaderIsEmptyElement(reader))
961     return NULL;
962    
963     cache_t *cache = malloc(sizeof(cache_t));
964     memset(cache, 0, sizeof(cache_t));
965    
966     /* set some defaults */
967     cache->type = CACHE_TYPE_TRADITIONAL;
968     cache->container = CACHE_CONT_UNKNOWN;
969     cache->available = TRUE;
970    
971     /* process all sub-nodes */
972     int depth = xmlTextReaderDepth(reader);
973     int ret = xmlTextReaderRead(reader);
974     while(ret == 1) {
975    
976     switch(xmlTextReaderNodeType(reader)) {
977     case XML_READER_TYPE_ELEMENT:
978     g_assert(xmlTextReaderDepth(reader) == depth+1);
979     char *name = (char*)xmlTextReaderConstName(reader);
980     if(strrchr(name, ':')) name = strrchr(name, ':')+1;
981    
982     if(name) {
983     if(strcasecmp(name, "name") == 0) {
984     cache->id = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id");
985     cache->name = process_text(reader);
986     } else if(strcasecmp(name, "link") == 0) {
987     cache->url = process_text(reader);
988     } else if(strcasecmp(name, "coord") == 0) {
989     cache->pos.lat = xml_get_prop_float(reader, "lat");
990     cache->pos.lon = xml_get_prop_float(reader, "lon");
991     skip_element(reader);
992     } else
993     skip_element(reader);
994     } else
995     skip_element(reader);
996     break;
997    
998     case XML_READER_TYPE_END_ELEMENT:
999     /* end element must be for the current element */
1000     g_assert(xmlTextReaderDepth(reader) == depth);
1001     return cache;
1002     break;
1003    
1004     default:
1005     break;
1006     }
1007     ret = xmlTextReaderRead(reader);
1008     }
1009    
1010     g_assert(0);
1011     return NULL;
1012     }
1013    
1014     static void process_loc(xmlTextReaderPtr reader, gpx_dialog_t *dialog,
1015     gpx_t *gpx) {
1016    
1017     /* find end of cache chain */
1018     cache_t **cache = &gpx->cache;
1019     while(*cache) cache = &(*cache)->next;
1020    
1021     const xmlChar *name = xmlTextReaderConstName(reader);
1022     g_assert(name);
1023    
1024     /* read next node */
1025     int ret = xmlTextReaderRead(reader);
1026     while(ret == 1) {
1027    
1028     switch(xmlTextReaderNodeType(reader)) {
1029     case XML_READER_TYPE_ELEMENT:
1030    
1031     g_assert(xmlTextReaderDepth(reader) == 1);
1032     char *name = (char*)xmlTextReaderConstName(reader);
1033    
1034     if(name && strcasecmp(name, "waypoint") == 0) {
1035     *cache = process_loc_waypoint(reader, dialog);
1036     if(*cache) cache = &(*cache)->next;
1037     } else
1038     skip_element(reader);
1039     break;
1040    
1041     case XML_READER_TYPE_END_ELEMENT:
1042     /* end element must be for the current element */
1043     g_assert(xmlTextReaderDepth(reader) == 0);
1044     return;
1045     break;
1046    
1047     default:
1048     break;
1049     }
1050     ret = xmlTextReaderRead(reader);
1051     }
1052    
1053     g_assert(0);
1054     return;
1055     }
1056    
1057     static gpx_t *process_root(xmlTextReaderPtr reader, gpx_dialog_t *dialog,
1058 harbaum 204 char *fname, gpx_t *in, char *username) {
1059 harbaum 1
1060     /* no gpx entry given, create a new one */
1061     gpx_t *gpx = NULL;
1062     if(!in) {
1063     /* allocate memory to hold gpx file description */
1064     gpx = malloc(sizeof(gpx_t));
1065     memset(gpx, 0, sizeof(gpx_t));
1066     gpx->filename = strdup(fname);
1067     } else
1068     gpx = in;
1069    
1070     int ret = xmlTextReaderRead(reader);
1071     while(ret == 1) {
1072     switch(xmlTextReaderNodeType(reader)) {
1073     case XML_READER_TYPE_ELEMENT:
1074     g_assert(xmlTextReaderDepth(reader) == 0);
1075     char *name = (char*)xmlTextReaderConstName(reader);
1076     if(name && strcasecmp(name, "gpx") == 0) {
1077 harbaum 204 process_gpx(reader, dialog, gpx, username);
1078 harbaum 1 } else if(name && strcasecmp(name, "loc") == 0) {
1079     process_loc(reader, dialog, gpx);
1080     } else {
1081     printf("something unknown found\n");
1082     skip_element(reader);
1083     }
1084     break;
1085    
1086     case XML_READER_TYPE_END_ELEMENT:
1087     /* end element must be for the current element */
1088     g_assert(xmlTextReaderDepth(reader) == 0);
1089     ret = -1;
1090     break;
1091    
1092     default:
1093     break;
1094     }
1095    
1096     if(ret == 1)
1097     ret = xmlTextReaderRead(reader);
1098     }
1099    
1100     /* check if a name has been set and use filename if not */
1101     if(!in && !gpx->name) {
1102     if(!gpx->desc) {
1103     char *str = strrchr(fname, '/');
1104     if(str) gpx->name = strdup(str+1);
1105     else gpx->name = strdup(fname);
1106     } else
1107     gpx->name = strdup(gpx->desc);
1108     }
1109    
1110     return gpx;
1111     }
1112    
1113 harbaum 204 static gpx_t *gpx_parse_file(gpx_dialog_t *dialog,
1114     char *filename, char *username) {
1115 harbaum 1 gpx_t *gpx = NULL;
1116    
1117     LIBXML_TEST_VERSION;
1118    
1119     gpx_busy_dialog_set(dialog, filename);
1120    
1121     xmlTextReaderPtr reader = xmlReaderForFile(filename, NULL, 0);
1122     if (reader != NULL) {
1123 harbaum 204 gpx = process_root(reader, dialog, filename, NULL, username);
1124 harbaum 1 xmlFreeTextReader(reader);
1125     } else {
1126     fprintf(stderr, "Unable to open %s\n", filename);
1127     }
1128    
1129     /* check of there's a waypoints file (*-wpts.gpx) for this */
1130     if(strrchr(filename, '.')) {
1131     char *dot = strrchr(filename, '.');
1132     char wpts_name[128];
1133     *dot = 0;
1134     snprintf(wpts_name, sizeof(wpts_name), "%s-wpts.gpx", filename);
1135     *dot = '.';
1136     if(g_file_test(wpts_name, G_FILE_TEST_EXISTS)) {
1137     xmlTextReaderPtr reader = xmlReaderForFile(wpts_name, NULL, 0);
1138     if (reader != NULL) {
1139 harbaum 204 gpx = process_root(reader, dialog, wpts_name, gpx, username);
1140 harbaum 1 xmlFreeTextReader(reader);
1141     } else {
1142     fprintf(stderr, "Unable to open %s\n", filename);
1143     }
1144     }
1145     }
1146    
1147     return gpx;
1148     }
1149    
1150     static gpx_t *decompress_file(unzFile file, gpx_dialog_t *dialog,
1151     char *name, char *filename,
1152 harbaum 204 gpx_t *gpx_in, char *username) {
1153 harbaum 1 unz_file_info info;
1154     gpx_t *gpx = NULL;
1155    
1156     gpx_busy_dialog_set(dialog, name);
1157    
1158     if((unzLocateFile(file, name, FALSE) != Z_OK) ||
1159     (unzGetCurrentFileInfo(file, &info, NULL,0, NULL,0, NULL,0) != Z_OK) ||
1160     (unzOpenCurrentFile(file) != UNZ_OK)) {
1161    
1162     /* do not complain if we are processing a waypoints file */
1163     if(!gpx_in)
1164     errorf("Unable to locate/get info/open\n%s\ninside\n%s",
1165     name, filename);
1166     else
1167     printf("Unable to locate/get info/open %s inside %s\n",
1168     name, filename);
1169    
1170     return gpx_in;
1171     }
1172    
1173     printf("file size is %ld\n", info.uncompressed_size);
1174    
1175     char *buffer = malloc(info.uncompressed_size);
1176     if(!buffer) {
1177     errorf("Out of memory while uncompressing file");
1178     unzCloseCurrentFile(file);
1179     return gpx_in;
1180     }
1181    
1182     if(unzReadCurrentFile(file, buffer, info.uncompressed_size) < 0) {
1183     errorf("Read error on compressed file");
1184     free(buffer);
1185     unzCloseCurrentFile(file);
1186     return gpx_in;
1187     }
1188    
1189     /* fire up libxml */
1190     LIBXML_TEST_VERSION;
1191    
1192     xmlTextReaderPtr reader =
1193     xmlReaderForMemory(buffer, info.uncompressed_size,
1194     NULL, NULL, 0);
1195     if (reader != NULL) {
1196 harbaum 204 gpx = process_root(reader, dialog, filename, gpx_in, username);
1197 harbaum 1 xmlFreeTextReader(reader);
1198     } else {
1199     fprintf(stderr, "Unable to open %s\n", filename);
1200     }
1201    
1202     free(buffer);
1203     unzCloseCurrentFile(file);
1204     return gpx;
1205     }
1206    
1207 harbaum 204 static gpx_t *decompress_zip(gpx_dialog_t *dialog,
1208     char *filename, char *username) {
1209 harbaum 1 char *gpx_name, *fbase;
1210     gpx_t *gpx = NULL;
1211    
1212     /* extract base name and allocate space for file names */
1213     fbase = strrchr(filename, '/');
1214     if(!fbase) fbase = filename;
1215     else fbase++; /* skip '/' */
1216     gpx_name = malloc(strlen(fbase)+strlen("-wpts")+1);
1217    
1218     unzFile file = unzOpen(filename);
1219     if(!file) {
1220     errorf("Error opening file %s for unzip", filename);
1221     free(gpx_name);
1222     return NULL;
1223     }
1224    
1225     printf("ZIP file successfully opened\n");
1226    
1227     /* try to open gpx file inside */
1228     strcpy(gpx_name, fbase);
1229     strcpy(gpx_name+strlen(gpx_name)-4, ".gpx");
1230     printf("gpx file name is %s\n", gpx_name);
1231    
1232 harbaum 204 gpx = decompress_file(file, dialog, gpx_name, filename, NULL, username);
1233 harbaum 1
1234     /* try to open -wpts.gpx file inside */
1235     strcpy(gpx_name, fbase);
1236     strcpy(gpx_name+strlen(gpx_name)-4, "-wpts.gpx");
1237     printf("gpx wpts file name is %s\n", gpx_name);
1238    
1239 harbaum 204 gpx = decompress_file(file, dialog, gpx_name, filename, gpx, username);
1240 harbaum 1
1241     unzClose(file);
1242     free(gpx_name);
1243     return gpx;
1244     }
1245    
1246 harbaum 204 gpx_t *gpx_parse(gpx_dialog_t *dialog, char *filename, char *username) {
1247 harbaum 1 gpx_t *gpx = NULL;
1248    
1249     /* show busy dialog */
1250     printf("load file %s\n", filename);
1251    
1252     if((strlen(filename) > 4) &&
1253     !strcasecmp(filename+strlen(filename)-4, ".zip")) {
1254     printf("trying to load a zip file!\n");
1255    
1256 harbaum 204 gpx = decompress_zip(dialog, filename, username);
1257 harbaum 1 } else
1258 harbaum 204 gpx = gpx_parse_file(dialog, filename, username);
1259 harbaum 1
1260     return gpx;
1261     }
1262    
1263     /* scan entire directory */
1264 harbaum 204 gpx_t *gpx_parse_dir(gpx_dialog_t *dialog, char *dirname, char *username) {
1265 harbaum 1 GnomeVFSResult result;
1266     GnomeVFSDirectoryHandle *handle;
1267     GnomeVFSFileInfo *finfo = gnome_vfs_file_info_new();;
1268    
1269     gpx_t *gpx = NULL;
1270    
1271     /* show busy dialog */
1272     printf("load dir %s\n", dirname);
1273     gpx_busy_dialog_set(dialog, dirname);
1274    
1275     LIBXML_TEST_VERSION;
1276    
1277     result = gnome_vfs_directory_open(&handle, dirname,
1278     GNOME_VFS_FILE_INFO_DEFAULT);
1279    
1280     if(result != GNOME_VFS_OK) {
1281     errorf("Unable to open directory \"%s\":\n%s",
1282     dirname, gnome_vfs_result_to_string(result));
1283     return NULL;
1284     }
1285    
1286     while(GNOME_VFS_OK == gnome_vfs_directory_read_next(handle, finfo)) {
1287     if(finfo->type == GNOME_VFS_FILE_TYPE_REGULAR) {
1288     char *ext = finfo->name+strlen(finfo->name)-4;
1289    
1290     /* check if file ends with .gpx or .loc */
1291     if((strcasecmp(ext, ".gpx") == 0) || (strcasecmp(ext, ".loc") == 0)) {
1292     char *filename = malloc(strlen(dirname)+strlen(finfo->name)+2);
1293    
1294     strcpy(filename, dirname);
1295     if(strlastchr(filename) != '/')
1296     strcat(filename, "/");
1297     strcat(filename, finfo->name);
1298    
1299     xmlTextReaderPtr reader = xmlReaderForFile(filename, NULL, 0);
1300     if (reader != NULL) {
1301 harbaum 204 gpx = process_root(reader, dialog, filename, gpx, username);
1302 harbaum 1 xmlFreeTextReader(reader);
1303     } else {
1304     fprintf(stderr, "Unable to open %s\n", filename);
1305     }
1306    
1307     free(filename);
1308     }
1309     }
1310     }
1311    
1312     if(gpx) {
1313     /* replace file name with directory name */
1314     free(gpx->filename);
1315     gpx->filename = strdup(dirname);
1316    
1317     /* replace gpx name with directory name */
1318     free(gpx->name);
1319    
1320     /* retrieve pure dirname if possible */
1321     char *n = strrchr(dirname, '/');
1322     if(!n) n = dirname;
1323     else n++;
1324    
1325     // gpx->name = malloc(strlen("<DIR> ")+strlen(n)+1);
1326     // strcpy(gpx->name, "<DIR> ");
1327     // strcat(gpx->name, n);
1328     gpx->name = strdup(n);
1329     }
1330    
1331     gnome_vfs_file_info_unref(finfo);
1332     gnome_vfs_directory_close(handle);
1333    
1334     return gpx;
1335     }
1336    
1337     /* return number of caches in given gpx file */
1338     int gpx_total_caches(gpx_t *gpx) {
1339     cache_t *cache = gpx->cache;
1340     int num = 0;
1341    
1342     while(cache) {
1343     num++;
1344     cache = cache->next;
1345     }
1346    
1347     return num;
1348     }
1349    
1350 harbaum 152 /* return number of caches in all gpx files */
1351     int gpx_total_caches_global(gpx_t *gpx) {
1352     int num = 0;
1353     while(gpx) {
1354     num += gpx_total_caches(gpx);
1355     gpx = gpx->next;
1356     }
1357    
1358     return num;
1359     }
1360    
1361 harbaum 1 int gpx_number_of_waypoints(wpt_t *wpt) {
1362     int num = 0;
1363    
1364     while(wpt) {
1365     num++;
1366     wpt = wpt->next;
1367     }
1368    
1369     return num;
1370     }
1371    
1372     int gpx_number_of_logs(log_t *log) {
1373     int num = 0;
1374    
1375     while(log) {
1376     num++;
1377     log = log->next;
1378     }
1379    
1380     return num;
1381     }
1382    
1383     int gpx_number_of_tbs(tb_t *tb) {
1384     int num = 0;
1385    
1386     while(tb) {
1387     num++;
1388     tb = tb->next;
1389     }
1390    
1391     return num;
1392     }
1393    
1394     /* http://mathforum.org/library/drmath/view/55417.html */
1395     float gpx_pos_get_bearing(pos_t p1, pos_t p2) {
1396     /* convert to radians */
1397     p1.lat *= (M_PI/180.0); p1.lon *= (M_PI/180.0);
1398     p2.lat *= (M_PI/180.0); p2.lon *= (M_PI/180.0);
1399    
1400     return fmodf(360.0 + (180.0/M_PI) *
1401     (atan2( sin(p2.lon - p1.lon) * cos(p2.lat),
1402     cos(p1.lat) * sin(p2.lat) -
1403     sin(p1.lat) * cos(p2.lat) * cos(p2.lon - p1.lon))),
1404     360.0);
1405     }
1406    
1407     /* http://mathforum.org/library/drmath/view/51722.html */
1408     float gpx_pos_get_distance(pos_t p1, pos_t p2, int miles) {
1409     /* convert to radians */
1410     p1.lat *= (M_PI/180.0); p1.lon *= (M_PI/180.0);
1411     p2.lat *= (M_PI/180.0); p2.lon *= (M_PI/180.0);
1412    
1413     float aob = acos(cos(p1.lat) * cos(p2.lat) * cos(p2.lon - p1.lon) +
1414     sin(p1.lat) * sin(p2.lat));
1415    
1416     if(miles)
1417     return(aob * 3959.0); /* great circle radius in miles */
1418    
1419     return(aob * 6371.0); /* great circle radius in kilometers */
1420     }
1421    
1422     void gpx_pos_get_distance_str(char *str, int len,
1423     pos_t p1, pos_t p2, int mil) {
1424 harbaum 34 if(isnan(p1.lat) || isnan(p1.lon)) {
1425 harbaum 1 snprintf(str, len, "---");
1426     return;
1427     }
1428    
1429     float dist = gpx_pos_get_distance(p1, p2, mil);
1430     distance_str(str, len, dist, mil);
1431     }
1432    
1433     void gpx_sort(gpx_t *gpx, int by, pos_t *refpos) {
1434     cache_t **new;
1435     cache_t *cur = gpx->cache;
1436     int total = gpx_total_caches(gpx);
1437     float *dist_cache = malloc(total * sizeof(float));
1438    
1439     gpx->cache = NULL; /* detach old chain */
1440     while(cur) {
1441 harbaum 13 float cur_dist = -1;
1442 harbaum 1 int cur_cnt = 0;
1443    
1444 harbaum 13 if(!isnan(cur->pos.lat) && !isnan(cur->pos.lon))
1445     cur_dist = gpx_pos_get_distance(*refpos, gpx_cache_pos(cur), 0);
1446    
1447 harbaum 1 new = &(gpx->cache);
1448    
1449     /* search for currect insertion point */
1450     while(*new && (dist_cache[cur_cnt] < cur_dist)) {
1451     new = &((*new)->next);
1452     cur_cnt++;
1453     }
1454    
1455     /* save distance for further comparisons */
1456     memmove(dist_cache+cur_cnt+1, dist_cache+cur_cnt,
1457     sizeof(float)*(total-cur_cnt-1));
1458     dist_cache[cur_cnt++] = cur_dist;
1459    
1460     cache_t *next = cur->next;
1461    
1462     /* insert into "new" chain */
1463     cur->next = *new;
1464     *new = cur;
1465    
1466     cur = next;
1467     }
1468    
1469     free(dist_cache);
1470     }
1471    
1472     gpx_t *gpx_cache2gpx(gpx_t *gpx, cache_t *search_cache) {
1473     while(gpx) {
1474     cache_t *cache = gpx->cache;
1475     while(cache) {
1476     if(cache == search_cache)
1477     return gpx;
1478    
1479     cache = cache->next;
1480     }
1481     gpx = gpx->next;
1482     }
1483    
1484     return NULL;
1485     }
1486    
1487     /* since the actual cache position may be overridden, we */
1488     /* always access the position through this function */
1489     pos_t gpx_cache_pos(cache_t *cache) {
1490     if(cache->notes && cache->notes->override)
1491     return cache->notes->pos;
1492    
1493     return cache->pos;
1494     }