1 /*
2 Utilities for parsing Character Separated Value files (CSV)
3
4 Copyright (C) 2002 Alex Mottram (geo_alexm at cox-internet.com)
5 Copyright (C) 2002-2007 Robert Lipe
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
20
21 */
22
23 #include <ctype.h>
24 #include <math.h>
25 #include "defs.h"
26 #include "csv_util.h"
27 #include "grtcirc.h"
28 #include "strptime.h"
29 #include "jeeps/gpsmath.h"
30 #include "xmlgeneric.h" // for xml_fill_in_time.
31 #include "garmin_fs.h"
32
33 #define MYNAME "CSV_UTIL"
34
35 /* macros */
36 #define LAT_DIR(a) a < 0.0 ? 'S' : 'N'
37 #define LON_DIR(a) a < 0.0 ? 'W' : 'E'
38 #define NONULL(a) a ? a : ""
39 #define ISWHITESPACE(a) ((a == ' ') || (a == '\t'))
40
41 /* convert excel time (days since 1900) to time_t and back again */
42 #define EXCEL_TO_TIMET(a) ((a - 25569.0) * 86400.0)
43 #define TIMET_TO_EXCEL(a) ((a / 86400.0) + 25569.0)
44
45 #define GPS_DATUM_WGS84 118
46
47
48 /*
49 * Internal numeric value to associate with each keyword in a style file.
50 * To add new keywords, just add an entry here, handle it in the switch
51 * statements below, add it to xcsv_tokens.in, and rebuild on a system
52 * that has GNU gperf on it.
53 */
54 typedef enum {
55 XT_unused = 0,
56 XT_ALT_FEET,
57 XT_ALT_METERS,
58 XT_ANYNAME,
59 XT_CADENCE,
60 XT_CITY,
61 XT_CONSTANT,
62 XT_COUNTRY,
63 XT_DESCRIPTION,
64 XT_EXCEL_TIME,
65 XT_FACILITY,
66 XT_FILENAME,
67 XT_FORMAT,
68 XT_GEOCACHE_CONTAINER,
69 XT_GEOCACHE_DIFF,
70 XT_GEOCACHE_HINT,
71 XT_GEOCACHE_LAST_FOUND,
72 XT_GEOCACHE_PLACER,
73 XT_GEOCACHE_TERR,
74 XT_GEOCACHE_TYPE,
75 XT_GEOCACHE_ISAVAILABLE,
76 XT_GEOCACHE_ISARCHIVED,
77 XT_GMT_TIME,
78 XT_GPS_FIX,
79 XT_GPS_HDOP,
80 XT_GPS_PDOP,
81 XT_GPS_SAT,
82 XT_GPS_VDOP,
83 XT_HEART_RATE,
84 XT_HMSG_TIME,
85 XT_HMSL_TIME,
86 XT_ICON_DESCR,
87 XT_IGNORE,
88 XT_INDEX,
89 XT_ISO_TIME,
90 XT_ISO_TIME_MS,
91 XT_LATLON_HUMAN_READABLE,
92 XT_LAT_DECIMAL,
93 XT_LAT_DECIMALDIR,
94 XT_LAT_DIR,
95 XT_LAT_DIRDECIMAL,
96 XT_LAT_HUMAN_READABLE,
97 XT_LAT_INT32DEG,
98 XT_LAT_DDMMDIR,
99 XT_LAT_NMEA,
100 XT_LOCAL_TIME,
101 XT_LON_DECIMAL,
102 XT_LON_DECIMALDIR,
103 XT_LON_DIR,
104 XT_LON_DIRDECIMAL,
105 XT_LON_HUMAN_READABLE,
106 XT_LON_INT32DEG,
107 XT_LON_DDMMDIR,
108 XT_LON_NMEA,
109 XT_MAP_EN_BNG,
110 XT_NOTES,
111 XT_NET_TIME,
112 XT_PATH_COURSE,
113 XT_PATH_DISTANCE_KM,
114 XT_PATH_DISTANCE_METERS,
115 XT_PATH_DISTANCE_MILES,
116 XT_PATH_SPEED,
117 XT_PATH_SPEED_KNOTS,
118 XT_PATH_SPEED_KPH,
119 XT_PATH_SPEED_MPH,
120 XT_PHONE_NR,
121 XT_POSTAL_CODE,
122 XT_POWER,
123 XT_ROUTE_NAME,
124 XT_SHORTNAME,
125 XT_STATE,
126 XT_STREET_ADDR,
127 XT_TEMPERATURE,
128 XT_TEMPERATURE_F,
129 XT_TIMET_TIME,
130 XT_TIMET_TIME_MS,
131 XT_TRACK_NAME,
132 XT_TRACK_NEW,
133 XT_URL,
134 XT_UTM,
135 XT_UTM_ZONE,
136 XT_UTM_ZONEC,
137 XT_UTM_ZONEF,
138 XT_UTM_EASTING,
139 XT_UTM_NORTHING,
140 XT_URL_LINK_TEXT,
141 XT_YYYYMMDD_TIME
142 } xcsv_token;
143
144 // Static definition of in_word_set to meet C99 rules as used by Clang.
145 static struct xt_mapping*
146 in_word_set(register const char* str, register unsigned int len);
147
148 #include "xcsv_tokens.gperf"
149
150 /****************************************************************************/
151 /* obligatory global struct */
152 /****************************************************************************/
153 xcsv_file_t xcsv_file;
154
155 extern char* xcsv_urlbase;
156 extern char* prefer_shortnames;
157
158 #if CSVFMTS_ENABLED
159 static double pathdist = 0;
160 static double oldlon = 999;
161 static double oldlat = 999;
162
163 static int waypt_out_count;
164 static route_head* csv_track, *csv_route;
165 static double utm_northing, utm_easting, utm_zone = 0;
166 static char utm_zonec;
167 #endif // CSVFMTS_ENABLED
168
169
170 /*********************************************************************/
171 /* csv_stringclean() - remove any unwanted characters from string. */
172 /* returns copy of string. */
173 /* usage: p = csv_stringclean(stringtoclean, "&,\"") */
174 /* (strip out ampersands, commas, and quotes. */
175 /*********************************************************************/
176 char*
177 #ifdef DEBUG_MEM
CSV_STRINGCLEAN(const char * string,const char * chararray,DEBUG_PARAMS)178 CSV_STRINGCLEAN(const char* string, const char* chararray, DEBUG_PARAMS)
179 #else
180 csv_stringclean(const char* string, const char* chararray)
181 #endif
182 {
183 char* p1;
184 char* p2;
185 const char* cp;
186 char* tmp = xxstrdup(string,file,line);
187
188 if ((! string) || (! chararray)) {
189 return (tmp);
190 }
191
192 /* p2 - end of the original string */
193 p2 = tmp + strlen(tmp);
194
195 cp = chararray;
196
197 while (*cp) {
198 p1 = tmp;
199 while (*p1) {
200 if (*cp == *p1) {
201 /* we don't want this character! */
202 memmove(p1, p1 + 1, (p2 - p1));
203 p1[p2 - p1] = '\0';
204 p2--;
205 } else {
206 p1++;
207 }
208 }
209 cp++;
210 }
211 return (tmp);
212 }
213
214 /***********************************************************************************/
215 /* csv_stringtrim() - trim whitespace and leading and trailing enclosures (quotes) */
216 /* returns a copy of the modified string */
217 /* usage: p = csv_stringtrim(string, "\"", 0) */
218 /***********************************************************************************/
219 char*
220 #ifdef DEBUG_MEM
CSV_STRINGTRIM(const char * string,const char * enclosure,int strip_max,DEBUG_PARAMS)221 CSV_STRINGTRIM(const char* string, const char* enclosure, int strip_max, DEBUG_PARAMS)
222 #else
223 csv_stringtrim(const char* string, const char* enclosure, int strip_max)
224 #endif
225 {
226 static const char* p1 = NULL;
227 char* p2 = NULL;
228 char* tmp = xxstrdup(string,file,line);
229 size_t elen;
230 int stripped = 0;
231
232 if (!strlen(string)) {
233 return (tmp);
234 }
235
236 if (!enclosure) {
237 elen = 0;
238 } else {
239 elen = strlen(enclosure);
240 }
241
242 p2 = tmp + strlen(tmp) - 1;
243 p1 = tmp;
244
245 /* trim off trailing whitespace */
246 while ((p2 > p1) && isspace(*p2)) {
247 p2--;
248 }
249
250 /* advance p1 past any leading whitespace */
251 while ((p1 < p2) && (isspace(*p1))) {
252 p1++;
253 }
254
255 /* if no maximum strippage, assign a reasonable value to max */
256 strip_max = strip_max ? strip_max : 9999;
257
258 /* if we have enclosures, skip past them in pairs */
259 if (elen) {
260 while (
261 (stripped < strip_max) &&
262 ((size_t)(p2 - p1 + 1) >= (elen * 2)) &&
263 (strncmp(p1, enclosure, elen) == 0) &&
264 (strncmp((p2 - elen + 1), enclosure, elen) == 0)) {
265 p2 -= elen;
266 p1 += elen;
267 stripped++;
268 }
269 }
270
271 /* copy what's left over back into tmp. */
272 memmove(tmp, p1, (p2 - p1) + 1);
273
274 tmp[(p2 - p1) + 1] = '\0';
275
276 return (tmp);
277 }
278
279 /*****************************************************************************/
280 /* csv_lineparse() - extract data fields from a delimited string. designed */
281 /* to handle quoted and delimited data within quotes. */
282 /* returns temporary COPY of delimited data field (use it */
283 /* or lose it on the next call). */
284 /* usage: p = csv_lineparse(string, ",", "\"", line) [initial call] */
285 /* p = csv_lineparse(NULL, ",", "\"", line) [subsequent calls] */
286 /*****************************************************************************/
287 char*
csv_lineparse(const char * stringstart,const char * delimited_by,const char * enclosed_in,const int line_no)288 csv_lineparse(const char* stringstart, const char* delimited_by,
289 const char* enclosed_in, const int line_no)
290 {
291 const char* sp;
292 static const char* p = NULL;
293 static char* tmp = NULL;
294 size_t dlen = 0, elen = 0, efound = 0;
295 int enclosedepth = 0;
296 short int dfound;
297 short int hyper_whitespace_delimiter = 0;
298
299 if (tmp) {
300 xfree(tmp);
301 tmp = NULL;
302 }
303
304 if (strcmp(delimited_by, "\\w") == 0) {
305 hyper_whitespace_delimiter = 1;
306 }
307
308 /*
309 * This is tacky. Our "csv" format is actually "commaspace" format.
310 * Changing that causes unwanted churn, but it also makes "real"
311 * comma separated data (such as likely to be produced by Excel, etc.)
312 * unreadable. So we silently change it here on a read and let the
313 * whitespace eater consume the space.
314 */
315 if (strcmp(delimited_by, ", ") == 0) {
316 delimited_by = ",";
317 }
318
319 if (!p) {
320 /* first pass thru */
321 p = stringstart;
322
323 if (!p) {
324 /* last pass out */
325 return (NULL);
326 }
327 }
328
329 /* the beginning of the string we start with (this pass) */
330 sp = p;
331
332 /* length of delimiters and enclosures */
333 if ((delimited_by) && (!hyper_whitespace_delimiter)) {
334 dlen = strlen(delimited_by);
335 }
336 if (enclosed_in) {
337 elen = strlen(enclosed_in);
338 }
339 dfound = 0;
340
341 while ((*p) && (!dfound)) {
342 if ((elen) && (strncmp(p, enclosed_in, elen) == 0)) {
343 efound = 1;
344 p+=elen;
345 if (enclosedepth) {
346 enclosedepth--;
347 } else {
348 enclosedepth++;
349 }
350 continue;
351 }
352
353 if (!enclosedepth) {
354 if ((dlen) && (strncmp(p, delimited_by, dlen) == 0)) {
355 dfound = 1;
356 } else if ((hyper_whitespace_delimiter) && (ISWHITESPACE(*p))) {
357 dfound = 1;
358 while (ISWHITESPACE(*p)) {
359 p++;
360 }
361 } else {
362 p++;
363 }
364 } else {
365 p++;
366 }
367 }
368
369 /* allocate enough space for this data field */
370 tmp = (char*) xcalloc((p - sp) + 1, sizeof(char));
371
372 strncpy(tmp, sp, (p - sp));
373 tmp[p - sp] = '\0';
374
375 if (elen && efound) {
376 char* c = csv_stringtrim(tmp, enclosed_in, 0);
377 xfree(tmp);
378 tmp = c;
379 }
380
381 if (dfound) {
382 /* skip over the delimited_by */
383 p += dlen;
384 } else {
385 /* end of the line */
386 p = NULL;
387 }
388
389 if (enclosedepth != 0) {
390 warning(MYNAME
391 ": Warning- Unbalanced Field Enclosures (%s) on line %d\n",
392 enclosed_in, line_no);
393 }
394 return (tmp);
395 }
396
397 #if CSVFMTS_ENABLED
398 /*****************************************************************************/
399 /* dec_to_intdeg() - convert decimal degrees to integer degreees */
400 /* usage: i = dec_to_intdeg(31.1234); */
401 /*****************************************************************************/
402 static int
dec_to_intdeg(const double d)403 dec_to_intdeg(const double d)
404 {
405 int ideg = 0;
406
407 if (d >= 0) {
408 ideg = (2147483647) - (d * 8388608);
409 } else {
410 ideg = (2147483647) - (fabs(d) * 8388608) + 1;
411 }
412
413 return(ideg);
414 }
415
416 /*****************************************************************************/
417 /* intdeg_to_dec() - convert integer degrees to decimal degreees */
418 /* usage: lat = dec_to_intdeg(ilat); */
419 /*****************************************************************************/
420 static double
intdeg_to_dec(const int ideg)421 intdeg_to_dec(const int ideg)
422 {
423 double d;
424
425 if (ideg >= 0) {
426 d = ((2147483647) - ideg) / (double)8388608;
427 } else {
428 d = ((-2147483647-1) + ideg) / (double)8388608;
429 }
430
431 return(d);
432 }
433
434 /*****************************************************************************/
435 /* decdir_to_dec() - convert a decimal/direction value into pure decimal. */
436 /* usage: lat = decdir_to_dec("W90.1234"); */
437 /* lat = decdir_to_dec("30.1234N"); */
438 /*****************************************************************************/
439 static double
decdir_to_dec(const char * decdir)440 decdir_to_dec(const char* decdir)
441 {
442 char* p;
443 const char* cp;
444 double rval;
445 int sign = 0;
446
447 cp = &decdir[0];
448
449 if ((*cp == 'W') || (*cp == 'S')) {
450 sign = -1;
451 } else if ((*cp == 'N') || (*cp == 'E')) {
452 sign = 1;
453 }
454
455 rval = sign ? strtod(&decdir[1], &p) : strtod(&decdir[0], &p);
456
457 if (sign == 0) {
458 if ((*p == 'W') || (*p == 'S')) {
459 sign = -1;
460 } else if ((*p == 'N') || (*p == 'E')) {
461 sign = 1;
462 }
463 }
464
465 return(rval * sign);
466 }
467
468 /*****************************************************************************/
469 /* ddmmdir_to_degrees() - convert ddmm/direction value into degrees */
470 /* usage: lat = ddmmdir_to_degrees("W90.1234"); */
471 /* lat = ddmmdir_to_degrees("30.1234N"); */
472 /*****************************************************************************/
473 static double
ddmmdir_to_degrees(const char * ddmmdir)474 ddmmdir_to_degrees(const char* ddmmdir)
475 {
476 // if not N or E, prepend a '-' to ddmm2degrees input
477 // see XT_LAT_NMEA which handles ddmm directly
478 if (strchr(ddmmdir, 'W') || strchr(ddmmdir, 'S')) {
479 return ddmm2degrees(- atof(ddmmdir));
480 }
481 return ddmm2degrees(atof(ddmmdir));
482
483 }
484 #endif
485
486 /*****************************************************************************
487 * human_to_dec() - convert a "human-readable" lat and/or lon to decimal
488 * usage: human_to_dec( "N 41� 09.12' W 085� 09.36'", &lat, &lon );
489 * human_to_dec( "41 9 5.652 N", &lat, &lon );
490 *
491 * which: 0-no preference 1-prefer lat 2-prefer lon
492 *****************************************************************************/
493
494 void
human_to_dec(const char * instr,double * outlat,double * outlon,int which)495 human_to_dec(const char* instr, double* outlat, double* outlon, int which)
496 {
497 double unk[3] = {999,999,999};
498 double lat[3] = {999,999,999};
499 double lon[3] = {999,999,999};
500 int latsign = 0;
501 int lonsign = 0;
502 int unksign = 1;
503
504 const char* cur;
505 double* numres = unk;
506 int numind = 0;
507 char* buff;
508
509 if (strchr(instr, ',') != NULL) {
510 char* c;
511 buff = xstrdup(instr);
512 while ((c = strchr(buff, ','))) {
513 *c = '.';
514 }
515 } else {
516 buff = (char*)instr;
517 }
518
519 cur = buff;
520
521 while (cur && *cur) {
522 switch (*cur) {
523 case 'n':
524 case 's':
525 case 'N':
526 case 'S':
527 if (unk[0] != 999) {
528 numind = 0;
529 numres = unk;
530 lat[0] = unk[0];
531 lat[1] = unk[1];
532 lat[2] = unk[2];
533 unk[0] = unk[1] = unk[2] = 999;
534 } else {
535 numres = lat;
536 numind = 0;
537 lat[0] = lat[1] = lat[2] = 999;
538 }
539
540 if (*cur == 'n' || *cur == 'N') {
541 latsign = 1;
542 } else {
543 latsign = -1;
544 }
545 cur++;
546 break;
547 case 'w':
548 case 'e':
549 case 'W':
550 case 'E':
551 if (unk[0] != 999) {
552 numind = 0;
553 numres = unk;
554 lon[0] = unk[0];
555 lon[1] = unk[1];
556 lon[2] = unk[2];
557 unk[0] = unk[1] = unk[2] = 999;
558 } else {
559 numres = lon;
560 numind = 0;
561 lon[0] = lon[1] = lon[2] = 999;
562 }
563
564 if (*cur == 'e' || *cur == 'E') {
565 lonsign = 1;
566 } else {
567 lonsign = -1;
568 }
569 cur++;
570 break;
571 case '1':
572 case '2':
573 case '3':
574 case '4':
575 case '5':
576 case '6':
577 case '7':
578 case '8':
579 case '9':
580 case '0':
581 case '.':
582 case ',':
583 numres[numind] = atof(cur);
584 while (cur && *cur && strchr("1234567890.,",*cur)) {
585 cur++;
586 }
587 break;
588 case '-':
589 unksign = -1;
590 cur++;
591 break;
592 default:
593 if (numres[numind] != 999) {
594 numind++;
595 if (numind > 2) {
596 numres = unk;
597 numind = 0;
598 }
599 }
600 cur++;
601 break;
602 }
603 }
604
605 if (lat[0] == 999 && lon[0] == 999) {
606 if (which == 1) {
607 lat[0] = unk[0];
608 lat[1] = unk[1];
609 lat[2] = unk[2];
610 latsign = unksign;
611 } else if (which == 2) {
612 lon[0] = unk[0];
613 lon[1] = unk[1];
614 lon[2] = unk[2];
615 lonsign = unksign;
616 }
617 }
618
619 if (outlat) {
620 if (lat[0] != 999) {
621 *outlat = lat[0];
622 }
623 if (lat[1] != 999) {
624 *outlat += lat[1]/60.0;
625 }
626 if (lat[2] != 999) {
627 *outlat += lat[2]/3600.0;
628 }
629 if (*outlat > 360) {
630 *outlat = ddmm2degrees(*outlat); /* NMEA style */
631 }
632 if (latsign) {
633 *outlat *= latsign;
634 }
635 }
636 if (outlon) {
637 if (lon[0] != 999) {
638 *outlon = lon[0];
639 }
640 if (lon[1] != 999) {
641 *outlon += lon[1]/60.0;
642 }
643 if (lon[2] != 999) {
644 *outlon += lon[2]/3600.0;
645 }
646 if (*outlon > 360) {
647 *outlon = ddmm2degrees(*outlon); /* NMEA style */
648 }
649 if (lonsign) {
650 *outlon *= lonsign;
651 }
652 }
653 if (buff != instr) {
654 xfree(buff);
655 }
656 }
657
658 #if CSVFMTS_ENABLED
659 /*
660 * dec_to_human - convert decimal degrees to human readable
661 */
662
663 void
dec_to_human(char * buff,const char * format,const char * dirs,double val)664 dec_to_human(char* buff, const char* format, const char* dirs, double val)
665 {
666 char* subformat = NULL;
667 const char* formatptr = NULL;
668 char* percent = NULL;
669 char* type = NULL;
670
671 int index = 0;
672 int intvals[3] = {0,0,0};
673 double dblvals[3] = {0,0,0};
674 int sign = 0;
675
676 sign = (val < 0) ? 0 : 1;
677
678 dblvals[0] = fabs(val);
679 intvals[0] = (int)dblvals[0];
680 dblvals[1] = 60*(dblvals[0]-intvals[0]);
681 intvals[1] = (int)dblvals[1];
682 dblvals[2] = 60*(dblvals[1]-intvals[1]);
683 intvals[2] = (int)dblvals[2];
684
685 subformat = (char*) xmalloc(strlen(format)+2);
686 formatptr = format;
687
688 buff[0] = '\0';
689
690 while (formatptr && *formatptr) {
691 strcpy(subformat, formatptr);
692 percent = strchr(subformat, '%');
693 if (percent) {
694 type = percent+1+strcspn(percent+1, "cdiouxXeEfgG%");
695 *(type+1) = '\0';
696 switch (*type) {
697 case 'c':
698 sprintf(buff+strlen(buff), subformat, dirs[sign]);
699 break;
700 case 'd':
701 case 'i':
702 case 'o':
703 case 'u':
704 case 'x':
705 case 'X':
706 if (index>2) {
707 fatal(MYNAME ": too many format specifiers\n");
708 }
709 sprintf(buff+strlen(buff), subformat, intvals[index]);
710 index++;
711 break;
712 case 'e':
713 case 'E':
714 case 'f':
715 case 'g':
716 case 'G':
717 if (index>2) {
718 fatal(MYNAME ": too many format specifiers\n");
719 }
720 sprintf(buff+strlen(buff), subformat, dblvals[index]);
721 index++;
722 break;
723 case '%':
724 sprintf(buff+strlen(buff), "%s", subformat);
725 break;
726 default:
727 fatal(MYNAME ": invalid format specifier\n");
728 break;
729
730 }
731 } else {
732 sprintf(buff+strlen(buff), "%s", subformat);
733 }
734 formatptr += strlen(subformat);
735 }
736
737 xfree(subformat);
738 }
739
740 /*****************************************************************************/
741 /* xcsv_file_init() - prepare xcsv_file for first use. */
742 /*****************************************************************************/
743 void
xcsv_file_init(void)744 xcsv_file_init(void)
745 {
746 memset(&xcsv_file, '\0', sizeof(xcsv_file_t));
747
748 QUEUE_INIT(&xcsv_file.prologue);
749 QUEUE_INIT(&xcsv_file.epilogue);
750
751 QUEUE_INIT(&xcsv_file.ifield);
752 /* ofield is alloced to allow pointing back at ifields
753 * where applicable.
754 */
755 xcsv_file.ofield = (queue*) xcalloc(sizeof(queue), 1);
756 QUEUE_INIT(xcsv_file.ofield);
757 /*
758 * Provide a sane default for CSV _files_.
759 */
760 xcsv_file.type = ff_type_file;
761
762 xcsv_file.mkshort_handle = mkshort_new_handle();
763 xcsv_file.gps_datum = GPS_DATUM_WGS84;
764 }
765
766 /*****************************************************************************/
767 /* xcsv_ifield_add() - add input field to ifield queue. */
768 /* usage: xcsv_ifield_add("DESCRIPTION", "", "%s") */
769 /*****************************************************************************/
770 void
xcsv_ifield_add(char * key,char * val,char * pfc)771 xcsv_ifield_add(char* key, char* val, char* pfc)
772 {
773 field_map_t* fmp = (field_map_t*) xcalloc(sizeof(*fmp), 1);
774 struct xt_mapping* xm = in_word_set(key, strlen(key));
775
776 fmp->key = key;
777 fmp->hashed_key = xm ? xm->xt_token : -1;
778 fmp->val = val;
779 fmp->printfc = pfc;
780
781 ENQUEUE_TAIL(&xcsv_file.ifield, &fmp->Q);
782 xcsv_file.ifield_ct++;
783 }
784
785 /*****************************************************************************/
786 /* xcsv_ofield_add() - add output field to ofield queue. */
787 /* usage: xcsv_ofield_add("LAT_DECIMAL", "", "%08.5lf") */
788 /*****************************************************************************/
789 void
xcsv_ofield_add(char * key,char * val,char * pfc,int options)790 xcsv_ofield_add(char* key, char* val, char* pfc, int options)
791 {
792 field_map_t* fmp = (field_map_t*) xcalloc(sizeof(*fmp), 1);
793 struct xt_mapping* xm = in_word_set(key, strlen(key));
794
795 fmp->key = key;
796 fmp->hashed_key = xm ? xm->xt_token : -1;
797 fmp->val = val;
798 fmp->printfc = pfc;
799 fmp->options = options;
800
801 ENQUEUE_TAIL(xcsv_file.ofield, &fmp->Q);
802 xcsv_file.ofield_ct++;
803 }
804
805 /*****************************************************************************/
806 /* xcsv_prologue_add() - add prologue line to prologue queue */
807 /* usage: xcsv_prologue_add("Four score and seven years ago today,") */
808 /*****************************************************************************/
809 void
xcsv_prologue_add(char * prologue)810 xcsv_prologue_add(char* prologue)
811 {
812 ogue_t* ogp = (ogue_t*) xcalloc(sizeof(*ogp), 1);
813
814 ogp->val = prologue;
815 ENQUEUE_TAIL(&xcsv_file.prologue, &ogp->Q);
816 xcsv_file.prologue_lines++;
817 }
818
819 /*****************************************************************************/
820 /* xcsv_epilogue_add() - add epilogue line to epilogue queue */
821 /* usage: xcsv_epilogue_add("shall not perish from the earth.") */
822 /*****************************************************************************/
823 void
xcsv_epilogue_add(char * epilogue)824 xcsv_epilogue_add(char* epilogue)
825 {
826 ogue_t* ogp = (ogue_t*) xcalloc(sizeof(*ogp), 1);
827
828 ogp->val = epilogue;
829 ENQUEUE_TAIL(&xcsv_file.epilogue, &ogp->Q);
830 xcsv_file.epilogue_lines++;
831 }
832
833 static
834 time_t
yyyymmdd_to_time(const char * s)835 yyyymmdd_to_time(const char* s)
836 {
837 int t = atol(s);
838 struct tm tm;
839
840 memset(&tm, 0, sizeof(tm));
841
842 tm.tm_mday = t % 100;
843 t = t / 100;
844 tm.tm_mon = t % 100 - 1;
845 t = t / 100;
846 tm.tm_year = t - 1900;
847
848 if (mkgmtime(&tm) > 0) {
849 return mktime(&tm);
850 } else {
851 return 0;
852 }
853 }
854
855
856
857 /*
858 * sscanftime - Parse a date buffer using strftime format
859 */
860 static
861 time_t
sscanftime(const char * s,const char * format,const int gmt)862 sscanftime(const char* s, const char* format, const int gmt)
863 {
864 struct tm stm;
865 memset(&stm, 0, sizeof(stm));
866
867 if (strptime(s, format, &stm)) {
868 if ((stm.tm_mday == 0) && (stm.tm_mon == 0) && (stm.tm_year == 0)) {
869 stm.tm_mday = 1;
870 stm.tm_mon = 0;
871 stm.tm_year = 70;
872 }
873 stm.tm_isdst = -1;
874 if (gmt) {
875 return mkgmtime(&stm);
876 } else {
877 return mktime(&stm);
878 }
879 }
880 // Don't fuss for empty strings.
881 if (*s) {
882 warning("date parse of string '%s' with format '%s' failed.\n",
883 s, format);
884 }
885 return 0;
886 }
887
888 static
889 time_t
addhms(const char * s,const char * format)890 addhms(const char* s, const char* format)
891 {
892 time_t tt =0;
893 int hour =0;
894 int min =0;
895 int sec =0;
896 char* ampm = NULL;
897 int ac;
898
899 ampm = (char*) xmalloc(strlen(s));
900 ac = sscanf(s, format, &hour, &min, &sec, &m);
901 /* If no time format in arg string, assume AM */
902 if (ac < 4) {
903 ampm[0] = 0;
904 }
905 if (ac) {
906 tt = ((tolower(ampm[0])=='P')?43200:0)+3600*hour+60*min+sec;
907 }
908 xfree(ampm);
909
910 return tt;
911 }
912
913 static
914 int
writetime(char * buff,size_t bufsize,const char * format,time_t t,int gmt)915 writetime(char* buff, size_t bufsize, const char* format, time_t t, int gmt)
916 {
917 static struct tm* stmp;
918
919 if (gmt) {
920 stmp = gmtime(&t);
921 } else {
922 stmp = localtime(&t);
923 }
924
925 return strftime(buff, bufsize, format, stmp);
926 }
927
928 #if 0
929 /* not used */
930 static
931 int
932 writeisotime(char* buff, size_t bufsize, const char* format, time_t t)
933 {
934 static struct tm* stmp;
935 char* ibuff = NULL;
936 int i;
937
938 ibuff = xmalloc(bufsize);
939 stmp = gmtime(&t);
940 strftime(ibuff, bufsize, format, stmp);
941 i = snprintf(buff, bufsize, format, ibuff);
942 xfree(ibuff);
943 return i;
944 }
945 #endif
946
947
948 static
949 int
writehms(char * buff,size_t bufsize,const char * format,time_t t,int gmt)950 writehms(char* buff, size_t bufsize, const char* format, time_t t, int gmt)
951 {
952 static struct tm no_time = {0};
953 static struct tm* stmp = &no_time;
954
955 if (gmt) {
956 stmp = gmtime(&t);
957 } else {
958 stmp = localtime(&t);
959 }
960
961 if (stmp == NULL) {
962 stmp = &no_time;
963 }
964
965 return snprintf(buff, bufsize, format,
966 stmp->tm_hour, stmp->tm_min, stmp->tm_sec,
967 (stmp->tm_hour>=12?"PM":"AM"));
968 }
969
970 static
971 long
time_to_yyyymmdd(time_t t)972 time_to_yyyymmdd(time_t t)
973 {
974 long b;
975 struct tm* tm = gmtime(&t);
976
977 b = (1900 + tm->tm_year) * 10000 +
978 (1 + tm->tm_mon) * 100 +
979 tm->tm_mday;
980
981 return b;
982 }
983
984 static garmin_fs_t*
gmsd_init(waypoint * wpt)985 gmsd_init(waypoint* wpt)
986 {
987 garmin_fs_t* gmsd = GMSD_FIND(wpt);
988 if (gmsd == NULL) {
989 gmsd = garmin_fs_alloc(-1);
990 fs_chain_add(&wpt->fs, (format_specific_data*) gmsd);
991 }
992 return gmsd;
993 }
994
995 /*****************************************************************************/
996 /* xcsv_parse_val() - parse incoming data into the waypt structure. */
997 /* usage: xcsv_parse_val("-123.34", *waypt, *field_map) */
998 /*****************************************************************************/
999 static void
xcsv_parse_val(const char * s,waypoint * wpt,const field_map_t * fmp,route_head ** trk)1000 xcsv_parse_val(const char* s, waypoint* wpt, const field_map_t* fmp,
1001 route_head** trk)
1002 {
1003 char* enclosure = "";
1004 geocache_data* gc_data = NULL;
1005
1006 if (!fmp->printfc) {
1007 fatal(MYNAME ": xcsv style '%s' is missing format specifier", fmp->key);
1008 }
1009
1010 if (0 == strcmp(fmp->printfc, "\"%s\"")) {
1011 enclosure = "\"";
1012 }
1013 switch (fmp->hashed_key) {
1014 case XT_IGNORE:
1015 /* IGNORE -- Categorically ignore this... */
1016 break;
1017 case XT_CONSTANT:
1018 /* CONSTANT -- Ignore on Input... */
1019 break;
1020 case XT_ANYNAME:
1021 /* ANYNAME -- Ignore -- this is output magic. */
1022 break;
1023 case XT_INDEX:
1024 /* IGNORE -- Calculated Sequence # For Ouput*/
1025 break;
1026 case XT_SHORTNAME:
1027 wpt->shortname = csv_stringtrim(s, enclosure, 0);
1028 break;
1029 case XT_DESCRIPTION:
1030 wpt->description = csv_stringtrim(s, enclosure, 0);
1031 break;
1032 case XT_NOTES:
1033 wpt->notes = csv_stringtrim(s, "", 0);
1034 break;
1035 case XT_URL:
1036 wpt->url = csv_stringtrim(s, "", 0);
1037 break;
1038 case XT_URL_LINK_TEXT:
1039 wpt->url_link_text = csv_stringtrim(s, "", 0);
1040 break;
1041 case XT_ICON_DESCR:
1042 wpt->icon_descr = csv_stringtrim(s, "", 0);
1043 wpt->wpt_flags.icon_descr_is_dynamic = 1;
1044 break;
1045
1046 /* LATITUDE CONVERSIONS**************************************************/
1047 case XT_LAT_DECIMAL:
1048 /* latitude as a pure decimal value */
1049 wpt->latitude = atof(s);
1050 break;
1051 case XT_LAT_DECIMALDIR:
1052 case XT_LAT_DIRDECIMAL:
1053 /* latitude as a decimal with N/S in it. */
1054 wpt->latitude = decdir_to_dec(s);
1055 break;
1056 case XT_LAT_INT32DEG:
1057 /* latitude as a 32 bit integer offset */
1058 wpt->latitude = intdeg_to_dec((int) atof(s));
1059 break;
1060 case XT_LAT_HUMAN_READABLE:
1061 human_to_dec(s, &wpt->latitude, &wpt->longitude, 1);
1062 break;
1063 case XT_LAT_DDMMDIR:
1064 wpt->latitude = ddmmdir_to_degrees(s);
1065 break;
1066 case XT_LAT_NMEA:
1067 wpt->latitude = ddmm2degrees(atof(s));
1068 break;
1069 // XT_LAT_10E is handled outside the switch.
1070 /* LONGITUDE CONVERSIONS ***********************************************/
1071 case XT_LON_DECIMAL:
1072 /* longitude as a pure decimal value */
1073 wpt->longitude = atof(s);
1074 break;
1075 case XT_LON_DECIMALDIR:
1076 case XT_LON_DIRDECIMAL:
1077 /* longitude as a decimal with N/S in it. */
1078 wpt->longitude = decdir_to_dec(s);
1079 break;
1080 case XT_LON_INT32DEG:
1081 /* longitude as a 32 bit integer offset */
1082 wpt->longitude = intdeg_to_dec((int) atof(s));
1083 break;
1084 case XT_LON_HUMAN_READABLE:
1085 human_to_dec(s, &wpt->latitude, &wpt->longitude, 2);
1086 break;
1087 case XT_LON_DDMMDIR:
1088 wpt->longitude = ddmmdir_to_degrees(s);
1089 break;
1090 case XT_LON_NMEA:
1091 wpt->longitude = ddmm2degrees(atof(s));
1092 break;
1093 // case XT_LON_10E is handled outside the switch.
1094 /* LAT AND LON CONVERSIONS ********************************************/
1095 case XT_LATLON_HUMAN_READABLE:
1096 human_to_dec(s, &wpt->latitude, &wpt->longitude, 0);
1097 break;
1098 /* DIRECTIONS **********************************************************/
1099 case XT_LAT_DIR:
1100 /* latitude N/S. Ignore on input for now */
1101 break;
1102 case XT_LON_DIR:
1103 /* longitude E/W. Ingore on input for now */
1104 break;
1105 /* SPECIAL COORDINATES/GRID */
1106 case XT_MAP_EN_BNG:
1107 parse_coordinates(s, DATUM_OSGB36, grid_bng,
1108 &wpt->latitude, &wpt->longitude, MYNAME);
1109 break;
1110 case XT_UTM_ZONE:
1111 utm_zone = atoi(s);
1112 break;
1113 case XT_UTM_ZONEC:
1114 utm_zonec = atoi(s);
1115 break;
1116 case XT_UTM_ZONEF:
1117 utm_zone = atoi(s);
1118 utm_zonec = s[strlen(s) - 1];
1119 break;
1120 case XT_UTM_EASTING:
1121 utm_easting = atof(s);
1122 break;
1123 case XT_UTM_NORTHING:
1124 utm_northing = atof(s);
1125 break;
1126 case XT_UTM: {
1127 char* ss;
1128 int i = 0;;
1129
1130 utm_zone = strtod(s, &ss);
1131 utm_zonec = ss[i];
1132 ss++;
1133 utm_easting = strtod(ss, &ss);
1134 while (*ss && !isdigit(*ss)) {
1135 ss++;
1136 }
1137 utm_northing = strtod(ss, NULL);
1138 }
1139 break;
1140 /* ALTITUDE CONVERSIONS ************************************************/
1141 case XT_ALT_FEET:
1142 /* altitude in feet as a decimal value */
1143 wpt->altitude = FEET_TO_METERS(atof(s));
1144 if (wpt->altitude < unknown_alt + 1) {
1145 wpt->altitude = unknown_alt;
1146 }
1147 break;
1148 case XT_ALT_METERS:
1149 /* altitude in meters as a decimal value */
1150 wpt->altitude = atof(s);
1151 if (wpt->altitude < unknown_alt + 1) {
1152 wpt->altitude = unknown_alt;
1153 }
1154 break;
1155
1156 /* PATH CONVERSIONS ************************************************/
1157 case XT_PATH_SPEED:
1158 WAYPT_SET(wpt, speed, atof(s));
1159 break;
1160 case XT_PATH_SPEED_KPH:
1161 WAYPT_SET(wpt, speed, KPH_TO_MPS(atof(s)));
1162 break;
1163 case XT_PATH_SPEED_MPH:
1164 WAYPT_SET(wpt, speed, MPH_TO_MPS(atof(s)));
1165 break;
1166 case XT_PATH_SPEED_KNOTS:
1167 WAYPT_SET(wpt, speed, KNOTS_TO_MPS(atof(s)));
1168 break;
1169 case XT_PATH_COURSE:
1170 WAYPT_SET(wpt, course, atof(s));
1171 break;
1172
1173 /* TIME CONVERSIONS ***************************************************/
1174 case XT_EXCEL_TIME:
1175 /* Time as Excel Time */
1176 wpt->creation_time = EXCEL_TO_TIMET(atof(s));
1177 break;
1178 case XT_TIMET_TIME:
1179 /* Time as time_t */
1180 wpt->creation_time = (time_t) atol(s);
1181 break;
1182 case XT_TIMET_TIME_MS: {
1183 /* Time as time_t in milliseconds */
1184 int s_len = strlen(s);
1185 if (s_len < 4) {
1186 /* less than 1 epochsecond, an unusual case */
1187 wpt->creation_time = (time_t) 0;
1188 wpt->microseconds = (int) atoi(s) * 1000;
1189 } else {
1190 char buff[32];
1191 int off = s_len - 3;
1192 strncpy(buff, s, off);
1193 buff[off] = '\0';
1194 wpt->creation_time = (time_t) atol(buff);
1195 s += off;
1196 strncpy(buff, s, 3);
1197 buff[3] = '\0';
1198 wpt->microseconds = (int) atoi(buff) * 1000;
1199 }
1200 }
1201 break;
1202 case XT_YYYYMMDD_TIME:
1203 wpt->creation_time = yyyymmdd_to_time(s);
1204 break;
1205 case XT_GMT_TIME:
1206 wpt->creation_time += sscanftime(s, fmp->printfc, 1);
1207 break;
1208 case XT_LOCAL_TIME:
1209 wpt->creation_time += sscanftime(s, fmp->printfc, 0);
1210 break;
1211 /* Useful when time and date are in separate fields
1212 GMT / Local offset is handled by the two cases above */
1213 case XT_HMSG_TIME:
1214 case XT_HMSL_TIME:
1215 wpt->creation_time += addhms(s, fmp->printfc);
1216 break;
1217 case XT_ISO_TIME:
1218 case XT_ISO_TIME_MS:
1219 wpt->creation_time = xml_parse_time(s, &wpt->microseconds);
1220 break;
1221 case XT_NET_TIME:
1222 dotnet_time_to_time_t(atof(s), &wpt->creation_time, &wpt->microseconds);
1223 break;
1224 case XT_GEOCACHE_LAST_FOUND:
1225 waypt_alloc_gc_data(wpt)->last_found = yyyymmdd_to_time(s);
1226 break;
1227
1228 /* GEOCACHING STUFF ***************************************************/
1229 case XT_GEOCACHE_DIFF:
1230 /* Geocache Difficulty as an int */
1231 waypt_alloc_gc_data(wpt)->diff = atof(s) * 10;
1232 break;
1233 case XT_GEOCACHE_TERR:
1234 /* Geocache Terrain as an int */
1235 waypt_alloc_gc_data(wpt)->terr = atof(s) * 10;
1236 break;
1237 case XT_GEOCACHE_TYPE:
1238 /* Geocache Type */
1239 waypt_alloc_gc_data(wpt)->type = gs_mktype(s);
1240 break;
1241 case XT_GEOCACHE_CONTAINER:
1242 waypt_alloc_gc_data(wpt)->container = gs_mkcont(s);
1243 break;
1244 case XT_GEOCACHE_HINT:
1245 waypt_alloc_gc_data(wpt)->hint = csv_stringtrim(s, "", 0);
1246 break;
1247 case XT_GEOCACHE_PLACER:
1248 waypt_alloc_gc_data(wpt)->placer = csv_stringtrim(s, "", 0);
1249 break;
1250 case XT_GEOCACHE_ISAVAILABLE:
1251 gc_data = waypt_alloc_gc_data(wpt);
1252 if (case_ignore_strcmp(csv_stringtrim(s, "", 0), "False") == 0) {
1253 gc_data->is_available = status_false;
1254 } else if (case_ignore_strcmp(csv_stringtrim(s, "", 0), "True") == 0) {
1255 gc_data->is_available = status_true;
1256 } else {
1257 gc_data->is_available = status_unknown;
1258 }
1259 break;
1260 case XT_GEOCACHE_ISARCHIVED:
1261 gc_data = waypt_alloc_gc_data(wpt);
1262 if (case_ignore_strcmp(csv_stringtrim(s, "", 0), "False") == 0) {
1263 gc_data->is_archived = status_false;
1264 } else if (case_ignore_strcmp(csv_stringtrim(s, "", 0), "True") == 0) {
1265 gc_data->is_archived = status_true;
1266 } else {
1267 gc_data->is_archived = status_unknown;
1268 }
1269 break;
1270
1271 /* GPS STUFF *******************************************************/
1272 case XT_GPS_HDOP:
1273 wpt->hdop = atof(s);
1274 break;
1275 case XT_GPS_VDOP:
1276 wpt->vdop = atof(s);
1277 break;
1278 case XT_GPS_PDOP:
1279 wpt->pdop = atof(s);
1280 break;
1281 case XT_GPS_SAT:
1282 wpt->sat = atoi(s);
1283 break;
1284 case XT_GPS_FIX:
1285 wpt->fix = (fix_type)(atoi(s)-(fix_type)1);
1286 if (wpt->fix < fix_2d) {
1287 if (!case_ignore_strcmp(s, "none")) {
1288 wpt->fix = fix_none;
1289 } else if (!case_ignore_strcmp(s, "dgps")) {
1290 wpt->fix = fix_dgps;
1291 } else if (!case_ignore_strcmp(s, "pps")) {
1292 wpt->fix = fix_pps;
1293 } else {
1294 wpt->fix = fix_unknown;
1295 }
1296 }
1297 break;
1298 /* Tracks and routes *********************************************/
1299 case XT_ROUTE_NAME:
1300 if (csv_route) {
1301 csv_route->rte_name = csv_stringtrim(s, enclosure, 0);
1302 }
1303 break;
1304 case XT_TRACK_NEW:
1305 if (atoi(s) && csv_track && !QUEUE_EMPTY(&csv_track->Q)) {
1306 *trk = route_head_alloc();
1307 csv_track = *trk;
1308
1309 track_add_head(*trk);
1310 }
1311 break;
1312 case XT_TRACK_NAME:
1313 if (!csv_track) {
1314 csv_track = route_head_alloc();
1315 }
1316 csv_track->rte_name = csv_stringtrim(s, enclosure, 0);
1317 break;
1318
1319 /* OTHER STUFF ***************************************************/
1320 case XT_PATH_DISTANCE_METERS:
1321 wpt->odometer_distance = atof(s);
1322 break;
1323 case XT_PATH_DISTANCE_KM:
1324 wpt->odometer_distance = atof(s) * 1000.0;
1325 break;
1326 case XT_PATH_DISTANCE_MILES:
1327 wpt->odometer_distance = MILES_TO_METERS(atof(s));
1328 break;
1329 case XT_HEART_RATE:
1330 wpt->heartrate = atoi(s);
1331 break;
1332 case XT_CADENCE:
1333 wpt->cadence = atoi(s);
1334 break;
1335 case XT_POWER:
1336 wpt->power = atof(s);
1337 break;
1338 case XT_TEMPERATURE:
1339 wpt->temperature = atof(s);
1340 break;
1341 case XT_TEMPERATURE_F:
1342 wpt->temperature = (FAHRENHEIT_TO_CELSIUS( atof(s) ));
1343 break;
1344 /* GMSD ****************************************************************/
1345 case XT_COUNTRY: {
1346 garmin_fs_t* gmsd = gmsd_init(wpt);
1347 GMSD_SET(country, csv_stringtrim(s, enclosure, 0));
1348 }
1349 break;
1350 case XT_STATE: {
1351 garmin_fs_t* gmsd = gmsd_init(wpt);
1352 GMSD_SET(state, csv_stringtrim(s, enclosure, 0));
1353 }
1354 break;
1355 case XT_CITY: {
1356 garmin_fs_t* gmsd = gmsd_init(wpt);
1357 GMSD_SET(city, csv_stringtrim(s, enclosure, 0));
1358 }
1359 break;
1360 case XT_STREET_ADDR: {
1361 garmin_fs_t* gmsd = gmsd_init(wpt);
1362 GMSD_SET(addr, csv_stringtrim(s, enclosure, 0));
1363 }
1364 break;
1365 case XT_POSTAL_CODE: {
1366 garmin_fs_t* gmsd = gmsd_init(wpt);
1367 GMSD_SET(postal_code, csv_stringtrim(s, enclosure, 0));
1368 }
1369 break;
1370 case XT_PHONE_NR: {
1371 garmin_fs_t* gmsd = gmsd_init(wpt);
1372 GMSD_SET(phone_nr, csv_stringtrim(s, enclosure, 0));
1373 }
1374 break;
1375 case XT_FACILITY: {
1376 garmin_fs_t* gmsd = gmsd_init(wpt);
1377 GMSD_SET(facility, csv_stringtrim(s, enclosure, 0));
1378 }
1379 break;
1380 case -1:
1381 if (strncmp(fmp->key, "LON_10E", 7) == 0) {
1382 wpt->longitude = atof(s) / pow((double)10, atof(fmp->key+7));
1383 } else if (strncmp(fmp->key, "LAT_10E", 7) == 0) {
1384 wpt->latitude = atof(s) / pow((double)10, atof(fmp->key+7));
1385 } else {
1386 warning(MYNAME ": Unknown style directive: %s\n", fmp->key);
1387 }
1388 break;
1389
1390 default:
1391 fatal("This can't happen\n");
1392 break;
1393 }
1394 }
1395
1396 /*****************************************************************************/
1397 /* xcsv_data_read() - read input file, parsing lines, fields and handling */
1398 /* any data conversion (the input meat) */
1399 /*****************************************************************************/
1400 void
xcsv_data_read(void)1401 xcsv_data_read(void)
1402 {
1403 char* buff;
1404 char* s;
1405 waypoint* wpt_tmp;
1406 int linecount = 0;
1407 queue* elem, *tmp;
1408 field_map_t* fmp;
1409 ogue_t* ogp;
1410 route_head* rte = NULL;
1411 route_head* trk = NULL;
1412 utm_northing = 0;
1413 utm_easting = 0;
1414 utm_zone = 0;
1415 utm_zonec = 'N';
1416
1417 csv_route = csv_track = NULL;
1418 if (xcsv_file.datatype == trkdata) {
1419 csv_track = trk;
1420 } else if (xcsv_file.datatype == rtedata) {
1421 csv_route = rte;
1422 }
1423
1424 while ((buff = gbfgetstr(xcsv_file.xcsvfp))) {
1425 if ((linecount == 0) && xcsv_file.xcsvfp->unicode) {
1426 cet_convert_init(CET_CHARSET_UTF8, 1);
1427 }
1428
1429 linecount++;
1430 /* Whack trailing space; leading space may matter if our field sep
1431 * is whitespace and we have leading whitespace.
1432 */
1433 rtrim(buff);
1434
1435 /* skip over x many lines on the top for the prologue... */
1436 if ((xcsv_file.prologue_lines) && ((linecount - 1) <
1437 xcsv_file.prologue_lines)) {
1438 continue;
1439 }
1440
1441 /* We should skip over epilogue lines also. Since we don't want to
1442 * pre-read the file to know how many data lines we should be seeing,
1443 * we take this cheap shot at the data and cross our fingers.
1444 */
1445
1446 QUEUE_FOR_EACH(&xcsv_file.epilogue, elem, tmp) {
1447 ogp = (ogue_t*) elem;
1448 if (strncmp(buff, ogp->val, strlen(ogp->val)) == 0) {
1449 buff[0] = '\0';
1450 break;
1451 }
1452 }
1453
1454 if (strlen(buff)) {
1455 wpt_tmp = waypt_new();
1456
1457 s = buff;
1458 s = csv_lineparse(s, xcsv_file.field_delimiter,
1459 xcsv_file.field_encloser, linecount);
1460
1461 if (QUEUE_EMPTY(&xcsv_file.ifield)) {
1462 fatal(MYNAME ": attempt to read, but style '%s' has no IFIELDs in it.\n", xcsv_file.description? xcsv_file.description : "unknown");
1463 }
1464
1465 /* reset the ifield queue */
1466 elem = QUEUE_FIRST(&xcsv_file.ifield);
1467
1468 /* now rip the line apart, advancing the queue for each tear
1469 * off the beginning of buff since there's no index into queue.
1470 */
1471 while (s) {
1472 fmp = (field_map_t*) elem;
1473 xcsv_parse_val(s, wpt_tmp, fmp, &trk);
1474
1475 elem = QUEUE_NEXT(elem);
1476
1477 if (elem == &xcsv_file.ifield) {
1478 /* we've wrapped the queue. so stop parsing! */
1479 while (s) {
1480 s=csv_lineparse(NULL, "\xff","",linecount);
1481 }
1482 break;
1483 }
1484
1485 s = csv_lineparse(NULL, xcsv_file.field_delimiter,
1486 xcsv_file.field_encloser, linecount);
1487 }
1488
1489 if ((xcsv_file.gps_datum > -1) && (xcsv_file.gps_datum != GPS_DATUM_WGS84)) {
1490 double alt;
1491 GPS_Math_Known_Datum_To_WGS84_M(wpt_tmp->latitude, wpt_tmp->longitude, 0.0,
1492 &wpt_tmp->latitude, &wpt_tmp->longitude, &alt, xcsv_file.gps_datum);
1493 }
1494
1495 if (utm_easting || utm_northing) {
1496 GPS_Math_UTM_EN_To_Known_Datum(&wpt_tmp->latitude,
1497 &wpt_tmp->longitude,
1498 utm_easting, utm_northing,
1499 utm_zone, utm_zonec,
1500 DATUM_WGS84);
1501 }
1502
1503 switch (xcsv_file.datatype) {
1504 case unknown_gpsdata:
1505 case wptdata:
1506 waypt_add(wpt_tmp);
1507 break;
1508 case trkdata:
1509 if (trk == NULL) {
1510 trk = route_head_alloc();
1511 csv_track = trk;
1512 track_add_head(trk);
1513 }
1514 track_add_wpt(trk, wpt_tmp);
1515 break;
1516 case rtedata:
1517 if (rte == NULL) {
1518 rte = route_head_alloc();
1519 csv_route = rte;
1520 route_add_head(rte);
1521 }
1522 route_add_wpt(rte, wpt_tmp);
1523 break;
1524 default:
1525 ;
1526 }
1527 }
1528
1529 }
1530 }
1531
1532 static void
xcsv_resetpathlen(const route_head * head)1533 xcsv_resetpathlen(const route_head* head)
1534 {
1535 pathdist = 0;
1536 oldlat = 999;
1537 oldlon = 999;
1538 csv_route = csv_track = NULL;
1539 switch (xcsv_file.datatype) {
1540 case trkdata:
1541 csv_track = (route_head*) head;
1542 break;
1543 case rtedata:
1544 csv_route = (route_head*) head;
1545 break;
1546 default:
1547 break;
1548 }
1549 }
1550
1551 /*****************************************************************************/
1552 /* xcsv_waypt_pr() - write output file, handling output conversions */
1553 /* (the output meat) */
1554 /*****************************************************************************/
1555 static void
xcsv_waypt_pr(const waypoint * wpt)1556 xcsv_waypt_pr(const waypoint* wpt)
1557 {
1558 char buff[1024];
1559 char* shortname = NULL;
1560 char* description = NULL;
1561 char* anyname = NULL;
1562 char* write_delimiter;
1563 int i;
1564 field_map_t* fmp;
1565 queue* elem, *tmp;
1566 double latitude, longitude;
1567 int32 utmz;
1568 double utme, utmn;
1569 char utmzc;
1570
1571 buff[0] = '\0';
1572
1573 if (oldlon < 900) {
1574 pathdist += radtomiles(gcdist(RAD(oldlat),RAD(oldlon),
1575 RAD(wpt->latitude),RAD(wpt->longitude)));
1576 }
1577 longitude = oldlon = wpt->longitude;
1578 latitude = oldlat = wpt->latitude;
1579
1580 if (xcsv_file.field_delimiter && strcmp(xcsv_file.field_delimiter, "\\w") == 0) {
1581 write_delimiter = " ";
1582 } else {
1583 write_delimiter = xcsv_file.field_delimiter;
1584 }
1585
1586 if ((! wpt->shortname) || (global_opts.synthesize_shortnames)) {
1587 if (wpt->description) {
1588 if (global_opts.synthesize_shortnames) {
1589 shortname = mkshort_from_wpt(xcsv_file.mkshort_handle, wpt);
1590 } else {
1591 shortname = csv_stringclean(wpt->description, xcsv_file.badchars);
1592 }
1593 } else {
1594 /* no shortname available -- let shortname default on output */
1595 }
1596 } else {
1597 shortname = csv_stringclean(wpt->shortname, xcsv_file.badchars);
1598 }
1599
1600 if (! wpt->description) {
1601 if (shortname) {
1602 description = csv_stringclean(shortname, xcsv_file.badchars);
1603 } else {
1604 /* no description -- let description default on output */
1605 }
1606 } else {
1607 description = csv_stringclean(wpt->description, xcsv_file.badchars);
1608 }
1609
1610 if (prefer_shortnames) {
1611 if (description) {
1612 xfree(description);
1613 }
1614 description = shortname;
1615 } else if (description) {
1616 char* odesc = description;
1617 description = xstrdup(odesc);
1618 xfree(odesc);
1619 }
1620 if ((xcsv_file.gps_datum > -1) && (xcsv_file.gps_datum != GPS_DATUM_WGS84)) {
1621 double alt;
1622 GPS_Math_WGS84_To_Known_Datum_M(latitude, longitude, 0.0,
1623 &latitude, &longitude, &alt, xcsv_file.gps_datum);
1624 }
1625
1626 i = 0;
1627 QUEUE_FOR_EACH(xcsv_file.ofield, elem, tmp) {
1628 char* obuff;
1629 double lat = latitude;
1630 double lon = longitude;
1631 /*
1632 * A klunky concept. This should evaluate to true for any
1633 * field if we think we don't have realistic value for it.
1634 * This is used by the 'optional' attribute for suppressing
1635 * fields on output.
1636 */
1637 int field_is_unknown = 0;
1638
1639 fmp = (field_map_t*) elem;
1640
1641 if ((i != 0) && !(fmp->options & OPTIONS_NODELIM)) {
1642 gbfprintf(xcsv_file.xcsvfp, write_delimiter);
1643 }
1644
1645 if (fmp->options & OPTIONS_ABSOLUTE) {
1646 lat = fabs(lat);
1647 lon = fabs(lon);
1648 }
1649
1650 i++;
1651 #define writebuff(b, fmt, data) snprintf(b, sizeof(b), fmt, data)
1652 switch (fmp->hashed_key) {
1653 case XT_IGNORE:
1654 /* IGNORE -- Write the char printf conversion */
1655 writebuff(buff, fmp->printfc, "");
1656 break;
1657 case XT_INDEX:
1658 writebuff(buff, fmp->printfc, waypt_out_count + atoi(fmp->val));
1659 break;
1660 case XT_CONSTANT: {
1661 const char* cp = xcsv_get_char_from_constant_table(fmp->val);
1662 if (cp) {
1663 writebuff(buff, fmp->printfc, cp);
1664 } else {
1665 writebuff(buff, fmp->printfc, fmp->val);
1666 }
1667 }
1668 break;
1669 case XT_SHORTNAME:
1670 writebuff(buff, fmp->printfc,
1671 (shortname && *shortname) ? shortname : fmp->val);
1672 break;
1673 case XT_ANYNAME:
1674 if (wpt->shortname) {
1675 anyname = xstrdup(wpt->shortname);
1676 } else if (wpt->description) {
1677 anyname = mkshort(xcsv_file.mkshort_handle, wpt->description);
1678 } else if (wpt->notes) {
1679 anyname = xstrdup(wpt->notes);
1680 } else {
1681 anyname = xstrdup(fmp->val);
1682 }
1683
1684 if ((anyname) && (global_opts.synthesize_shortnames)) {
1685 anyname = xstrdup(shortname);
1686 }
1687
1688 writebuff(buff, fmp->printfc, anyname);
1689
1690 xfree(anyname);
1691 break;
1692 case XT_DESCRIPTION:
1693 writebuff(buff, fmp->printfc,
1694 (description && *description) ? description : fmp->val);
1695 break;
1696 case XT_NOTES:
1697 writebuff(buff, fmp->printfc,
1698 (wpt->notes && *wpt->notes) ? wpt->notes : fmp->val);
1699 break;
1700 case XT_URL: {
1701 int off = 0;
1702 if (xcsv_urlbase) {
1703 strcpy(buff, xcsv_urlbase);
1704 off = strlen(xcsv_urlbase);
1705 }
1706 if (wpt->url) {
1707 snprintf(buff + off, sizeof(buff) - off, fmp->printfc, wpt->url);
1708 } else {
1709 strcpy(buff, (fmp->val && *fmp->val) ? fmp->val : "\"\"");
1710 }
1711 }
1712 break;
1713 case XT_URL_LINK_TEXT:
1714 snprintf(buff, sizeof(buff), fmp->printfc,
1715 (wpt->url_link_text && *wpt->url_link_text) ? wpt->url_link_text : fmp->val);
1716 break;
1717 case XT_ICON_DESCR:
1718 writebuff(buff, fmp->printfc,
1719 (wpt->icon_descr && *wpt->icon_descr) ?
1720 wpt->icon_descr : fmp->val);
1721 break;
1722
1723 /* LATITUDE CONVERSION***********************************************/
1724 case XT_LAT_DECIMAL:
1725 /* latitude as a pure decimal value */
1726 writebuff(buff, fmp->printfc, lat);
1727 break;
1728 case XT_LAT_DECIMALDIR:
1729 /* latitude as a decimal value with N/S after it */
1730 snprintf(buff, sizeof(buff), fmp->printfc, fabs(lat),
1731 LAT_DIR(lat));
1732 break;
1733 case XT_LAT_DIRDECIMAL:
1734 /* latitude as a decimal value with N/S before it */
1735 snprintf(buff, sizeof(buff), fmp->printfc,
1736 LAT_DIR(lat),
1737 fabs(lat));
1738 break;
1739 case XT_LAT_INT32DEG:
1740 /* latitude as an integer offset from 0 degrees */
1741 writebuff(buff, fmp->printfc,
1742 dec_to_intdeg(lat));
1743 break;
1744 case XT_LAT_DDMMDIR:
1745 /*latitude as (degrees * 100) + decimal minutes, with N/S after it */
1746 dec_to_human(buff, fmp->printfc, "SN", degrees2ddmm(lat));
1747 break;
1748 case XT_LAT_HUMAN_READABLE:
1749 dec_to_human(buff, fmp->printfc, "SN", lat);
1750 break;
1751 case XT_LAT_NMEA:
1752 writebuff(buff, fmp->printfc, degrees2ddmm(lat));
1753 break;
1754 // case XT_LAT_10E is handled outside the switch.
1755 /* LONGITUDE CONVERSIONS*********************************************/
1756 case XT_LON_DECIMAL:
1757 /* longitude as a pure decimal value */
1758 writebuff(buff, fmp->printfc, lon);
1759 break;
1760 case XT_LON_DECIMALDIR:
1761 /* latitude as a decimal value with N/S after it */
1762 snprintf(buff, sizeof(buff), fmp->printfc,
1763 fabs(lon),
1764 LON_DIR(lon));
1765 break;
1766 case XT_LON_DIRDECIMAL:
1767 /* latitude as a decimal value with N/S before it */
1768 snprintf(buff, sizeof(buff), fmp->printfc,
1769 LON_DIR(lon),
1770 fabs(lon));
1771 break;
1772 case XT_LON_INT32DEG:
1773 /* longitudee as an integer offset from 0 degrees */
1774 writebuff(buff, fmp->printfc,
1775 dec_to_intdeg(lon));
1776 break;
1777 case XT_LON_DDMMDIR:
1778 /* longidute as (degrees * 100) + decimal minutes, with W/E after it*/
1779 dec_to_human(buff, fmp->printfc, "WE", degrees2ddmm(lon));
1780 break;
1781 case XT_LON_HUMAN_READABLE:
1782 dec_to_human(buff, fmp->printfc, "WE", lon);
1783 break;
1784 case XT_LATLON_HUMAN_READABLE:
1785 dec_to_human(buff, fmp->printfc, "SN", lat);
1786 if (!isspace(buff[strlen(buff)])) {
1787 strcat(buff, " ");
1788 }
1789 dec_to_human(buff+strlen(buff), fmp->printfc, "WE",
1790 lon);
1791 break;
1792 case XT_LON_NMEA:
1793 writebuff(buff, fmp->printfc, degrees2ddmm(lon));
1794 break;
1795 // case XT_LON_10E is handled outside the switch.
1796 /* DIRECTIONS *******************************************************/
1797 case XT_LAT_DIR:
1798 /* latitude N/S as a char */
1799 writebuff(buff, fmp->printfc,
1800 LAT_DIR(lat));
1801 break;
1802 case XT_LON_DIR:
1803 /* longitude E/W as a char */
1804 writebuff(buff, fmp->printfc,
1805 LON_DIR(lon));
1806 break;
1807
1808 /* SPECIAL COORDINATES */
1809 case XT_MAP_EN_BNG: {
1810 char map[3];
1811 double north, east;
1812 if (! GPS_Math_WGS84_To_UKOSMap_M(wpt->latitude, wpt->longitude, &east, &north, map))
1813 fatal(MYNAME ": Position (%.5f/%.5f) outside of BNG.\n",
1814 wpt->latitude, wpt->longitude);
1815 snprintf(buff, sizeof(buff), fmp->printfc, map, (int)(east + 0.5), (int)(north + 0.5));
1816 }
1817 break;
1818 case XT_UTM: {
1819 char tbuf[100];
1820 GPS_Math_WGS84_To_UTM_EN(wpt->latitude, wpt->longitude,
1821 &utme, &utmn, &utmz, &utmzc);
1822 snprintf(tbuf, sizeof(tbuf), "%d%c %6.0f %7.0f",
1823 utmz, utmzc, utme, utmn);
1824 writebuff(buff, fmp->printfc, tbuf);
1825 }
1826 break;
1827 case XT_UTM_ZONE:
1828 GPS_Math_WGS84_To_UTM_EN(wpt->latitude, wpt->longitude,
1829 &utme, &utmn, &utmz, &utmzc);
1830 writebuff(buff, fmp->printfc, utmz);
1831 break;
1832 case XT_UTM_ZONEC:
1833 GPS_Math_WGS84_To_UTM_EN(wpt->latitude, wpt->longitude,
1834 &utme, &utmn, &utmz, &utmzc);
1835 writebuff(buff, fmp->printfc, utmzc);
1836 break;
1837 case XT_UTM_ZONEF: {
1838 char tbuf[10];
1839 GPS_Math_WGS84_To_UTM_EN(wpt->latitude, wpt->longitude,
1840 &utme, &utmn, &utmz, &utmzc);
1841 tbuf[0] = 0;
1842 snprintf(tbuf, sizeof(tbuf), "%d%c", utmz, utmzc);
1843 writebuff(buff, fmp->printfc, tbuf);
1844 }
1845 break;
1846 case XT_UTM_NORTHING:
1847 GPS_Math_WGS84_To_UTM_EN(wpt->latitude, wpt->longitude,
1848 &utme, &utmn, &utmz, &utmzc);
1849 writebuff(buff, fmp->printfc, utmn);
1850 break;
1851 case XT_UTM_EASTING:
1852 GPS_Math_WGS84_To_UTM_EN(wpt->latitude, wpt->longitude,
1853 &utme, &utmn, &utmz, &utmzc);
1854 writebuff(buff, fmp->printfc, utme);
1855 break;
1856
1857 /* ALTITUDE CONVERSIONS**********************************************/
1858 case XT_ALT_FEET:
1859 /* altitude in feet as a decimal value */
1860 writebuff(buff, fmp->printfc,
1861 METERS_TO_FEET(wpt->altitude));
1862 break;
1863 case XT_ALT_METERS:
1864 /* altitude in meters as a decimal value */
1865 writebuff(buff, fmp->printfc,
1866 wpt->altitude);
1867 break;
1868
1869 /* DISTANCE CONVERSIONS**********************************************/
1870 /* prefer odometer distance. */
1871 /* if not available, use calculated distance from positions */
1872 case XT_PATH_DISTANCE_MILES:
1873 /* path (route/track) distance in miles */
1874 if (wpt->odometer_distance) {
1875 writebuff(buff, fmp->printfc, METERS_TO_MILES(wpt->odometer_distance));
1876 } else {
1877 writebuff(buff, fmp->printfc, pathdist);
1878 }
1879 break;
1880 case XT_PATH_DISTANCE_METERS:
1881 /* path (route/track) distance in meters */
1882 if (wpt->odometer_distance) {
1883 writebuff(buff, fmp->printfc, wpt->odometer_distance);
1884 } else {
1885 writebuff(buff, fmp->printfc, MILES_TO_METERS(pathdist));
1886 }
1887 break;
1888 case XT_PATH_DISTANCE_KM:
1889 /* path (route/track) distance in kilometers */
1890 if (wpt->odometer_distance) {
1891 writebuff(buff, fmp->printfc, wpt->odometer_distance / 1000.0);
1892 } else {
1893 writebuff(buff, fmp->printfc, MILES_TO_METERS(pathdist) / 1000.0);
1894 }
1895 break;
1896 case XT_PATH_SPEED:
1897 writebuff(buff, fmp->printfc, wpt->speed);
1898 break;
1899 case XT_PATH_SPEED_KPH:
1900 writebuff(buff, fmp->printfc, MPS_TO_KPH(wpt->speed));
1901 break;
1902 case XT_PATH_SPEED_MPH:
1903 writebuff(buff, fmp->printfc, MPS_TO_MPH(wpt->speed));
1904 break;
1905 case XT_PATH_SPEED_KNOTS:
1906 writebuff(buff, fmp->printfc, MPS_TO_KNOTS(wpt->speed));
1907 break;
1908 case XT_PATH_COURSE:
1909 writebuff(buff, fmp->printfc, wpt->course);
1910 break;
1911
1912 /* HEART RATE CONVERSION***********************************************/
1913 case XT_HEART_RATE:
1914 writebuff(buff, fmp->printfc, wpt->heartrate);
1915 break;
1916 /* CADENCE CONVERSION***********************************************/
1917 case XT_CADENCE:
1918 writebuff(buff, fmp->printfc, wpt->cadence);
1919 break;
1920 /* POWER CONVERSION***********************************************/
1921 case XT_POWER:
1922 writebuff(buff, fmp->printfc, wpt->power);
1923 break;
1924 case XT_TEMPERATURE:
1925 writebuff(buff, fmp->printfc, wpt->temperature);
1926 break;
1927 case XT_TEMPERATURE_F:
1928 writebuff(buff, fmp->printfc, CELSIUS_TO_FAHRENHEIT(wpt->temperature));
1929 break;
1930 /* TIME CONVERSIONS**************************************************/
1931 case XT_EXCEL_TIME:
1932 /* creation time as an excel (double) time */
1933 writebuff(buff, fmp->printfc, TIMET_TO_EXCEL(wpt->creation_time));
1934 break;
1935 case XT_TIMET_TIME:
1936 /* time as a time_t variable */
1937 writebuff(buff, fmp->printfc, wpt->creation_time);
1938 break;
1939 case XT_TIMET_TIME_MS: {
1940 /* time as a time_t variable in milliseconds */
1941 char tbuf[24];
1942 writetime(tbuf, sizeof(tbuf), "%s", wpt->creation_time, 0);
1943 char mbuf[32];
1944 snprintf(mbuf, sizeof(mbuf), "%s%03d", tbuf, wpt->microseconds / 1000);
1945 writebuff(buff, "%s", mbuf);
1946 }
1947 break;
1948 case XT_YYYYMMDD_TIME:
1949 writebuff(buff, fmp->printfc, time_to_yyyymmdd(wpt->creation_time));
1950 break;
1951 case XT_GMT_TIME:
1952 writetime(buff, sizeof buff, fmp->printfc, wpt->creation_time, 1);
1953 break;
1954 case XT_LOCAL_TIME:
1955 writetime(buff, sizeof buff, fmp->printfc, wpt->creation_time, 0);
1956 break;
1957 case XT_HMSG_TIME:
1958 writehms(buff, sizeof buff, fmp->printfc, wpt->creation_time, 1);
1959 break;
1960 case XT_HMSL_TIME:
1961 writehms(buff, sizeof buff, fmp->printfc, wpt->creation_time, 0);
1962 break;
1963 case XT_ISO_TIME:
1964 writetime(buff, sizeof buff, "%Y-%m-%dT%H:%M:%SZ", wpt->creation_time, 1);
1965 break;
1966 case XT_ISO_TIME_MS:
1967 xml_fill_in_time(buff, wpt->creation_time,
1968 wpt->microseconds, XML_LONG_TIME);
1969 break;
1970 case XT_GEOCACHE_LAST_FOUND:
1971 writebuff(buff, fmp->printfc, time_to_yyyymmdd(wpt->gc_data->last_found));
1972 break;
1973
1974 /* GEOCACHE STUFF **************************************************/
1975 case XT_GEOCACHE_DIFF:
1976 /* Geocache Difficulty as a double */
1977 writebuff(buff, fmp->printfc, wpt->gc_data->diff / 10.0);
1978 field_is_unknown = !wpt->gc_data->diff;
1979 break;
1980 case XT_GEOCACHE_TERR:
1981 /* Geocache Terrain as a double */
1982 writebuff(buff, fmp->printfc, wpt->gc_data->terr / 10.0);
1983 field_is_unknown = !wpt->gc_data->terr;
1984 break;
1985 case XT_GEOCACHE_CONTAINER:
1986 /* Geocache Container */
1987 writebuff(buff, fmp->printfc, gs_get_container(wpt->gc_data->container));
1988 field_is_unknown = wpt->gc_data->container == gc_unknown;
1989 break;
1990 case XT_GEOCACHE_TYPE:
1991 /* Geocache Type */
1992 writebuff(buff, fmp->printfc, gs_get_cachetype(wpt->gc_data->type));
1993 field_is_unknown = wpt->gc_data->type == gt_unknown;
1994 break;
1995 case XT_GEOCACHE_HINT:
1996 writebuff(buff, fmp->printfc, NONULL(wpt->gc_data->hint));
1997 field_is_unknown = !wpt->gc_data->hint;
1998 break;
1999 case XT_GEOCACHE_PLACER:
2000 writebuff(buff, fmp->printfc, NONULL(wpt->gc_data->placer));
2001 field_is_unknown = !wpt->gc_data->placer;
2002 break;
2003 case XT_GEOCACHE_ISAVAILABLE:
2004 if (wpt->gc_data->is_available == status_false) {
2005 writebuff(buff, fmp->printfc, "False");
2006 } else if (wpt->gc_data->is_available == status_true) {
2007 writebuff(buff, fmp->printfc, "True");
2008 } else {
2009 writebuff(buff, fmp->printfc, "Unknown");
2010 }
2011 break;
2012 case XT_GEOCACHE_ISARCHIVED:
2013 if (wpt->gc_data->is_archived == status_false) {
2014 writebuff(buff, fmp->printfc, "False");
2015 } else if (wpt->gc_data->is_archived == status_true) {
2016 writebuff(buff, fmp->printfc, "True");
2017 } else {
2018 writebuff(buff, fmp->printfc, "Unknown");
2019 }
2020 break;
2021 /* Tracks and Routes ***********************************************/
2022 case XT_TRACK_NAME:
2023 if (csv_track) {
2024 writebuff(buff, fmp->printfc, NONULL(csv_track->rte_name));
2025 }
2026 break;
2027 case XT_ROUTE_NAME:
2028 if (csv_route) {
2029 writebuff(buff, fmp->printfc, NONULL(csv_route->rte_name));
2030 }
2031 break;
2032
2033 /* GPS STUFF *******************************************************/
2034 case XT_GPS_HDOP:
2035 writebuff(buff, fmp->printfc, wpt->hdop);
2036 field_is_unknown = !wpt->hdop;
2037 break;
2038 case XT_GPS_VDOP:
2039 writebuff(buff, fmp->printfc, wpt->vdop);
2040 field_is_unknown = !wpt->vdop;
2041 break;
2042 case XT_GPS_PDOP:
2043 writebuff(buff, fmp->printfc, wpt->pdop);
2044 field_is_unknown = !wpt->pdop;
2045 break;
2046 case XT_GPS_SAT:
2047 writebuff(buff, fmp->printfc, wpt->sat);
2048 field_is_unknown = !wpt->sat;
2049 break;
2050 case XT_GPS_FIX: {
2051 char* fix = NULL;
2052 switch (wpt->fix) {
2053 case fix_unknown:
2054 field_is_unknown = 1;
2055 fix = "Unknown";
2056 break;
2057 case fix_none:
2058 fix = "None";
2059 break;
2060 case fix_2d:
2061 fix = "2d";
2062 break;
2063 case fix_3d:
2064 fix = "3d";
2065 break;
2066 case fix_dgps:
2067 fix = "dgps";
2068 break;
2069 case fix_pps:
2070 fix = "pps";
2071 break;
2072 }
2073 writebuff(buff, fmp->printfc, fix);
2074 }
2075 break;
2076 /* GMSD ************************************************************/
2077 case XT_COUNTRY: {
2078 garmin_fs_t* gmsd = GMSD_FIND(wpt);
2079 writebuff(buff, fmp->printfc, GMSD_GET(country, ""));
2080 }
2081 break;
2082 case XT_STATE: {
2083 garmin_fs_t* gmsd = GMSD_FIND(wpt);
2084 writebuff(buff, fmp->printfc, GMSD_GET(state, ""));
2085 }
2086 break;
2087 case XT_CITY: {
2088 garmin_fs_t* gmsd = GMSD_FIND(wpt);
2089 writebuff(buff, fmp->printfc, GMSD_GET(city, ""));
2090 }
2091 break;
2092 case XT_POSTAL_CODE: {
2093 garmin_fs_t* gmsd = GMSD_FIND(wpt);
2094 writebuff(buff, fmp->printfc, GMSD_GET(postal_code, ""));
2095 }
2096 break;
2097 case XT_STREET_ADDR: {
2098 garmin_fs_t* gmsd = GMSD_FIND(wpt);
2099 writebuff(buff, fmp->printfc, GMSD_GET(addr, ""));
2100 }
2101 break;
2102 case XT_PHONE_NR: {
2103 garmin_fs_t* gmsd = GMSD_FIND(wpt);
2104 writebuff(buff, fmp->printfc, GMSD_GET(phone_nr, ""));
2105 }
2106 break;
2107 case XT_FACILITY: {
2108 garmin_fs_t* gmsd = GMSD_FIND(wpt);
2109 writebuff(buff, fmp->printfc, GMSD_GET(facility, ""));
2110 }
2111 break;
2112 /* specials */
2113 case XT_FILENAME:
2114 writebuff(buff, fmp->printfc, wpt->session->filename);
2115 break;
2116 case XT_FORMAT:
2117 writebuff(buff, fmp->printfc, wpt->session->name);
2118 break;
2119 case -1:
2120 if (strncmp(fmp->key, "LON_10E", 7) == 0) {
2121 writebuff(buff, fmp->printfc, lon * pow((double)10, atof(fmp->key+7)));
2122 } else if (strncmp(fmp->key, "LAT_10E", 7) == 0) {
2123 writebuff(buff, fmp->printfc, lat * pow((double)10, atof(fmp->key+7)));
2124 }
2125 break;
2126 default:
2127 warning(MYNAME ": Unknown style directive: %s\n", fmp->key);
2128 break;
2129 }
2130 obuff = csv_stringclean(buff, xcsv_file.badchars);
2131
2132 if (field_is_unknown && fmp->options & OPTIONS_OPTIONAL) {
2133 goto next;
2134 }
2135
2136 if (xcsv_file.field_encloser) {
2137 /* print the enclosing character(s) */
2138 gbfprintf(xcsv_file.xcsvfp, "%s", xcsv_file.field_encloser);
2139 }
2140
2141 /* As a special case (pronounced "horrible hack") we allow
2142 * ""%s"" to smuggle bad characters through.
2143 */
2144 if (0 == strcmp(fmp->printfc, "\"%s\"")) {
2145 gbfprintf(xcsv_file.xcsvfp, "\"%s\"", obuff);
2146 } else {
2147 gbfprintf(xcsv_file.xcsvfp, "%s", obuff);
2148 }
2149
2150 if (xcsv_file.field_encloser) {
2151 /* print the enclosing character(s) */
2152 gbfprintf(xcsv_file.xcsvfp, "%s", xcsv_file.field_encloser);
2153 }
2154
2155 next:
2156 xfree(obuff);
2157 }
2158
2159 gbfprintf(xcsv_file.xcsvfp, "%s", xcsv_file.record_delimiter);
2160
2161 if (description && description != shortname) {
2162 xfree(description);
2163 }
2164
2165 if (shortname) {
2166 xfree(shortname);
2167 }
2168
2169 /* increment the index counter */
2170 waypt_out_count++;
2171 }
2172
2173 static void
xcsv_noop(const route_head * wp)2174 xcsv_noop(const route_head* wp)
2175 {
2176 /* no-op */
2177 }
2178
2179 /*****************************************************************************/
2180 /* xcsv_data_write(void) - write prologues, spawn the output loop, and write */
2181 /* epilogues. */
2182 /*****************************************************************************/
2183 void
xcsv_data_write(void)2184 xcsv_data_write(void)
2185 {
2186 queue* elem, *tmp;
2187 ogue_t* ogp;
2188 time_t time;
2189 struct tm tm;
2190 char tbuf[32];
2191
2192 /* reset the index counter */
2193 waypt_out_count = 0;
2194
2195 time = gpsbabel_time;
2196 if (time == 0) { /* testo script ? */
2197 tm = *gmtime(&time);
2198 } else {
2199 tm = *localtime(&time);
2200 }
2201
2202 /* output prologue lines, if any. */
2203 QUEUE_FOR_EACH(&xcsv_file.prologue, elem, tmp) {
2204 char* cout, *ctmp;
2205 ogp = (ogue_t*) elem;
2206
2207 cout = xstrdup((ogp->val) ? ogp->val : "");
2208
2209 while ((ctmp = strsub(cout, "__FILE__", xcsv_file.fname))) {
2210 xfree(cout);
2211 cout = ctmp;
2212 }
2213
2214 while ((ctmp = strsub(cout, "__VERSION__", (time == 0) ? "" : gpsbabel_version))) {
2215 xfree(cout);
2216 cout = ctmp;
2217 }
2218
2219 while (strstr(cout, "__DATE__")) {
2220 strftime(tbuf, sizeof(tbuf), "%m/%d/%Y", &tm);
2221 ctmp = strsub(cout, "__DATE__", tbuf);
2222 xfree(cout);
2223 cout = ctmp;
2224 }
2225
2226 while (strstr(cout, "__TIME__")) {
2227 strftime(tbuf, sizeof(tbuf), "%H:%S:%M", &tm);
2228 ctmp = strsub(cout, "__TIME__", tbuf);
2229 xfree(cout);
2230 cout = ctmp;
2231 }
2232
2233 while (strstr(cout, "__DATE_AND_TIME__")) {
2234 strftime(tbuf, sizeof(tbuf), "%a %b %d %H:%M:%S %Y", &tm);
2235 ctmp = strsub(cout, "__DATE_AND_TIME__", tbuf);
2236 xfree(cout);
2237 cout = ctmp;
2238 }
2239
2240 gbfprintf(xcsv_file.xcsvfp, "%s", cout);
2241 xfree(cout);
2242 gbfprintf(xcsv_file.xcsvfp, "%s", xcsv_file.record_delimiter);
2243 }
2244
2245 if ((xcsv_file.datatype == 0) || (xcsv_file.datatype == wptdata)) {
2246 waypt_disp_all(xcsv_waypt_pr);
2247 }
2248 if ((xcsv_file.datatype == 0) || (xcsv_file.datatype == rtedata)) {
2249 route_disp_all(xcsv_resetpathlen,xcsv_noop,xcsv_waypt_pr);
2250 }
2251 if ((xcsv_file.datatype == 0) || (xcsv_file.datatype == trkdata)) {
2252 track_disp_all(xcsv_resetpathlen,xcsv_noop,xcsv_waypt_pr);
2253 }
2254
2255 /* output epilogue lines, if any. */
2256 QUEUE_FOR_EACH(&xcsv_file.epilogue, elem, tmp) {
2257 ogp = (ogue_t*) elem;
2258 gbfprintf(xcsv_file.xcsvfp, "%s%s", ogp->val, xcsv_file.record_delimiter);
2259 }
2260 }
2261 #endif
2262