Code

Fix handle for LPERotateCopies
[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/gutils.h>
19 #include <glibmm/fileutils.h>
20 #if GLIB_CHECK_VERSION(2,6,0)
21     #include <glib/gstdio.h>
22 #endif
23 #include <glibmm/ustring.h>
24 #include <gtk/gtkmessagedialog.h>
26 #include "prefs-utils.h"
27 #include "sys.h"
29 #ifdef WIN32
31 #define BYPASS_GLIB_SPAWN 1
33 #ifdef BYPASS_GLIB_SPAWN
35 #include <process.h>    // declares spawn functions
36 #include <wchar.h>      // declares _wspawn functions
37 #ifdef __cplusplus
38 extern "C" {
39 #endif
40 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnl    (int, const wchar_t*, const wchar_t*, ...);
41 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnle   (int, const wchar_t*, const wchar_t*, ...);
42 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnlp   (int, const wchar_t*, const wchar_t*, ...);
43 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnlpe  (int, const wchar_t*, const wchar_t*, ...);
44 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnv    (int, const wchar_t*, const wchar_t* const*);
45 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnve   (int, const wchar_t*, const wchar_t* const*, const wchar_t* const*);
46 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnvp   (int, const wchar_t*, const wchar_t* const*);
47 _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnvpe  (int, const wchar_t*, const wchar_t* const*, const wchar_t* const*);
48 #ifdef __cplusplus
49 }
50 #endif
51 #include <unistd.h>
52 #include <glibmm/i18n.h>
53 #include <fcntl.h>
54 #include <io.h>
56 #endif // BYPASS_GLIB_SPAWN
58 // For now to get at is_os_wide().
59 #include "extension/internal/win32.h"
60 using Inkscape::Extension::Internal::PrintWin32;
62 #endif // WIN32
64 //#define INK_DUMP_FILENAME_CONV 1
65 #undef INK_DUMP_FILENAME_CONV
67 //#define INK_DUMP_FOPEN 1
68 #undef INK_DUMP_FOPEN
70 void dump_str(gchar const *str, gchar const *prefix);
71 void dump_ustr(Glib::ustring const &ustr);
73 extern guint update_in_progress;
76 #define DEBUG_MESSAGE(key, ...) \
77 {\
78     gint dump = prefs_get_int_attribute_limited("options.bulia", #key, 0, 0, 1);\
79     gint dumpD = prefs_get_int_attribute_limited("options.bulia", #key"D", 0, 0, 1);\
80     gint dumpD2 = prefs_get_int_attribute_limited("options.bulia", #key"D2", 0, 0, 1);\
81     dumpD &= ( (update_in_progress == 0) || dumpD2 );\
82     if ( dump )\
83     {\
84         g_message( __VA_ARGS__ );\
85 \
86     }\
87     if ( dumpD )\
88     {\
89         GtkWidget *dialog = gtk_message_dialog_new(NULL,\
90                                                    GTK_DIALOG_DESTROY_WITH_PARENT, \
91                                                    GTK_MESSAGE_INFO,    \
92                                                    GTK_BUTTONS_OK,      \
93                                                    __VA_ARGS__          \
94                                                    );\
95         g_signal_connect_swapped(dialog, "response",\
96                                  G_CALLBACK(gtk_widget_destroy),        \
97                                  dialog);                               \
98         gtk_widget_show_all( dialog );\
99     }\
105 void Inkscape::IO::dump_fopen_call( char const *utf8name, char const *id )
107 #ifdef INK_DUMP_FOPEN
108     Glib::ustring str;
109     for ( int i = 0; utf8name[i]; i++ )
110     {
111         if ( utf8name[i] == '\\' )
112         {
113             str += "\\\\";
114         }
115         else if ( (utf8name[i] >= 0x20) && ((0x0ff & utf8name[i]) <= 0x7f) )
116         {
117             str += utf8name[i];
118         }
119         else
120         {
121             gchar tmp[32];
122             g_snprintf( tmp, sizeof(tmp), "\\x%02x", (0x0ff & utf8name[i]) );
123             str += tmp;
124         }
125     }
126     g_message( "fopen call %s for [%s]", id, str.data() );
127 #else
128     (void)utf8name;
129     (void)id;
130 #endif
133 FILE *Inkscape::IO::fopen_utf8name( char const *utf8name, char const *mode )
135     static gint counter = 0;
136     FILE* fp = NULL;
138     DEBUG_MESSAGE( dumpOne, "entering fopen_utf8name( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
140 #ifndef WIN32
141     DEBUG_MESSAGE( dumpOne, "           STEP 0              ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
142     gchar *filename = g_filename_from_utf8( utf8name, -1, NULL, NULL, NULL );
143     if ( filename )
144     {
145         DEBUG_MESSAGE( dumpOne, "           STEP 1              ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
146         fp = std::fopen(filename, mode);
147         DEBUG_MESSAGE( dumpOne, "           STEP 2              ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
148         g_free(filename);
149         DEBUG_MESSAGE( dumpOne, "           STEP 3              ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
150         filename = 0;
151     }
152 #else
153     Glib::ustring how( mode );
154     how.append("b");
155     DEBUG_MESSAGE( dumpOne, "   calling is_os_wide()       ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
157     fp = g_fopen(utf8name, how.c_str());
158 #endif
160     DEBUG_MESSAGE( dumpOne, "leaving fopen_utf8name( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
162     return fp;
166 int Inkscape::IO::mkdir_utf8name( char const *utf8name )
168     static gint counter = 0;
169     int retval = -1;
171     DEBUG_MESSAGE( dumpMk, "entering mkdir_utf8name( '%s' )[%d]", utf8name, (counter++) );
173 #ifndef WIN32
174     DEBUG_MESSAGE( dumpMk, "           STEP 0              ( '%s' )[%d]", utf8name, (counter++) );
175     gchar *filename = g_filename_from_utf8( utf8name, -1, NULL, NULL, NULL );
176     if ( filename )
177     {
178         DEBUG_MESSAGE( dumpMk, "           STEP 1              ( '%s' )[%d]", utf8name, (counter++) );
179         retval = ::mkdir(filename, S_IRWXU | S_IRGRP | S_IXGRP);
180         DEBUG_MESSAGE( dumpMk, "           STEP 2              ( '%s' )[%d]", utf8name, (counter++) );
181         g_free(filename);
182         DEBUG_MESSAGE( dumpMk, "           STEP 3              ( '%s' )[%d]", utf8name, (counter++) );
183         filename = 0;
184     }
185 #else
186     DEBUG_MESSAGE( dumpMk, "   calling is_os_wide()       ( '%s' )[%d]", utf8name, (counter++) );
188     // Mode should be ingnored inside of glib on the way in
189     retval = g_mkdir( utf8name, 0 );
190 #endif
192     DEBUG_MESSAGE( dumpMk, "leaving mkdir_utf8name( '%s' )[%d]", utf8name, (counter++) );
194     return retval;
197 /* 
198  * Wrapper around Glib::file_open_tmp()
199  * Returns a handle to the temp file
200  * name_used contains the actual name used
201  * 
202  * Returns:
203  * A file handle (as from open()) to the file opened for reading and writing. 
204  * The file is opened in binary mode on platforms where there is a difference. 
205  * The file handle should be closed with close().
206  * 
207  * Note:
208  * On Windows Vista Glib::file_open_tmp fails with the current version of glibmm
209  * A special case is implemented for WIN32. This can be removed if the issue is fixed
210  * in future versions of glibmm 
211  * */
212 int Inkscape::IO::file_open_tmp(std::string& name_used, const std::string& prefix)
214 #ifndef WIN32
215     return Glib::file_open_tmp(name_used, prefix);
216 #else
217     /* Special case for WIN32 due to a bug in glibmm
218      * (only needed for Windows Vista, but since there is only one windows build all builds get the workaround)
219      * The workaround can be removed if the bug is fixed in glibmm
220      * 
221      * The code is mostly identical to the implementation in glibmm
222      * http://svn.gnome.org/svn/glibmm/branches/glibmm-2-12/glib/src/fileutils.ccg
223      * */
224     
225     std::string basename_template (prefix);
226     basename_template += "XXXXXX"; // this sillyness shouldn't be in the interface
227     
228     GError* error = 0;
229     gchar *buf_name_used;
230     
231     gint fileno = g_file_open_tmp(basename_template.c_str(), &buf_name_used, &error);
232     
233     if(error)
234         Glib::Error::throw_exception(error);
235     
236     name_used = g_strdup(buf_name_used);
237     g_free(buf_name_used);
238     return fileno;
239 #endif
242 bool Inkscape::IO::file_test( char const *utf8name, GFileTest test )
244     bool exists = false;
246     if ( utf8name ) {
247         gchar *filename = NULL;
248         if (utf8name && !g_utf8_validate(utf8name, -1, NULL)) {
249             /* FIXME: Trying to guess whether or not a filename is already in utf8 is unreliable.
250                If any callers pass non-utf8 data (e.g. using g_get_home_dir), then change caller to
251                use simple g_file_test.  Then add g_return_val_if_fail(g_utf_validate(...), false)
252                to beginning of this function. */
253             filename = g_strdup(utf8name);
254             // Looks like g_get_home_dir isn't safe.
255             //g_warning("invalid UTF-8 detected internally. HUNT IT DOWN AND KILL IT!!!");
256         } else {
257             filename = g_filename_from_utf8 ( utf8name, -1, NULL, NULL, NULL );
258         }
259         if ( filename ) {
260             exists = g_file_test (filename, test);
261             g_free(filename);
262             filename = NULL;
263         } else {
264             g_warning( "Unable to convert filename in IO:file_test" );
265         }
266     }
268     return exists;
271 /** Wrapper around g_dir_open, but taking a utf8name as first argument. */
272 GDir *
273 Inkscape::IO::dir_open(gchar const *const utf8name, guint const flags, GError **const error)
275     gchar *const opsys_name = g_filename_from_utf8(utf8name, -1, NULL, NULL, error);
276     if (opsys_name) {
277         GDir *ret = g_dir_open(opsys_name, flags, error);
278         g_free(opsys_name);
279         return ret;
280     } else {
281         return NULL;
282     }
285 /**
286  * Like g_dir_read_name, but returns a utf8name (which must be freed, unlike g_dir_read_name).
287  *
288  * N.B. Skips over any dir entries that fail to convert to utf8.
289  */
290 gchar *
291 Inkscape::IO::dir_read_utf8name(GDir *dir)
293     for (;;) {
294         gchar const *const opsys_name = g_dir_read_name(dir);
295         if (!opsys_name) {
296             return NULL;
297         }
298         gchar *utf8_name = g_filename_to_utf8(opsys_name, -1, NULL, NULL, NULL);
299         if (utf8_name) {
300             return utf8_name;
301         }
302     }
306 gchar* Inkscape::IO::locale_to_utf8_fallback( const gchar *opsysstring,
307                                               gssize len,
308                                               gsize *bytes_read,
309                                               gsize *bytes_written,
310                                               GError **error )
312     gchar *result = NULL;
313     if ( opsysstring ) {
314         gchar *newFileName = g_locale_to_utf8( opsysstring, len, bytes_read, bytes_written, error );
315         if ( newFileName ) {
316             if ( !g_utf8_validate(newFileName, -1, NULL) ) {
317                 g_warning( "input filename did not yield UTF-8" );
318                 g_free( newFileName );
319             } else {
320                 result = newFileName;
321             }
322             newFileName = 0;
323         } else if ( g_utf8_validate(opsysstring, -1, NULL) ) {
324             // This *might* be a case that we want
325             // g_warning( "input failed filename->utf8, fell back to original" );
326             // TODO handle cases when len >= 0
327             result = g_strdup( opsysstring );
328         } else {
329             gchar const *charset = 0;
330             g_get_charset(&charset);
331             g_warning( "input filename conversion failed for file with locale charset '%s'", charset );
332         }
333     }
334     return result;
337 #ifdef BYPASS_GLIB_SPAWN
338 /*
339         this code was taken from the original glib sources
340 */
341 #define GSPAWN_HELPER
342  
343 enum
345   CHILD_NO_ERROR,
346   CHILD_CHDIR_FAILED,
347   CHILD_SPAWN_FAILED,
348 };
350 enum {
351   ARG_CHILD_ERR_REPORT = 1,
352   ARG_HELPER_SYNC,
353   ARG_STDIN,
354   ARG_STDOUT,
355   ARG_STDERR,
356   ARG_WORKING_DIRECTORY,
357   ARG_CLOSE_DESCRIPTORS,
358   ARG_USE_PATH,
359   ARG_WAIT,
360   ARG_PROGRAM,
361   ARG_COUNT = ARG_PROGRAM
362 };
363 static int debug = 0;
364 #define HELPER_PROCESS "gspawn-win32-helper"
367 static int
368 dup_noninherited (int fd,
369                   int mode)
371   HANDLE filehandle;
373   DuplicateHandle (GetCurrentProcess (), (LPHANDLE) _get_osfhandle (fd),
374                    GetCurrentProcess (), &filehandle,
375                    0, FALSE, DUPLICATE_SAME_ACCESS);
376   close (fd);
377   return _open_osfhandle ((long) filehandle, mode | _O_NOINHERIT);
381 /* The helper process writes a status report back to us, through a
382  * pipe, consisting of two ints.
383  */
384 static gboolean
385 read_helper_report (int      fd,
386                     gint     report[2],
387                     GError **error)
389   gint bytes = 0;
391   while (bytes < sizeof(gint)*2)
392     {
393       gint chunk;
395       if (debug)
396         g_print ("%s:read_helper_report: read %d...\n",
397                  __FILE__,
398                  sizeof(gint)*2 - bytes);
400       chunk = read (fd, ((gchar*)report) + bytes,
401                     sizeof(gint)*2 - bytes);
403       if (debug)
404         g_print ("...got %d bytes\n", chunk);
406       if (chunk < 0)
407         {
408           /* Some weird shit happened, bail out */
410           g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
411                        _("Failed to read from child pipe (%s)"),
412                        g_strerror (errno));
414           return FALSE;
415         }
416       else if (chunk == 0)
417         {
418           g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
419                        _("Failed to read from child pipe (%s)"),
420                        "EOF");
421           break; /* EOF */
422         }
423       else
424         bytes += chunk;
425     }
427   if (bytes < sizeof(gint)*2)
428     return FALSE;
430   return TRUE;
434 static void
435 set_child_error (gint         report[2],
436                  const gchar *working_directory,
437                  GError     **error)
439   switch (report[0])
440     {
441     case CHILD_CHDIR_FAILED:
442       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
443                    _("Failed to change to directory '%s' (%s)"),
444                    working_directory,
445                    g_strerror (report[1]));
446       break;
447     case CHILD_SPAWN_FAILED:
448       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
449                    _("Failed to execute child process (%s)"),
450                    g_strerror (report[1]));
451       break;
452     default:
453       g_assert_not_reached ();
454     }
457 static gchar *
458 protect_argv_string (const gchar *string)
460   const gchar *p = string;
461   gchar *retval, *q;
462   gint len = 0;
463   gboolean need_dblquotes = FALSE;
464   while (*p)
465     {
466       if (*p == ' ' || *p == '\t')
467         need_dblquotes = TRUE;
468       else if (*p == '"')
469         len++;
470       else if (*p == '\\')
471         {
472           const gchar *pp = p;
473           while (*pp && *pp == '\\')
474             pp++;
475           if (*pp == '"')
476             len++;
477         }
478       len++;
479       p++;
480     }
482   q = retval = (gchar *)g_malloc (len + need_dblquotes*2 + 1);
483   p = string;
485   if (need_dblquotes)
486     *q++ = '"';
488   while (*p)
489     {
490       if (*p == '"')
491         *q++ = '\\';
492       else if (*p == '\\')
493         {
494           const gchar *pp = p;
495           while (*pp && *pp == '\\')
496             pp++;
497           if (*pp == '"')
498             *q++ = '\\';
499         }
500       *q++ = *p;
501       p++;
502     }
504   if (need_dblquotes)
505     *q++ = '"';
506   *q++ = '\0';
508   return retval;
512 static gint
513 protect_argv (gchar  **argv,
514               gchar ***new_argv)
516   gint i;
517   gint argc = 0;
519   while (argv[argc])
520     ++argc;
521   *new_argv = g_new (gchar *, argc+1);
523   /* Quote each argv element if necessary, so that it will get
524    * reconstructed correctly in the C runtime startup code.  Note that
525    * the unquoting algorithm in the C runtime is really weird, and
526    * rather different than what Unix shells do. See stdargv.c in the C
527    * runtime sources (in the Platform SDK, in src/crt).
528    *
529    * Note that an new_argv[0] constructed by this function should
530    * *not* be passed as the filename argument to a spawn* or exec*
531    * family function. That argument should be the real file name
532    * without any quoting.
533    */
534   for (i = 0; i < argc; i++)
535     (*new_argv)[i] = protect_argv_string (argv[i]);
537   (*new_argv)[argc] = NULL;
539   return argc;
543 static gboolean
544 utf8_charv_to_wcharv (char     **utf8_charv,
545                       wchar_t ***wcharv,
546                       int       *error_index,
547                       GError   **error)
549   wchar_t **retval = NULL;
551   *wcharv = NULL;
552   if (utf8_charv != NULL)
553     {
554       int n = 0, i;
556       while (utf8_charv[n])
557         n++;
558       retval = g_new (wchar_t *, n + 1);
560       for (i = 0; i < n; i++)
561         {
562           retval[i] = (wchar_t *)g_utf8_to_utf16 (utf8_charv[i], -1, NULL, NULL, error);
563           if (retval[i] == NULL)
564             {
565               if (error_index)
566                 *error_index = i;
567               while (i)
568                 g_free (retval[--i]);
569               g_free (retval);
570               return FALSE;
571             }
572         }
574       retval[n] = NULL;
575     }
576   *wcharv = retval;
577   return TRUE;
581 /* Avoids a danger in threaded situations (calling close()
582  * on a file descriptor twice, and another thread has
583  * re-opened it since the first close)
584  */
585 static void
586 close_and_invalidate (gint *fd)
588   if (*fd < 0)
589     return;
591   close (*fd);
592   *fd = -1;
596 static gboolean
597 do_spawn_directly (gint                 *exit_status,
598                    gboolean              do_return_handle,
599                    GSpawnFlags           flags,
600                    gchar               **argv,
601                    char                **envp,
602                    char                **protected_argv,
603                    GSpawnChildSetupFunc  child_setup,
604                    gpointer              user_data,
605                    GPid                 *child_handle,
606                    GError              **error)
608   const int mode = (exit_status == NULL) ? P_NOWAIT : P_WAIT;
609   char **new_argv;
610   int rc = -1;
611   int saved_errno;
612   GError *conv_error = NULL;
613   gint conv_error_index;
614   wchar_t *wargv0, **wargv, **wenvp;
616   new_argv = (flags & G_SPAWN_FILE_AND_ARGV_ZERO) ? protected_argv + 1 : protected_argv;
618   wargv0 = (wchar_t *)g_utf8_to_utf16 (argv[0], -1, NULL, NULL, &conv_error);
619   if (wargv0 == NULL)
620     {
621       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
622                    _("Invalid program name: %s"),
623                    conv_error->message);
624       g_error_free (conv_error);
626       return FALSE;
627     }
629   if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
630     {
631       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
632                    _("Invalid string in argument vector at %d: %s"),
633                    conv_error_index, conv_error->message);
634       g_error_free (conv_error);
635       g_free (wargv0);
637       return FALSE;
638     }
640   if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
641     {
642       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
643                    _("Invalid string in environment: %s"),
644                    conv_error->message);
645       g_error_free (conv_error);
646       g_free (wargv0);
647       g_strfreev ((gchar **) wargv);
649       return FALSE;
650     }
652   if (child_setup)
653     (* child_setup) (user_data);
655   if (flags & G_SPAWN_SEARCH_PATH)
656     if (wenvp != NULL)
657       rc = _wspawnvpe (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
658     else
659       rc = _wspawnvp (mode, wargv0, (const wchar_t **) wargv);
660   else
661     if (wenvp != NULL)
662       rc = _wspawnve (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
663     else
664       rc = _wspawnv (mode, wargv0, (const wchar_t **) wargv);
666   g_free (wargv0);
667   g_strfreev ((gchar **) wargv);
668   g_strfreev ((gchar **) wenvp);
670   saved_errno = errno;
672   if (rc == -1 && saved_errno != 0)
673     {
674       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
675                    _("Failed to execute child process (%s)"),
676                    g_strerror (saved_errno));
677       return FALSE;
678     }
680   if (exit_status == NULL)
681     {
682       if (child_handle && do_return_handle)
683         *child_handle = (GPid) rc;
684       else
685         {
686           CloseHandle ((HANDLE) rc);
687           if (child_handle)
688             *child_handle = 0;
689         }
690     }
691   else
692     *exit_status = rc;
694   return TRUE;
697 static gboolean
698 make_pipe (gint     p[2],
699            GError **error)
701   if (_pipe (p, 4096, _O_BINARY) < 0)
702     {
703       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
704                    _("Failed to create pipe for communicating with child process (%s)"),
705                    g_strerror (errno));
706       return FALSE;
707     }
708   else
709     return TRUE;
713 static gboolean
714 do_spawn_with_pipes (gint                 *exit_status,
715                      gboolean              do_return_handle,
716                      const gchar          *working_directory,
717                      gchar               **argv,
718                      char                **envp,
719                      GSpawnFlags           flags,
720                      GSpawnChildSetupFunc  child_setup,
721                      gpointer              user_data,
722                      GPid                 *child_handle,
723                      gint                 *standard_input,
724                      gint                 *standard_output,
725                      gint                 *standard_error,
726                      gint                 *err_report,
727                      GError              **error)
729   char **protected_argv;
730   char args[ARG_COUNT][10];
731   char **new_argv;
732   int i;
733   int rc = -1;
734   int saved_errno;
735   int argc;
736   int stdin_pipe[2] = { -1, -1 };
737   int stdout_pipe[2] = { -1, -1 };
738   int stderr_pipe[2] = { -1, -1 };
739   int child_err_report_pipe[2] = { -1, -1 };
740   int helper_sync_pipe[2] = { -1, -1 };
741   int helper_report[2];
742   static gboolean warned_about_child_setup = FALSE;
743   GError *conv_error = NULL;
744   gint conv_error_index;
745   gchar *helper_process;
746   CONSOLE_CURSOR_INFO cursor_info;
747   wchar_t *whelper, **wargv, **wenvp;
748   //extern gchar *_glib_get_installation_directory (void);
749   gchar *glib_top;
751   if (child_setup && !warned_about_child_setup)
752     {
753       warned_about_child_setup = TRUE;
754       g_warning ("passing a child setup function to the g_spawn functions is pointless and dangerous on Win32");
755     }
757   argc = protect_argv (argv, &protected_argv);
759   if (!standard_input && !standard_output && !standard_error &&
760       (flags & G_SPAWN_CHILD_INHERITS_STDIN) &&
761       !(flags & G_SPAWN_STDOUT_TO_DEV_NULL) &&
762       !(flags & G_SPAWN_STDERR_TO_DEV_NULL) &&
763       (working_directory == NULL || !*working_directory) &&
764       (flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
765     {
766       /* We can do without the helper process */
767       gboolean retval =
768         do_spawn_directly (exit_status, do_return_handle, flags,
769                            argv, envp, protected_argv,
770                            child_setup, user_data, child_handle,
771                            error);
772       g_strfreev (protected_argv);
773       return retval;
774     }
776   if (standard_input && !make_pipe (stdin_pipe, error))
777     goto cleanup_and_fail;
779   if (standard_output && !make_pipe (stdout_pipe, error))
780     goto cleanup_and_fail;
782   if (standard_error && !make_pipe (stderr_pipe, error))
783     goto cleanup_and_fail;
785   if (!make_pipe (child_err_report_pipe, error))
786     goto cleanup_and_fail;
788   if (!make_pipe (helper_sync_pipe, error))
789     goto cleanup_and_fail;
791   new_argv = g_new (char *, argc + 1 + ARG_COUNT);
792   if (GetConsoleCursorInfo (GetStdHandle (STD_OUTPUT_HANDLE), &cursor_info))
793     helper_process = HELPER_PROCESS "-console.exe";
794   else
795     helper_process = HELPER_PROCESS ".exe";
797   glib_top = NULL;
798   if (glib_top != NULL)
799     {
800       helper_process = g_build_filename (glib_top, "bin", helper_process, NULL);
801       g_free (glib_top);
802     }
803   else
804     helper_process = g_strdup (helper_process);
806   new_argv[0] = protect_argv_string (helper_process);
808   sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_pipe[1]);
809   new_argv[ARG_CHILD_ERR_REPORT] = args[ARG_CHILD_ERR_REPORT];
811   /* Make the read end of the child error report pipe
812    * noninherited. Otherwise it will needlessly be inherited by the
813    * helper process, and the started actual user process. As such that
814    * shouldn't harm, but it is unnecessary.
815    */
816   child_err_report_pipe[0] = dup_noninherited (child_err_report_pipe[0], _O_RDONLY);
818   if (flags & G_SPAWN_FILE_AND_ARGV_ZERO)
819     {
820       /* Overload ARG_CHILD_ERR_REPORT to also encode the
821        * G_SPAWN_FILE_AND_ARGV_ZERO functionality.
822        */
823       strcat (args[ARG_CHILD_ERR_REPORT], "#");
824     }
826   sprintf (args[ARG_HELPER_SYNC], "%d", helper_sync_pipe[0]);
827   new_argv[ARG_HELPER_SYNC] = args[ARG_HELPER_SYNC];
829   /* Make the write end of the sync pipe noninherited. Otherwise the
830    * helper process will inherit it, and thus if this process happens
831    * to crash before writing the sync byte to the pipe, the helper
832    * process won't read but won't get any EOF either, as it has the
833    * write end open itself.
834    */
835   helper_sync_pipe[1] = dup_noninherited (helper_sync_pipe[1], _O_WRONLY);
837   if (standard_input)
838     {
839       sprintf (args[ARG_STDIN], "%d", stdin_pipe[0]);
840       new_argv[ARG_STDIN] = args[ARG_STDIN];
841     }
842   else if (flags & G_SPAWN_CHILD_INHERITS_STDIN)
843     {
844       /* Let stdin be alone */
845       new_argv[ARG_STDIN] = "-";
846     }
847   else
848     {
849       /* Keep process from blocking on a read of stdin */
850       new_argv[ARG_STDIN] = "z";
851     }
853   if (standard_output)
854     {
855       sprintf (args[ARG_STDOUT], "%d", stdout_pipe[1]);
856       new_argv[ARG_STDOUT] = args[ARG_STDOUT];
857     }
858   else if (flags & G_SPAWN_STDOUT_TO_DEV_NULL)
859     {
860       new_argv[ARG_STDOUT] = "z";
861     }
862   else
863     {
864       new_argv[ARG_STDOUT] = "-";
865     }
867   if (standard_error)
868     {
869       sprintf (args[ARG_STDERR], "%d", stderr_pipe[1]);
870      new_argv[ARG_STDERR] = args[ARG_STDERR];
871     }
872   else if (flags & G_SPAWN_STDERR_TO_DEV_NULL)
873     {
874       new_argv[ARG_STDERR] = "z";
875     }
876   else
877     {
878       new_argv[ARG_STDERR] = "-";
879     }
881   if (working_directory && *working_directory)
882     new_argv[ARG_WORKING_DIRECTORY] = protect_argv_string (working_directory);
883   else
884     new_argv[ARG_WORKING_DIRECTORY] = g_strdup ("-");
886   if (!(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
887     new_argv[ARG_CLOSE_DESCRIPTORS] = "y";
888   else
889     new_argv[ARG_CLOSE_DESCRIPTORS] = "-";
891   if (flags & G_SPAWN_SEARCH_PATH)
892     new_argv[ARG_USE_PATH] = "y";
893   else
894     new_argv[ARG_USE_PATH] = "-";
896   if (exit_status == NULL)
897     new_argv[ARG_WAIT] = "-";
898   else
899     new_argv[ARG_WAIT] = "w";
901   for (i = 0; i <= argc; i++)
902     new_argv[ARG_PROGRAM + i] = protected_argv[i];
904   //SETUP_DEBUG();
906   if (debug)
907     {
908       g_print ("calling %s with argv:\n", helper_process);
909       for (i = 0; i < argc + 1 + ARG_COUNT; i++)
910         g_print ("argv[%d]: %s\n", i, (new_argv[i] ? new_argv[i] : "NULL"));
911     }
913   if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
914     {
915       if (conv_error_index == ARG_WORKING_DIRECTORY)
916         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
917                      _("Invalid working directory: %s"),
918                      conv_error->message);
919       else
920         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
921                      _("Invalid string in argument vector at %d: %s"),
922                      conv_error_index - ARG_PROGRAM, conv_error->message);
923       g_error_free (conv_error);
924       g_strfreev (protected_argv);
925       g_free (new_argv[0]);
926       g_free (new_argv[ARG_WORKING_DIRECTORY]);
927       g_free (new_argv);
928       g_free (helper_process);
930       goto cleanup_and_fail;
931     }
933   if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
934     {
935       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
936                    _("Invalid string in environment: %s"),
937                    conv_error->message);
938       g_error_free (conv_error);
939       g_strfreev (protected_argv);
940       g_free (new_argv[0]);
941       g_free (new_argv[ARG_WORKING_DIRECTORY]);
942       g_free (new_argv);
943       g_free (helper_process);
944       g_strfreev ((gchar **) wargv);
946       goto cleanup_and_fail;
947     }
949   if (child_setup)
950     (* child_setup) (user_data);
952   whelper = (wchar_t *)g_utf8_to_utf16 (helper_process, -1, NULL, NULL, NULL);
953   g_free (helper_process);
955   if (wenvp != NULL)
956     rc = _wspawnvpe (P_NOWAIT, whelper, (const wchar_t **) wargv, (const wchar_t **) wenvp);
957   else
958     rc = _wspawnvp (P_NOWAIT, whelper, (const wchar_t **) wargv);
960   saved_errno = errno;
962   g_free (whelper);
963   g_strfreev ((gchar **) wargv);
964   g_strfreev ((gchar **) wenvp);
966   /* Close the other process's ends of the pipes in this process,
967    * otherwise the reader will never get EOF.
968    */
969   close_and_invalidate (&child_err_report_pipe[1]);
970   close_and_invalidate (&helper_sync_pipe[0]);
971   close_and_invalidate (&stdin_pipe[0]);
972   close_and_invalidate (&stdout_pipe[1]);
973   close_and_invalidate (&stderr_pipe[1]);
975   g_strfreev (protected_argv);
977   g_free (new_argv[0]);
978   g_free (new_argv[ARG_WORKING_DIRECTORY]);
979   g_free (new_argv);
981   /* Check if gspawn-win32-helper couldn't be run */
982   if (rc == -1 && saved_errno != 0)
983     {
984       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
985                    _("Failed to execute helper program (%s)"),
986                    g_strerror (saved_errno));
987       goto cleanup_and_fail;
988     }
990   if (exit_status != NULL)
991     {
992       /* Synchronous case. Pass helper's report pipe back to caller,
993        * which takes care of reading it after the grandchild has
994        * finished.
995        */
996       g_assert (err_report != NULL);
997       *err_report = child_err_report_pipe[0];
998       write (helper_sync_pipe[1], " ", 1);
999       close_and_invalidate (&helper_sync_pipe[1]);
1000     }
1001   else
1002     {
1003       /* Asynchronous case. We read the helper's report right away. */
1004       if (!read_helper_report (child_err_report_pipe[0], helper_report, error))
1005         goto cleanup_and_fail;
1007       close_and_invalidate (&child_err_report_pipe[0]);
1009       switch (helper_report[0])
1010         {
1011         case CHILD_NO_ERROR:
1012           if (child_handle && do_return_handle)
1013             {
1014               /* rc is our HANDLE for gspawn-win32-helper. It has
1015                * told us the HANDLE of its child. Duplicate that into
1016                * a HANDLE valid in this process.
1017                */
1018               if (!DuplicateHandle ((HANDLE) rc, (HANDLE) helper_report[1],
1019                                     GetCurrentProcess (), (LPHANDLE) child_handle,
1020                                     0, TRUE, DUPLICATE_SAME_ACCESS))
1021                 {
1022                   char *emsg = g_win32_error_message (GetLastError ());
1023                   g_print("%s\n", emsg);
1024                   *child_handle = 0;
1025                 }
1026             }
1027           else if (child_handle)
1028             *child_handle = 0;
1029           write (helper_sync_pipe[1], " ", 1);
1030           close_and_invalidate (&helper_sync_pipe[1]);
1031           break;
1033         default:
1034           write (helper_sync_pipe[1], " ", 1);
1035           close_and_invalidate (&helper_sync_pipe[1]);
1036           set_child_error (helper_report, working_directory, error);
1037           goto cleanup_and_fail;
1038         }
1039     }
1041   /* Success against all odds! return the information */
1043   if (standard_input)
1044     *standard_input = stdin_pipe[1];
1045   if (standard_output)
1046     *standard_output = stdout_pipe[0];
1047   if (standard_error)
1048     *standard_error = stderr_pipe[0];
1049   if (rc != -1)
1050     CloseHandle ((HANDLE) rc);
1052   return TRUE;
1054   cleanup_and_fail:
1056   if (rc != -1)
1057     CloseHandle ((HANDLE) rc);
1058   if (child_err_report_pipe[0] != -1)
1059     close (child_err_report_pipe[0]);
1060   if (child_err_report_pipe[1] != -1)
1061     close (child_err_report_pipe[1]);
1062   if (helper_sync_pipe[0] != -1)
1063     close (helper_sync_pipe[0]);
1064   if (helper_sync_pipe[1] != -1)
1065     close (helper_sync_pipe[1]);
1066   if (stdin_pipe[0] != -1)
1067     close (stdin_pipe[0]);
1068   if (stdin_pipe[1] != -1)
1069     close (stdin_pipe[1]);
1070   if (stdout_pipe[0] != -1)
1071     close (stdout_pipe[0]);
1072   if (stdout_pipe[1] != -1)
1073     close (stdout_pipe[1]);
1074   if (stderr_pipe[0] != -1)
1075     close (stderr_pipe[0]);
1076   if (stderr_pipe[1] != -1)
1077     close (stderr_pipe[1]);
1079   return FALSE;
1082 gboolean
1083 my_spawn_async_with_pipes_utf8 (const gchar          *working_directory,
1084                                gchar               **argv,
1085                                gchar               **envp,
1086                                GSpawnFlags           flags,
1087                                GSpawnChildSetupFunc  child_setup,
1088                                gpointer              user_data,
1089                                GPid                 *child_handle,
1090                                gint                 *standard_input,
1091                                gint                 *standard_output,
1092                                gint                 *standard_error,
1093                                GError              **error)
1095   g_return_val_if_fail (argv != NULL, FALSE);
1096   g_return_val_if_fail (standard_output == NULL ||
1097                         !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
1098   g_return_val_if_fail (standard_error == NULL ||
1099                         !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
1100   /* can't inherit stdin if we have an input pipe. */
1101   g_return_val_if_fail (standard_input == NULL ||
1102                         !(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
1104   return do_spawn_with_pipes (NULL,
1105                               (flags & G_SPAWN_DO_NOT_REAP_CHILD),
1106                               working_directory,
1107                               argv,
1108                               envp,
1109                               flags,
1110                               child_setup,
1111                               user_data,
1112                               child_handle,
1113                               standard_input,
1114                               standard_output,
1115                               standard_error,
1116                               NULL,
1117                               error);
1120 typedef GPid Pid;
1122 // _WRAP_ENUM(SpawnFlags, GSpawnFlags, NO_GTYPE)
1124 /* Helper callback to invoke the actual sigc++ slot.
1125  * We don't need to worry about (un)referencing, since the
1126  * child process gets its own copy of the parent's memory anyway.
1127  */
1128 static void child_setup_callback(void* user_data)
1130   #ifdef GLIBMM_EXCEPTIONS_ENABLED
1131   try
1132   {
1133   #endif //GLIBMM_EXCEPTIONS_ENABLED
1134     (*reinterpret_cast<sigc::slot<void>*>(user_data))();
1135   #ifdef GLIBMM_EXCEPTIONS_ENABLED
1136   }
1137   catch(...)
1138   {
1139     Glib::exception_handlers_invoke();
1140   }
1141   #endif //GLIBMM_EXCEPTIONS_ENABLED
1145 void my_spawn_async_with_pipes(const std::string& working_directory,
1146                             const Glib::ArrayHandle<std::string>& argv,
1147                             GSpawnFlags flags,
1148                             const sigc::slot<void>& child_setup,
1149                             Pid* child_pid,
1150                             int* standard_input,
1151                             int* standard_output,
1152                             int* standard_error)
1154   const bool setup_slot = !child_setup.empty();
1155   sigc::slot<void> child_setup_ = child_setup;
1156   GError* error = 0;
1158   my_spawn_async_with_pipes_utf8(
1159       working_directory.c_str(),
1160       const_cast<char**>(argv.data()), 0,
1161       static_cast<GSpawnFlags>(unsigned(flags)),
1162       (setup_slot) ? &child_setup_callback : 0,
1163       (setup_slot) ? &child_setup_         : 0,
1164       child_pid,
1165       standard_input, standard_output, standard_error,
1166       &error);
1168   if(error)
1169     Glib::Error::throw_exception(error);
1172 #endif
1174 void
1175 Inkscape::IO::spawn_async_with_pipes( const std::string& working_directory,
1176                                       const Glib::ArrayHandle<std::string>& argv,
1177                                       Glib::SpawnFlags flags,
1178                                       const sigc::slot<void>& child_setup,
1179                                       Glib::Pid* child_pid,
1180                                       int* standard_input,
1181                                       int* standard_output,
1182                                       int* standard_error)
1184 #ifndef BYPASS_GLIB_SPAWN
1185     Glib::spawn_async_with_pipes(working_directory,
1186                                  argv,
1187                                  flags,
1188                                  child_setup,
1189                                  child_pid,
1190                                  standard_input,
1191                                  standard_output,
1192                                  standard_error);
1193 #else
1194     my_spawn_async_with_pipes(working_directory,
1195                               argv,
1196                               static_cast<GSpawnFlags>(flags),
1197                               child_setup,
1198                               child_pid,
1199                               standard_input,
1200                               standard_output,
1201                               standard_error);
1202 #endif
1206 gchar* Inkscape::IO::sanitizeString( gchar const * str )
1208     gchar *result = NULL;
1209     if ( str ) {
1210         if ( g_utf8_validate(str, -1, NULL) ) {
1211             result = g_strdup(str);
1212         } else {
1213             guchar scratch[8];
1214             Glib::ustring buf;
1215             guchar const *ptr = (guchar const*)str;
1216             while ( *ptr )
1217             {
1218                 if ( *ptr == '\\' )
1219                 {
1220                     buf.append("\\\\");
1221                 } else if ( *ptr < 0x80 ) {
1222                     buf += (char)(*ptr);
1223                 } else {
1224                     g_snprintf((gchar*)scratch, sizeof(scratch), "\\x%02x", *ptr);
1225                     buf.append((const char*)scratch);
1226                 }
1227                 ptr++;
1228             }
1229             result = g_strdup(buf.c_str());
1230         }
1231     }
1232     return result;
1235 /*
1236   Local Variables:
1237   mode:c++
1238   c-file-style:"stroustrup"
1239   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1240   indent-tabs-mode:nil
1241   fill-column:99
1242   End:
1243 */
1244 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :