1 /*
2     Access Nokia Landmark Exchange files.
3 
4     Copyright (C) 2007  Robert Lipe, robertlipe@gpsbabel.org
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
19 
20  */
21 
22 
23 /*
24  * Nokia's Landmark Exchange (LMX) format is a straight-forward XML
25  * format.  Though they do support a compact binary representation,
26  * we don't implement that at this time in GPSBabel.
27  */
28 
29 #include "defs.h"
30 #include "xmlgeneric.h"
31 
32 static gbfile* ofd;
33 static waypoint* wpt_tmp;
34 char* urllink, *urllinkt;
35 static char* binary = NULL;
36 
37 #define MYNAME "lmx"
38 
39 static
40 arglist_t lmx_args[] = {
41   {
42     "binary", &binary,
43     "Compact binary representation",
44     NULL, ARGTYPE_BOOL, ARG_NOMINMAX
45   },
46   ARG_TERMINATOR
47 };
48 
49 /*
50  * Writer
51  */
52 
53 
54 static void
lmx_wr_init(const char * fname)55 lmx_wr_init(const char* fname)
56 {
57   ofd = gbfopen(fname, "w", MYNAME);
58 }
59 
60 static void
lmx_wr_deinit(void)61 lmx_wr_deinit(void)
62 {
63   gbfclose(ofd);
64 }
65 
66 static char*
lmx_stag(int tag)67 lmx_stag(int tag)
68 {
69   switch (tag) {
70   case 0xC5:
71     return "lmx";
72   case 0x46:
73     return "landmarkCollection";
74   case 0x47:
75     return "landmark";
76   case 0x48:
77     return "name";
78   case 0x49:
79     return "description";
80   case 0x4A:
81     return "coordinates";
82   case 0x4B:
83     return "latitude";
84   case 0x4C:
85     return "longitude";
86   case 0x4D:
87     return "altitude";
88   case 0x4E:
89     return "horizontalAccuracy";
90   case 0x4F:
91     return "verticalAccuracy";
92   case 0x50:
93     return "timeStamp";
94   case 0x51:
95     return "coverageRadius";
96   case 0x52:
97     return "category";
98   case 0x53:
99     return "id";
100   case 0x54:
101     return "addressInfo";
102   case 0x55:
103     return "country";
104   case 0x56:
105     return "countryCode";
106   case 0x57:
107     return "state";
108   case 0x58:
109     return "county";
110   case 0x59:
111     return "city";
112   case 0x5A:
113     return "district";
114   case 0x5B:
115     return "postalCode";
116   case 0x5C:
117     return "crossing1";
118   case 0x5D:
119     return "crossing2";
120   case 0x5E:
121     return "street";
122   case 0x5F:
123     return "buildingName";
124   case 0x60:
125     return "buildingFloor";
126   case 0x61:
127     return "buildingZone";
128   case 0x62:
129     return "buildingRoom";
130   case 0x63:
131     return "extension";
132   case 0x64:
133     return "phoneNumber";
134   case 0x65:
135     return "mediaLink";
136   case 0x66:
137     return "mime";
138   case 0x67:
139     return "url";
140   default:
141     return 0;
142   }
143 }
144 
145 static void
lmx_indent(int count)146 lmx_indent(int count)
147 {
148   int i;
149   for (i=0; i<count; i++) {
150     gbfputc('\t', ofd);
151   }
152 }
153 
154 static void
lmx_start_tag(int tag,int indent)155 lmx_start_tag(int tag, int indent)
156 {
157   if (binary) {
158     gbfputc(tag, ofd);
159   } else {
160     lmx_indent(indent);
161     gbfprintf(ofd, "<lm:%s>", lmx_stag(tag));
162   }
163 }
164 
165 static void
lmx_end_tag(int tag,int indent)166 lmx_end_tag(int tag, int indent)
167 {
168   if (binary) {
169     gbfputc(0x01, ofd);
170   } else {
171     lmx_indent(indent);
172     gbfprintf(ofd, "</lm:%s>\n", lmx_stag(tag));
173   }
174 }
175 
176 static void
lmx_write_xml(int tag,const char * data,int indent)177 lmx_write_xml(int tag, const char* data, int indent)
178 {
179   lmx_start_tag(tag, indent);
180 
181   if (binary) {
182     gbfputc(0x03, ofd); // inline string follows
183     gbfputcstr(data, ofd);
184   } else {
185     char* tmp_ent = xml_entitize(data);
186     gbfputs(tmp_ent, ofd);
187     xfree(tmp_ent);
188   }
189 
190   lmx_end_tag(tag, 0);
191 }
192 
193 static void
lmx_print(const waypoint * wpt)194 lmx_print(const waypoint* wpt)
195 {
196   const char* oname;
197   char* odesc;
198   char tbuf[100];
199 
200   /*
201    * Desparation time, try very hard to get a good shortname
202    */
203   odesc = wpt->notes;
204   if (!odesc) {
205     odesc = wpt->description;
206   }
207   if (!odesc) {
208     odesc = wpt->shortname;
209   }
210 
211   oname = global_opts.synthesize_shortnames ? odesc : wpt->shortname;
212 
213   lmx_start_tag(0x47, 2); // landmark
214   if (!binary) {
215     gbfputc('\n', ofd);
216   }
217   if (oname) {
218     lmx_write_xml(0x48, oname, 3); // name
219   }
220   if (wpt->description) {
221     lmx_write_xml(0x49, wpt->description, 3); // description
222   }
223   lmx_start_tag(0x4A, 3); // coordinates
224   if (!binary) {
225     gbfputc('\n', ofd);
226   }
227 
228   sprintf(tbuf, "%f", wpt->latitude);
229   lmx_write_xml(0x4B, tbuf, 4); // latitude
230 
231   sprintf(tbuf, "%f", wpt->longitude);
232   lmx_write_xml(0x4C, tbuf, 4); // longitude
233 
234   if (wpt->altitude && (wpt->altitude != unknown_alt)) {
235     sprintf(tbuf, "%f", wpt->altitude);
236     lmx_write_xml(0x4D, tbuf, 4); // altitude
237   }
238   lmx_end_tag(0x4A, 3); // coordinates
239 
240   if (wpt->url && wpt->url[0]) {
241     lmx_start_tag(0x65, 3); // mediaLink
242     if (!binary) {
243       gbfputc('\n', ofd);
244     }
245     if (wpt->url_link_text) {
246       lmx_write_xml(0x48, wpt->url_link_text, 4);  // name
247     }
248     lmx_write_xml(0x67, wpt->url, 4); // url
249     lmx_end_tag(0x65, 3); // mediaLink
250   }
251 
252   lmx_end_tag(0x47, 2); // landmark
253 }
254 
255 
256 static void
lmx_write(void)257 lmx_write(void)
258 {
259   if (binary) {
260     gbfputc(0x03, ofd); // WBXML version 1.3
261     gbfputuint16(0x04A4, ofd); // "-//NOKIA//DTD LANDMARKS 1.0//EN"
262     gbfputc(106, ofd); // Charset=UTF-8
263     gbfputc(0x00, ofd); // empty string table
264     gbfputc(0xC5, ofd); // lmx
265     gbfputc(0x05, ofd); // xmlns=http://www.nokia.com/schemas/location/landmarks/
266     gbfputc(0x85, ofd); // 1/0/
267     gbfputc(0x06, ofd); // xmlns:xsi=
268     gbfputc(0x86, ofd); // http://www.w3.org/2001/XMLSchema-instance
269     gbfputc(0x07, ofd); // xsi:schemaLocation=http://www.nokia.com/schemas/location/landmarks/
270     gbfputc(0x85, ofd); // 1/0/
271     gbfputc(0x87, ofd); // whitespace
272     gbfputc(0x88, ofd); // lmx.xsd
273     gbfputc(0x01, ofd); // END lmx attributes
274   } else {
275     gbfprintf(ofd, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
276     gbfprintf(ofd, "<lm:lmx xmlns:lm=\"http://www.nokia.com/schemas/location/landmarks/1/0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.nokia.com/schemas/location/landmarks/1/0/ lmx.xsd\">\n");
277   }
278 
279   lmx_start_tag(0x46, 1); // landmarkCollection
280   if (!binary) {
281     gbfputc('\n', ofd);
282   }
283   waypt_disp_all(lmx_print);
284   lmx_end_tag(0x46, 1); // landmarkCollection
285   lmx_end_tag(0xC5, 0); // lmx
286 }
287 
288 /*
289  * Reader
290  */
291 
292 static xg_callback	lmx_lm_start, lmx_lm_end;
293 static xg_callback	lmx_lm_name,lmx_lm_desc;
294 static xg_callback	lmx_lm_lat, lmx_lm_lon, lmx_lm_alt;
295 static xg_callback	lmx_lm_mlink_s, lmx_lm_mlink_e;
296 static xg_callback	lmx_lm_link, lmx_lm_linkt;
297 
298 static xg_tag_mapping gl_map[] = {
299 #define LM "/lm:lmx/lm:landmarkCollection/lm:landmark"
300   { lmx_lm_start, 	cb_start, 	LM },
301   { lmx_lm_end,   	cb_end, 	LM },
302   { lmx_lm_name,	 	cb_cdata, 	LM "/lm:name" },
303   { lmx_lm_desc,  	cb_cdata, 	LM "/lm:description" },
304   { lmx_lm_lat,   	cb_cdata, 	LM "/lm:coordinates/lm:latitude" },
305   { lmx_lm_lon, 		cb_cdata, 	LM "/lm:coordinates/lm:longitude" },
306   { lmx_lm_alt,		cb_cdata, 	LM "/lm:coordinates/lm:altitude" },
307   { lmx_lm_mlink_s,  	cb_start, 	LM "/lm:mediaLink" },
308   { lmx_lm_link,  	cb_cdata, 	LM "/lm:mediaLink/lm:url" },
309   { lmx_lm_linkt, 	cb_cdata, 	LM "/lm:mediaLink/lm:name" },
310   { lmx_lm_mlink_e,	cb_end, 	LM "/lm:mediaLink" },
311   { NULL,	(xg_cb_type)0,         NULL}
312 };
313 
314 static void
lmx_rd_init(const char * fname)315 lmx_rd_init(const char* fname)
316 {
317   xml_init(fname, gl_map, NULL);
318 }
319 
320 static void
lmx_read(void)321 lmx_read(void)
322 {
323   xml_read();
324 }
325 
326 static void
lmx_rd_deinit(void)327 lmx_rd_deinit(void)
328 {
329   xml_deinit();
330 }
331 
332 
333 
334 static void
lmx_lm_start(const char * args,const char ** unused)335 lmx_lm_start(const char* args, const char** unused)
336 {
337   wpt_tmp = waypt_new();
338 }
339 
340 static void
lmx_lm_end(const char * args,const char ** unused)341 lmx_lm_end(const char* args, const char** unused)
342 {
343   waypt_add(wpt_tmp);
344 }
345 
346 static void
lmx_lm_lat(const char * args,const char ** unused)347 lmx_lm_lat(const char* args, const char** unused)
348 {
349   wpt_tmp->latitude = atof(args);
350 }
351 
352 static void
lmx_lm_lon(const char * args,const char ** unused)353 lmx_lm_lon(const char* args, const char** unused)
354 {
355   wpt_tmp->longitude = atof(args);
356 }
357 
358 static void
lmx_lm_alt(const char * args,const char ** unused)359 lmx_lm_alt(const char* args, const char** unused)
360 {
361   wpt_tmp->altitude = atof(args);
362 }
363 
364 static void
lmx_lm_name(const char * args,const char ** unused)365 lmx_lm_name(const char* args, const char** unused)
366 {
367   wpt_tmp->shortname = xstrdup(args);
368 }
369 
370 static void
lmx_lm_desc(const char * args,const char ** unused)371 lmx_lm_desc(const char* args, const char** unused)
372 {
373   wpt_tmp->description = xstrdup(args);
374 }
375 
376 static void
lmx_lm_mlink_s(const char * args,const char ** unused)377 lmx_lm_mlink_s(const char* args, const char** unused)
378 {
379   urllink = urllinkt = NULL;
380 }
381 
382 static void
lmx_lm_link(const char * args,const char ** unused)383 lmx_lm_link(const char* args, const char** unused)
384 {
385   urllink = xstrdup(args);
386 }
387 
388 static void
lmx_lm_linkt(const char * args,const char ** unused)389 lmx_lm_linkt(const char* args, const char** unused)
390 {
391   urllinkt = xstrdup(args);
392 }
393 
394 static void
lmx_lm_mlink_e(const char * args,const char ** unused)395 lmx_lm_mlink_e(const char* args, const char** unused)
396 {
397   waypt_add_url(wpt_tmp, urllink, urllinkt);
398 }
399 
400 
401 ff_vecs_t lmx_vecs = {
402   ff_type_file,
403   {
404     (ff_cap)(ff_cap_read | ff_cap_write),	/* waypoints */
405     ff_cap_none,			/* tracks */
406     ff_cap_none			/* routes */
407   },
408   lmx_rd_init,
409   lmx_wr_init,
410   lmx_rd_deinit,
411   lmx_wr_deinit,
412   lmx_read,
413   lmx_write,
414   NULL,
415   lmx_args,
416   CET_CHARSET_UTF8, 0	/* CET-REVIEW */
417 };
418