1 /* 2 Copyright (C) 2002 Alex Mottram (geo_alexm at cox-internet.com) 3 Copyright (C) 2002-2014 Robert Lipe 4 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 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 19 */ 20 21 #ifndef XCSV_H_INCLUDED_ 22 #define XCSV_H_INCLUDED_ 23 24 #include <ctime> 25 #include <utility> // for move 26 27 #include <QtCore/QByteArray> // for QByteArray 28 #include <QtCore/QDateTime> // for QDateTime 29 #include <QtCore/QList> // for QList 30 #include <QtCore/QString> // for QString 31 #include <QtCore/QStringList> // for QStringList 32 #include <QtCore/QVector> // for QVector 33 34 #include "defs.h" 35 #include "format.h" 36 #include "garmin_fs.h" 37 #include "src/core/datetime.h" // for DateTime 38 #include "src/core/optional.h" // for optional 39 #include "src/core/textstream.h" // for TextStream 40 41 #if CSVFMTS_ENABLED 42 43 /* 44 * Class describing an xcsv format. 45 */ 46 47 class XcsvStyle 48 { 49 public: 50 /* Types */ 51 52 /* something to map fields to waypts */ 53 struct field_map { 54 // We use QByteArrays because consumers want char* data and QByteArrays supply this through constData(). 55 // If we used QStrings, then we would have to convert to QByteArrays to get the char* data. 56 // If we use char* then we have to manage memory allocation/deallocation. 57 // TODO: when consumers use QStrings then we can store QStrings instead of QByteArrays. 58 QByteArray key; 59 QByteArray val; 60 QByteArray printfc; 61 int hashed_key{0}; 62 unsigned options{0}; 63 64 field_map() = default; field_mapfield_map65 field_map(QByteArray k, QByteArray v, QByteArray p, int hk) : key{std::move(k)},val{std::move(v)},printfc{std::move(p)},hashed_key{hk} {} field_mapfield_map66 field_map(QByteArray k, QByteArray v, QByteArray p, int hk, unsigned o) : key{std::move(k)},val{std::move(v)},printfc{ 67 std::move(p)},hashed_key{hk},options{o} {} 68 }; 69 70 /* Constants */ 71 72 static constexpr unsigned options_nodelim = 1; 73 static constexpr unsigned options_absolute = 2; 74 static constexpr unsigned options_optional = 4; 75 76 /* Member Functions */ 77 78 static QString xcsv_get_char_from_constant_table(const QString& key); 79 static XcsvStyle xcsv_read_internal_style(const char* style_buf); 80 static XcsvStyle xcsv_read_style(const char* fname); 81 82 /* Data Members */ 83 84 /* PROLOGUE from style file */ 85 /* header lines for writing at the top of the file. */ 86 QStringList prologue; 87 88 /* EPILOGUE from style file */ 89 /* footer lines for writing at the bottom of the file. */ 90 QStringList epilogue; 91 92 /* FIELD_DELIMITER from style file */ 93 /* comma, quote, etc... */ 94 QString field_delimiter; 95 96 /* FIELD_ENCLOSER from style file */ 97 /* doublequote, etc... */ 98 QString field_encloser; 99 100 /* RECORD_DELIMITER from style file */ 101 /* newline, c/r, etc... */ 102 QString record_delimiter; 103 104 /* BADCHARS from style file */ 105 /* characters we never write to output */ 106 QString badchars; 107 108 /* IFIELDS from style file */ 109 /* input field mapping */ 110 QList<field_map> ifields; 111 112 /* OFIELDS from style file */ 113 /* output field mapping */ 114 QList<field_map> ofields; 115 116 /* ENCODING from style file */ 117 QString codecname; 118 119 /* DESCRIPTION from style file */ 120 /* for help text */ 121 QString description; 122 123 /* EXTENSION from style file */ 124 /* preferred filename extension (for wrappers)*/ 125 QString extension; 126 127 /* FORMAT_TYPE from style file */ 128 /* format type for GUI wrappers. */ 129 ff_type type{ff_type_file}; 130 131 /* DATUM from style file */ 132 QString gps_datum_name; 133 134 /* DATATYPE from style file */ 135 /* can be wptdata, rtedata or trkdata */ 136 /* ... or ZERO to keep the old behaviour */ 137 gpsdata_type datatype{unknown_gpsdata}; 138 139 /* SHORTLEN from style file */ 140 gpsbabel_optional::optional<int> shortlen; 141 142 /* SHORTWHITE from style file */ 143 gpsbabel_optional::optional<int> whitespace_ok; 144 145 private: 146 /* Types */ 147 148 /* something to map config file constants to chars */ 149 struct char_map_t { 150 const QString key; 151 const QString chars; 152 }; 153 154 /* Member Functions */ 155 156 static QString dequote(const QString& in); 157 static void validate_fieldmap(const field_map& fmp, bool is_output); 158 static void xcsv_ifield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc); 159 static void xcsv_ofield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc, unsigned int options); 160 static void xcsv_parse_style_line(XcsvStyle* style, QString line); 161 static XcsvStyle xcsv_parse_style_buff(const char* sbuff); 162 163 /* Data Members */ 164 165 /* a table of config file constants mapped to chars */ 166 static const char_map_t xcsv_char_table[]; 167 }; 168 169 class XcsvFormat : public Format 170 { 171 public: 172 /* Member Functions */ get_args()173 QVector<arglist_t>* get_args() override 174 { 175 return &xcsv_args; 176 } 177 get_type()178 ff_type get_type() const override 179 { 180 return ff_type_internal; 181 } 182 get_cap()183 QVector<ff_cap> get_cap() const override 184 { 185 return FF_CAP_RW_WPT; /* This is a bit of a lie for now... */ 186 } 187 get_encode()188 QString get_encode() const override 189 { 190 return CET_CHARSET_UTF8; 191 } 192 get_fixed_encode()193 int get_fixed_encode() const override 194 { 195 return 0; 196 } 197 198 void rd_init(const QString& fname) override; 199 void read() override; 200 void rd_deinit() override; 201 void wr_init(const QString& fname) override; 202 void write() override; 203 void wr_deinit() override; 204 void wr_position_init(const QString& fname) override; 205 void wr_position(Waypoint* wpt) override; 206 void wr_position_deinit() override; 207 208 void xcsv_setup_internal_style(const char* style_buf); 209 210 private: 211 /* Types */ 212 213 class XcsvFile 214 { 215 public: 216 /* Special Member Functions */ 217 XcsvFile()218 XcsvFile() : mkshort_handle(mkshort_new_handle()) {} 219 // delete copy and move constructors and assignment operators. 220 // The defaults are not appropriate, and we haven't implemented proper ones. 221 XcsvFile(const XcsvFile&) = delete; 222 XcsvFile& operator=(const XcsvFile&) = delete; 223 XcsvFile(XcsvFile&&) = delete; 224 XcsvFile& operator=(XcsvFile&&) = delete; ~XcsvFile()225 ~XcsvFile() 226 { 227 if (mkshort_handle != nullptr) { 228 mkshort_del_handle(&mkshort_handle); 229 } 230 } 231 232 /* Data Members */ 233 234 gpsbabel::TextStream stream; 235 QString fname; 236 int gps_datum_idx{-1}; /* result of GPS_Lookup_Datum_Index */ 237 short_handle mkshort_handle{nullptr}; 238 }; 239 240 struct xcsv_parse_data { 241 QString rte_name; 242 QString trk_name; 243 bool new_track{false}; 244 double utm_northing{0}; 245 double utm_easting{0}; 246 double utm_zone{0}; 247 char utm_zonec{'N'}; 248 UrlLink* link_{nullptr}; 249 gpsbabel_optional::optional<bool> lat_dir_positive; 250 gpsbabel_optional::optional<bool> lon_dir_positive; 251 }; 252 253 /* Constants */ 254 lat_dir(double a)255 static constexpr char lat_dir(double a) 256 { 257 return a < 0.0 ? 'S' : 'N'; 258 } lon_dir(double a)259 static constexpr char lon_dir(double a) 260 { 261 return a < 0.0 ? 'W' : 'E'; 262 } 263 264 /* convert excel time (days since 1900) to time_t and back again */ excel_to_timet(double a)265 static constexpr double excel_to_timet(double a) 266 { 267 return (a - 25569.0) * 86400.0; 268 } timet_to_excel(double a)269 static constexpr double timet_to_excel(double a) 270 { 271 return (a / 86400.0) + 25569.0; 272 } 273 274 static constexpr int gps_datum_wgs84 = 118; // GPS_Lookup_Datum_Index("WGS 84") 275 276 /* Member Functions */ 277 278 static QDateTime yyyymmdd_to_time(const char* s); 279 static time_t sscanftime(const char* s, const char* format, int gmt); 280 static time_t addhms(const char* s, const char* format); 281 static QString writetime(const char* format, time_t t, bool gmt); 282 static QString writetime(const char* format, const gpsbabel::DateTime& t, bool gmt); 283 static QString writehms(const char* format, time_t t, int gmt); 284 static QString writehms(const char* format, const gpsbabel::DateTime& t, int gmt); 285 static long int time_to_yyyymmdd(const QDateTime& t); 286 static garmin_fs_t* gmsd_init(Waypoint* wpt); 287 static void xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle::field_map& fmp, xcsv_parse_data* parse_data, int line_no); 288 void xcsv_resetpathlen(const route_head* head); 289 void xcsv_waypt_pr(const Waypoint* wpt); 290 QString xcsv_replace_tokens(const QString& original) const; 291 292 /* Data Members */ 293 294 XcsvFile* xcsv_file{nullptr}; 295 const XcsvStyle* xcsv_style{nullptr}; 296 double pathdist = 0; 297 double oldlon = 999; 298 double oldlat = 999; 299 300 int waypt_out_count = 0; 301 const route_head* csv_track = nullptr; 302 const route_head* csv_route = nullptr; 303 304 char* styleopt = nullptr; 305 char* snlenopt = nullptr; 306 char* snwhiteopt = nullptr; 307 char* snupperopt = nullptr; 308 char* snuniqueopt = nullptr; 309 char* prefer_shortnames = nullptr; 310 char* xcsv_urlbase = nullptr; 311 char* opt_datum = nullptr; 312 313 const char* intstylebuf = nullptr; 314 315 QVector<arglist_t> xcsv_args = { 316 { 317 "style", &styleopt, "Full path to XCSV style file", nullptr, 318 ARGTYPE_FILE | ARGTYPE_REQUIRED, ARG_NOMINMAX, nullptr 319 }, 320 { 321 "snlen", &snlenopt, "Max synthesized shortname length", nullptr, 322 ARGTYPE_INT, "1", nullptr, nullptr 323 }, 324 { 325 "snwhite", &snwhiteopt, "Allow whitespace synth. shortnames", 326 nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr 327 }, 328 { 329 "snupper", &snupperopt, "UPPERCASE synth. shortnames", 330 nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr 331 }, 332 { 333 "snunique", &snuniqueopt, "Make synth. shortnames unique", 334 nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr 335 }, 336 { 337 "urlbase", &xcsv_urlbase, "Basename prepended to URL on output", 338 nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr 339 }, 340 { 341 "prefer_shortnames", &prefer_shortnames, 342 "Use shortname instead of description", 343 nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr 344 }, 345 { 346 "datum", &opt_datum, "GPS datum (def. WGS 84)", 347 nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr 348 }, 349 }; 350 351 }; 352 353 #endif // CSVFMTS_ENABLED 354 #endif // XCSV_H_INCLUDED_ 355