Contents of /trunk/src/gpx.c

Parent Directory Parent Directory | Revision Log Revision Log


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