Code

Cast cleanup.
[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(reinterpret_cast<LONG_PTR>(filehandle), mode | _O_NOINHERIT);
418 /* The helper process writes a status report back to us, through a
419  * pipe, consisting of two ints.
420  */
421 static gboolean
422 read_helper_report (int      fd,
423                     gint     report[2],
424                     GError **error)
426   gint bytes = 0;
428   while (bytes < sizeof(gint)*2)
429     {
430       gint chunk;
432       if (debug)
433         g_print ("%s:read_helper_report: read %d...\n",
434                  __FILE__,
435                  sizeof(gint)*2 - bytes);
437       chunk = read (fd, ((gchar*)report) + bytes,
438                     sizeof(gint)*2 - bytes);
440       if (debug)
441         g_print ("...got %d bytes\n", chunk);
443       if (chunk < 0)
444         {
445           /* Some weird shit happened, bail out */
447           g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
448                        _("Failed to read from child pipe (%s)"),
449                        g_strerror (errno));
451           return FALSE;
452         }
453       else if (chunk == 0)
454         {
455           g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
456                        _("Failed to read from child pipe (%s)"),
457                        "EOF");
458           break; /* EOF */
459         }
460       else
461         bytes += chunk;
462     }
464   if (bytes < sizeof(gint)*2)
465     return FALSE;
467   return TRUE;
471 static void
472 set_child_error (gint         report[2],
473                  const gchar *working_directory,
474                  GError     **error)
476   switch (report[0])
477     {
478     case CHILD_CHDIR_FAILED:
479       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
480                    _("Failed to change to directory '%s' (%s)"),
481                    working_directory,
482                    g_strerror (report[1]));
483       break;
484     case CHILD_SPAWN_FAILED:
485       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
486                    _("Failed to execute child process (%s)"),
487                    g_strerror (report[1]));
488       break;
489     default:
490       g_assert_not_reached ();
491     }
494 static gchar *
495 protect_argv_string (const gchar *string)
497   const gchar *p = string;
498   gchar *retval, *q;
499   gint len = 0;
500   gboolean need_dblquotes = FALSE;
501   while (*p)
502     {
503       if (*p == ' ' || *p == '\t')
504         need_dblquotes = TRUE;
505       else if (*p == '"')
506         len++;
507       else if (*p == '\\')
508         {
509           const gchar *pp = p;
510           while (*pp && *pp == '\\')
511             pp++;
512           if (*pp == '"')
513             len++;
514         }
515       len++;
516       p++;
517     }
519   q = retval = (gchar *)g_malloc (len + need_dblquotes*2 + 1);
520   p = string;
522   if (need_dblquotes)
523     *q++ = '"';
525   while (*p)
526     {
527       if (*p == '"')
528         *q++ = '\\';
529       else if (*p == '\\')
530         {
531           const gchar *pp = p;
532           while (*pp && *pp == '\\')
533             pp++;
534           if (*pp == '"')
535             *q++ = '\\';
536         }
537       *q++ = *p;
538       p++;
539     }
541   if (need_dblquotes)
542     *q++ = '"';
543   *q++ = '\0';
545   return retval;
549 static gint
550 protect_argv (gchar  **argv,
551               gchar ***new_argv)
553   gint i;
554   gint argc = 0;
556   while (argv[argc])
557     ++argc;
558   *new_argv = g_new (gchar *, argc+1);
560   /* Quote each argv element if necessary, so that it will get
561    * reconstructed correctly in the C runtime startup code.  Note that
562    * the unquoting algorithm in the C runtime is really weird, and
563    * rather different than what Unix shells do. See stdargv.c in the C
564    * runtime sources (in the Platform SDK, in src/crt).
565    *
566    * Note that an new_argv[0] constructed by this function should
567    * *not* be passed as the filename argument to a spawn* or exec*
568    * family function. That argument should be the real file name
569    * without any quoting.
570    */
571   for (i = 0; i < argc; i++)
572     (*new_argv)[i] = protect_argv_string (argv[i]);
574   (*new_argv)[argc] = NULL;
576   return argc;
580 static gboolean
581 utf8_charv_to_wcharv (char     **utf8_charv,
582                       wchar_t ***wcharv,
583                       int       *error_index,
584                       GError   **error)
586   wchar_t **retval = NULL;
588   *wcharv = NULL;
589   if (utf8_charv != NULL)
590     {
591       int n = 0, i;
593       while (utf8_charv[n])
594         n++;
595       retval = g_new (wchar_t *, n + 1);
597       for (i = 0; i < n; i++)
598         {
599           retval[i] = (wchar_t *)g_utf8_to_utf16 (utf8_charv[i], -1, NULL, NULL, error);
600           if (retval[i] == NULL)
601             {
602               if (error_index)
603                 *error_index = i;
604               while (i)
605                 g_free (retval[--i]);
606               g_free (retval);
607               return FALSE;
608             }
609         }
611       retval[n] = NULL;
612     }
613   *wcharv = retval;
614   return TRUE;
618 /* Avoids a danger in threaded situations (calling close()
619  * on a file descriptor twice, and another thread has
620  * re-opened it since the first close)
621  */
622 static void
623 close_and_invalidate (gint *fd)
625   if (*fd < 0)
626     return;
628   close (*fd);
629   *fd = -1;
633 static gboolean
634 do_spawn_directly (gint                 *exit_status,
635                    gboolean              do_return_handle,
636                    GSpawnFlags           flags,
637                    gchar               **argv,
638                    char                **envp,
639                    char                **protected_argv,
640                    GSpawnChildSetupFunc  child_setup,
641                    gpointer              user_data,
642                    GPid                 *child_handle,
643                    GError              **error)
645   const int mode = (exit_status == NULL) ? P_NOWAIT : P_WAIT;
646   char **new_argv;
647   int rc = -1;
648   int saved_errno;
649   GError *conv_error = NULL;
650   gint conv_error_index;
651   wchar_t *wargv0, **wargv, **wenvp;
653   new_argv = (flags & G_SPAWN_FILE_AND_ARGV_ZERO) ? protected_argv + 1 : protected_argv;
655   wargv0 = (wchar_t *)g_utf8_to_utf16 (argv[0], -1, NULL, NULL, &conv_error);
656   if (wargv0 == NULL)
657     {
658       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
659                    _("Invalid program name: %s"),
660                    conv_error->message);
661       g_error_free (conv_error);
663       return FALSE;
664     }
666   if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
667     {
668       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
669                    _("Invalid string in argument vector at %d: %s"),
670                    conv_error_index, conv_error->message);
671       g_error_free (conv_error);
672       g_free (wargv0);
674       return FALSE;
675     }
677   if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
678     {
679       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
680                    _("Invalid string in environment: %s"),
681                    conv_error->message);
682       g_error_free (conv_error);
683       g_free (wargv0);
684       g_strfreev ((gchar **) wargv);
686       return FALSE;
687     }
689   if (child_setup)
690     (* child_setup) (user_data);
692   if (flags & G_SPAWN_SEARCH_PATH)
693     if (wenvp != NULL)
694       rc = _wspawnvpe (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
695     else
696       rc = _wspawnvp (mode, wargv0, (const wchar_t **) wargv);
697   else
698     if (wenvp != NULL)
699       rc = _wspawnve (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
700     else
701       rc = _wspawnv (mode, wargv0, (const wchar_t **) wargv);
703   g_free (wargv0);
704   g_strfreev ((gchar **) wargv);
705   g_strfreev ((gchar **) wenvp);
707   saved_errno = errno;
709   if (rc == -1 && saved_errno != 0)
710     {
711       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
712                    _("Failed to execute child process (%s)"),
713                    g_strerror (saved_errno));
714       return FALSE;
715     }
717   if (exit_status == NULL)
718     {
719       if (child_handle && do_return_handle)
720         *child_handle = (GPid) rc;
721       else
722         {
723           CloseHandle ((HANDLE) rc);
724           if (child_handle)
725             *child_handle = 0;
726         }
727     }
728   else
729     *exit_status = rc;
731   return TRUE;
734 static gboolean
735 make_pipe (gint     p[2],
736            GError **error)
738   if (_pipe (p, 4096, _O_BINARY) < 0)
739     {
740       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
741                    _("Failed to create pipe for communicating with child process (%s)"),
742                    g_strerror (errno));
743       return FALSE;
744     }
745   else
746     return TRUE;
750 static gboolean
751 do_spawn_with_pipes (gint                 *exit_status,
752                      gboolean              do_return_handle,
753                      const gchar          *working_directory,
754                      gchar               **argv,
755                      char                **envp,
756                      GSpawnFlags           flags,
757                      GSpawnChildSetupFunc  child_setup,
758                      gpointer              user_data,
759                      GPid                 *child_handle,
760                      gint                 *standard_input,
761                      gint                 *standard_output,
762                      gint                 *standard_error,
763                      gint                 *err_report,
764                      GError              **error)
766   char **protected_argv;
767   char args[ARG_COUNT][10];
768   char **new_argv;
769   int i;
770   int rc = -1;
771   int saved_errno;
772   int argc;
773   int stdin_pipe[2] = { -1, -1 };
774   int stdout_pipe[2] = { -1, -1 };
775   int stderr_pipe[2] = { -1, -1 };
776   int child_err_report_pipe[2] = { -1, -1 };
777   int helper_sync_pipe[2] = { -1, -1 };
778   int helper_report[2];
779   static gboolean warned_about_child_setup = FALSE;
780   GError *conv_error = NULL;
781   gint conv_error_index;
782   gchar *helper_process;
783   CONSOLE_CURSOR_INFO cursor_info;
784   wchar_t *whelper, **wargv, **wenvp;
785   //extern gchar *_glib_get_installation_directory (void);
786   gchar *glib_top;
788   if (child_setup && !warned_about_child_setup)
789     {
790       warned_about_child_setup = TRUE;
791       g_warning ("passing a child setup function to the g_spawn functions is pointless and dangerous on Win32");
792     }
794   argc = protect_argv (argv, &protected_argv);
796   if (!standard_input && !standard_output && !standard_error &&
797       (flags & G_SPAWN_CHILD_INHERITS_STDIN) &&
798       !(flags & G_SPAWN_STDOUT_TO_DEV_NULL) &&
799       !(flags & G_SPAWN_STDERR_TO_DEV_NULL) &&
800       (working_directory == NULL || !*working_directory) &&
801       (flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
802     {
803       /* We can do without the helper process */
804       gboolean retval =
805         do_spawn_directly (exit_status, do_return_handle, flags,
806                            argv, envp, protected_argv,
807                            child_setup, user_data, child_handle,
808                            error);
809       g_strfreev (protected_argv);
810       return retval;
811     }
813   if (standard_input && !make_pipe (stdin_pipe, error))
814     goto cleanup_and_fail;
816   if (standard_output && !make_pipe (stdout_pipe, error))
817     goto cleanup_and_fail;
819   if (standard_error && !make_pipe (stderr_pipe, error))
820     goto cleanup_and_fail;
822   if (!make_pipe (child_err_report_pipe, error))
823     goto cleanup_and_fail;
825   if (!make_pipe (helper_sync_pipe, error))
826     goto cleanup_and_fail;
828   new_argv = g_new (char *, argc + 1 + ARG_COUNT);
829   if (GetConsoleCursorInfo (GetStdHandle (STD_OUTPUT_HANDLE), &cursor_info))
830     helper_process = HELPER_PROCESS "-console.exe";
831   else
832     helper_process = HELPER_PROCESS ".exe";
834   glib_top = NULL;
835   if (glib_top != NULL)
836     {
837       helper_process = g_build_filename (glib_top, "bin", helper_process, NULL);
838       g_free (glib_top);
839     }
840   else
841     helper_process = g_strdup (helper_process);
843   new_argv[0] = protect_argv_string (helper_process);
845   sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_pipe[1]);
846   new_argv[ARG_CHILD_ERR_REPORT] = args[ARG_CHILD_ERR_REPORT];
848   /* Make the read end of the child error report pipe
849    * noninherited. Otherwise it will needlessly be inherited by the
850    * helper process, and the started actual user process. As such that
851    * shouldn't harm, but it is unnecessary.
852    */
853   child_err_report_pipe[0] = dup_noninherited (child_err_report_pipe[0], _O_RDONLY);
855   if (flags & G_SPAWN_FILE_AND_ARGV_ZERO)
856     {
857       /* Overload ARG_CHILD_ERR_REPORT to also encode the
858        * G_SPAWN_FILE_AND_ARGV_ZERO functionality.
859        */
860       strcat (args[ARG_CHILD_ERR_REPORT], "#");
861     }
863   sprintf (args[ARG_HELPER_SYNC], "%d", helper_sync_pipe[0]);
864   new_argv[ARG_HELPER_SYNC] = args[ARG_HELPER_SYNC];
866   /* Make the write end of the sync pipe noninherited. Otherwise the
867    * helper process will inherit it, and thus if this process happens
868    * to crash before writing the sync byte to the pipe, the helper
869    * process won't read but won't get any EOF either, as it has the
870    * write end open itself.
871    */
872   helper_sync_pipe[1] = dup_noninherited (helper_sync_pipe[1], _O_WRONLY);
874   if (standard_input)
875     {
876       sprintf (args[ARG_STDIN], "%d", stdin_pipe[0]);
877       new_argv[ARG_STDIN] = args[ARG_STDIN];
878     }
879   else if (flags & G_SPAWN_CHILD_INHERITS_STDIN)
880     {
881       /* Let stdin be alone */
882       new_argv[ARG_STDIN] = "-";
883     }
884   else
885     {
886       /* Keep process from blocking on a read of stdin */
887       new_argv[ARG_STDIN] = "z";
888     }
890   if (standard_output)
891     {
892       sprintf (args[ARG_STDOUT], "%d", stdout_pipe[1]);
893       new_argv[ARG_STDOUT] = args[ARG_STDOUT];
894     }
895   else if (flags & G_SPAWN_STDOUT_TO_DEV_NULL)
896     {
897       new_argv[ARG_STDOUT] = "z";
898     }
899   else
900     {
901       new_argv[ARG_STDOUT] = "-";
902     }
904   if (standard_error)
905     {
906       sprintf (args[ARG_STDERR], "%d", stderr_pipe[1]);
907      new_argv[ARG_STDERR] = args[ARG_STDERR];
908     }
909   else if (flags & G_SPAWN_STDERR_TO_DEV_NULL)
910     {
911       new_argv[ARG_STDERR] = "z";
912     }
913   else
914     {
915       new_argv[ARG_STDERR] = "-";
916     }
918   if (working_directory && *working_directory)
919     new_argv[ARG_WORKING_DIRECTORY] = protect_argv_string (working_directory);
920   else
921     new_argv[ARG_WORKING_DIRECTORY] = g_strdup ("-");
923   if (!(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
924     new_argv[ARG_CLOSE_DESCRIPTORS] = "y";
925   else
926     new_argv[ARG_CLOSE_DESCRIPTORS] = "-";
928   if (flags & G_SPAWN_SEARCH_PATH)
929     new_argv[ARG_USE_PATH] = "y";
930   else
931     new_argv[ARG_USE_PATH] = "-";
933   if (exit_status == NULL)
934     new_argv[ARG_WAIT] = "-";
935   else
936     new_argv[ARG_WAIT] = "w";
938   for (i = 0; i <= argc; i++)
939     new_argv[ARG_PROGRAM + i] = protected_argv[i];
941   //SETUP_DEBUG();
943   if (debug)
944     {
945       g_print ("calling %s with argv:\n", helper_process);
946       for (i = 0; i < argc + 1 + ARG_COUNT; i++)
947         g_print ("argv[%d]: %s\n", i, (new_argv[i] ? new_argv[i] : "NULL"));
948     }
950   if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
951     {
952       if (conv_error_index == ARG_WORKING_DIRECTORY)
953         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
954                      _("Invalid working directory: %s"),
955                      conv_error->message);
956       else
957         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
958                      _("Invalid string in argument vector at %d: %s"),
959                      conv_error_index - ARG_PROGRAM, conv_error->message);
960       g_error_free (conv_error);
961       g_strfreev (protected_argv);
962       g_free (new_argv[0]);
963       g_free (new_argv[ARG_WORKING_DIRECTORY]);
964       g_free (new_argv);
965       g_free (helper_process);
967       goto cleanup_and_fail;
968     }
970   if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
971     {
972       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
973                    _("Invalid string in environment: %s"),
974                    conv_error->message);
975       g_error_free (conv_error);
976       g_strfreev (protected_argv);
977       g_free (new_argv[0]);
978       g_free (new_argv[ARG_WORKING_DIRECTORY]);
979       g_free (new_argv);
980       g_free (helper_process);
981       g_strfreev ((gchar **) wargv);
983       goto cleanup_and_fail;
984     }
986   if (child_setup)
987     (* child_setup) (user_data);
989   whelper = (wchar_t *)g_utf8_to_utf16 (helper_process, -1, NULL, NULL, NULL);
990   g_free (helper_process);
992   if (wenvp != NULL)
993     rc = _wspawnvpe (P_NOWAIT, whelper, (const wchar_t **) wargv, (const wchar_t **) wenvp);
994   else
995     rc = _wspawnvp (P_NOWAIT, whelper, (const wchar_t **) wargv);
997   saved_errno = errno;
999   g_free (whelper);
1000   g_strfreev ((gchar **) wargv);
1001   g_strfreev ((gchar **) wenvp);
1003   /* Close the other process's ends of the pipes in this process,
1004    * otherwise the reader will never get EOF.
1005    */
1006   close_and_invalidate (&child_err_report_pipe[1]);
1007   close_and_invalidate (&helper_sync_pipe[0]);
1008   close_and_invalidate (&stdin_pipe[0]);
1009   close_and_invalidate (&stdout_pipe[1]);
1010   close_and_invalidate (&stderr_pipe[1]);
1012   g_strfreev (protected_argv);
1014   g_free (new_argv[0]);
1015   g_free (new_argv[ARG_WORKING_DIRECTORY]);
1016   g_free (new_argv);
1018   /* Check if gspawn-win32-helper couldn't be run */
1019   if (rc == -1 && saved_errno != 0)
1020     {
1021       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
1022                    _("Failed to execute helper program (%s)"),
1023                    g_strerror (saved_errno));
1024       goto cleanup_and_fail;
1025     }
1027   if (exit_status != NULL)
1028     {
1029       /* Synchronous case. Pass helper's report pipe back to caller,
1030        * which takes care of reading it after the grandchild has
1031        * finished.
1032        */
1033       g_assert (err_report != NULL);
1034       *err_report = child_err_report_pipe[0];
1035       write (helper_sync_pipe[1], " ", 1);
1036       close_and_invalidate (&helper_sync_pipe[1]);
1037     }
1038   else
1039     {
1040       /* Asynchronous case. We read the helper's report right away. */
1041       if (!read_helper_report (child_err_report_pipe[0], helper_report, error))
1042         goto cleanup_and_fail;
1044       close_and_invalidate (&child_err_report_pipe[0]);
1046       switch (helper_report[0])
1047         {
1048         case CHILD_NO_ERROR:
1049           if (child_handle && do_return_handle)
1050             {
1051               /* rc is our HANDLE for gspawn-win32-helper. It has
1052                * told us the HANDLE of its child. Duplicate that into
1053                * a HANDLE valid in this process.
1054                */
1055               if (!DuplicateHandle ((HANDLE) rc, (HANDLE) helper_report[1],
1056                                     GetCurrentProcess (), (LPHANDLE) child_handle,
1057                                     0, TRUE, DUPLICATE_SAME_ACCESS))
1058                 {
1059                   char *emsg = g_win32_error_message (GetLastError ());
1060                   g_print("%s\n", emsg);
1061                   *child_handle = 0;
1062                 }
1063             }
1064           else if (child_handle)
1065             *child_handle = 0;
1066           write (helper_sync_pipe[1], " ", 1);
1067           close_and_invalidate (&helper_sync_pipe[1]);
1068           break;
1070         default:
1071           write (helper_sync_pipe[1], " ", 1);
1072           close_and_invalidate (&helper_sync_pipe[1]);
1073           set_child_error (helper_report, working_directory, error);
1074           goto cleanup_and_fail;
1075         }
1076     }
1078   /* Success against all odds! return the information */
1080   if (standard_input)
1081     *standard_input = stdin_pipe[1];
1082   if (standard_output)
1083     *standard_output = stdout_pipe[0];
1084   if (standard_error)
1085     *standard_error = stderr_pipe[0];
1086   if (rc != -1)
1087     CloseHandle ((HANDLE) rc);
1089   return TRUE;
1091   cleanup_and_fail:
1093   if (rc != -1)
1094     CloseHandle ((HANDLE) rc);
1095   if (child_err_report_pipe[0] != -1)
1096     close (child_err_report_pipe[0]);
1097   if (child_err_report_pipe[1] != -1)
1098     close (child_err_report_pipe[1]);
1099   if (helper_sync_pipe[0] != -1)
1100     close (helper_sync_pipe[0]);
1101   if (helper_sync_pipe[1] != -1)
1102     close (helper_sync_pipe[1]);
1103   if (stdin_pipe[0] != -1)
1104     close (stdin_pipe[0]);
1105   if (stdin_pipe[1] != -1)
1106     close (stdin_pipe[1]);
1107   if (stdout_pipe[0] != -1)
1108     close (stdout_pipe[0]);
1109   if (stdout_pipe[1] != -1)
1110     close (stdout_pipe[1]);
1111   if (stderr_pipe[0] != -1)
1112     close (stderr_pipe[0]);
1113   if (stderr_pipe[1] != -1)
1114     close (stderr_pipe[1]);
1116   return FALSE;
1119 gboolean
1120 my_spawn_async_with_pipes_utf8 (const gchar          *working_directory,
1121                                gchar               **argv,
1122                                gchar               **envp,
1123                                GSpawnFlags           flags,
1124                                GSpawnChildSetupFunc  child_setup,
1125                                gpointer              user_data,
1126                                GPid                 *child_handle,
1127                                gint                 *standard_input,
1128                                gint                 *standard_output,
1129                                gint                 *standard_error,
1130                                GError              **error)
1132   g_return_val_if_fail (argv != NULL, FALSE);
1133   g_return_val_if_fail (standard_output == NULL ||
1134                         !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
1135   g_return_val_if_fail (standard_error == NULL ||
1136                         !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
1137   /* can't inherit stdin if we have an input pipe. */
1138   g_return_val_if_fail (standard_input == NULL ||
1139                         !(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
1141   return do_spawn_with_pipes (NULL,
1142                               (flags & G_SPAWN_DO_NOT_REAP_CHILD),
1143                               working_directory,
1144                               argv,
1145                               envp,
1146                               flags,
1147                               child_setup,
1148                               user_data,
1149                               child_handle,
1150                               standard_input,
1151                               standard_output,
1152                               standard_error,
1153                               NULL,
1154                               error);
1157 typedef GPid Pid;
1159 // _WRAP_ENUM(SpawnFlags, GSpawnFlags, NO_GTYPE)
1161 /* Helper callback to invoke the actual sigc++ slot.
1162  * We don't need to worry about (un)referencing, since the
1163  * child process gets its own copy of the parent's memory anyway.
1164  */
1165 static void child_setup_callback(void* user_data)
1167   #ifdef GLIBMM_EXCEPTIONS_ENABLED
1168   try
1169   {
1170   #endif //GLIBMM_EXCEPTIONS_ENABLED
1171     (*reinterpret_cast<sigc::slot<void>*>(user_data))();
1172   #ifdef GLIBMM_EXCEPTIONS_ENABLED
1173   }
1174   catch(...)
1175   {
1176     Glib::exception_handlers_invoke();
1177   }
1178   #endif //GLIBMM_EXCEPTIONS_ENABLED
1182 void my_spawn_async_with_pipes(const std::string& working_directory,
1183                             const Glib::ArrayHandle<std::string>& argv,
1184                             GSpawnFlags flags,
1185                             const sigc::slot<void>& child_setup,
1186                             Pid* child_pid,
1187                             int* standard_input,
1188                             int* standard_output,
1189                             int* standard_error)
1191   const bool setup_slot = !child_setup.empty();
1192   sigc::slot<void> child_setup_ = child_setup;
1193   GError* error = 0;
1195   my_spawn_async_with_pipes_utf8(
1196       working_directory.c_str(),
1197       const_cast<char**>(argv.data()), 0,
1198       static_cast<GSpawnFlags>(unsigned(flags)),
1199       (setup_slot) ? &child_setup_callback : 0,
1200       (setup_slot) ? &child_setup_         : 0,
1201       child_pid,
1202       standard_input, standard_output, standard_error,
1203       &error);
1205   if(error)
1206     Glib::Error::throw_exception(error);
1209 #endif
1211 void
1212 Inkscape::IO::spawn_async_with_pipes( const std::string& working_directory,
1213                                       const Glib::ArrayHandle<std::string>& argv,
1214                                       Glib::SpawnFlags flags,
1215                                       const sigc::slot<void>& child_setup,
1216                                       Glib::Pid* child_pid,
1217                                       int* standard_input,
1218                                       int* standard_output,
1219                                       int* standard_error)
1221 #ifndef BYPASS_GLIB_SPAWN
1222     Glib::spawn_async_with_pipes(working_directory,
1223                                  argv,
1224                                  flags,
1225                                  child_setup,
1226                                  child_pid,
1227                                  standard_input,
1228                                  standard_output,
1229                                  standard_error);
1230 #else
1231     my_spawn_async_with_pipes(working_directory,
1232                               argv,
1233                               static_cast<GSpawnFlags>(flags),
1234                               child_setup,
1235                               child_pid,
1236                               standard_input,
1237                               standard_output,
1238                               standard_error);
1239 #endif
1243 gchar* Inkscape::IO::sanitizeString( gchar const * str )
1245     gchar *result = NULL;
1246     if ( str ) {
1247         if ( g_utf8_validate(str, -1, NULL) ) {
1248             result = g_strdup(str);
1249         } else {
1250             guchar scratch[8];
1251             Glib::ustring buf;
1252             guchar const *ptr = (guchar const*)str;
1253             while ( *ptr )
1254             {
1255                 if ( *ptr == '\\' )
1256                 {
1257                     buf.append("\\\\");
1258                 } else if ( *ptr < 0x80 ) {
1259                     buf += (char)(*ptr);
1260                 } else {
1261                     g_snprintf((gchar*)scratch, sizeof(scratch), "\\x%02x", *ptr);
1262                     buf.append((const char*)scratch);
1263                 }
1264                 ptr++;
1265             }
1266             result = g_strdup(buf.c_str());
1267         }
1268     }
1269     return result;
1272 /*
1273   Local Variables:
1274   mode:c++
1275   c-file-style:"stroustrup"
1276   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1277   indent-tabs-mode:nil
1278   fill-column:99
1279   End:
1280 */
1281 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :