Contents of /trunk/src/gpx.c

Parent Directory Parent Directory | Revision Log Revision Log


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