Code

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