1 /*
2     Access to PsiTrex text files.
3     Based on information provided by Ian Cowley.
4 
5     Copyright (C) 2003 Mark Bradley, mrcb.gpsb@osps.net
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20  */
21 
22 #include <cctype>                  // for isspace
23 #include <cstdio>                  // for sscanf, EOF, size_t
24 #include <cstdlib>                 // for atof, atoi
25 #include <cstring>                 // for strcmp, strlen, strcpy
26 #include <ctime>                   // for gmtime
27 
28 #include <QtCore/QChar>            // for QChar
29 #include <QtCore/QLatin1String>    // for QLatin1String
30 #include <QtCore/QString>          // for QString
31 #include <QtCore/QtGlobal>         // for foreach, uint
32 
33 #include "defs.h"
34 #include "garmin_tables.h"         // for gt_find_desc_from_icon_number, gt_find_icon_number_from_desc, PCX
35 #include "gbfile.h"                // for gbfprintf, gbfeof, gbfile, gbfgetc, gbfclose, gbfgets, gbfopen, gbfputs
36 #include "src/core/datetime.h"     // for DateTime
37 
38 
39 #define MYNAME "PSITREX"
40 
41 enum psit_tokenSep_type {
42   ltrimEOL = 1 ,		/* skip spaces & tabs to start; ends on EOL */
43   EOL,				/* don't skip spaces and tabs to start; end on EOL */
44   comma,				/* skip spaces & tabs to start; ends on comma or EOL */
45   whitespace,			/* skip spaces & tabs to start; ends on white space or EOL */
46   wscomma				/* skip spaces & tabs to start; ends on white space, comma or EOL */
47 };
48 
49 struct psit_icon_mapping_t {
50   int	value;
51   const char*	icon;
52 };
53 
54 static gbfile* psit_file_in;
55 static gbfile* psit_file_out;
56 static short_handle mkshort_handle;
57 
58 /* 2 = not written any tracks out
59    1 = change of track to write out track header
60    0 = in the middle of writing out track datapoints, so don't write a header */
61 static int	psit_track_state = 2;
62 
63 static char psit_current_token[256];
64 
65 static char* snlen;
66 
67 static
68 QVector<arglist_t> psit_args = {
69   /*	{"snlen", &snlen, "Length of generated shortnames",
70    	NULL, ARGTYPE_INT, "1", NULL }, */
71 };
72 
73 /* Taken from PsiTrex 1.13 */
74 static
75 const psit_icon_mapping_t psit_icon_value_table[] = {
76   {   0x00, "anchor" },
77   {   0x06, "dollar" },
78   {   0x07, "fish" },
79   {   0x08, "fuel" },
80   {   0x0a, "house" },
81   {   0x0b, "knife" },
82   {   0x0d, "mug" },
83   {   0x0e, "skull" },
84   {   0x12, "wpt_dot" },
85   {   0x13, "wreck" },
86   {   0x15, "mob" },
87   { 0x0096, "boat_ramp" },
88   { 0x0097, "camp" },
89   { 0x0098, "restrooms" },
90   { 0x0099, "showers" },
91   { 0x009a, "drinking_wtr" },
92   { 0x009b, "phone" },
93   { 0x009c, "1st_aid" },
94   { 0x009d, "info" },
95   { 0x009e, "parking" },
96   { 0x009f, "park" },
97   { 0x00a0, "picnic" },
98   { 0x00a1, "scenic" },
99   { 0x00a2, "skiing" },
100   { 0x00a3, "swimming" },
101   { 0x00a4, "dam" },
102   { 0x00a6, "danger" },
103   { 0x00a9, "ball" },
104   { 0x00aa, "car" },
105   { 0x00ab, "deer" },
106   { 0x00ac, "shpng_cart" },
107   { 0x00ad, "lodging" },
108   { 0x00ae, "mine" },
109   { 0x00af, "trail_head" },
110   { 0x00b0, "truck_stop" },
111   { 0x00b2, "flag" },
112   { 0x2005, "golf" },
113   { 0x2006, "sml_cty" },
114   { 0x2007, "med_cty" },
115   { 0x2008, "lrg_cty" },
116   { 0x200c, "amuse_pk" },
117   { 0x200d, "bowling" },
118   { 0x200e, "car_rental" },
119   { 0x200f, "car_repair" },
120   { 0x2010, "fastfood" },
121   { 0x2011, "fitness" },
122   { 0x2012, "movie" },
123   { 0x2013, "museum" },
124   { 0x2014, "pharmacy" },
125   { 0x2015, "pizza" },		/* how specific does this really need to be? C'mon! */
126   { 0x2016, "post_ofc" },
127   { 0x2017, "rv_park" },
128   { 0x2018, "school" },
129   { 0x2019, "stadium" },
130   { 0x201a, "store" },
131   { 0x201b, "zoo" },
132   { 0x201c, "gas_plus" },
133   { 0x201d, "faces" },
134   { 0x2022, "weigh_sttn" },
135   { 0x2023, "toll_booth" },
136   { 0x2029, "bridge" },
137   { 0x202a, "building" },
138   { 0x202b, "cemetery" },
139   { 0x202c, "church" },
140   { 0x202e, "crossing" },
141   { 0x2032, "oil_field" },
142   { 0x2033, "tunnel" },
143   { 0x2035, "forest" },
144   { 0x2036, "summit" },
145   { 0x203f, "geocache" },
146   { 0x2040, "geocache_fnd" },
147   { 0x4000, "airport" },
148   { 0x4007, "tall_tower" },
149   { 0x4008, "short_tower" },
150   { 0x4009, "glider" },
151   { 0x400a, "ultralight" },
152   { 0x400b, "parachute" },
153   { 0x4012, "seaplane" },
154   {     -1, nullptr }
155 };
156 
157 static const char*
psit_find_desc_from_icon_number(const int icon)158 psit_find_desc_from_icon_number(const int icon)
159 {
160   for (const psit_icon_mapping_t* i = psit_icon_value_table; i->icon; i++) {
161     if (icon == i->value) {
162       return i->icon;
163     }
164   }
165   return "";
166 }
167 
168 static int
psit_find_icon_number_from_desc(const char * desc)169 psit_find_icon_number_from_desc(const char* desc)
170 {
171   int def_icon = 18;
172 
173   if (!desc) {
174     return def_icon;
175   }
176 
177   for (const psit_icon_mapping_t* i = psit_icon_value_table; i->icon; i++) {
178     if (case_ignore_strcmp(desc,i->icon) == 0) {
179       return i->value;
180     }
181   }
182   if (atoi(desc) > 0) {
183     return atoi(desc);
184   }
185   return def_icon;
186 }
187 
188 static void
psit_rd_init(const QString & fname)189 psit_rd_init(const QString& fname)
190 {
191   psit_file_in = gbfopen(fname, "r", MYNAME);
192 }
193 
194 static void
psit_rd_deinit()195 psit_rd_deinit()
196 {
197   gbfclose(psit_file_in);
198 }
199 
200 static void
psit_wr_init(const QString & fname)201 psit_wr_init(const QString& fname)
202 {
203   psit_file_out = gbfopen(fname, "w", MYNAME);
204 }
205 
206 static void
psit_wr_deinit()207 psit_wr_deinit()
208 {
209   gbfclose(psit_file_out);
210 }
211 
212 /*
213  * get characters until and including terminating NULL from psit_file_in
214  * and write into buf.
215  */
216 static void
psit_getToken(gbfile * psit_file,char * buf,size_t sz,psit_tokenSep_type delimType)217 psit_getToken(gbfile* psit_file, char* buf, size_t sz, psit_tokenSep_type delimType)
218 {
219   int c = -1;
220 
221   *buf = 0;
222 
223   if (delimType != EOL) {
224     while ((c = gbfgetc(psit_file)) != EOF) {
225       if (!isspace(c)) {
226         break;
227       }
228     }
229   }
230 
231   if (gbfeof(psit_file)) {
232     return;
233   }
234 
235   if (delimType == EOL) {
236     c = gbfgetc(psit_file);
237   }
238 
239   if (c == '#') {
240     if (gbfgets(buf, sz, psit_file) == nullptr) {
241       *buf = 0;
242       return;
243     }
244     /* use recursion to skip multiple comment lines or just to return the next token */
245     psit_getToken(psit_file, buf, sz, delimType);
246     return;
247   }
248 
249   if ((delimType == EOL) || (delimType == ltrimEOL)) {
250     *buf = c;
251     buf++;
252     gbfgets(buf, sz-1, psit_file);
253     return;
254   }
255 
256   while (sz--) {
257     *buf++ = c;
258     if ((c = gbfgetc(psit_file)) == EOF) {
259       *buf = 0;
260       return;
261     }
262     if (((c == 0) || isspace(c)) &&
263         ((delimType == whitespace) || (delimType == wscomma))) {
264       *buf = 0;
265       return;
266     }
267     if (((delimType == comma) || (delimType == wscomma)) &&
268         (c == ',')) {
269       *buf = 0;
270       return;
271     }
272   }
273 }
274 
275 
276 /*
277  * test if a token is known
278  *
279  */
280 static int
psit_isKnownToken(char * buf)281 psit_isKnownToken(char* buf)
282 {
283   if (strcmp(buf, "Track:") == 0) {
284     return 0;
285   }
286   if (strcmp(buf, "Route:") == 0) {
287     return 0;
288   }
289   if (strcmp(buf, "Waypoint:") == 0) {
290     return 0;
291   }
292   if (strcmp(buf, "Map:") == 0) {
293     return 0;
294   }
295   return 1;
296 }
297 
298 /*
299  * read in from file a waypoint record
300  * MRCB
301  */
302 static void
psit_waypoint_r(gbfile * psit_file,Waypoint **)303 psit_waypoint_r(gbfile* psit_file, Waypoint**)
304 {
305   if (strlen(psit_current_token) > 0) {
306     auto* thisWaypoint = new Waypoint;
307 
308     thisWaypoint->latitude = atof(psit_current_token);
309 
310     psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), comma);
311     thisWaypoint->longitude = atof(psit_current_token);
312 
313     psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), comma);
314     if (psit_current_token[0] == '*') {
315       thisWaypoint->altitude = unknown_alt;
316     } else {
317       thisWaypoint->altitude = atof(psit_current_token);
318     }
319 
320     /* the name */
321     psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), comma);
322     rtrim(psit_current_token);
323     thisWaypoint->shortname = psit_current_token;
324     thisWaypoint->description = "";
325 
326     psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), ltrimEOL);
327     rtrim(psit_current_token);
328     /* since PsiTrex only deals with Garmins, let's use the "proper" Garmin icon name */
329     /* convert the PsiTrex name to the number, which is the PCX one; from there to Garmin desc */
330     int garmin_icon_num = psit_find_icon_number_from_desc(psit_current_token);
331     thisWaypoint->icon_descr = gt_find_desc_from_icon_number(garmin_icon_num, PCX);
332 
333     waypt_add(thisWaypoint);
334 
335     psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), wscomma);
336   }
337 }
338 
339 /*
340  * write out to file a waypoint record
341  * MRCB
342  */
343 static void
psit_waypoint_w(gbfile * psit_file,const Waypoint * wpt)344 psit_waypoint_w(gbfile* psit_file, const Waypoint* wpt)
345 {
346   gbfprintf(psit_file, "%11.6f,%11.6f,",
347             wpt->latitude,
348             wpt->longitude);
349 
350   if (wpt->altitude == unknown_alt) {
351     gbfprintf(psit_file, "********,");
352   } else
353     gbfprintf(psit_file, "%8.2f,",
354               wpt->altitude);
355 
356   const char* ident = global_opts.synthesize_shortnames ?
357                          mkshort(mkshort_handle, "WPT", false) :
358                          xstrdup(wpt->shortname);
359 
360   gbfprintf(psit_file, " %-6s, ", ident);
361   xfree(ident);
362 
363   int icon = gt_find_icon_number_from_desc(wpt->icon_descr, PCX);
364 
365   if (get_cache_icon(wpt) && wpt->icon_descr.compare(QLatin1String("Geocache Found")) != 0) {
366     icon = gt_find_icon_number_from_desc(get_cache_icon(wpt), PCX);
367   }
368 
369   ident = psit_find_desc_from_icon_number(icon);
370   if (strlen(ident) == 0) {
371     gbfprintf(psit_file, "%1d\n", icon);
372   } else {
373     gbfprintf(psit_file, "%s\n", ident);
374   }
375 
376 }
377 
378 static void
psit_waypoint_w_wrapper(const Waypoint * wpt)379 psit_waypoint_w_wrapper(const Waypoint* wpt)
380 {
381   psit_waypoint_w(psit_file_out, wpt);
382 }
383 
384 /*
385  * read in from file a route record
386  * MRCB
387  */
388 static void
psit_route_r(gbfile * psit_file,route_head ** rte)389 psit_route_r(gbfile* psit_file, route_head** rte)
390 {
391   char rtename[256];
392 
393   psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), ltrimEOL);
394 
395   if (strlen(psit_current_token) == 0) {
396     strcpy(rtename, "ROUTE");
397   } else {
398     strcpy(rtename, psit_current_token);
399   }
400 
401   rtrim(rtename);
402 
403   auto* rte_head = new route_head;
404   rte_head->rte_name = rtename;
405   route_add_head(rte_head);
406   *rte = rte_head;
407 
408   psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), wscomma);
409 
410   while (psit_isKnownToken(psit_current_token) != 0) {
411     if (strlen(psit_current_token) > 0) {
412       auto* thisWaypoint = new Waypoint;
413 
414       thisWaypoint->latitude = atof(psit_current_token);
415 
416       psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), comma);
417       thisWaypoint->longitude = atof(psit_current_token);
418 
419       psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), comma);
420       if (psit_current_token[0] == '*') {
421         thisWaypoint->altitude = unknown_alt;
422       } else {
423         thisWaypoint->altitude = atof(psit_current_token);
424       }
425 
426       /* the name */
427       psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), comma);
428       rtrim(psit_current_token);
429       thisWaypoint->shortname = psit_current_token;
430       thisWaypoint->description = "";
431 
432       psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), ltrimEOL);
433       rtrim(psit_current_token);
434       /* since PsiTrex only deals with Garmins, let's use the "proper" Garmin icon name */
435       /* convert the PsiTrex name to the number, which is the PCX one; from there to Garmin desc */
436       int garmin_icon_num = psit_find_icon_number_from_desc(psit_current_token);
437       thisWaypoint->icon_descr = gt_find_desc_from_icon_number(garmin_icon_num, PCX);
438 
439       route_add_wpt(rte_head, thisWaypoint);
440 
441       if (gbfeof(psit_file)) {
442         break;
443       }
444 
445       psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), wscomma);
446     } else {
447       break;
448     }
449   }
450 }
451 
452 /*
453  * write out to file a route header
454  * MRCB
455  */
456 static void
psit_routehdr_w(gbfile * psit_file,const route_head * rte)457 psit_routehdr_w(gbfile* psit_file, const route_head* rte)
458 {
459   QString rname;
460 
461   /* total nodes (waypoints) this route */
462   //if (rte->waypoint_list.next) {
463   if (true) {
464     // this test doesn't do what I w ant i.e test if this is a valid
465     // route - treat as a placeholder for now .
466     time_t uniqueValue = 0;
467     unsigned int rte_datapoints = 0;
468     foreach (const Waypoint* testwpt, rte->waypoint_list) {
469       if (rte_datapoints == 0) {
470         uniqueValue = testwpt->GetCreationTime().toTime_t();
471       }
472       rte_datapoints++;
473     }
474 
475     if (uniqueValue == 0) {
476       uniqueValue = current_time().toTime_t();
477     }
478 
479     /* route name */
480     if (rte->rte_name.isEmpty()) {
481       rname = QString("Route%1").arg((uint) uniqueValue, 4, 16, QChar('0'));
482     } else {
483       rname = rte->rte_name;
484     }
485     /* check for psitrex comment sign; replace with '$' */
486     rname = rname.replace(QChar('#'), QChar('$'));
487 
488     gbfputs(QString("Route:  %1\n").arg(rname), psit_file);
489   }
490 }
491 
492 static void
psit_routehdr_w_wrapper(const route_head * rte)493 psit_routehdr_w_wrapper(const route_head* rte)
494 {
495   psit_routehdr_w(psit_file_out, rte);
496 }
497 
498 
499 /*
500  * read in from file a track record
501  * MRCB
502  */
503 static void
psit_track_r(gbfile * psit_file,route_head **)504 psit_track_r(gbfile* psit_file, route_head**)
505 {
506   char trkname[256];
507 
508   struct tm tmTime;
509 
510   psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), ltrimEOL);
511   if (strlen(psit_current_token) == 0) {
512     strcpy(trkname, "TRACK");
513   } else {
514     strcpy(trkname, psit_current_token);
515   }
516 
517   rtrim(trkname);
518 
519   unsigned int trk_num = 0;
520   route_head* track_head = nullptr;
521 
522   psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), wscomma);
523 
524   while (psit_isKnownToken(psit_current_token) != 0) {
525     if (strlen(psit_current_token) > 0) {
526       auto* thisWaypoint = new Waypoint;
527 
528       thisWaypoint->latitude = atof(psit_current_token);
529 
530       psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), comma);
531       thisWaypoint->longitude = atof(psit_current_token);
532 
533       psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), comma);
534       if (psit_current_token[0] == '*') {
535         thisWaypoint->altitude = unknown_alt;
536       } else {
537         thisWaypoint->altitude = atof(psit_current_token);
538       }
539 
540       /* date portion of the date time DD/MM/YY */
541       psit_getToken(psit_file, psit_current_token,
542                     sizeof(psit_current_token), whitespace);
543       sscanf(psit_current_token, "%02d/%02d/%02d",
544              &(tmTime.tm_mday) , &(tmTime.tm_mon),
545              &(tmTime.tm_year));
546 
547       /* years are less 1900 in the tm struct */
548       tmTime.tm_year += (tmTime.tm_year > 50 ? 0 : 100);
549       /* months are 0 to 11 in the tm struct */
550       tmTime.tm_mon--;
551       /* time portion of the date time hh:mm:ss */
552       psit_getToken(psit_file,psit_current_token,
553                     sizeof(psit_current_token), wscomma);
554       sscanf(psit_current_token, "%02d:%02d:%02d",
555              &(tmTime.tm_hour) , &(tmTime.tm_min),
556              &(tmTime.tm_sec));
557 
558       tmTime.tm_isdst = 0;
559       time_t dateTime = mkgmtime(&tmTime);
560 
561       psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), whitespace);
562 
563       if ((strcmp(psit_current_token, "1") == 0) || (track_head == nullptr)) {
564         track_head = new route_head;
565         /* Add a number to the track name.  With Garmins, the "first"
566          tracklog is usually ACTIVE LOG
567          the second is ACTIVE LOG001 and so on */
568         if (trk_num > 0) {
569           track_head->rte_name = QString::asprintf("%s%03d", trkname, trk_num);
570         } else {
571           track_head->rte_name = trkname;
572         }
573         trk_num++;
574         track_add_head(track_head);
575       }
576 
577       thisWaypoint->SetCreationTime(dateTime);
578       track_add_wpt(track_head, thisWaypoint);
579 
580       if (gbfeof(psit_file)) {
581         break;
582       }
583 
584       psit_getToken(psit_file,psit_current_token,sizeof(psit_current_token), wscomma);
585     } else {
586       break;
587     }
588   }
589 }
590 
591 /*
592  * write out to file a tracklog header
593  * MRCB
594  */
595 static void
psit_trackhdr_w(gbfile * psit_file,const route_head * trk)596 psit_trackhdr_w(gbfile* psit_file, const route_head* trk)
597 {
598   QString tname;
599   time_t uniqueValue = 0;
600 
601   if (psit_track_state == 2) {
602     /* total nodes (waypoints) this track */
603     //if (trk->waypoint_list.next) {	/* this test doesn't do what I want i.e test if this is a valid track - treat as a placeholder for now */
604     if (true) {
605 
606       unsigned int trk_datapoints = 0;
607       foreach (const Waypoint* testwpt, trk->waypoint_list) {
608         if (trk_datapoints == 0) {
609           uniqueValue = testwpt->GetCreationTime().toTime_t();
610         }
611         trk_datapoints++;
612       }
613 
614       if (uniqueValue == 0) {
615         uniqueValue = current_time().toTime_t();
616       }
617 
618       /* track name */
619       if (trk->rte_name.isEmpty()) {
620         tname = QString("Track%1").arg((uint) uniqueValue, 4, 16, QChar('0'));
621       } else {
622         tname = trk->rte_name;
623       }
624 
625       /* check for psitrex comment sign; replace with '$' */
626       tname = tname.replace(QChar('#'), QChar('$'));
627 
628       gbfputs(QString("Track:  %1\n").arg(tname), psit_file);
629     }
630   }
631   psit_track_state = 1;
632 }
633 
634 static void
psit_trackhdr_w_wrapper(const route_head * trk)635 psit_trackhdr_w_wrapper(const route_head* trk)
636 {
637   psit_trackhdr_w(psit_file_out, trk);
638 }
639 
640 
641 /*
642  * write out to file a tracklog datapoint
643  * MRCB
644  */
645 static void
psit_trackdatapoint_w(gbfile * psit_file,const Waypoint * wpt)646 psit_trackdatapoint_w(gbfile* psit_file, const Waypoint* wpt)
647 {
648   time_t	t = wpt->GetCreationTime().toTime_t();
649   struct tm* tmTime = gmtime(&t);
650 
651   gbfprintf(psit_file, "%11.6f,%11.6f,",
652             wpt->latitude,
653             wpt->longitude);
654 
655   if (wpt->altitude == unknown_alt) {
656     gbfprintf(psit_file, "********, ");
657   } else
658     gbfprintf(psit_file, "%8.2f, ",
659               wpt->altitude);
660 
661   /* Following date time format is fixed and reveals the origin of PsiTrex (i.e. the UK) */
662   gbfprintf(psit_file, "%02d/%02d/%02d %02d:%02d:%02d,",
663             tmTime->tm_mday,
664             tmTime->tm_mon+1,
665             tmTime->tm_year % 100,
666             tmTime->tm_hour,
667             tmTime->tm_min,
668             tmTime->tm_sec);
669 
670   gbfprintf(psit_file," %d\n", psit_track_state);
671   psit_track_state = 0;
672 }
673 
674 static void
psit_trackdatapoint_w_wrapper(const Waypoint * wpt)675 psit_trackdatapoint_w_wrapper(const Waypoint* wpt)
676 {
677   psit_trackdatapoint_w(psit_file_out, wpt);
678 }
679 
680 
681 static void
psit_read()682 psit_read()
683 {
684   Waypoint*	wpt;
685   route_head*	rte;
686   route_head*	trk;
687 
688 #ifdef DUMP_ICON_TABLE
689   printf("static icon_mapping_t icon_table[] = {\n");
690 #endif
691 
692   psit_getToken(psit_file_in, psit_current_token, sizeof(psit_current_token), whitespace);
693 
694   do {
695     if (strlen(psit_current_token) == 0) {
696       break;
697     }
698 
699     if (strcmp(psit_current_token, "Track:") == 0) {
700       if (global_opts.objective == trkdata) {
701         psit_track_r(psit_file_in, &trk);
702       } else {
703         break;  /* psitrex files only have one format in; */
704       }
705       /* if this is a track file and we don't want them, them bail out */
706     } else if (strcmp(psit_current_token, "Route:") == 0) {
707       if (global_opts.objective == rtedata) {
708         psit_route_r(psit_file_in, &rte);
709       } else {
710         break;  /* ditto, but for routes */
711       }
712     } else {
713       /* Must be waypoints in this file */
714       if (global_opts.objective == wptdata) {
715         psit_waypoint_r(psit_file_in, &wpt);
716 #ifdef DUMP_ICON_TABLE
717         printf("\t{  %4u, \"%s\" },\n", icon, wpt->shortname);
718 #endif
719 
720       } else {
721         break;
722       }
723     }
724   } while (!gbfeof(psit_file_in));
725 
726 #ifdef DUMP_ICON_TABLE
727   printf("\t{ -1, NULL },\n");
728   printf("};\n");
729 #endif
730 }
731 
732 static void
psit_write()733 psit_write()
734 {
735   int short_length;
736 
737   if (snlen) {
738     short_length = atoi(snlen);
739   } else {
740     short_length = 10;
741   }
742 
743   mkshort_handle = mkshort_new_handle();
744 
745   setshort_length(mkshort_handle, short_length);
746   setshort_whitespace_ok(mkshort_handle, 0);
747 
748   psit_track_state = 2;
749 
750   if (global_opts.objective == wptdata) {
751     waypt_disp_all(psit_waypoint_w_wrapper);
752   }
753   if (global_opts.objective == rtedata) {
754     route_disp_all(psit_routehdr_w_wrapper, nullptr, psit_waypoint_w_wrapper);
755   }
756   if (global_opts.objective == trkdata) {
757     track_disp_all(psit_trackhdr_w_wrapper,
758                    nullptr, psit_trackdatapoint_w_wrapper);
759   }
760 
761   mkshort_del_handle(&mkshort_handle);
762 
763 }
764 
765 ff_vecs_t psit_vecs = {
766   ff_type_file,
767   FF_CAP_RW_ALL,
768   psit_rd_init,
769   psit_wr_init,
770   psit_rd_deinit,
771   psit_wr_deinit,
772   psit_read,
773   psit_write,
774   nullptr,
775   &psit_args,
776   CET_CHARSET_ASCII, 0	/* CET-REVIEW */
777   , NULL_POS_OPS,
778   nullptr
779 };
780