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