]> git.street.me.uk Git - andy/viking.git/blame - src/babel.c
[QA] Rename and correct some map cache variables usage for better understanding.
[andy/viking.git] / src / babel.c
CommitLineData
1d1bc3c1
EB
1/*
2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3 *
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
a482007a 5 * Copyright (C) 2006, Quy Tonthat <qtonthat@gmail.com>
7f95fd54 6 * Copyright (C) 2013, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
1d1bc3c1
EB
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
072cbb13
GB
24/**
25 * SECTION:babel
26 * @short_description: running external programs and redirecting to TRWLayers.
27 *
28c82d8b 28 * GPSBabel may not be necessary for everything -- for instance,
072cbb13 29 * use a_babel_convert_from_shellcommand() with input_file_type == %NULL
28c82d8b
EB
30 * for an external program that outputs GPX.
31 */
32
45acf79e
MA
33#ifdef HAVE_CONFIG_H
34#include "config.h"
35#endif
36
1d1bc3c1
EB
37#include "viking.h"
38#include "gpx.h"
39#include "babel.h"
8c060406 40#include <stdio.h>
45acf79e
MA
41#ifdef HAVE_UNISTD_H
42#include <unistd.h>
43#endif
79e0f36e 44#include <string.h>
45acf79e
MA
45#include <glib.h>
46#include <glib/gstdio.h>
1d1bc3c1 47
072cbb13 48/* TODO in the future we could have support for other shells (change command strings), or not use a shell at all */
8cf048bd
EB
49#define BASH_LOCATION "/bin/bash"
50
7f95fd54
GB
51/**
52 * List of supported protocols.
53 */
54const gchar *PROTOS[] = { "http://", "https://", "ftp://", NULL };
55
18ec873d
GB
56/**
57 * Path to gpsbabel
58 */
59static gchar *gpsbabel_loc = NULL;
60
61/**
62 * Path to unbuffer
63 */
64static gchar *unbuffer_loc = NULL;
65
79e0f36e
GB
66/**
67 * List of file formats supported by gpsbabel.
68 */
69GList *a_babel_file_list;
70
71/**
72 * List of device supported by gpsbabel.
73 */
74GList *a_babel_device_list;
75
b666a8ba
GB
76/**
77 * Run a function on all file formats supporting a given mode.
78 */
79void a_babel_foreach_file_with_mode (BabelMode mode, GFunc func, gpointer user_data)
80{
81 GList *current;
82 for ( current = g_list_first (a_babel_file_list) ;
83 current != NULL ;
84 current = g_list_next (current) )
85 {
86 BabelFile *currentFile = current->data;
87 /* Check compatibility of modes */
88 gboolean compat = TRUE;
89 if (mode.waypointsRead && ! currentFile->mode.waypointsRead) compat = FALSE;
90 if (mode.waypointsWrite && ! currentFile->mode.waypointsWrite) compat = FALSE;
91 if (mode.tracksRead && ! currentFile->mode.tracksRead) compat = FALSE;
92 if (mode.tracksWrite && ! currentFile->mode.tracksWrite) compat = FALSE;
93 if (mode.routesRead && ! currentFile->mode.routesRead) compat = FALSE;
94 if (mode.routesWrite && ! currentFile->mode.routesWrite) compat = FALSE;
95 /* Do call */
96 if (compat)
97 func (currentFile, user_data);
98 }
99}
100
2f5f0fb8
RN
101/**
102 * a_babel_foreach_file_read_any:
103 * @func: The function to be called on any file format with a read method
104 * @user_data: Data passed into the function
105 *
106 * Run a function on all file formats with any kind of read method
107 * (which is almost all but not quite - e.g. with GPSBabel v1.4.4 - PalmDoc is write only waypoints)
108 */
109void a_babel_foreach_file_read_any (GFunc func, gpointer user_data)
110{
111 GList *current;
112 for ( current = g_list_first (a_babel_file_list) ;
113 current != NULL ;
114 current = g_list_next (current) )
115 {
116 BabelFile *currentFile = current->data;
117 // Call function when any read mode found
118 if ( currentFile->mode.waypointsRead ||
119 currentFile->mode.tracksRead ||
120 currentFile->mode.routesRead)
121 func (currentFile, user_data);
122 }
123}
124
072cbb13
GB
125/**
126 * a_babel_convert:
127 * @vt: The TRW layer to modify. All data will be deleted, and replaced by what gpsbabel outputs.
128 * @babelargs: A string containing gpsbabel command line filter options. No file types or names should
129 * be specified.
130 * @cb: A callback function.
ed691ed1
RN
131 * @user_data: passed along to cb
132 * @not_used: Must use NULL
072cbb13
GB
133 *
134 * This function modifies data in a trw layer using gpsbabel filters. This routine is synchronous;
135 * that is, it will block the calling program until the conversion is done. To avoid blocking, call
136 * this routine from a worker thread.
137 *
138 * Returns: %TRUE on success
139 */
ed691ed1 140gboolean a_babel_convert( VikTrwLayer *vt, const char *babelargs, BabelStatusFunc cb, gpointer user_data, gpointer not_used )
1d1bc3c1 141{
1d1bc3c1 142 gboolean ret = FALSE;
3adc68b0 143 gchar *bargs = g_strconcat(babelargs, " -i gpx", NULL);
12ed2b58 144 gchar *name_src = a_gpx_write_tmp_file ( vt, NULL );
1d1bc3c1 145
12ed2b58 146 if ( name_src ) {
ed691ed1 147 ret = a_babel_convert_from ( vt, bargs, name_src, cb, user_data, not_used );
18ec873d
GB
148 g_remove(name_src);
149 g_free(name_src);
1d1bc3c1
EB
150 }
151
152 g_free(bargs);
1d1bc3c1
EB
153 return ret;
154}
155
c3ac9df8
RN
156/**
157 * Perform any cleanup actions once GPSBabel has completed running
158 */
159static void babel_watch ( GPid pid,
160 gint status,
161 gpointer user_data )
70548902 162{
c3ac9df8 163 g_spawn_close_pid ( pid );
70548902 164}
c3ac9df8
RN
165
166/**
167 * babel_general_convert:
168 * @args: The command line arguments passed to GPSBabel
169 * @cb: callback that is run for each line of GPSBabel output and at completion of the run
170 * callback may be NULL
171 * @user_data: passed along to cb
172 *
173 * The function to actually invoke the GPSBabel external command
174 *
175 * Returns: %TRUE on successful invocation of GPSBabel command
176 */
883e5ed6 177static gboolean babel_general_convert( BabelStatusFunc cb, gchar **args, gpointer user_data )
8cf048bd 178{
2ecf5998 179 gboolean ret = FALSE;
8cf048bd 180 GPid pid;
6c641b1a
MA
181 GError *error = NULL;
182 gint babel_stdout;
8cf048bd 183
c3ac9df8 184 if (!g_spawn_async_with_pipes (NULL, args, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL, &babel_stdout, NULL, &error)) {
f2131761 185 g_warning ("Async command failed: %s", error->message);
6c641b1a 186 g_error_free(error);
8cf048bd
EB
187 ret = FALSE;
188 } else {
2b756ea0 189
8cf048bd
EB
190 gchar line[512];
191 FILE *diag;
192 diag = fdopen(babel_stdout, "r");
193 setvbuf(diag, NULL, _IONBF, 0);
194
195 while (fgets(line, sizeof(line), diag)) {
7b3479e3
EB
196 if ( cb )
197 cb(BABEL_DIAG_OUTPUT, line, user_data);
8cf048bd 198 }
7b3479e3
EB
199 if ( cb )
200 cb(BABEL_DONE, NULL, user_data);
8cf048bd 201 fclose(diag);
8c060406 202 diag = NULL;
c3ac9df8
RN
203
204 g_child_watch_add ( pid, (GChildWatchFunc) babel_watch, NULL );
8cf048bd 205
2634ad33
GB
206 ret = TRUE;
207 }
208
209 return ret;
210}
2634ad33
GB
211
212/**
213 * babel_general_convert_from:
733949a2
RN
214 * @vtl: The TrackWaypoint Layer to save the data into
215 * If it is null it signifies that no data is to be processed,
216 * however the gpsbabel command is still ran as it can be for non-data related options eg:
217 * for use with the power off command - 'command_off'
2634ad33
GB
218 * @cb: callback that is run upon new data from STDOUT (?)
219 * (TODO: STDERR would be nice since we usually redirect STDOUT)
220 * @user_data: passed along to cb
221 *
222 * Runs args[0] with the arguments and uses the GPX module
223 * to import the GPX data into layer vt. Assumes that upon
224 * running the command, the data will appear in the (usually
225 * temporary) file name_dst.
226 *
227 * Returns: %TRUE on success
228 */
229static gboolean babel_general_convert_from( VikTrwLayer *vt, BabelStatusFunc cb, gchar **args, const gchar *name_dst, gpointer user_data )
230{
231 gboolean ret = FALSE;
232 FILE *f = NULL;
233
883e5ed6 234 if (babel_general_convert(cb, args, user_data)) {
2634ad33 235
733949a2
RN
236 /* No data actually required but still need to have run gpsbabel anyway
237 - eg using the device power command_off */
238 if ( vt == NULL )
239 return TRUE;
240
8c060406
MA
241 f = g_fopen(name_dst, "r");
242 if (f) {
efe9a9c3 243 ret = a_gpx_read_file ( vt, f );
8c060406
MA
244 fclose(f);
245 f = NULL;
2ecf5998 246 }
8cf048bd
EB
247 }
248
249 return ret;
250}
251
072cbb13
GB
252/**
253 * a_babel_convert_from:
254 * @vt: The TRW layer to place data into. Duplicate items will be overwritten.
255 * @babelargs: A string containing gpsbabel command line options. In addition to any filters, this string
256 * must include the input file type (-i) option.
257 * @cb: Optional callback function. Same usage as in a_babel_convert().
ce4f6212 258 * @user_data: passed along to cb
ed691ed1 259 * @not_used: Must use NULL
072cbb13
GB
260 *
261 * Loads data into a trw layer from a file, using gpsbabel. This routine is synchronous;
262 * that is, it will block the calling program until the conversion is done. To avoid blocking, call
263 * this routine from a worker thread.
264 *
265 * Returns: %TRUE on success
266 */
ed691ed1 267gboolean a_babel_convert_from( VikTrwLayer *vt, const char *babelargs, const char *from, BabelStatusFunc cb, gpointer user_data, gpointer not_used )
1d1bc3c1 268{
f3cd9987 269 int i,j;
1d1bc3c1 270 int fd_dst;
18ec873d 271 gchar *name_dst = NULL;
1d1bc3c1 272 gboolean ret = FALSE;
f3cd9987 273 gchar *args[64];
1d1bc3c1 274
2ecf5998 275 if ((fd_dst = g_file_open_tmp("tmp-viking.XXXXXX", &name_dst, NULL)) >= 0) {
e45b3da1 276 g_debug ("%s: temporary file: %s", __FUNCTION__, name_dst);
1d1bc3c1
EB
277 close(fd_dst);
278
92255687 279 if (gpsbabel_loc ) {
f3cd9987
QT
280 gchar **sub_args = g_strsplit(babelargs, " ", 0);
281
282 i = 0;
283 if (unbuffer_loc)
284 args[i++] = unbuffer_loc;
285 args[i++] = gpsbabel_loc;
e208e3c0
QT
286 for (j = 0; sub_args[j]; j++) {
287 /* some version of gpsbabel can not take extra blank arg */
288 if (sub_args[j][0] != '\0')
289 args[i++] = sub_args[j];
290 }
f3cd9987
QT
291 args[i++] = "-o";
292 args[i++] = "gpx";
e208e3c0 293 args[i++] = "-f";
8398d878 294 args[i++] = (char *)from;
e208e3c0 295 args[i++] = "-F";
f3cd9987
QT
296 args[i++] = name_dst;
297 args[i] = NULL;
298
299 ret = babel_general_convert_from ( vt, cb, args, name_dst, user_data );
300
f3cd9987 301 g_strfreev(sub_args);
374b7f45 302 } else
24ff9c7b 303 g_critical("gpsbabel not found in PATH");
18ec873d
GB
304 g_remove(name_dst);
305 g_free(name_dst);
1d1bc3c1
EB
306 }
307
1d1bc3c1
EB
308 return ret;
309}
8cf048bd 310
072cbb13
GB
311/**
312 * a_babel_convert_from_shellcommand:
ce4f6212
GB
313 * @vt: The #VikTrwLayer where to insert the collected data
314 * @input_cmd: the command to run
315 * @cb: Optional callback function. Same usage as in a_babel_convert().
316 * @user_data: passed along to cb
ed691ed1 317 * @not_used: Must use NULL
072cbb13
GB
318 *
319 * Runs the input command in a shell (bash) and optionally uses GPSBabel to convert from input_file_type.
320 * If input_file_type is %NULL, doesn't use GPSBabel. Input must be GPX (or Geocaching *.loc)
28c82d8b 321 *
072cbb13 322 * Uses babel_general_convert_from() to actually run the command. This function
28c82d8b
EB
323 * prepares the command and temporary file, and sets up the arguments for bash.
324 */
ed691ed1 325gboolean a_babel_convert_from_shellcommand ( VikTrwLayer *vt, const char *input_cmd, const char *input_file_type, BabelStatusFunc cb, gpointer user_data, gpointer not_used )
8cf048bd
EB
326{
327 int fd_dst;
18ec873d 328 gchar *name_dst = NULL;
8cf048bd
EB
329 gboolean ret = FALSE;
330 gchar **args;
331
2ecf5998 332 if ((fd_dst = g_file_open_tmp("tmp-viking.XXXXXX", &name_dst, NULL)) >= 0) {
e45b3da1 333 g_debug ("%s: temporary file: %s", __FUNCTION__, name_dst);
28c82d8b
EB
334 gchar *shell_command;
335 if ( input_file_type )
18ec873d
GB
336 shell_command = g_strdup_printf("%s | %s -i %s -f - -o gpx -F %s",
337 input_cmd, gpsbabel_loc, input_file_type, name_dst);
28c82d8b
EB
338 else
339 shell_command = g_strdup_printf("%s > %s", input_cmd, name_dst);
340
29a5a545 341 g_debug("%s: %s", __FUNCTION__, shell_command);
8cf048bd
EB
342 close(fd_dst);
343
344 args = g_malloc(sizeof(gchar *)*4);
345 args[0] = BASH_LOCATION;
346 args[1] = "-c";
347 args[2] = shell_command;
348 args[3] = NULL;
349
7b3479e3 350 ret = babel_general_convert_from ( vt, cb, args, name_dst, user_data );
8cf048bd
EB
351 g_free ( args );
352 g_free ( shell_command );
18ec873d
GB
353 g_remove(name_dst);
354 g_free(name_dst);
8cf048bd
EB
355 }
356
8cf048bd
EB
357 return ret;
358}
359
ce4f6212
GB
360/**
361 * a_babel_convert_from_url:
362 * @vt: The #VikTrwLayer where to insert the collected data
363 * @url: the URL to fetch
364 * @cb: Optional callback function. Same usage as in a_babel_convert().
365 * @user_data: passed along to cb
ed691ed1 366 * @options: download options. Maybe NULL.
ce4f6212 367 *
cc2e600e
GB
368 * Download the file pointed by the URL and optionally uses GPSBabel to convert from input_file_type.
369 * If input_file_type is %NULL, doesn't use GPSBabel. Input must be GPX.
ed691ed1
RN
370 *
371 * Returns: %TRUE on successful invocation of GPSBabel or read of the GPX
372 *
ce4f6212 373 */
ed691ed1 374gboolean a_babel_convert_from_url ( VikTrwLayer *vt, const char *url, const char *input_type, BabelStatusFunc cb, gpointer user_data, DownloadMapOptions *options )
533bbf34 375{
ed691ed1 376 // If no download options specified, use defaults:
a3697549 377 DownloadMapOptions myoptions = { FALSE, FALSE, NULL, 2, NULL, NULL, NULL };
ed691ed1
RN
378 if ( options )
379 myoptions = *options;
533bbf34 380 gint fd_src;
8bd9fe17
GB
381 int fetch_ret;
382 gboolean ret = FALSE;
18ec873d
GB
383 gchar *name_src = NULL;
384 gchar *babelargs = NULL;
533bbf34
MA
385
386 g_debug("%s: input_type=%s url=%s", __FUNCTION__, input_type, url);
387
8bd9fe17 388 if ((fd_src = g_file_open_tmp("tmp-viking.XXXXXX", &name_src, NULL)) >= 0) {
e45b3da1 389 g_debug ("%s: temporary file: %s", __FUNCTION__, name_src);
533bbf34
MA
390 close(fd_src);
391 g_remove(name_src);
392
ed691ed1 393 fetch_ret = a_http_download_get_url(url, "", name_src, &myoptions, NULL);
4e815e90 394 if (fetch_ret == DOWNLOAD_SUCCESS) {
cc2e600e
GB
395 if (input_type != NULL) {
396 babelargs = g_strdup_printf(" -i %s", input_type);
ed691ed1 397 ret = a_babel_convert_from( vt, babelargs, name_src, NULL, NULL, NULL );
cc2e600e
GB
398 } else {
399 /* Process directly the retrieved file */
400 g_debug("%s: directly read GPX file %s", __FUNCTION__, name_src);
401 FILE *f = g_fopen(name_src, "r");
402 if (f) {
403 ret = a_gpx_read_file ( vt, f );
404 fclose(f);
405 f = NULL;
406 }
407 }
408 }
533bbf34
MA
409 g_remove(name_src);
410 g_free(babelargs);
411 g_free(name_src);
412 }
413
414 return ret;
415}
416
7f95fd54
GB
417/**
418 * a_babel_convert_from_url_or_shell:
419 * @vt: The #VikTrwLayer where to insert the collected data
420 * @url: the URL to fetch
421 * @cb: Optional callback function. Same usage as in a_babel_convert().
422 * @user_data: passed along to cb
423 * @options: download options. Maybe NULL.
424 *
425 * Download the file pointed by the URL and optionally uses GPSBabel to convert from input_file_type.
426 * If input_file_type is %NULL, doesn't use GPSBabel. Input must be GPX.
427 *
428 * Returns: %TRUE on successful invocation of GPSBabel or read of the GPX
429 *
430 */
431gboolean a_babel_convert_from_url_or_shell ( VikTrwLayer *vt, const char *input, const char *input_type, BabelStatusFunc cb, gpointer user_data, DownloadMapOptions *options )
432{
433
434 /* Check nature of input */
435 gboolean isUrl = FALSE;
436 int i = 0;
437 for (i = 0 ; PROTOS[i] != NULL ; i++)
438 {
439 const gchar *proto = PROTOS[i];
440 if (strncmp (input, proto, strlen(proto)) == 0)
441 {
442 /* Procotol matches: save result */
443 isUrl = TRUE;
444 }
445 }
446
447 /* Do the job */
448 if (isUrl)
449 return a_babel_convert_from_url (vt, input, input_type, cb, user_data, options);
450 else
451 return a_babel_convert_from_shellcommand (vt, input, input_type, cb, user_data, options);
452}
453
6f94db6d 454static gboolean babel_general_convert_to( VikTrwLayer *vt, VikTrack *trk, BabelStatusFunc cb, gchar **args, const gchar *name_src, gpointer user_data )
70548902 455{
208d2084
RN
456 // Now strips out invisible tracks and waypoints
457 if (!a_file_export(vt, name_src, FILE_TYPE_GPX, trk, FALSE)) {
24ff9c7b 458 g_critical("Error exporting to %s", name_src);
18ec873d 459 return FALSE;
70548902
MA
460 }
461
883e5ed6 462 return babel_general_convert (cb, args, user_data);
b364d6bc
QT
463}
464
6f94db6d
RN
465/**
466 * a_babel_convert_to:
ce4f6212
GB
467 * @vt: The TRW layer from which data is taken.
468 * @track: Operate on the individual track if specified. Use NULL when operating on a TRW layer
469 * @babelargs: A string containing gpsbabel command line options. In addition to any filters, this string
6f94db6d 470 * must include the input file type (-i) option.
ce4f6212
GB
471 * @to: Filename or device the data is written to.
472 * @cb: Optional callback function. Same usage as in a_babel_convert.
473 * @user_data: passed along to cb
6f94db6d
RN
474 *
475 * Exports data using gpsbabel. This routine is synchronous;
476 * that is, it will block the calling program until the conversion is done. To avoid blocking, call
477 * this routine from a worker thread.
478 *
479 * Returns: %TRUE on successful invocation of GPSBabel command
480 */
481gboolean a_babel_convert_to( VikTrwLayer *vt, VikTrack *track, const char *babelargs, const char *to, BabelStatusFunc cb, gpointer user_data )
b364d6bc 482{
f3cd9987 483 int i,j;
b364d6bc 484 int fd_src;
18ec873d 485 gchar *name_src = NULL;
b364d6bc 486 gboolean ret = FALSE;
f3cd9987 487 gchar *args[64];
b364d6bc 488
2ecf5998 489 if ((fd_src = g_file_open_tmp("tmp-viking.XXXXXX", &name_src, NULL)) >= 0) {
e45b3da1 490 g_debug ("%s: temporary file: %s", __FUNCTION__, name_src);
b364d6bc
QT
491 close(fd_src);
492
b364d6bc 493 if (gpsbabel_loc ) {
f3cd9987
QT
494 gchar **sub_args = g_strsplit(babelargs, " ", 0);
495
496 i = 0;
497 if (unbuffer_loc)
498 args[i++] = unbuffer_loc;
499 args[i++] = gpsbabel_loc;
500 args[i++] = "-i";
501 args[i++] = "gpx";
502 for (j = 0; sub_args[j]; j++)
e208e3c0
QT
503 /* some version of gpsbabel can not take extra blank arg */
504 if (sub_args[j][0] != '\0')
505 args[i++] = sub_args[j];
506 args[i++] = "-f";
f3cd9987 507 args[i++] = name_src;
e208e3c0 508 args[i++] = "-F";
8398d878 509 args[i++] = (char *)to;
f3cd9987
QT
510 args[i] = NULL;
511
6f94db6d 512 ret = babel_general_convert_to ( vt, track, cb, args, name_src, user_data );
f3cd9987 513
f3cd9987 514 g_strfreev(sub_args);
374b7f45 515 } else
24ff9c7b 516 g_critical("gpsbabel not found in PATH");
18ec873d
GB
517 g_remove(name_src);
518 g_free(name_src);
b364d6bc
QT
519 }
520
b364d6bc
QT
521 return ret;
522}
18ec873d 523
6f2551f1 524static void set_mode(BabelMode *mode, gchar *smode)
79e0f36e 525{
6f2551f1
GB
526 mode->waypointsRead = smode[0] == 'r';
527 mode->waypointsWrite = smode[1] == 'w';
528 mode->tracksRead = smode[2] == 'r';
529 mode->tracksWrite = smode[3] == 'w';
530 mode->routesRead = smode[4] == 'r';
531 mode->routesWrite = smode[5] == 'w';
79e0f36e
GB
532}
533
534/**
ce4f6212 535 * load_feature_parse_line:
79e0f36e
GB
536 *
537 * Load a single feature stored in the given line.
538 */
539static void load_feature_parse_line (gchar *line)
540{
541 gchar **tokens = g_strsplit ( line, "\t", 0 );
542 if ( tokens != NULL
543 && tokens[0] != NULL ) {
544 if ( strcmp("serial", tokens[0]) == 0 ) {
545 if ( tokens[1] != NULL
546 && tokens[2] != NULL
547 && tokens[3] != NULL
548 && tokens[4] != NULL ) {
549 BabelDevice *device = g_malloc ( sizeof (BabelDevice) );
6f2551f1 550 set_mode (&(device->mode), tokens[1]);
79e0f36e 551 device->name = g_strdup (tokens[2]);
431624c5 552 device->label = g_strndup (tokens[4], 50); // Limit really long label text
79e0f36e 553 a_babel_device_list = g_list_append (a_babel_device_list, device);
6f2551f1
GB
554 g_debug ("New gpsbabel device: %s, %d%d%d%d%d%d(%s)",
555 device->name,
556 device->mode.waypointsRead, device->mode.waypointsWrite,
557 device->mode.tracksRead, device->mode.tracksWrite,
558 device->mode.routesRead, device->mode.routesWrite,
559 tokens[1]);
79e0f36e
GB
560 } else {
561 g_warning ( "Unexpected gpsbabel format string: %s", line);
562 }
563 } else if ( strcmp("file", tokens[0]) == 0 ) {
564 if ( tokens[1] != NULL
565 && tokens[2] != NULL
566 && tokens[3] != NULL
567 && tokens[4] != NULL ) {
568 BabelFile *file = g_malloc ( sizeof (BabelFile) );
6f2551f1 569 set_mode (&(file->mode), tokens[1]);
79e0f36e
GB
570 file->name = g_strdup (tokens[2]);
571 file->ext = g_strdup (tokens[3]);
572 file->label = g_strdup (tokens[4]);
573 a_babel_file_list = g_list_append (a_babel_file_list, file);
6f2551f1 574 g_debug ("New gpsbabel file: %s, %d%d%d%d%d%d(%s)",
17720e63
GB
575 file->name,
576 file->mode.waypointsRead, file->mode.waypointsWrite,
577 file->mode.tracksRead, file->mode.tracksWrite,
578 file->mode.routesRead, file->mode.routesWrite,
579 tokens[1]);
79e0f36e
GB
580 } else {
581 g_warning ( "Unexpected gpsbabel format string: %s", line);
582 }
583 } /* else: ignore */
584 } else {
585 g_warning ( "Unexpected gpsbabel format string: %s", line);
586 }
587 g_strfreev ( tokens );
588}
589
590static void load_feature_cb (BabelProgressCode code, gpointer line, gpointer user_data)
591{
592 if (line != NULL)
593 load_feature_parse_line (line);
594}
595
596static gboolean load_feature ()
597{
598 int i;
599 gboolean ret = FALSE;
600 gchar *args[4];
601
602 if ( gpsbabel_loc ) {
603 i = 0;
604 if ( unbuffer_loc )
605 args[i++] = unbuffer_loc;
606 args[i++] = gpsbabel_loc;
607 args[i++] = "-^3";
608 args[i] = NULL;
609
610 ret = babel_general_convert (load_feature_cb, args, NULL);
611 } else
24ff9c7b 612 g_critical("gpsbabel not found in PATH");
79e0f36e
GB
613
614 return ret;
615}
18ec873d 616
ce4f6212
GB
617/**
618 * a_babel_init:
619 *
620 * Initialises babel module.
621 * Mainly check existence of gpsbabel progam
622 * and load all features available in ths version.
623 */
18ec873d
GB
624void a_babel_init ()
625{
626 /* TODO allow to set gpsbabel path via command line */
627 gpsbabel_loc = g_find_program_in_path( "gpsbabel" );
628 if ( !gpsbabel_loc )
24ff9c7b 629 g_critical( "gpsbabel not found in PATH" );
3684f362
RN
630
631 // Unlikely to package unbuffer on Windows so ATM don't even bother trying
632 // Highly unlikely unbuffer is available on a Windows system otherwise
633#ifndef WINDOWS
18ec873d
GB
634 unbuffer_loc = g_find_program_in_path( "unbuffer" );
635 if ( !unbuffer_loc )
636 g_warning( "unbuffer not found in PATH" );
3684f362 637#endif
18ec873d 638
79e0f36e 639 load_feature ();
18ec873d
GB
640}
641
ce4f6212
GB
642/**
643 * a_babel_uninit:
644 *
645 * Free resources acquired by a_babel_init.
646 */
18ec873d
GB
647void a_babel_uninit ()
648{
649 g_free ( gpsbabel_loc );
650 g_free ( unbuffer_loc );
67c32f00
RN
651
652 if ( a_babel_file_list ) {
653 GList *gl;
654 for (gl = a_babel_file_list; gl != NULL; gl = g_list_next(gl)) {
655 BabelFile *file = gl->data;
656 g_free ( file->name );
657 g_free ( file->ext );
658 g_free ( file->label );
659 g_free ( gl->data );
660 }
661 g_list_free ( a_babel_file_list );
662 }
663
664 if ( a_babel_device_list ) {
665 GList *gl;
666 for (gl = a_babel_device_list; gl != NULL; gl = g_list_next(gl)) {
667 BabelDevice *device = gl->data;
668 g_free ( device->name );
669 g_free ( device->label );
670 g_free ( gl->data );
671 }
672 g_list_free ( a_babel_device_list );
673 }
674
18ec873d 675}
430a37a9
GB
676
677/**
678 * a_babel_available:
679 *
680 * Indicates if babel is available or not.
681 *
682 * Returns: true if babel available
683 */
684gboolean a_babel_available ()
685{
686 return a_babel_device_list != NULL;
687}