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