]> git.street.me.uk Git - andy/viking.git/blob - src/babel.c
Add menu item for help contents
[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     gchar line[512];
162     FILE *diag;
163     diag = fdopen(babel_stdout, "r");
164     setvbuf(diag, NULL, _IONBF, 0);
165
166     while (fgets(line, sizeof(line), diag)) {
167       if ( cb )
168         cb(BABEL_DIAG_OUTPUT, line, user_data);
169     }
170     if ( cb )
171       cb(BABEL_DONE, NULL, user_data);
172     fclose(diag);
173     diag = NULL;
174     waitpid(pid, NULL, 0);
175     g_spawn_close_pid(pid);
176
177     f = g_fopen(name_dst, "r");
178     if (f) {
179       a_gpx_read_file ( vt, f );
180       fclose(f);
181       f = NULL;
182       ret = TRUE;
183     }
184   }
185     
186   return ret;
187 }
188 #endif /* Posix */
189
190 gboolean a_babel_convert_from( VikTrwLayer *vt, const char *babelargs, BabelStatusFunc cb, const char *from, gpointer user_data )
191 {
192   int i,j;
193   int fd_dst;
194   gchar *name_dst;
195   gboolean ret = FALSE;
196   gchar *args[64];
197
198   if ((fd_dst = g_file_open_tmp("tmp-viking.XXXXXX", &name_dst, NULL)) >= 0) {
199     gchar *gpsbabel_loc;
200     close(fd_dst);
201
202     gpsbabel_loc = g_find_program_in_path("gpsbabel");
203
204     if (gpsbabel_loc ) {
205       gchar *unbuffer_loc = g_find_program_in_path("unbuffer");
206       gchar **sub_args = g_strsplit(babelargs, " ", 0);
207
208       i = 0;
209       if (unbuffer_loc)
210         args[i++] = unbuffer_loc;
211       args[i++] = gpsbabel_loc;
212       for (j = 0; sub_args[j]; j++) {
213         /* some version of gpsbabel can not take extra blank arg */
214         if (sub_args[j][0] != '\0')
215           args[i++] = sub_args[j];
216       }
217       args[i++] = "-o";
218       args[i++] = "gpx";
219       args[i++] = "-f";
220       args[i++] = from;
221       args[i++] = "-F";
222       args[i++] = name_dst;
223       args[i] = NULL;
224
225       ret = babel_general_convert_from ( vt, cb, args, name_dst, user_data );
226
227       g_free ( unbuffer_loc );
228       g_strfreev(sub_args);
229     }
230     g_free(gpsbabel_loc);
231   }
232
233   g_remove(name_dst);
234   g_free(name_dst);
235   return ret;
236 }
237
238 /* Runs the input command in a shell (bash) and optionally uses GPSBabel to convert from input_file_type.
239  * If input_file_type is NULL, doesn't use GPSBabel. Input must be GPX (or Geocaching *.loc)
240  *
241  * Uses babel_general_convert_from to actually run the command. This function
242  * prepares the command and temporary file, and sets up the arguments for bash.
243  */
244 gboolean a_babel_convert_from_shellcommand ( VikTrwLayer *vt, const char *input_cmd, const char *input_file_type, BabelStatusFunc cb, gpointer user_data )
245 {
246   int fd_dst;
247   gchar *name_dst;
248   gboolean ret = FALSE;
249   gchar **args;  
250
251   if ((fd_dst = g_file_open_tmp("tmp-viking.XXXXXX", &name_dst, NULL)) >= 0) {
252     gchar *shell_command;
253     if ( input_file_type )
254       shell_command = g_strdup_printf("%s | gpsbabel -i %s -f - -o gpx -F %s", input_cmd, input_file_type, name_dst);
255     else
256       shell_command = g_strdup_printf("%s > %s", input_cmd, name_dst);
257
258     g_debug("%s: %s", __FUNCTION__, shell_command);
259     close(fd_dst);
260
261     args = g_malloc(sizeof(gchar *)*4);
262     args[0] = BASH_LOCATION;
263     args[1] = "-c";
264     args[2] = shell_command;
265     args[3] = NULL;
266
267     ret = babel_general_convert_from ( vt, cb, args, name_dst, user_data );
268     g_free ( args );
269     g_free ( shell_command );
270   }
271
272   g_remove(name_dst);
273   g_free(name_dst);
274   return ret;
275 }
276
277 gboolean a_babel_convert_from_url ( VikTrwLayer *vt, const char *url, const char *input_type, BabelStatusFunc cb, gpointer user_data )
278 {
279   static DownloadOptions options = {NULL, 0, a_check_kml_file};
280   gint fd_src;
281   int fetch_ret;
282   gboolean ret = FALSE;
283   gchar *name_src;
284   gchar *babelargs;
285
286   g_debug("%s: input_type=%s url=%s", __FUNCTION__, input_type, url);
287
288   if ((fd_src = g_file_open_tmp("tmp-viking.XXXXXX", &name_src, NULL)) >= 0) {
289     close(fd_src);
290     g_remove(name_src);
291
292     babelargs = g_strdup_printf(" -i %s", input_type);
293
294     fetch_ret = a_http_download_get_url(url, "", name_src, &options);
295     if (fetch_ret == 0)
296       ret = a_babel_convert_from( vt, babelargs, NULL, name_src, NULL);
297  
298     g_remove(name_src);
299     g_free(babelargs);
300     g_free(name_src);
301   }
302
303   return ret;
304 }
305
306 #ifdef WINDOWS
307 gboolean babel_general_convert_to( VikTrwLayer *vt, BabelStatusFunc cb, gchar **args, const gchar *name_src, gpointer user_data )
308 {
309   gboolean ret;
310   gchar *cmd;
311   gchar **args2;
312   
313   if (!a_file_export(vt, name_src, FILE_TYPE_GPX)) {
314     g_warning("%s(): error exporting to %s", __FUNCTION__, name_src);
315     return(FALSE);
316   }
317        
318   STARTUPINFO si;
319   PROCESS_INFORMATION pi;
320
321   ZeroMemory( &si, sizeof(si) );
322   ZeroMemory( &pi, sizeof(pi) );
323   si.cb = sizeof(si);
324   si.dwFlags = STARTF_USESHOWWINDOW;
325   si.wShowWindow = SW_HIDE;
326   
327   
328   cmd = g_strjoinv( " ", args);
329   args2 = g_strsplit(cmd, "\\", 0);
330   cmd = g_strjoinv( "\\\\", args2);
331   g_free(args2);
332        args2 = g_strsplit(cmd, "/", 0);
333        g_free(cmd);
334        cmd = g_strjoinv( "\\\\", args2);
335        
336   if( !CreateProcess(
337              NULL,                   // No module name (use command line).
338         (LPTSTR)cmd,           // Command line.
339         NULL,                   // Process handle not inheritable.
340         NULL,                   // Thread handle not inheritable.
341         FALSE,                  // Set handle inheritance to FALSE.
342         0,                      // No creation flags.
343         NULL,                   // Use parent's environment block.
344         NULL,                   // Use parent's starting directory.
345         &si,                    // Pointer to STARTUPINFO structure.
346         &pi )                   // Pointer to PROCESS_INFORMATION structure.
347     ){
348     g_warning( "CreateProcess failed" );
349     ret = FALSE;
350   }
351   else {
352     
353     WaitForSingleObject(pi.hProcess, INFINITE);
354     WaitForSingleObject(pi.hThread, INFINITE);
355     
356     CloseHandle(pi.hThread);
357     CloseHandle(pi.hProcess);
358
359     if ( cb )
360       cb(BABEL_DONE, NULL, user_data);
361     
362     ret = TRUE;
363   }
364   
365   g_strfreev(args2);
366   g_free( cmd );
367   
368   return ret;
369 }
370 /* Windows */
371 #else
372 /* Posix */
373 gboolean babel_general_convert_to( VikTrwLayer *vt, BabelStatusFunc cb, gchar **args, const gchar *name_src, gpointer user_data )
374 {
375   gboolean ret = FALSE;
376   GPid pid;
377   GError *error = NULL;
378   gint babel_stdout;
379
380   if (!a_file_export(vt, name_src, FILE_TYPE_GPX)) {
381     g_warning("%s(): error exporting to %s", __FUNCTION__, name_src);
382     return(FALSE);
383   }
384
385   if (!g_spawn_async_with_pipes (NULL, args, NULL, 0, NULL, NULL, &pid, NULL, &babel_stdout, NULL, &error)) {
386     g_warning("Error : %s", error->message);
387     g_error_free(error);
388     ret = FALSE;
389   } else {
390     gchar line[512];
391     FILE *diag;
392     diag = fdopen(babel_stdout, "r");
393     setvbuf(diag, NULL, _IONBF, 0);
394
395     while (fgets(line, sizeof(line), diag)) {
396       if ( cb )
397         cb(BABEL_DIAG_OUTPUT, line, user_data);
398     }
399     if ( cb )
400       cb(BABEL_DONE, NULL, user_data);
401     fclose(diag);
402     diag = NULL;
403     waitpid(pid, NULL, 0);
404     g_spawn_close_pid(pid);
405
406     ret = TRUE;
407   }
408     
409   return ret;
410 }
411 #endif /* Posix */
412
413 gboolean a_babel_convert_to( VikTrwLayer *vt, const char *babelargs, BabelStatusFunc cb, const char *to, gpointer user_data )
414 {
415   int i,j;
416   int fd_src;
417   gchar *name_src;
418   gboolean ret = FALSE;
419   gchar *args[64];  
420
421   if ((fd_src = g_file_open_tmp("tmp-viking.XXXXXX", &name_src, NULL)) >= 0) {
422     gchar *gpsbabel_loc;
423     close(fd_src);
424
425     gpsbabel_loc = g_find_program_in_path("gpsbabel");
426
427     if (gpsbabel_loc ) {
428       gchar *unbuffer_loc = g_find_program_in_path("unbuffer");
429       gchar **sub_args = g_strsplit(babelargs, " ", 0);
430
431       i = 0;
432       if (unbuffer_loc)
433         args[i++] = unbuffer_loc;
434       args[i++] = gpsbabel_loc;
435       args[i++] = "-i";
436       args[i++] = "gpx";
437       for (j = 0; sub_args[j]; j++)
438         /* some version of gpsbabel can not take extra blank arg */
439         if (sub_args[j][0] != '\0')
440           args[i++] = sub_args[j];
441       args[i++] = "-f";
442       args[i++] = name_src;
443       args[i++] = "-F";
444       args[i++] = to;
445       args[i] = NULL;
446
447       ret = babel_general_convert_to ( vt, cb, args, name_src, user_data );
448
449       g_free ( unbuffer_loc );
450       g_strfreev(sub_args);
451     }
452     g_free(gpsbabel_loc);
453   }
454
455   g_remove(name_src);
456   g_free(name_src);
457   return ret;
458 }