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