]> git.street.me.uk Git - andy/viking.git/blame - src/babel.c
Fix: SF#3042692 - Tiles May Get Deleted During Offline Usage.
[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>
1d1bc3c1
EB
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
072cbb13
GB
23/**
24 * SECTION:babel
25 * @short_description: running external programs and redirecting to TRWLayers.
26 *
28c82d8b 27 * GPSBabel may not be necessary for everything -- for instance,
072cbb13 28 * use a_babel_convert_from_shellcommand() with input_file_type == %NULL
28c82d8b
EB
29 * for an external program that outputs GPX.
30 */
31
45acf79e
MA
32#ifdef HAVE_CONFIG_H
33#include "config.h"
34#endif
35
1d1bc3c1
EB
36#include "viking.h"
37#include "gpx.h"
38#include "babel.h"
8c060406 39#include <stdio.h>
e81e41fb 40#ifdef HAVE_SYS_WAIT_H
29a5a545 41#include <sys/wait.h>
e81e41fb 42#endif
45acf79e
MA
43#ifdef HAVE_UNISTD_H
44#include <unistd.h>
45#endif
46#include <glib.h>
47#include <glib/gstdio.h>
1d1bc3c1 48
072cbb13 49/* TODO in the future we could have support for other shells (change command strings), or not use a shell at all */
8cf048bd
EB
50#define BASH_LOCATION "/bin/bash"
51
072cbb13
GB
52/**
53 * a_babel_convert:
54 * @vt: The TRW layer to modify. All data will be deleted, and replaced by what gpsbabel outputs.
55 * @babelargs: A string containing gpsbabel command line filter options. No file types or names should
56 * be specified.
57 * @cb: A callback function.
58 *
59 * This function modifies data in a trw layer using gpsbabel filters. This routine is synchronous;
60 * that is, it will block the calling program until the conversion is done. To avoid blocking, call
61 * this routine from a worker thread.
62 *
63 * Returns: %TRUE on success
64 */
7b3479e3 65gboolean a_babel_convert( VikTrwLayer *vt, const char *babelargs, BabelStatusFunc cb, gpointer user_data )
1d1bc3c1
EB
66{
67 int fd_src;
68 FILE *f;
69 gchar *name_src;
70 gboolean ret = FALSE;
3adc68b0 71 gchar *bargs = g_strconcat(babelargs, " -i gpx", NULL);
1d1bc3c1 72
2ecf5998 73 if ((fd_src = g_file_open_tmp("tmp-viking.XXXXXX", &name_src, NULL)) >= 0) {
1d1bc3c1
EB
74 f = fdopen(fd_src, "w");
75 a_gpx_write_file(vt, f);
76 fclose(f);
8c060406 77 f = NULL;
7b3479e3 78 ret = a_babel_convert_from ( vt, bargs, cb, name_src, user_data );
1d1bc3c1
EB
79 }
80
81 g_free(bargs);
8c060406 82 g_remove(name_src);
1d1bc3c1
EB
83 g_free(name_src);
84 return ret;
85}
86
70548902 87#ifdef WINDOWS
883e5ed6 88static gboolean babel_general_convert( BabelStatusFunc cb, gchar **args, gpointer user_data )
70548902
MA
89{
90 gboolean ret;
91 FILE *f;
92 gchar *cmd;
93 gchar **args2;
94
95 STARTUPINFO si;
96 PROCESS_INFORMATION pi;
97
70548902
MA
98 ZeroMemory( &si, sizeof(si) );
99 ZeroMemory( &pi, sizeof(pi) );
100 si.cb = sizeof(si);
101 si.dwFlags = STARTF_USESHOWWINDOW;
102 si.wShowWindow = SW_HIDE;
103
104 cmd = g_strjoinv( " ", args);
105 args2 = g_strsplit(cmd, "\\", 0);
106 g_free(cmd);
107 cmd = g_strjoinv( "\\\\", args2);
108 g_free(args2);
109 args2 = g_strsplit(cmd, "/", 0);
110 g_free(cmd);
111 cmd = g_strjoinv( "\\\\", args2);
112
113 if( !CreateProcess(
114 NULL, // No module name (use command line).
115 (LPTSTR)cmd, // Command line.
116 NULL, // Process handle not inheritable.
117 NULL, // Thread handle not inheritable.
118 FALSE, // Set handle inheritance to FALSE.
119 0, // No creation flags.
120 NULL, // Use parent's environment block.
121 NULL, // Use parent's starting directory.
122 &si, // Pointer to STARTUPINFO structure.
123 &pi ) // Pointer to PROCESS_INFORMATION structure.
124 ){
125 g_warning( "CreateProcess failed");
126 ret = FALSE;
127 }
128 else {
129 WaitForSingleObject(pi.hProcess, INFINITE);
130 WaitForSingleObject(pi.hThread, INFINITE);
131
132 CloseHandle(pi.hThread);
133 CloseHandle(pi.hProcess);
134
135 if ( cb )
136 cb(BABEL_DONE, NULL, user_data);
137
70548902
MA
138 ret = TRUE;
139 }
140
141 g_strfreev( args2 );
142 g_free( cmd );
143
144 return ret;
145}
146/* Windows */
147#else
148/* Posix */
883e5ed6 149static gboolean babel_general_convert( BabelStatusFunc cb, gchar **args, gpointer user_data )
8cf048bd 150{
2ecf5998 151 gboolean ret = FALSE;
8cf048bd 152 GPid pid;
6c641b1a
MA
153 GError *error = NULL;
154 gint babel_stdout;
8cf048bd 155
6c641b1a
MA
156 if (!g_spawn_async_with_pipes (NULL, args, NULL, 0, NULL, NULL, &pid, NULL, &babel_stdout, NULL, &error)) {
157 g_warning("Error : %s", error->message);
158 g_error_free(error);
8cf048bd
EB
159 ret = FALSE;
160 } else {
2b756ea0 161
8cf048bd
EB
162 gchar line[512];
163 FILE *diag;
164 diag = fdopen(babel_stdout, "r");
165 setvbuf(diag, NULL, _IONBF, 0);
166
167 while (fgets(line, sizeof(line), diag)) {
7b3479e3
EB
168 if ( cb )
169 cb(BABEL_DIAG_OUTPUT, line, user_data);
8cf048bd 170 }
7b3479e3
EB
171 if ( cb )
172 cb(BABEL_DONE, NULL, user_data);
8cf048bd 173 fclose(diag);
8c060406 174 diag = NULL;
8cf048bd
EB
175 waitpid(pid, NULL, 0);
176 g_spawn_close_pid(pid);
177
2634ad33
GB
178 ret = TRUE;
179 }
180
181 return ret;
182}
183#endif /* Posix */
184
185/**
186 * babel_general_convert_from:
187 * @cb: callback that is run upon new data from STDOUT (?)
188 * (TODO: STDERR would be nice since we usually redirect STDOUT)
189 * @user_data: passed along to cb
190 *
191 * Runs args[0] with the arguments and uses the GPX module
192 * to import the GPX data into layer vt. Assumes that upon
193 * running the command, the data will appear in the (usually
194 * temporary) file name_dst.
195 *
196 * Returns: %TRUE on success
197 */
198static gboolean babel_general_convert_from( VikTrwLayer *vt, BabelStatusFunc cb, gchar **args, const gchar *name_dst, gpointer user_data )
199{
200 gboolean ret = FALSE;
201 FILE *f = NULL;
202
203 /* No data required */
204 if ( vt == NULL )
205 return TRUE;
206
883e5ed6 207 if (babel_general_convert(cb, args, user_data)) {
2634ad33 208
8c060406
MA
209 f = g_fopen(name_dst, "r");
210 if (f) {
211 a_gpx_read_file ( vt, f );
212 fclose(f);
213 f = NULL;
214 ret = TRUE;
2ecf5998 215 }
8cf048bd
EB
216 }
217
218 return ret;
219}
220
072cbb13
GB
221/**
222 * a_babel_convert_from:
223 * @vt: The TRW layer to place data into. Duplicate items will be overwritten.
224 * @babelargs: A string containing gpsbabel command line options. In addition to any filters, this string
225 * must include the input file type (-i) option.
226 * @cb: Optional callback function. Same usage as in a_babel_convert().
227 *
228 * Loads data into a trw layer from a file, using gpsbabel. This routine is synchronous;
229 * that is, it will block the calling program until the conversion is done. To avoid blocking, call
230 * this routine from a worker thread.
231 *
232 * Returns: %TRUE on success
233 */
7b3479e3 234gboolean a_babel_convert_from( VikTrwLayer *vt, const char *babelargs, BabelStatusFunc cb, const char *from, gpointer user_data )
1d1bc3c1 235{
f3cd9987 236 int i,j;
1d1bc3c1 237 int fd_dst;
1d1bc3c1 238 gchar *name_dst;
1d1bc3c1 239 gboolean ret = FALSE;
f3cd9987 240 gchar *args[64];
1d1bc3c1 241
2ecf5998 242 if ((fd_dst = g_file_open_tmp("tmp-viking.XXXXXX", &name_dst, NULL)) >= 0) {
92255687 243 gchar *gpsbabel_loc;
1d1bc3c1
EB
244 close(fd_dst);
245
92255687
EB
246 gpsbabel_loc = g_find_program_in_path("gpsbabel");
247
248 if (gpsbabel_loc ) {
249 gchar *unbuffer_loc = g_find_program_in_path("unbuffer");
f3cd9987
QT
250 gchar **sub_args = g_strsplit(babelargs, " ", 0);
251
252 i = 0;
253 if (unbuffer_loc)
254 args[i++] = unbuffer_loc;
255 args[i++] = gpsbabel_loc;
e208e3c0
QT
256 for (j = 0; sub_args[j]; j++) {
257 /* some version of gpsbabel can not take extra blank arg */
258 if (sub_args[j][0] != '\0')
259 args[i++] = sub_args[j];
260 }
f3cd9987
QT
261 args[i++] = "-o";
262 args[i++] = "gpx";
e208e3c0 263 args[i++] = "-f";
8398d878 264 args[i++] = (char *)from;
e208e3c0 265 args[i++] = "-F";
f3cd9987
QT
266 args[i++] = name_dst;
267 args[i] = NULL;
268
269 ret = babel_general_convert_from ( vt, cb, args, name_dst, user_data );
270
271 g_free ( unbuffer_loc );
272 g_strfreev(sub_args);
374b7f45
GB
273 } else
274 g_warning("gpsbabel not found in PATH");
f3cd9987 275 g_free(gpsbabel_loc);
1d1bc3c1
EB
276 }
277
8c060406 278 g_remove(name_dst);
1d1bc3c1
EB
279 g_free(name_dst);
280 return ret;
281}
8cf048bd 282
072cbb13
GB
283/**
284 * a_babel_convert_from_shellcommand:
285 *
286 * Runs the input command in a shell (bash) and optionally uses GPSBabel to convert from input_file_type.
287 * If input_file_type is %NULL, doesn't use GPSBabel. Input must be GPX (or Geocaching *.loc)
28c82d8b 288 *
072cbb13 289 * Uses babel_general_convert_from() to actually run the command. This function
28c82d8b
EB
290 * prepares the command and temporary file, and sets up the arguments for bash.
291 */
292gboolean a_babel_convert_from_shellcommand ( VikTrwLayer *vt, const char *input_cmd, const char *input_file_type, BabelStatusFunc cb, gpointer user_data )
8cf048bd
EB
293{
294 int fd_dst;
295 gchar *name_dst;
296 gboolean ret = FALSE;
297 gchar **args;
298
2ecf5998 299 if ((fd_dst = g_file_open_tmp("tmp-viking.XXXXXX", &name_dst, NULL)) >= 0) {
28c82d8b
EB
300 gchar *shell_command;
301 if ( input_file_type )
302 shell_command = g_strdup_printf("%s | gpsbabel -i %s -f - -o gpx -F %s", input_cmd, input_file_type, name_dst);
303 else
304 shell_command = g_strdup_printf("%s > %s", input_cmd, name_dst);
305
29a5a545 306 g_debug("%s: %s", __FUNCTION__, shell_command);
8cf048bd
EB
307 close(fd_dst);
308
309 args = g_malloc(sizeof(gchar *)*4);
310 args[0] = BASH_LOCATION;
311 args[1] = "-c";
312 args[2] = shell_command;
313 args[3] = NULL;
314
7b3479e3 315 ret = babel_general_convert_from ( vt, cb, args, name_dst, user_data );
8cf048bd
EB
316 g_free ( args );
317 g_free ( shell_command );
318 }
319
8c060406 320 g_remove(name_dst);
8cf048bd
EB
321 g_free(name_dst);
322 return ret;
323}
324
533bbf34
MA
325gboolean a_babel_convert_from_url ( VikTrwLayer *vt, const char *url, const char *input_type, BabelStatusFunc cb, gpointer user_data )
326{
74fbca98 327 static DownloadMapOptions options = { FALSE, FALSE, NULL, 0, a_check_kml_file};
533bbf34 328 gint fd_src;
8bd9fe17
GB
329 int fetch_ret;
330 gboolean ret = FALSE;
533bbf34
MA
331 gchar *name_src;
332 gchar *babelargs;
333
334 g_debug("%s: input_type=%s url=%s", __FUNCTION__, input_type, url);
335
8bd9fe17 336 if ((fd_src = g_file_open_tmp("tmp-viking.XXXXXX", &name_src, NULL)) >= 0) {
533bbf34
MA
337 close(fd_src);
338 g_remove(name_src);
339
340 babelargs = g_strdup_printf(" -i %s", input_type);
341
825413ba 342 fetch_ret = a_http_download_get_url(url, "", name_src, &options, NULL);
8bd9fe17
GB
343 if (fetch_ret == 0)
344 ret = a_babel_convert_from( vt, babelargs, NULL, name_src, NULL);
533bbf34
MA
345
346 g_remove(name_src);
347 g_free(babelargs);
348 g_free(name_src);
349 }
350
351 return ret;
352}
353
8f96e967 354static gboolean babel_general_convert_to( VikTrwLayer *vt, BabelStatusFunc cb, gchar **args, const gchar *name_src, gpointer user_data )
70548902 355{
f7f8a0a6 356 if (!a_file_export(vt, name_src, FILE_TYPE_GPX, NULL)) {
70548902
MA
357 g_warning("%s(): error exporting to %s", __FUNCTION__, name_src);
358 return(FALSE);
359 }
360
883e5ed6 361 return babel_general_convert (cb, args, user_data);
b364d6bc
QT
362}
363
364gboolean a_babel_convert_to( VikTrwLayer *vt, const char *babelargs, BabelStatusFunc cb, const char *to, gpointer user_data )
365{
f3cd9987 366 int i,j;
b364d6bc
QT
367 int fd_src;
368 gchar *name_src;
b364d6bc 369 gboolean ret = FALSE;
f3cd9987 370 gchar *args[64];
b364d6bc 371
2ecf5998 372 if ((fd_src = g_file_open_tmp("tmp-viking.XXXXXX", &name_src, NULL)) >= 0) {
b364d6bc
QT
373 gchar *gpsbabel_loc;
374 close(fd_src);
375
376 gpsbabel_loc = g_find_program_in_path("gpsbabel");
377
378 if (gpsbabel_loc ) {
379 gchar *unbuffer_loc = g_find_program_in_path("unbuffer");
f3cd9987
QT
380 gchar **sub_args = g_strsplit(babelargs, " ", 0);
381
382 i = 0;
383 if (unbuffer_loc)
384 args[i++] = unbuffer_loc;
385 args[i++] = gpsbabel_loc;
386 args[i++] = "-i";
387 args[i++] = "gpx";
388 for (j = 0; sub_args[j]; j++)
e208e3c0
QT
389 /* some version of gpsbabel can not take extra blank arg */
390 if (sub_args[j][0] != '\0')
391 args[i++] = sub_args[j];
392 args[i++] = "-f";
f3cd9987 393 args[i++] = name_src;
e208e3c0 394 args[i++] = "-F";
8398d878 395 args[i++] = (char *)to;
f3cd9987
QT
396 args[i] = NULL;
397
398 ret = babel_general_convert_to ( vt, cb, args, name_src, user_data );
399
400 g_free ( unbuffer_loc );
401 g_strfreev(sub_args);
374b7f45
GB
402 } else
403 g_warning("gpsbabel not found in PATH");
f3cd9987 404 g_free(gpsbabel_loc);
b364d6bc
QT
405 }
406
8c060406 407 g_remove(name_src);
b364d6bc
QT
408 g_free(name_src);
409 return ret;
410}