]> git.street.me.uk Git - andy/viking.git/blob - src/babel.c
Merge branch 'i18n-launchpad' into i18n
[andy/viking.git] / src / babel.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5  * Copyright (C) 2006, Quy Tonthat <qtonthat@gmail.com>
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
23 /* babel.c: running external programs and redirecting to TRWLayers.
24  * GPSBabel may not be necessary for everything -- for instance,
25  *   use a_babel_convert_from_shellcommand with input_file_type == NULL
26  *   for an external program that outputs GPX.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include "viking.h"
34 #include "gpx.h"
35 #include "babel.h"
36 #include <stdio.h>
37 #ifdef HAVE_SYS_WAIT_H
38 #include <sys/wait.h>
39 #endif
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 #include <glib.h>
44 #include <glib/gstdio.h>
45
46 /* in the future we could have support for other shells (change command strings), or not use a shell at all */
47 #define BASH_LOCATION "/bin/bash"
48
49 gboolean a_babel_convert( VikTrwLayer *vt, const char *babelargs, BabelStatusFunc cb, gpointer user_data )
50 {
51   int fd_src;
52   FILE *f;
53   gchar *name_src;
54   gboolean ret = FALSE;
55   gchar *bargs = g_strconcat(babelargs, " -i gpx", NULL);
56
57   if ((fd_src = g_file_open_tmp("tmp-viking.XXXXXX", &name_src, NULL)) >= 0) {
58     f = fdopen(fd_src, "w");
59     a_gpx_write_file(vt, f);
60     fclose(f);
61     f = NULL;
62     ret = a_babel_convert_from ( vt, bargs, cb, name_src, user_data );
63   }
64
65   g_free(bargs);
66   g_remove(name_src);
67   g_free(name_src);
68   return ret;
69 }
70
71 /* Runs args[0] with the arguments and uses the GPX module
72  * to import the GPX data into layer vt. Assumes that upon
73  * running the command, the data will appear in the (usually
74  * temporary) file name_dst.
75  *
76  * cb: callback that is run upon new data from STDOUT (?)
77  *     (TODO: STDERR would be nice since we usually redirect STDOUT)
78  * user_data: passed along to cb
79  *
80  * returns TRUE on success
81  */
82 #ifdef WINDOWS
83 gboolean babel_general_convert_from( VikTrwLayer *vt, BabelStatusFunc cb, gchar **args, const gchar *name_dst, gpointer user_data )
84 {
85   gboolean ret;
86   FILE *f;
87   gchar *cmd;
88   gchar **args2;
89   
90   STARTUPINFO si;
91   PROCESS_INFORMATION pi;
92
93   
94   ZeroMemory( &si, sizeof(si) );
95   ZeroMemory( &pi, sizeof(pi) );
96   si.cb = sizeof(si);
97   si.dwFlags = STARTF_USESHOWWINDOW;
98   si.wShowWindow = SW_HIDE;
99   
100   cmd = g_strjoinv( " ", args);
101   args2 = g_strsplit(cmd, "\\", 0);
102   g_free(cmd);
103   cmd = g_strjoinv( "\\\\", args2);
104   g_free(args2);
105   args2 = g_strsplit(cmd, "/", 0);
106   g_free(cmd);
107   cmd = g_strjoinv( "\\\\", args2);
108
109   if( !CreateProcess(
110              NULL,                   // No module name (use command line).
111         (LPTSTR)cmd,           // Command line.
112         NULL,                   // Process handle not inheritable.
113         NULL,                   // Thread handle not inheritable.
114         FALSE,                  // Set handle inheritance to FALSE.
115         0,                      // No creation flags.
116         NULL,                   // Use parent's environment block.
117         NULL,                   // Use parent's starting directory.
118         &si,                    // Pointer to STARTUPINFO structure.
119         &pi )                   // Pointer to PROCESS_INFORMATION structure.
120     ){
121     g_warning( "CreateProcess failed");
122     ret = FALSE;
123   }
124   else {
125     WaitForSingleObject(pi.hProcess, INFINITE);
126     WaitForSingleObject(pi.hThread, INFINITE);
127     
128     CloseHandle(pi.hThread);
129     CloseHandle(pi.hProcess);
130     
131     if ( cb )
132       cb(BABEL_DONE, NULL, user_data);
133     
134     f = g_fopen(name_dst, "r");
135     a_gpx_read_file( vt, f );
136     fclose(f);
137     ret = TRUE;
138   }
139
140   g_strfreev( args2 );
141   g_free( cmd );
142  
143   return ret;
144 }
145 /* Windows */
146 #else
147 /* Posix */
148 gboolean babel_general_convert_from( VikTrwLayer *vt, BabelStatusFunc cb, gchar **args, const gchar *name_dst, gpointer user_data )
149 {
150   gboolean ret = FALSE;
151   GPid pid;
152   GError *error = NULL;
153   gint babel_stdout;
154   FILE *f;
155
156
157   if (!g_spawn_async_with_pipes (NULL, args, NULL, 0, NULL, NULL, &pid, NULL, &babel_stdout, NULL, &error)) {
158     g_warning("Error : %s", error->message);
159     g_error_free(error);
160     ret = FALSE;
161   } else {
162     /* No data required */
163     if ( vt == NULL )
164       return TRUE;
165
166     gchar line[512];
167     FILE *diag;
168     diag = fdopen(babel_stdout, "r");
169     setvbuf(diag, NULL, _IONBF, 0);
170
171     while (fgets(line, sizeof(line), diag)) {
172       if ( cb )
173         cb(BABEL_DIAG_OUTPUT, line, user_data);
174     }
175     if ( cb )
176       cb(BABEL_DONE, NULL, user_data);
177     fclose(diag);
178     diag = NULL;
179     waitpid(pid, NULL, 0);
180     g_spawn_close_pid(pid);
181
182     f = g_fopen(name_dst, "r");
183     if (f) {
184       a_gpx_read_file ( vt, f );
185       fclose(f);
186       f = NULL;
187       ret = TRUE;
188     }
189   }
190     
191   return ret;
192 }
193 #endif /* Posix */
194
195 gboolean a_babel_convert_from( VikTrwLayer *vt, const char *babelargs, BabelStatusFunc cb, const char *from, gpointer user_data )
196 {
197   int i,j;
198   int fd_dst;
199   gchar *name_dst;
200   gboolean ret = FALSE;
201   gchar *args[64];
202
203   if ((fd_dst = g_file_open_tmp("tmp-viking.XXXXXX", &name_dst, NULL)) >= 0) {
204     gchar *gpsbabel_loc;
205     close(fd_dst);
206
207     gpsbabel_loc = g_find_program_in_path("gpsbabel");
208
209     if (gpsbabel_loc ) {
210       gchar *unbuffer_loc = g_find_program_in_path("unbuffer");
211       gchar **sub_args = g_strsplit(babelargs, " ", 0);
212
213       i = 0;
214       if (unbuffer_loc)
215         args[i++] = unbuffer_loc;
216       args[i++] = gpsbabel_loc;
217       for (j = 0; sub_args[j]; j++) {
218         /* some version of gpsbabel can not take extra blank arg */
219         if (sub_args[j][0] != '\0')
220           args[i++] = sub_args[j];
221       }
222       args[i++] = "-o";
223       args[i++] = "gpx";
224       args[i++] = "-f";
225       args[i++] = (char *)from;
226       args[i++] = "-F";
227       args[i++] = name_dst;
228       args[i] = NULL;
229
230       ret = babel_general_convert_from ( vt, cb, args, name_dst, user_data );
231
232       g_free ( unbuffer_loc );
233       g_strfreev(sub_args);
234     } else
235       g_warning("gpsbabel not found in PATH");
236     g_free(gpsbabel_loc);
237   }
238
239   g_remove(name_dst);
240   g_free(name_dst);
241   return ret;
242 }
243
244 /* Runs the input command in a shell (bash) and optionally uses GPSBabel to convert from input_file_type.
245  * If input_file_type is NULL, doesn't use GPSBabel. Input must be GPX (or Geocaching *.loc)
246  *
247  * Uses babel_general_convert_from to actually run the command. This function
248  * prepares the command and temporary file, and sets up the arguments for bash.
249  */
250 gboolean a_babel_convert_from_shellcommand ( VikTrwLayer *vt, const char *input_cmd, const char *input_file_type, BabelStatusFunc cb, gpointer user_data )
251 {
252   int fd_dst;
253   gchar *name_dst;
254   gboolean ret = FALSE;
255   gchar **args;  
256
257   if ((fd_dst = g_file_open_tmp("tmp-viking.XXXXXX", &name_dst, NULL)) >= 0) {
258     gchar *shell_command;
259     if ( input_file_type )
260       shell_command = g_strdup_printf("%s | gpsbabel -i %s -f - -o gpx -F %s", input_cmd, input_file_type, name_dst);
261     else
262       shell_command = g_strdup_printf("%s > %s", input_cmd, name_dst);
263
264     g_debug("%s: %s", __FUNCTION__, shell_command);
265     close(fd_dst);
266
267     args = g_malloc(sizeof(gchar *)*4);
268     args[0] = BASH_LOCATION;
269     args[1] = "-c";
270     args[2] = shell_command;
271     args[3] = NULL;
272
273     ret = babel_general_convert_from ( vt, cb, args, name_dst, user_data );
274     g_free ( args );
275     g_free ( shell_command );
276   }
277
278   g_remove(name_dst);
279   g_free(name_dst);
280   return ret;
281 }
282
283 gboolean a_babel_convert_from_url ( VikTrwLayer *vt, const char *url, const char *input_type, BabelStatusFunc cb, gpointer user_data )
284 {
285   static DownloadMapOptions options = { FALSE, FALSE, NULL, 0, a_check_kml_file};
286   gint fd_src;
287   int fetch_ret;
288   gboolean ret = FALSE;
289   gchar *name_src;
290   gchar *babelargs;
291
292   g_debug("%s: input_type=%s url=%s", __FUNCTION__, input_type, url);
293
294   if ((fd_src = g_file_open_tmp("tmp-viking.XXXXXX", &name_src, NULL)) >= 0) {
295     close(fd_src);
296     g_remove(name_src);
297
298     babelargs = g_strdup_printf(" -i %s", input_type);
299
300     fetch_ret = a_http_download_get_url(url, "", name_src, &options, NULL);
301     if (fetch_ret == 0)
302       ret = a_babel_convert_from( vt, babelargs, NULL, name_src, NULL);
303  
304     g_remove(name_src);
305     g_free(babelargs);
306     g_free(name_src);
307   }
308
309   return ret;
310 }
311
312 #ifdef WINDOWS
313 gboolean babel_general_convert_to( VikTrwLayer *vt, BabelStatusFunc cb, gchar **args, const gchar *name_src, gpointer user_data )
314 {
315   gboolean ret;
316   gchar *cmd;
317   gchar **args2;
318   
319   if (!a_file_export(vt, name_src, FILE_TYPE_GPX, NULL)) {
320     g_warning("%s(): error exporting to %s", __FUNCTION__, name_src);
321     return(FALSE);
322   }
323        
324   STARTUPINFO si;
325   PROCESS_INFORMATION pi;
326
327   ZeroMemory( &si, sizeof(si) );
328   ZeroMemory( &pi, sizeof(pi) );
329   si.cb = sizeof(si);
330   si.dwFlags = STARTF_USESHOWWINDOW;
331   si.wShowWindow = SW_HIDE;
332   
333   
334   cmd = g_strjoinv( " ", args);
335   args2 = g_strsplit(cmd, "\\", 0);
336   cmd = g_strjoinv( "\\\\", args2);
337   g_free(args2);
338        args2 = g_strsplit(cmd, "/", 0);
339        g_free(cmd);
340        cmd = g_strjoinv( "\\\\", args2);
341        
342   if( !CreateProcess(
343              NULL,                   // No module name (use command line).
344         (LPTSTR)cmd,           // Command line.
345         NULL,                   // Process handle not inheritable.
346         NULL,                   // Thread handle not inheritable.
347         FALSE,                  // Set handle inheritance to FALSE.
348         0,                      // No creation flags.
349         NULL,                   // Use parent's environment block.
350         NULL,                   // Use parent's starting directory.
351         &si,                    // Pointer to STARTUPINFO structure.
352         &pi )                   // Pointer to PROCESS_INFORMATION structure.
353     ){
354     g_warning( "CreateProcess failed" );
355     ret = FALSE;
356   }
357   else {
358     
359     WaitForSingleObject(pi.hProcess, INFINITE);
360     WaitForSingleObject(pi.hThread, INFINITE);
361     
362     CloseHandle(pi.hThread);
363     CloseHandle(pi.hProcess);
364
365     if ( cb )
366       cb(BABEL_DONE, NULL, user_data);
367     
368     ret = TRUE;
369   }
370   
371   g_strfreev(args2);
372   g_free( cmd );
373   
374   return ret;
375 }
376 /* Windows */
377 #else
378 /* Posix */
379 gboolean babel_general_convert_to( VikTrwLayer *vt, BabelStatusFunc cb, gchar **args, const gchar *name_src, gpointer user_data )
380 {
381   gboolean ret = FALSE;
382   GPid pid;
383   GError *error = NULL;
384   gint babel_stdout;
385
386   if (!a_file_export(vt, name_src, FILE_TYPE_GPX, NULL)) {
387     g_warning("%s(): error exporting to %s", __FUNCTION__, name_src);
388     return(FALSE);
389   }
390
391   if (!g_spawn_async_with_pipes (NULL, args, NULL, 0, NULL, NULL, &pid, NULL, &babel_stdout, NULL, &error)) {
392     g_warning("Error : %s", error->message);
393     g_error_free(error);
394     ret = FALSE;
395   } else {
396     gchar line[512];
397     FILE *diag;
398     diag = fdopen(babel_stdout, "r");
399     setvbuf(diag, NULL, _IONBF, 0);
400
401     while (fgets(line, sizeof(line), diag)) {
402       if ( cb )
403         cb(BABEL_DIAG_OUTPUT, line, user_data);
404     }
405     if ( cb )
406       cb(BABEL_DONE, NULL, user_data);
407     fclose(diag);
408     diag = NULL;
409     waitpid(pid, NULL, 0);
410     g_spawn_close_pid(pid);
411
412     ret = TRUE;
413   }
414     
415   return ret;
416 }
417 #endif /* Posix */
418
419 gboolean a_babel_convert_to( VikTrwLayer *vt, const char *babelargs, BabelStatusFunc cb, const char *to, gpointer user_data )
420 {
421   int i,j;
422   int fd_src;
423   gchar *name_src;
424   gboolean ret = FALSE;
425   gchar *args[64];  
426
427   if ((fd_src = g_file_open_tmp("tmp-viking.XXXXXX", &name_src, NULL)) >= 0) {
428     gchar *gpsbabel_loc;
429     close(fd_src);
430
431     gpsbabel_loc = g_find_program_in_path("gpsbabel");
432
433     if (gpsbabel_loc ) {
434       gchar *unbuffer_loc = g_find_program_in_path("unbuffer");
435       gchar **sub_args = g_strsplit(babelargs, " ", 0);
436
437       i = 0;
438       if (unbuffer_loc)
439         args[i++] = unbuffer_loc;
440       args[i++] = gpsbabel_loc;
441       args[i++] = "-i";
442       args[i++] = "gpx";
443       for (j = 0; sub_args[j]; j++)
444         /* some version of gpsbabel can not take extra blank arg */
445         if (sub_args[j][0] != '\0')
446           args[i++] = sub_args[j];
447       args[i++] = "-f";
448       args[i++] = name_src;
449       args[i++] = "-F";
450       args[i++] = (char *)to;
451       args[i] = NULL;
452
453       ret = babel_general_convert_to ( vt, cb, args, name_src, user_data );
454
455       g_free ( unbuffer_loc );
456       g_strfreev(sub_args);
457     } else
458       g_warning("gpsbabel not found in PATH");
459     g_free(gpsbabel_loc);
460   }
461
462   g_remove(name_src);
463   g_free(name_src);
464   return ret;
465 }