Code

f25575c9c236728197dab6f011a74740a43fb588
[inkscape.git] / src / io / sys.cpp
2 /*
3  * System abstraction utility routines
4  *
5  * Authors:
6  *   Jon A. Cruz <jon@joncruz.org>
7  *
8  * Copyright (C) 2004-2005 Authors
9  *
10  * Released under GNU GPL, read the file 'COPYING' for more information
11  */
14 #ifdef HAVE_CONFIG_H
15 # include "config.h"
16 #endif
18 #include <glib.h>
19 #include <glib/gstdio.h>
20 #include <glib/gutils.h>
21 #include <glibmm/fileutils.h>
22 #if GLIB_CHECK_VERSION(2,6,0)
23     #include <glib/gstdio.h>
24 #endif
25 #include <glibmm/ustring.h>
26 #include <gtk/gtkmessagedialog.h>
28 #include "preferences.h"
29 #include "sys.h"
31 #ifdef WIN32
33 #define BYPASS_GLIB_SPAWN 1
35 #ifdef BYPASS_GLIB_SPAWN
37 #include <process.h>    // declares spawn functions
38 #include <wchar.h>      // declares _wspawn functions
40 #ifndef __MINGW32__
41 # ifdef __cplusplus
42 extern "C" {
43 # endif
44 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnl    (int, const wchar_t*, const wchar_t*, ...);
45 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnle   (int, const wchar_t*, const wchar_t*, ...);
46 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnlp   (int, const wchar_t*, const wchar_t*, ...);
47 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnlpe  (int, const wchar_t*, const wchar_t*, ...);
48 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnv    (int, const wchar_t*, const wchar_t* const*);
49 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnve   (int, const wchar_t*, const wchar_t* const*, const wchar_t* const*);
50 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnvp   (int, const wchar_t*, const wchar_t* const*);
51 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnvpe  (int, const wchar_t*, const wchar_t* const*, const wchar_t* const*);
52 # ifdef __cplusplus
53 }
54 # endif
55 #endif
56 #include <unistd.h>
57 #include <glibmm/i18n.h>
58 #include <fcntl.h>
59 #include <io.h>
61 #endif // BYPASS_GLIB_SPAWN
63 // For now to get at is_os_wide().
64 #include "extension/internal/win32.h"
65 using Inkscape::Extension::Internal::PrintWin32;
67 #endif // WIN32
69 //#define INK_DUMP_FILENAME_CONV 1
70 #undef INK_DUMP_FILENAME_CONV
72 //#define INK_DUMP_FOPEN 1
73 #undef INK_DUMP_FOPEN
75 void dump_str(gchar const *str, gchar const *prefix);
76 void dump_ustr(Glib::ustring const &ustr);
78 extern guint update_in_progress;
81 #define DEBUG_MESSAGE(key, ...) \
82 {\
83     Inkscape::Preferences *prefs = Inkscape::Preferences::get(); \
84     gint dump = prefs->getBool("/options/bulia/" #key) ? 1 : 0;\
85     gint dumpD = prefs->getBool("/options/bulia/" #key"D") ? 1 : 0;\
86     gint dumpD2 = prefs->getBool("/options/bulia/" #key"D2") ? 1 : 0;\
87     dumpD &= ( (update_in_progress == 0) || dumpD2 );\
88     if ( dump )\
89     {\
90         g_message( __VA_ARGS__ );\
91 \
92     }\
93     if ( dumpD )\
94     {\
95         GtkWidget *dialog = gtk_message_dialog_new(NULL,\
96                                                    GTK_DIALOG_DESTROY_WITH_PARENT, \
97                                                    GTK_MESSAGE_INFO,    \
98                                                    GTK_BUTTONS_OK,      \
99                                                    __VA_ARGS__          \
100                                                    );\
101         g_signal_connect_swapped(dialog, "response",\
102                                  G_CALLBACK(gtk_widget_destroy),        \
103                                  dialog);                               \
104         gtk_widget_show_all( dialog );\
105     }\
111 void Inkscape::IO::dump_fopen_call( char const *utf8name, char const *id )
113 #ifdef INK_DUMP_FOPEN
114     Glib::ustring str;
115     for ( int i = 0; utf8name[i]; i++ )
116     {
117         if ( utf8name[i] == '\\' )
118         {
119             str += "\\\\";
120         }
121         else if ( (utf8name[i] >= 0x20) && ((0x0ff & utf8name[i]) <= 0x7f) )
122         {
123             str += utf8name[i];
124         }
125         else
126         {
127             gchar tmp[32];
128             g_snprintf( tmp, sizeof(tmp), "\\x%02x", (0x0ff & utf8name[i]) );
129             str += tmp;
130         }
131     }
132     g_message( "fopen call %s for [%s]", id, str.data() );
133 #else
134     (void)utf8name;
135     (void)id;
136 #endif
139 FILE *Inkscape::IO::fopen_utf8name( char const *utf8name, char const *mode )
141     static gint counter = 0;
142     FILE* fp = NULL;
144     DEBUG_MESSAGE( dumpOne, "entering fopen_utf8name( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
146 #ifndef WIN32
147     DEBUG_MESSAGE( dumpOne, "           STEP 0              ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
148     gchar *filename = g_filename_from_utf8( utf8name, -1, NULL, NULL, NULL );
149     if ( filename )
150     {
151         DEBUG_MESSAGE( dumpOne, "           STEP 1              ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
152         fp = std::fopen(filename, mode);
153         DEBUG_MESSAGE( dumpOne, "           STEP 2              ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
154         g_free(filename);
155         DEBUG_MESSAGE( dumpOne, "           STEP 3              ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
156         filename = 0;
157     }
158 #else
159     Glib::ustring how( mode );
160     how.append("b");
161     DEBUG_MESSAGE( dumpOne, "   calling is_os_wide()       ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
163     fp = g_fopen(utf8name, how.c_str());
164 #endif
166     DEBUG_MESSAGE( dumpOne, "leaving fopen_utf8name( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
168     return fp;
172 int Inkscape::IO::mkdir_utf8name( char const *utf8name )
174     static gint counter = 0;
175     int retval = -1;
177     DEBUG_MESSAGE( dumpMk, "entering mkdir_utf8name( '%s' )[%d]", utf8name, (counter++) );
179 #ifndef WIN32
180     DEBUG_MESSAGE( dumpMk, "           STEP 0              ( '%s' )[%d]", utf8name, (counter++) );
181     gchar *filename = g_filename_from_utf8( utf8name, -1, NULL, NULL, NULL );
182     if ( filename )
183     {
184         DEBUG_MESSAGE( dumpMk, "           STEP 1              ( '%s' )[%d]", utf8name, (counter++) );
185         retval = ::mkdir(filename, S_IRWXU | S_IRGRP | S_IXGRP);
186         DEBUG_MESSAGE( dumpMk, "           STEP 2              ( '%s' )[%d]", utf8name, (counter++) );
187         g_free(filename);
188         DEBUG_MESSAGE( dumpMk, "           STEP 3              ( '%s' )[%d]", utf8name, (counter++) );
189         filename = 0;
190     }
191 #else
192     DEBUG_MESSAGE( dumpMk, "   calling is_os_wide()       ( '%s' )[%d]", utf8name, (counter++) );
194     // Mode should be ingnored inside of glib on the way in
195     retval = g_mkdir( utf8name, 0 );
196 #endif
198     DEBUG_MESSAGE( dumpMk, "leaving mkdir_utf8name( '%s' )[%d]", utf8name, (counter++) );
200     return retval;
203 /* 
204  * Wrapper around Glib::file_open_tmp().
205  * Returns a handle to the temp file.
206  * name_used contains the actual name used (a raw filename, not necessarily utf8).
207  * 
208  * Returns:
209  * A file handle (as from open()) to the file opened for reading and writing. 
210  * The file is opened in binary mode on platforms where there is a difference. 
211  * The file handle should be closed with close().
212  * 
213  * Note:
214  * On Windows Vista Glib::file_open_tmp fails with the current version of glibmm
215  * A special case is implemented for WIN32. This can be removed if the issue is fixed
216  * in future versions of glibmm 
217  * */
218 int Inkscape::IO::file_open_tmp(std::string& name_used, const std::string& prefix)
220 #ifndef WIN32
221     return Glib::file_open_tmp(name_used, prefix);
222 #else
223     /* Special case for WIN32 due to a bug in glibmm
224      * (only needed for Windows Vista, but since there is only one windows build all builds get the workaround)
225      * The workaround can be removed if the bug is fixed in glibmm
226      * 
227      * The code is mostly identical to the implementation in glibmm
228      * http://svn.gnome.org/svn/glibmm/branches/glibmm-2-12/glib/src/fileutils.ccg
229      * */
230     
231     std::string basename_template (prefix);
232     basename_template += "XXXXXX"; // this sillyness shouldn't be in the interface
233     
234     GError* error = 0;
235     gchar *buf_name_used;
236     
237     gint fileno = g_file_open_tmp(basename_template.c_str(), &buf_name_used, &error);
238     
239     if(error)
240         Glib::Error::throw_exception(error);
241     
242     name_used = g_strdup(buf_name_used);
243     g_free(buf_name_used);
244     return fileno;
245 #endif
248 bool Inkscape::IO::file_test( char const *utf8name, GFileTest test )
250     bool exists = false;
252     if ( utf8name ) {
253         gchar *filename = NULL;
254         if (utf8name && !g_utf8_validate(utf8name, -1, NULL)) {
255             /* FIXME: Trying to guess whether or not a filename is already in utf8 is unreliable.
256                If any callers pass non-utf8 data (e.g. using g_get_home_dir), then change caller to
257                use simple g_file_test.  Then add g_return_val_if_fail(g_utf_validate(...), false)
258                to beginning of this function. */
259             filename = g_strdup(utf8name);
260             // Looks like g_get_home_dir isn't safe.
261             //g_warning("invalid UTF-8 detected internally. HUNT IT DOWN AND KILL IT!!!");
262         } else {
263             filename = g_filename_from_utf8 ( utf8name, -1, NULL, NULL, NULL );
264         }
265         if ( filename ) {
266             exists = g_file_test (filename, test);
267             g_free(filename);
268             filename = NULL;
269         } else {
270             g_warning( "Unable to convert filename in IO:file_test" );
271         }
272     }
274     return exists;
277 bool Inkscape::IO::file_is_writable( char const *utf8name)
279     bool success = true;
281     if ( utf8name) {
282         gchar *filename = NULL;
283         if (utf8name && !g_utf8_validate(utf8name, -1, NULL)) {
284             /* FIXME: Trying to guess whether or not a filename is already in utf8 is unreliable.
285                If any callers pass non-utf8 data (e.g. using g_get_home_dir), then change caller to
286                use simple g_file_test.  Then add g_return_val_if_fail(g_utf_validate(...), false)
287                to beginning of this function. */
288             filename = g_strdup(utf8name);
289             // Looks like g_get_home_dir isn't safe.
290             //g_warning("invalid UTF-8 detected internally. HUNT IT DOWN AND KILL IT!!!");
291         } else {
292             filename = g_filename_from_utf8 ( utf8name, -1, NULL, NULL, NULL );
293         }
294         if ( filename ) {
295             struct stat st;
296             if(g_lstat (filename, &st) == 0) {
297                 success = ((st.st_mode & S_IWRITE) != 0);
298             }
299             g_free(filename);
300             filename = NULL;
301         } else {
302             g_warning( "Unable to convert filename in IO:file_test" );
303         }
304     }
306     return success;
309 /** Wrapper around g_dir_open, but taking a utf8name as first argument. */
310 GDir *
311 Inkscape::IO::dir_open(gchar const *const utf8name, guint const flags, GError **const error)
313     gchar *const opsys_name = g_filename_from_utf8(utf8name, -1, NULL, NULL, error);
314     if (opsys_name) {
315         GDir *ret = g_dir_open(opsys_name, flags, error);
316         g_free(opsys_name);
317         return ret;
318     } else {
319         return NULL;
320     }
323 /**
324  * Like g_dir_read_name, but returns a utf8name (which must be freed, unlike g_dir_read_name).
325  *
326  * N.B. Skips over any dir entries that fail to convert to utf8.
327  */
328 gchar *
329 Inkscape::IO::dir_read_utf8name(GDir *dir)
331     for (;;) {
332         gchar const *const opsys_name = g_dir_read_name(dir);
333         if (!opsys_name) {
334             return NULL;
335         }
336         gchar *utf8_name = g_filename_to_utf8(opsys_name, -1, NULL, NULL, NULL);
337         if (utf8_name) {
338             return utf8_name;
339         }
340     }
344 gchar* Inkscape::IO::locale_to_utf8_fallback( const gchar *opsysstring,
345                                               gssize len,
346                                               gsize *bytes_read,
347                                               gsize *bytes_written,
348                                               GError **error )
350     gchar *result = NULL;
351     if ( opsysstring ) {
352         gchar *newFileName = g_locale_to_utf8( opsysstring, len, bytes_read, bytes_written, error );
353         if ( newFileName ) {
354             if ( !g_utf8_validate(newFileName, -1, NULL) ) {
355                 g_warning( "input filename did not yield UTF-8" );
356                 g_free( newFileName );
357             } else {
358                 result = newFileName;
359             }
360             newFileName = 0;
361         } else if ( g_utf8_validate(opsysstring, -1, NULL) ) {
362             // This *might* be a case that we want
363             // g_warning( "input failed filename->utf8, fell back to original" );
364             // TODO handle cases when len >= 0
365             result = g_strdup( opsysstring );
366         } else {
367             gchar const *charset = 0;
368             g_get_charset(&charset);
369             g_warning( "input filename conversion failed for file with locale charset '%s'", charset );
370         }
371     }
372     return result;
375 #ifdef BYPASS_GLIB_SPAWN
376 /*
377         this code was taken from the original glib sources
378 */
379 #define GSPAWN_HELPER
380  
381 enum
383   CHILD_NO_ERROR,
384   CHILD_CHDIR_FAILED,
385   CHILD_SPAWN_FAILED,
386 };
388 enum {
389   ARG_CHILD_ERR_REPORT = 1,
390   ARG_HELPER_SYNC,
391   ARG_STDIN,
392   ARG_STDOUT,
393   ARG_STDERR,
394   ARG_WORKING_DIRECTORY,
395   ARG_CLOSE_DESCRIPTORS,
396   ARG_USE_PATH,
397   ARG_WAIT,
398   ARG_PROGRAM,
399   ARG_COUNT = ARG_PROGRAM
400 };
401 static int debug = 0;
402 #define HELPER_PROCESS "gspawn-win32-helper"
405 static int
406 dup_noninherited (int fd,
407                   int mode)
409   HANDLE filehandle;
411   DuplicateHandle (GetCurrentProcess (), (LPHANDLE) _get_osfhandle (fd),
412                    GetCurrentProcess (), &filehandle,
413                    0, FALSE, DUPLICATE_SAME_ACCESS);
414   close (fd);
415   return _open_osfhandle ((LONG_PTR) filehandle, mode | _O_NOINHERIT);
419 /* The helper process writes a status report back to us, through a
420  * pipe, consisting of two ints.
421  */
422 static gboolean
423 read_helper_report (int      fd,
424                     gint     report[2],
425                     GError **error)
427   gint bytes = 0;
429   while (bytes < sizeof(gint)*2)
430     {
431       gint chunk;
433       if (debug)
434         g_print ("%s:read_helper_report: read %d...\n",
435                  __FILE__,
436                  sizeof(gint)*2 - bytes);
438       chunk = read (fd, ((gchar*)report) + bytes,
439                     sizeof(gint)*2 - bytes);
441       if (debug)
442         g_print ("...got %d bytes\n", chunk);
444       if (chunk < 0)
445         {
446           /* Some weird shit happened, bail out */
448           g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
449                        _("Failed to read from child pipe (%s)"),
450                        g_strerror (errno));
452           return FALSE;
453         }
454       else if (chunk == 0)
455         {
456           g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
457                        _("Failed to read from child pipe (%s)"),
458                        "EOF");
459           break; /* EOF */
460         }
461       else
462         bytes += chunk;
463     }
465   if (bytes < sizeof(gint)*2)
466     return FALSE;
468   return TRUE;
472 static void
473 set_child_error (gint         report[2],
474                  const gchar *working_directory,
475                  GError     **error)
477   switch (report[0])
478     {
479     case CHILD_CHDIR_FAILED:
480       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
481                    _("Failed to change to directory '%s' (%s)"),
482                    working_directory,
483                    g_strerror (report[1]));
484       break;
485     case CHILD_SPAWN_FAILED:
486       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
487                    _("Failed to execute child process (%s)"),
488                    g_strerror (report[1]));
489       break;
490     default:
491       g_assert_not_reached ();
492     }
495 static gchar *
496 protect_argv_string (const gchar *string)
498   const gchar *p = string;
499   gchar *retval, *q;
500   gint len = 0;
501   gboolean need_dblquotes = FALSE;
502   while (*p)
503     {
504       if (*p == ' ' || *p == '\t')
505         need_dblquotes = TRUE;
506       else if (*p == '"')
507         len++;
508       else if (*p == '\\')
509         {
510           const gchar *pp = p;
511           while (*pp && *pp == '\\')
512             pp++;
513           if (*pp == '"')
514             len++;
515         }
516       len++;
517       p++;
518     }
520   q = retval = (gchar *)g_malloc (len + need_dblquotes*2 + 1);
521   p = string;
523   if (need_dblquotes)
524     *q++ = '"';
526   while (*p)
527     {
528       if (*p == '"')
529         *q++ = '\\';
530       else if (*p == '\\')
531         {
532           const gchar *pp = p;
533           while (*pp && *pp == '\\')
534             pp++;
535           if (*pp == '"')
536             *q++ = '\\';
537         }
538       *q++ = *p;
539       p++;
540     }
542   if (need_dblquotes)
543     *q++ = '"';
544   *q++ = '\0';
546   return retval;
550 static gint
551 protect_argv (gchar  **argv,
552               gchar ***new_argv)
554   gint i;
555   gint argc = 0;
557   while (argv[argc])
558     ++argc;
559   *new_argv = g_new (gchar *, argc+1);
561   /* Quote each argv element if necessary, so that it will get
562    * reconstructed correctly in the C runtime startup code.  Note that
563    * the unquoting algorithm in the C runtime is really weird, and
564    * rather different than what Unix shells do. See stdargv.c in the C
565    * runtime sources (in the Platform SDK, in src/crt).
566    *
567    * Note that an new_argv[0] constructed by this function should
568    * *not* be passed as the filename argument to a spawn* or exec*
569    * family function. That argument should be the real file name
570    * without any quoting.
571    */
572   for (i = 0; i < argc; i++)
573     (*new_argv)[i] = protect_argv_string (argv[i]);
575   (*new_argv)[argc] = NULL;
577   return argc;
581 static gboolean
582 utf8_charv_to_wcharv (char     **utf8_charv,
583                       wchar_t ***wcharv,
584                       int       *error_index,
585                       GError   **error)
587   wchar_t **retval = NULL;
589   *wcharv = NULL;
590   if (utf8_charv != NULL)
591     {
592       int n = 0, i;
594       while (utf8_charv[n])
595         n++;
596       retval = g_new (wchar_t *, n + 1);
598       for (i = 0; i < n; i++)
599         {
600           retval[i] = (wchar_t *)g_utf8_to_utf16 (utf8_charv[i], -1, NULL, NULL, error);
601           if (retval[i] == NULL)
602             {
603               if (error_index)
604                 *error_index = i;
605               while (i)
606                 g_free (retval[--i]);
607               g_free (retval);
608               return FALSE;
609             }
610         }
612       retval[n] = NULL;
613     }
614   *wcharv = retval;
615   return TRUE;
619 /* Avoids a danger in threaded situations (calling close()
620  * on a file descriptor twice, and another thread has
621  * re-opened it since the first close)
622  */
623 static void
624 close_and_invalidate (gint *fd)
626   if (*fd < 0)
627     return;
629   close (*fd);
630   *fd = -1;
634 static gboolean
635 do_spawn_directly (gint                 *exit_status,
636                    gboolean              do_return_handle,
637                    GSpawnFlags           flags,
638                    gchar               **argv,
639                    char                **envp,
640                    char                **protected_argv,
641                    GSpawnChildSetupFunc  child_setup,
642                    gpointer              user_data,
643                    GPid                 *child_handle,
644                    GError              **error)
646   const int mode = (exit_status == NULL) ? P_NOWAIT : P_WAIT;
647   char **new_argv;
648   int rc = -1;
649   int saved_errno;
650   GError *conv_error = NULL;
651   gint conv_error_index;
652   wchar_t *wargv0, **wargv, **wenvp;
654   new_argv = (flags & G_SPAWN_FILE_AND_ARGV_ZERO) ? protected_argv + 1 : protected_argv;
656   wargv0 = (wchar_t *)g_utf8_to_utf16 (argv[0], -1, NULL, NULL, &conv_error);
657   if (wargv0 == NULL)
658     {
659       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
660                    _("Invalid program name: %s"),
661                    conv_error->message);
662       g_error_free (conv_error);
664       return FALSE;
665     }
667   if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
668     {
669       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
670                    _("Invalid string in argument vector at %d: %s"),
671                    conv_error_index, conv_error->message);
672       g_error_free (conv_error);
673       g_free (wargv0);
675       return FALSE;
676     }
678   if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
679     {
680       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
681                    _("Invalid string in environment: %s"),
682                    conv_error->message);
683       g_error_free (conv_error);
684       g_free (wargv0);
685       g_strfreev ((gchar **) wargv);
687       return FALSE;
688     }
690   if (child_setup)
691     (* child_setup) (user_data);
693   if (flags & G_SPAWN_SEARCH_PATH)
694     if (wenvp != NULL)
695       rc = _wspawnvpe (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
696     else
697       rc = _wspawnvp (mode, wargv0, (const wchar_t **) wargv);
698   else
699     if (wenvp != NULL)
700       rc = _wspawnve (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
701     else
702       rc = _wspawnv (mode, wargv0, (const wchar_t **) wargv);
704   g_free (wargv0);
705   g_strfreev ((gchar **) wargv);
706   g_strfreev ((gchar **) wenvp);
708   saved_errno = errno;
710   if (rc == -1 && saved_errno != 0)
711     {
712       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
713                    _("Failed to execute child process (%s)"),
714                    g_strerror (saved_errno));
715       return FALSE;
716     }
718   if (exit_status == NULL)
719     {
720       if (child_handle && do_return_handle)
721         *child_handle = (GPid) rc;
722       else
723         {
724           CloseHandle ((HANDLE) rc);
725           if (child_handle)
726             *child_handle = 0;
727         }
728     }
729   else
730     *exit_status = rc;
732   return TRUE;
735 static gboolean
736 make_pipe (gint     p[2],
737            GError **error)
739   if (_pipe (p, 4096, _O_BINARY) < 0)
740     {
741       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
742                    _("Failed to create pipe for communicating with child process (%s)"),
743                    g_strerror (errno));
744       return FALSE;
745     }
746   else
747     return TRUE;
751 static gboolean
752 do_spawn_with_pipes (gint                 *exit_status,
753                      gboolean              do_return_handle,
754                      const gchar          *working_directory,
755                      gchar               **argv,
756                      char                **envp,
757                      GSpawnFlags           flags,
758                      GSpawnChildSetupFunc  child_setup,
759                      gpointer              user_data,
760                      GPid                 *child_handle,
761                      gint                 *standard_input,
762                      gint                 *standard_output,
763                      gint                 *standard_error,
764                      gint                 *err_report,
765                      GError              **error)
767   char **protected_argv;
768   char args[ARG_COUNT][10];
769   char **new_argv;
770   int i;
771   int rc = -1;
772   int saved_errno;
773   int argc;
774   int stdin_pipe[2] = { -1, -1 };
775   int stdout_pipe[2] = { -1, -1 };
776   int stderr_pipe[2] = { -1, -1 };
777   int child_err_report_pipe[2] = { -1, -1 };
778   int helper_sync_pipe[2] = { -1, -1 };
779   int helper_report[2];
780   static gboolean warned_about_child_setup = FALSE;
781   GError *conv_error = NULL;
782   gint conv_error_index;
783   gchar *helper_process;
784   CONSOLE_CURSOR_INFO cursor_info;
785   wchar_t *whelper, **wargv, **wenvp;
786   //extern gchar *_glib_get_installation_directory (void);
787   gchar *glib_top;
789   if (child_setup && !warned_about_child_setup)
790     {
791       warned_about_child_setup = TRUE;
792       g_warning ("passing a child setup function to the g_spawn functions is pointless and dangerous on Win32");
793     }
795   argc = protect_argv (argv, &protected_argv);
797   if (!standard_input && !standard_output && !standard_error &&
798       (flags & G_SPAWN_CHILD_INHERITS_STDIN) &&
799       !(flags & G_SPAWN_STDOUT_TO_DEV_NULL) &&
800       !(flags & G_SPAWN_STDERR_TO_DEV_NULL) &&
801       (working_directory == NULL || !*working_directory) &&
802       (flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
803     {
804       /* We can do without the helper process */
805       gboolean retval =
806         do_spawn_directly (exit_status, do_return_handle, flags,
807                            argv, envp, protected_argv,
808                            child_setup, user_data, child_handle,
809                            error);
810       g_strfreev (protected_argv);
811       return retval;
812     }
814   if (standard_input && !make_pipe (stdin_pipe, error))
815     goto cleanup_and_fail;
817   if (standard_output && !make_pipe (stdout_pipe, error))
818     goto cleanup_and_fail;
820   if (standard_error && !make_pipe (stderr_pipe, error))
821     goto cleanup_and_fail;
823   if (!make_pipe (child_err_report_pipe, error))
824     goto cleanup_and_fail;
826   if (!make_pipe (helper_sync_pipe, error))
827     goto cleanup_and_fail;
829   new_argv = g_new (char *, argc + 1 + ARG_COUNT);
830   if (GetConsoleCursorInfo (GetStdHandle (STD_OUTPUT_HANDLE), &cursor_info))
831     helper_process = HELPER_PROCESS "-console.exe";
832   else
833     helper_process = HELPER_PROCESS ".exe";
835   glib_top = NULL;
836   if (glib_top != NULL)
837     {
838       helper_process = g_build_filename (glib_top, "bin", helper_process, NULL);
839       g_free (glib_top);
840     }
841   else
842     helper_process = g_strdup (helper_process);
844   new_argv[0] = protect_argv_string (helper_process);
846   sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_pipe[1]);
847   new_argv[ARG_CHILD_ERR_REPORT] = args[ARG_CHILD_ERR_REPORT];
849   /* Make the read end of the child error report pipe
850    * noninherited. Otherwise it will needlessly be inherited by the
851    * helper process, and the started actual user process. As such that
852    * shouldn't harm, but it is unnecessary.
853    */
854   child_err_report_pipe[0] = dup_noninherited (child_err_report_pipe[0], _O_RDONLY);
856   if (flags & G_SPAWN_FILE_AND_ARGV_ZERO)
857     {
858       /* Overload ARG_CHILD_ERR_REPORT to also encode the
859        * G_SPAWN_FILE_AND_ARGV_ZERO functionality.
860        */
861       strcat (args[ARG_CHILD_ERR_REPORT], "#");
862     }
864   sprintf (args[ARG_HELPER_SYNC], "%d", helper_sync_pipe[0]);
865   new_argv[ARG_HELPER_SYNC] = args[ARG_HELPER_SYNC];
867   /* Make the write end of the sync pipe noninherited. Otherwise the
868    * helper process will inherit it, and thus if this process happens
869    * to crash before writing the sync byte to the pipe, the helper
870    * process won't read but won't get any EOF either, as it has the
871    * write end open itself.
872    */
873   helper_sync_pipe[1] = dup_noninherited (helper_sync_pipe[1], _O_WRONLY);
875   if (standard_input)
876     {
877       sprintf (args[ARG_STDIN], "%d", stdin_pipe[0]);
878       new_argv[ARG_STDIN] = args[ARG_STDIN];
879     }
880   else if (flags & G_SPAWN_CHILD_INHERITS_STDIN)
881     {
882       /* Let stdin be alone */
883       new_argv[ARG_STDIN] = "-";
884     }
885   else
886     {
887       /* Keep process from blocking on a read of stdin */
888       new_argv[ARG_STDIN] = "z";
889     }
891   if (standard_output)
892     {
893       sprintf (args[ARG_STDOUT], "%d", stdout_pipe[1]);
894       new_argv[ARG_STDOUT] = args[ARG_STDOUT];
895     }
896   else if (flags & G_SPAWN_STDOUT_TO_DEV_NULL)
897     {
898       new_argv[ARG_STDOUT] = "z";
899     }
900   else
901     {
902       new_argv[ARG_STDOUT] = "-";
903     }
905   if (standard_error)
906     {
907       sprintf (args[ARG_STDERR], "%d", stderr_pipe[1]);
908      new_argv[ARG_STDERR] = args[ARG_STDERR];
909     }
910   else if (flags & G_SPAWN_STDERR_TO_DEV_NULL)
911     {
912       new_argv[ARG_STDERR] = "z";
913     }
914   else
915     {
916       new_argv[ARG_STDERR] = "-";
917     }
919   if (working_directory && *working_directory)
920     new_argv[ARG_WORKING_DIRECTORY] = protect_argv_string (working_directory);
921   else
922     new_argv[ARG_WORKING_DIRECTORY] = g_strdup ("-");
924   if (!(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
925     new_argv[ARG_CLOSE_DESCRIPTORS] = "y";
926   else
927     new_argv[ARG_CLOSE_DESCRIPTORS] = "-";
929   if (flags & G_SPAWN_SEARCH_PATH)
930     new_argv[ARG_USE_PATH] = "y";
931   else
932     new_argv[ARG_USE_PATH] = "-";
934   if (exit_status == NULL)
935     new_argv[ARG_WAIT] = "-";
936   else
937     new_argv[ARG_WAIT] = "w";
939   for (i = 0; i <= argc; i++)
940     new_argv[ARG_PROGRAM + i] = protected_argv[i];
942   //SETUP_DEBUG();
944   if (debug)
945     {
946       g_print ("calling %s with argv:\n", helper_process);
947       for (i = 0; i < argc + 1 + ARG_COUNT; i++)
948         g_print ("argv[%d]: %s\n", i, (new_argv[i] ? new_argv[i] : "NULL"));
949     }
951   if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
952     {
953       if (conv_error_index == ARG_WORKING_DIRECTORY)
954         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
955                      _("Invalid working directory: %s"),
956                      conv_error->message);
957       else
958         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
959                      _("Invalid string in argument vector at %d: %s"),
960                      conv_error_index - ARG_PROGRAM, conv_error->message);
961       g_error_free (conv_error);
962       g_strfreev (protected_argv);
963       g_free (new_argv[0]);
964       g_free (new_argv[ARG_WORKING_DIRECTORY]);
965       g_free (new_argv);
966       g_free (helper_process);
968       goto cleanup_and_fail;
969     }
971   if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
972     {
973       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
974                    _("Invalid string in environment: %s"),
975                    conv_error->message);
976       g_error_free (conv_error);
977       g_strfreev (protected_argv);
978       g_free (new_argv[0]);
979       g_free (new_argv[ARG_WORKING_DIRECTORY]);
980       g_free (new_argv);
981       g_free (helper_process);
982       g_strfreev ((gchar **) wargv);
984       goto cleanup_and_fail;
985     }
987   if (child_setup)
988     (* child_setup) (user_data);
990   whelper = (wchar_t *)g_utf8_to_utf16 (helper_process, -1, NULL, NULL, NULL);
991   g_free (helper_process);
993   if (wenvp != NULL)
994     rc = _wspawnvpe (P_NOWAIT, whelper, (const wchar_t **) wargv, (const wchar_t **) wenvp);
995   else
996     rc = _wspawnvp (P_NOWAIT, whelper, (const wchar_t **) wargv);
998   saved_errno = errno;
1000   g_free (whelper);
1001   g_strfreev ((gchar **) wargv);
1002   g_strfreev ((gchar **) wenvp);
1004   /* Close the other process's ends of the pipes in this process,
1005    * otherwise the reader will never get EOF.
1006    */
1007   close_and_invalidate (&child_err_report_pipe[1]);
1008   close_and_invalidate (&helper_sync_pipe[0]);
1009   close_and_invalidate (&stdin_pipe[0]);
1010   close_and_invalidate (&stdout_pipe[1]);
1011   close_and_invalidate (&stderr_pipe[1]);
1013   g_strfreev (protected_argv);
1015   g_free (new_argv[0]);
1016   g_free (new_argv[ARG_WORKING_DIRECTORY]);
1017   g_free (new_argv);
1019   /* Check if gspawn-win32-helper couldn't be run */
1020   if (rc == -1 && saved_errno != 0)
1021     {
1022       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
1023                    _("Failed to execute helper program (%s)"),
1024                    g_strerror (saved_errno));
1025       goto cleanup_and_fail;
1026     }
1028   if (exit_status != NULL)
1029     {
1030       /* Synchronous case. Pass helper's report pipe back to caller,
1031        * which takes care of reading it after the grandchild has
1032        * finished.
1033        */
1034       g_assert (err_report != NULL);
1035       *err_report = child_err_report_pipe[0];
1036       write (helper_sync_pipe[1], " ", 1);
1037       close_and_invalidate (&helper_sync_pipe[1]);
1038     }
1039   else
1040     {
1041       /* Asynchronous case. We read the helper's report right away. */
1042       if (!read_helper_report (child_err_report_pipe[0], helper_report, error))
1043         goto cleanup_and_fail;
1045       close_and_invalidate (&child_err_report_pipe[0]);
1047       switch (helper_report[0])
1048         {
1049         case CHILD_NO_ERROR:
1050           if (child_handle && do_return_handle)
1051             {
1052               /* rc is our HANDLE for gspawn-win32-helper. It has
1053                * told us the HANDLE of its child. Duplicate that into
1054                * a HANDLE valid in this process.
1055                */
1056               if (!DuplicateHandle ((HANDLE) rc, (HANDLE) helper_report[1],
1057                                     GetCurrentProcess (), (LPHANDLE) child_handle,
1058                                     0, TRUE, DUPLICATE_SAME_ACCESS))
1059                 {
1060                   char *emsg = g_win32_error_message (GetLastError ());
1061                   g_print("%s\n", emsg);
1062                   *child_handle = 0;
1063                 }
1064             }
1065           else if (child_handle)
1066             *child_handle = 0;
1067           write (helper_sync_pipe[1], " ", 1);
1068           close_and_invalidate (&helper_sync_pipe[1]);
1069           break;
1071         default:
1072           write (helper_sync_pipe[1], " ", 1);
1073           close_and_invalidate (&helper_sync_pipe[1]);
1074           set_child_error (helper_report, working_directory, error);
1075           goto cleanup_and_fail;
1076         }
1077     }
1079   /* Success against all odds! return the information */
1081   if (standard_input)
1082     *standard_input = stdin_pipe[1];
1083   if (standard_output)
1084     *standard_output = stdout_pipe[0];
1085   if (standard_error)
1086     *standard_error = stderr_pipe[0];
1087   if (rc != -1)
1088     CloseHandle ((HANDLE) rc);
1090   return TRUE;
1092   cleanup_and_fail:
1094   if (rc != -1)
1095     CloseHandle ((HANDLE) rc);
1096   if (child_err_report_pipe[0] != -1)
1097     close (child_err_report_pipe[0]);
1098   if (child_err_report_pipe[1] != -1)
1099     close (child_err_report_pipe[1]);
1100   if (helper_sync_pipe[0] != -1)
1101     close (helper_sync_pipe[0]);
1102   if (helper_sync_pipe[1] != -1)
1103     close (helper_sync_pipe[1]);
1104   if (stdin_pipe[0] != -1)
1105     close (stdin_pipe[0]);
1106   if (stdin_pipe[1] != -1)
1107     close (stdin_pipe[1]);
1108   if (stdout_pipe[0] != -1)
1109     close (stdout_pipe[0]);
1110   if (stdout_pipe[1] != -1)
1111     close (stdout_pipe[1]);
1112   if (stderr_pipe[0] != -1)
1113     close (stderr_pipe[0]);
1114   if (stderr_pipe[1] != -1)
1115     close (stderr_pipe[1]);
1117   return FALSE;
1120 gboolean
1121 my_spawn_async_with_pipes_utf8 (const gchar          *working_directory,
1122                                gchar               **argv,
1123                                gchar               **envp,
1124                                GSpawnFlags           flags,
1125                                GSpawnChildSetupFunc  child_setup,
1126                                gpointer              user_data,
1127                                GPid                 *child_handle,
1128                                gint                 *standard_input,
1129                                gint                 *standard_output,
1130                                gint                 *standard_error,
1131                                GError              **error)
1133   g_return_val_if_fail (argv != NULL, FALSE);
1134   g_return_val_if_fail (standard_output == NULL ||
1135                         !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
1136   g_return_val_if_fail (standard_error == NULL ||
1137                         !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
1138   /* can't inherit stdin if we have an input pipe. */
1139   g_return_val_if_fail (standard_input == NULL ||
1140                         !(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
1142   return do_spawn_with_pipes (NULL,
1143                               (flags & G_SPAWN_DO_NOT_REAP_CHILD),
1144                               working_directory,
1145                               argv,
1146                               envp,
1147                               flags,
1148                               child_setup,
1149                               user_data,
1150                               child_handle,
1151                               standard_input,
1152                               standard_output,
1153                               standard_error,
1154                               NULL,
1155                               error);
1158 typedef GPid Pid;
1160 // _WRAP_ENUM(SpawnFlags, GSpawnFlags, NO_GTYPE)
1162 /* Helper callback to invoke the actual sigc++ slot.
1163  * We don't need to worry about (un)referencing, since the
1164  * child process gets its own copy of the parent's memory anyway.
1165  */
1166 static void child_setup_callback(void* user_data)
1168   #ifdef GLIBMM_EXCEPTIONS_ENABLED
1169   try
1170   {
1171   #endif //GLIBMM_EXCEPTIONS_ENABLED
1172     (*reinterpret_cast<sigc::slot<void>*>(user_data))();
1173   #ifdef GLIBMM_EXCEPTIONS_ENABLED
1174   }
1175   catch(...)
1176   {
1177     Glib::exception_handlers_invoke();
1178   }
1179   #endif //GLIBMM_EXCEPTIONS_ENABLED
1183 void my_spawn_async_with_pipes(const std::string& working_directory,
1184                             const Glib::ArrayHandle<std::string>& argv,
1185                             GSpawnFlags flags,
1186                             const sigc::slot<void>& child_setup,
1187                             Pid* child_pid,
1188                             int* standard_input,
1189                             int* standard_output,
1190                             int* standard_error)
1192   const bool setup_slot = !child_setup.empty();
1193   sigc::slot<void> child_setup_ = child_setup;
1194   GError* error = 0;
1196   my_spawn_async_with_pipes_utf8(
1197       working_directory.c_str(),
1198       const_cast<char**>(argv.data()), 0,
1199       static_cast<GSpawnFlags>(unsigned(flags)),
1200       (setup_slot) ? &child_setup_callback : 0,
1201       (setup_slot) ? &child_setup_         : 0,
1202       child_pid,
1203       standard_input, standard_output, standard_error,
1204       &error);
1206   if(error)
1207     Glib::Error::throw_exception(error);
1210 #endif
1212 void
1213 Inkscape::IO::spawn_async_with_pipes( const std::string& working_directory,
1214                                       const Glib::ArrayHandle<std::string>& argv,
1215                                       Glib::SpawnFlags flags,
1216                                       const sigc::slot<void>& child_setup,
1217                                       Glib::Pid* child_pid,
1218                                       int* standard_input,
1219                                       int* standard_output,
1220                                       int* standard_error)
1222 #ifndef BYPASS_GLIB_SPAWN
1223     Glib::spawn_async_with_pipes(working_directory,
1224                                  argv,
1225                                  flags,
1226                                  child_setup,
1227                                  child_pid,
1228                                  standard_input,
1229                                  standard_output,
1230                                  standard_error);
1231 #else
1232     my_spawn_async_with_pipes(working_directory,
1233                               argv,
1234                               static_cast<GSpawnFlags>(flags),
1235                               child_setup,
1236                               child_pid,
1237                               standard_input,
1238                               standard_output,
1239                               standard_error);
1240 #endif
1244 gchar* Inkscape::IO::sanitizeString( gchar const * str )
1246     gchar *result = NULL;
1247     if ( str ) {
1248         if ( g_utf8_validate(str, -1, NULL) ) {
1249             result = g_strdup(str);
1250         } else {
1251             guchar scratch[8];
1252             Glib::ustring buf;
1253             guchar const *ptr = (guchar const*)str;
1254             while ( *ptr )
1255             {
1256                 if ( *ptr == '\\' )
1257                 {
1258                     buf.append("\\\\");
1259                 } else if ( *ptr < 0x80 ) {
1260                     buf += (char)(*ptr);
1261                 } else {
1262                     g_snprintf((gchar*)scratch, sizeof(scratch), "\\x%02x", *ptr);
1263                     buf.append((const char*)scratch);
1264                 }
1265                 ptr++;
1266             }
1267             result = g_strdup(buf.c_str());
1268         }
1269     }
1270     return result;
1273 /*
1274   Local Variables:
1275   mode:c++
1276   c-file-style:"stroustrup"
1277   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1278   indent-tabs-mode:nil
1279   fill-column:99
1280   End:
1281 */
1282 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :