You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

csv_util.cc 63KB

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