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, &ampm);
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