Code

updated spanish.nsh and inkscape.nsi to reflect latest file-changes
[inkscape.git] / trunk / 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 "preferences.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     Inkscape::Preferences *prefs = Inkscape::Preferences::get(); \
79     gint dump = prefs->getBool("/options/bulia/" #key) ? 1 : 0;\
80     gint dumpD = prefs->getBool("/options/bulia/" #key"D") ? 1 : 0;\
81     gint dumpD2 = prefs->getBool("/options/bulia/" #key"D2") ? 1 : 0;\
82     dumpD &= ( (update_in_progress == 0) || dumpD2 );\
83     if ( dump )\
84     {\
85         g_message( __VA_ARGS__ );\
86 \
87     }\
88     if ( dumpD )\
89     {\
90         GtkWidget *dialog = gtk_message_dialog_new(NULL,\
91                                                    GTK_DIALOG_DESTROY_WITH_PARENT, \
92                                                    GTK_MESSAGE_INFO,    \
93                                                    GTK_BUTTONS_OK,      \
94                                                    __VA_ARGS__          \
95                                                    );\
96         g_signal_connect_swapped(dialog, "response",\
97                                  G_CALLBACK(gtk_widget_destroy),        \
98                                  dialog);                               \
99         gtk_widget_show_all( dialog );\
100     }\
106 void Inkscape::IO::dump_fopen_call( char const *utf8name, char const *id )
108 #ifdef INK_DUMP_FOPEN
109     Glib::ustring str;
110     for ( int i = 0; utf8name[i]; i++ )
111     {
112         if ( utf8name[i] == '\\' )
113         {
114             str += "\\\\";
115         }
116         else if ( (utf8name[i] >= 0x20) && ((0x0ff & utf8name[i]) <= 0x7f) )
117         {
118             str += utf8name[i];
119         }
120         else
121         {
122             gchar tmp[32];
123             g_snprintf( tmp, sizeof(tmp), "\\x%02x", (0x0ff & utf8name[i]) );
124             str += tmp;
125         }
126     }
127     g_message( "fopen call %s for [%s]", id, str.data() );
128 #else
129     (void)utf8name;
130     (void)id;
131 #endif
134 FILE *Inkscape::IO::fopen_utf8name( char const *utf8name, char const *mode )
136     static gint counter = 0;
137     FILE* fp = NULL;
139     DEBUG_MESSAGE( dumpOne, "entering fopen_utf8name( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
141 #ifndef WIN32
142     DEBUG_MESSAGE( dumpOne, "           STEP 0              ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
143     gchar *filename = g_filename_from_utf8( utf8name, -1, NULL, NULL, NULL );
144     if ( filename )
145     {
146         DEBUG_MESSAGE( dumpOne, "           STEP 1              ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
147         fp = std::fopen(filename, mode);
148         DEBUG_MESSAGE( dumpOne, "           STEP 2              ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
149         g_free(filename);
150         DEBUG_MESSAGE( dumpOne, "           STEP 3              ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
151         filename = 0;
152     }
153 #else
154     Glib::ustring how( mode );
155     how.append("b");
156     DEBUG_MESSAGE( dumpOne, "   calling is_os_wide()       ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
158     fp = g_fopen(utf8name, how.c_str());
159 #endif
161     DEBUG_MESSAGE( dumpOne, "leaving fopen_utf8name( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
163     return fp;
167 int Inkscape::IO::mkdir_utf8name( char const *utf8name )
169     static gint counter = 0;
170     int retval = -1;
172     DEBUG_MESSAGE( dumpMk, "entering mkdir_utf8name( '%s' )[%d]", utf8name, (counter++) );
174 #ifndef WIN32
175     DEBUG_MESSAGE( dumpMk, "           STEP 0              ( '%s' )[%d]", utf8name, (counter++) );
176     gchar *filename = g_filename_from_utf8( utf8name, -1, NULL, NULL, NULL );
177     if ( filename )
178     {
179         DEBUG_MESSAGE( dumpMk, "           STEP 1              ( '%s' )[%d]", utf8name, (counter++) );
180         retval = ::mkdir(filename, S_IRWXU | S_IRGRP | S_IXGRP);
181         DEBUG_MESSAGE( dumpMk, "           STEP 2              ( '%s' )[%d]", utf8name, (counter++) );
182         g_free(filename);
183         DEBUG_MESSAGE( dumpMk, "           STEP 3              ( '%s' )[%d]", utf8name, (counter++) );
184         filename = 0;
185     }
186 #else
187     DEBUG_MESSAGE( dumpMk, "   calling is_os_wide()       ( '%s' )[%d]", utf8name, (counter++) );
189     // Mode should be ingnored inside of glib on the way in
190     retval = g_mkdir( utf8name, 0 );
191 #endif
193     DEBUG_MESSAGE( dumpMk, "leaving mkdir_utf8name( '%s' )[%d]", utf8name, (counter++) );
195     return retval;
198 /* 
199  * Wrapper around Glib::file_open_tmp()
200  * Returns a handle to the temp file
201  * name_used contains the actual name used
202  * 
203  * Returns:
204  * A file handle (as from open()) to the file opened for reading and writing. 
205  * The file is opened in binary mode on platforms where there is a difference. 
206  * The file handle should be closed with close().
207  * 
208  * Note:
209  * On Windows Vista Glib::file_open_tmp fails with the current version of glibmm
210  * A special case is implemented for WIN32. This can be removed if the issue is fixed
211  * in future versions of glibmm 
212  * */
213 int Inkscape::IO::file_open_tmp(std::string& name_used, const std::string& prefix)
215 #ifndef WIN32
216     return Glib::file_open_tmp(name_used, prefix);
217 #else
218     /* Special case for WIN32 due to a bug in glibmm
219      * (only needed for Windows Vista, but since there is only one windows build all builds get the workaround)
220      * The workaround can be removed if the bug is fixed in glibmm
221      * 
222      * The code is mostly identical to the implementation in glibmm
223      * http://svn.gnome.org/svn/glibmm/branches/glibmm-2-12/glib/src/fileutils.ccg
224      * */
225     
226     std::string basename_template (prefix);
227     basename_template += "XXXXXX"; // this sillyness shouldn't be in the interface
228     
229     GError* error = 0;
230     gchar *buf_name_used;
231     
232     gint fileno = g_file_open_tmp(basename_template.c_str(), &buf_name_used, &error);
233     
234     if(error)
235         Glib::Error::throw_exception(error);
236     
237     name_used = g_strdup(buf_name_used);
238     g_free(buf_name_used);
239     return fileno;
240 #endif
243 bool Inkscape::IO::file_test( char const *utf8name, GFileTest test )
245     bool exists = false;
247     if ( utf8name ) {
248         gchar *filename = NULL;
249         if (utf8name && !g_utf8_validate(utf8name, -1, NULL)) {
250             /* FIXME: Trying to guess whether or not a filename is already in utf8 is unreliable.
251                If any callers pass non-utf8 data (e.g. using g_get_home_dir), then change caller to
252                use simple g_file_test.  Then add g_return_val_if_fail(g_utf_validate(...), false)
253                to beginning of this function. */
254             filename = g_strdup(utf8name);
255             // Looks like g_get_home_dir isn't safe.
256             //g_warning("invalid UTF-8 detected internally. HUNT IT DOWN AND KILL IT!!!");
257         } else {
258             filename = g_filename_from_utf8 ( utf8name, -1, NULL, NULL, NULL );
259         }
260         if ( filename ) {
261             exists = g_file_test (filename, test);
262             g_free(filename);
263             filename = NULL;
264         } else {
265             g_warning( "Unable to convert filename in IO:file_test" );
266         }
267     }
269     return exists;
272 /** Wrapper around g_dir_open, but taking a utf8name as first argument. */
273 GDir *
274 Inkscape::IO::dir_open(gchar const *const utf8name, guint const flags, GError **const error)
276     gchar *const opsys_name = g_filename_from_utf8(utf8name, -1, NULL, NULL, error);
277     if (opsys_name) {
278         GDir *ret = g_dir_open(opsys_name, flags, error);
279         g_free(opsys_name);
280         return ret;
281     } else {
282         return NULL;
283     }
286 /**
287  * Like g_dir_read_name, but returns a utf8name (which must be freed, unlike g_dir_read_name).
288  *
289  * N.B. Skips over any dir entries that fail to convert to utf8.
290  */
291 gchar *
292 Inkscape::IO::dir_read_utf8name(GDir *dir)
294     for (;;) {
295         gchar const *const opsys_name = g_dir_read_name(dir);
296         if (!opsys_name) {
297             return NULL;
298         }
299         gchar *utf8_name = g_filename_to_utf8(opsys_name, -1, NULL, NULL, NULL);
300         if (utf8_name) {
301             return utf8_name;
302         }
303     }
307 gchar* Inkscape::IO::locale_to_utf8_fallback( const gchar *opsysstring,
308                                               gssize len,
309                                               gsize *bytes_read,
310                                               gsize *bytes_written,
311                                               GError **error )
313     gchar *result = NULL;
314     if ( opsysstring ) {
315         gchar *newFileName = g_locale_to_utf8( opsysstring, len, bytes_read, bytes_written, error );
316         if ( newFileName ) {
317             if ( !g_utf8_validate(newFileName, -1, NULL) ) {
318                 g_warning( "input filename did not yield UTF-8" );
319                 g_free( newFileName );
320             } else {
321                 result = newFileName;
322             }
323             newFileName = 0;
324         } else if ( g_utf8_validate(opsysstring, -1, NULL) ) {
325             // This *might* be a case that we want
326             // g_warning( "input failed filename->utf8, fell back to original" );
327             // TODO handle cases when len >= 0
328             result = g_strdup( opsysstring );
329         } else {
330             gchar const *charset = 0;
331             g_get_charset(&charset);
332             g_warning( "input filename conversion failed for file with locale charset '%s'", charset );
333         }
334     }
335     return result;
338 #ifdef BYPASS_GLIB_SPAWN
339 /*
340         this code was taken from the original glib sources
341 */
342 #define GSPAWN_HELPER
343  
344 enum
346   CHILD_NO_ERROR,
347   CHILD_CHDIR_FAILED,
348   CHILD_SPAWN_FAILED,
349 };
351 enum {
352   ARG_CHILD_ERR_REPORT = 1,
353   ARG_HELPER_SYNC,
354   ARG_STDIN,
355   ARG_STDOUT,
356   ARG_STDERR,
357   ARG_WORKING_DIRECTORY,
358   ARG_CLOSE_DESCRIPTORS,
359   ARG_USE_PATH,
360   ARG_WAIT,
361   ARG_PROGRAM,
362   ARG_COUNT = ARG_PROGRAM
363 };
364 static int debug = 0;
365 #define HELPER_PROCESS "gspawn-win32-helper"
368 static int
369 dup_noninherited (int fd,
370                   int mode)
372   HANDLE filehandle;
374   DuplicateHandle (GetCurrentProcess (), (LPHANDLE) _get_osfhandle (fd),
375                    GetCurrentProcess (), &filehandle,
376                    0, FALSE, DUPLICATE_SAME_ACCESS);
377   close (fd);
378   return _open_osfhandle ((long) filehandle, mode | _O_NOINHERIT);
382 /* The helper process writes a status report back to us, through a
383  * pipe, consisting of two ints.
384  */
385 static gboolean
386 read_helper_report (int      fd,
387                     gint     report[2],
388                     GError **error)
390   gint bytes = 0;
392   while (bytes < sizeof(gint)*2)
393     {
394       gint chunk;
396       if (debug)
397         g_print ("%s:read_helper_report: read %d...\n",
398                  __FILE__,
399                  sizeof(gint)*2 - bytes);
401       chunk = read (fd, ((gchar*)report) + bytes,
402                     sizeof(gint)*2 - bytes);
404       if (debug)
405         g_print ("...got %d bytes\n", chunk);
407       if (chunk < 0)
408         {
409           /* Some weird shit happened, bail out */
411           g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
412                        _("Failed to read from child pipe (%s)"),
413                        g_strerror (errno));
415           return FALSE;
416         }
417       else if (chunk == 0)
418         {
419           g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
420                        _("Failed to read from child pipe (%s)"),
421                        "EOF");
422           break; /* EOF */
423         }
424       else
425         bytes += chunk;
426     }
428   if (bytes < sizeof(gint)*2)
429     return FALSE;
431   return TRUE;
435 static void
436 set_child_error (gint         report[2],
437                  const gchar *working_directory,
438                  GError     **error)
440   switch (report[0])
441     {
442     case CHILD_CHDIR_FAILED:
443       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
444                    _("Failed to change to directory '%s' (%s)"),
445                    working_directory,
446                    g_strerror (report[1]));
447       break;
448     case CHILD_SPAWN_FAILED:
449       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
450                    _("Failed to execute child process (%s)"),
451                    g_strerror (report[1]));
452       break;
453     default:
454       g_assert_not_reached ();
455     }
458 static gchar *
459 protect_argv_string (const gchar *string)
461   const gchar *p = string;
462   gchar *retval, *q;
463   gint len = 0;
464   gboolean need_dblquotes = FALSE;
465   while (*p)
466     {
467       if (*p == ' ' || *p == '\t')
468         need_dblquotes = TRUE;
469       else if (*p == '"')
470         len++;
471       else if (*p == '\\')
472         {
473           const gchar *pp = p;
474           while (*pp && *pp == '\\')
475             pp++;
476           if (*pp == '"')
477             len++;
478         }
479       len++;
480       p++;
481     }
483   q = retval = (gchar *)g_malloc (len + need_dblquotes*2 + 1);
484   p = string;
486   if (need_dblquotes)
487     *q++ = '"';
489   while (*p)
490     {
491       if (*p == '"')
492         *q++ = '\\';
493       else if (*p == '\\')
494         {
495           const gchar *pp = p;
496           while (*pp && *pp == '\\')
497             pp++;
498           if (*pp == '"')
499             *q++ = '\\';
500         }
501       *q++ = *p;
502       p++;
503     }
505   if (need_dblquotes)
506     *q++ = '"';
507   *q++ = '\0';
509   return retval;
513 static gint
514 protect_argv (gchar  **argv,
515               gchar ***new_argv)
517   gint i;
518   gint argc = 0;
520   while (argv[argc])
521     ++argc;
522   *new_argv = g_new (gchar *, argc+1);
524   /* Quote each argv element if necessary, so that it will get
525    * reconstructed correctly in the C runtime startup code.  Note that
526    * the unquoting algorithm in the C runtime is really weird, and
527    * rather different than what Unix shells do. See stdargv.c in the C
528    * runtime sources (in the Platform SDK, in src/crt).
529    *
530    * Note that an new_argv[0] constructed by this function should
531    * *not* be passed as the filename argument to a spawn* or exec*
532    * family function. That argument should be the real file name
533    * without any quoting.
534    */
535   for (i = 0; i < argc; i++)
536     (*new_argv)[i] = protect_argv_string (argv[i]);
538   (*new_argv)[argc] = NULL;
540   return argc;
544 static gboolean
545 utf8_charv_to_wcharv (char     **utf8_charv,
546                       wchar_t ***wcharv,
547                       int       *error_index,
548                       GError   **error)
550   wchar_t **retval = NULL;
552   *wcharv = NULL;
553   if (utf8_charv != NULL)
554     {
555       int n = 0, i;
557       while (utf8_charv[n])
558         n++;
559       retval = g_new (wchar_t *, n + 1);
561       for (i = 0; i < n; i++)
562         {
563           retval[i] = (wchar_t *)g_utf8_to_utf16 (utf8_charv[i], -1, NULL, NULL, error);
564           if (retval[i] == NULL)
565             {
566               if (error_index)
567                 *error_index = i;
568               while (i)
569                 g_free (retval[--i]);
570               g_free (retval);
571               return FALSE;
572             }
573         }
575       retval[n] = NULL;
576     }
577   *wcharv = retval;
578   return TRUE;
582 /* Avoids a danger in threaded situations (calling close()
583  * on a file descriptor twice, and another thread has
584  * re-opened it since the first close)
585  */
586 static void
587 close_and_invalidate (gint *fd)
589   if (*fd < 0)
590     return;
592   close (*fd);
593   *fd = -1;
597 static gboolean
598 do_spawn_directly (gint                 *exit_status,
599                    gboolean              do_return_handle,
600                    GSpawnFlags           flags,
601                    gchar               **argv,
602                    char                **envp,
603                    char                **protected_argv,
604                    GSpawnChildSetupFunc  child_setup,
605                    gpointer              user_data,
606                    GPid                 *child_handle,
607                    GError              **error)
609   const int mode = (exit_status == NULL) ? P_NOWAIT : P_WAIT;
610   char **new_argv;
611   int rc = -1;
612   int saved_errno;
613   GError *conv_error = NULL;
614   gint conv_error_index;
615   wchar_t *wargv0, **wargv, **wenvp;
617   new_argv = (flags & G_SPAWN_FILE_AND_ARGV_ZERO) ? protected_argv + 1 : protected_argv;
619   wargv0 = (wchar_t *)g_utf8_to_utf16 (argv[0], -1, NULL, NULL, &conv_error);
620   if (wargv0 == NULL)
621     {
622       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
623                    _("Invalid program name: %s"),
624                    conv_error->message);
625       g_error_free (conv_error);
627       return FALSE;
628     }
630   if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
631     {
632       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
633                    _("Invalid string in argument vector at %d: %s"),
634                    conv_error_index, conv_error->message);
635       g_error_free (conv_error);
636       g_free (wargv0);
638       return FALSE;
639     }
641   if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
642     {
643       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
644                    _("Invalid string in environment: %s"),
645                    conv_error->message);
646       g_error_free (conv_error);
647       g_free (wargv0);
648       g_strfreev ((gchar **) wargv);
650       return FALSE;
651     }
653   if (child_setup)
654     (* child_setup) (user_data);
656   if (flags & G_SPAWN_SEARCH_PATH)
657     if (wenvp != NULL)
658       rc = _wspawnvpe (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
659     else
660       rc = _wspawnvp (mode, wargv0, (const wchar_t **) wargv);
661   else
662     if (wenvp != NULL)
663       rc = _wspawnve (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
664     else
665       rc = _wspawnv (mode, wargv0, (const wchar_t **) wargv);
667   g_free (wargv0);
668   g_strfreev ((gchar **) wargv);
669   g_strfreev ((gchar **) wenvp);
671   saved_errno = errno;
673   if (rc == -1 && saved_errno != 0)
674     {
675       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
676                    _("Failed to execute child process (%s)"),
677                    g_strerror (saved_errno));
678       return FALSE;
679     }
681   if (exit_status == NULL)
682     {
683       if (child_handle && do_return_handle)
684         *child_handle = (GPid) rc;
685       else
686         {
687           CloseHandle ((HANDLE) rc);
688           if (child_handle)
689             *child_handle = 0;
690         }
691     }
692   else
693     *exit_status = rc;
695   return TRUE;
698 static gboolean
699 make_pipe (gint     p[2],
700            GError **error)
702   if (_pipe (p, 4096, _O_BINARY) < 0)
703     {
704       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
705                    _("Failed to create pipe for communicating with child process (%s)"),
706                    g_strerror (errno));
707       return FALSE;
708     }
709   else
710     return TRUE;
714 static gboolean
715 do_spawn_with_pipes (gint                 *exit_status,
716                      gboolean              do_return_handle,
717                      const gchar          *working_directory,
718                      gchar               **argv,
719                      char                **envp,
720                      GSpawnFlags           flags,
721                      GSpawnChildSetupFunc  child_setup,
722                      gpointer              user_data,
723                      GPid                 *child_handle,
724                      gint                 *standard_input,
725                      gint                 *standard_output,
726                      gint                 *standard_error,
727                      gint                 *err_report,
728                      GError              **error)
730   char **protected_argv;
731   char args[ARG_COUNT][10];
732   char **new_argv;
733   int i;
734   int rc = -1;
735   int saved_errno;
736   int argc;
737   int stdin_pipe[2] = { -1, -1 };
738   int stdout_pipe[2] = { -1, -1 };
739   int stderr_pipe[2] = { -1, -1 };
740   int child_err_report_pipe[2] = { -1, -1 };
741   int helper_sync_pipe[2] = { -1, -1 };
742   int helper_report[2];
743   static gboolean warned_about_child_setup = FALSE;
744   GError *conv_error = NULL;
745   gint conv_error_index;
746   gchar *helper_process;
747   CONSOLE_CURSOR_INFO cursor_info;
748   wchar_t *whelper, **wargv, **wenvp;
749   //extern gchar *_glib_get_installation_directory (void);
750   gchar *glib_top;
752   if (child_setup && !warned_about_child_setup)
753     {
754       warned_about_child_setup = TRUE;
755       g_warning ("passing a child setup function to the g_spawn functions is pointless and dangerous on Win32");
756     }
758   argc = protect_argv (argv, &protected_argv);
760   if (!standard_input && !standard_output && !standard_error &&
761       (flags & G_SPAWN_CHILD_INHERITS_STDIN) &&
762       !(flags & G_SPAWN_STDOUT_TO_DEV_NULL) &&
763       !(flags & G_SPAWN_STDERR_TO_DEV_NULL) &&
764       (working_directory == NULL || !*working_directory) &&
765       (flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
766     {
767       /* We can do without the helper process */
768       gboolean retval =
769         do_spawn_directly (exit_status, do_return_handle, flags,
770                            argv, envp, protected_argv,
771                            child_setup, user_data, child_handle,
772                            error);
773       g_strfreev (protected_argv);
774       return retval;
775     }
777   if (standard_input && !make_pipe (stdin_pipe, error))
778     goto cleanup_and_fail;
780   if (standard_output && !make_pipe (stdout_pipe, error))
781     goto cleanup_and_fail;
783   if (standard_error && !make_pipe (stderr_pipe, error))
784     goto cleanup_and_fail;
786   if (!make_pipe (child_err_report_pipe, error))
787     goto cleanup_and_fail;
789   if (!make_pipe (helper_sync_pipe, error))
790     goto cleanup_and_fail;
792   new_argv = g_new (char *, argc + 1 + ARG_COUNT);
793   if (GetConsoleCursorInfo (GetStdHandle (STD_OUTPUT_HANDLE), &cursor_info))
794     helper_process = HELPER_PROCESS "-console.exe";
795   else
796     helper_process = HELPER_PROCESS ".exe";
798   glib_top = NULL;
799   if (glib_top != NULL)
800     {
801       helper_process = g_build_filename (glib_top, "bin", helper_process, NULL);
802       g_free (glib_top);
803     }
804   else
805     helper_process = g_strdup (helper_process);
807   new_argv[0] = protect_argv_string (helper_process);
809   sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_pipe[1]);
810   new_argv[ARG_CHILD_ERR_REPORT] = args[ARG_CHILD_ERR_REPORT];
812   /* Make the read end of the child error report pipe
813    * noninherited. Otherwise it will needlessly be inherited by the
814    * helper process, and the started actual user process. As such that
815    * shouldn't harm, but it is unnecessary.
816    */
817   child_err_report_pipe[0] = dup_noninherited (child_err_report_pipe[0], _O_RDONLY);
819   if (flags & G_SPAWN_FILE_AND_ARGV_ZERO)
820     {
821       /* Overload ARG_CHILD_ERR_REPORT to also encode the
822        * G_SPAWN_FILE_AND_ARGV_ZERO functionality.
823        */
824       strcat (args[ARG_CHILD_ERR_REPORT], "#");
825     }
827   sprintf (args[ARG_HELPER_SYNC], "%d", helper_sync_pipe[0]);
828   new_argv[ARG_HELPER_SYNC] = args[ARG_HELPER_SYNC];
830   /* Make the write end of the sync pipe noninherited. Otherwise the
831    * helper process will inherit it, and thus if this process happens
832    * to crash before writing the sync byte to the pipe, the helper
833    * process won't read but won't get any EOF either, as it has the
834    * write end open itself.
835    */
836   helper_sync_pipe[1] = dup_noninherited (helper_sync_pipe[1], _O_WRONLY);
838   if (standard_input)
839     {
840       sprintf (args[ARG_STDIN], "%d", stdin_pipe[0]);
841       new_argv[ARG_STDIN] = args[ARG_STDIN];
842     }
843   else if (flags & G_SPAWN_CHILD_INHERITS_STDIN)
844     {
845       /* Let stdin be alone */
846       new_argv[ARG_STDIN] = "-";
847     }
848   else
849     {
850       /* Keep process from blocking on a read of stdin */
851       new_argv[ARG_STDIN] = "z";
852     }
854   if (standard_output)
855     {
856       sprintf (args[ARG_STDOUT], "%d", stdout_pipe[1]);
857       new_argv[ARG_STDOUT] = args[ARG_STDOUT];
858     }
859   else if (flags & G_SPAWN_STDOUT_TO_DEV_NULL)
860     {
861       new_argv[ARG_STDOUT] = "z";
862     }
863   else
864     {
865       new_argv[ARG_STDOUT] = "-";
866     }
868   if (standard_error)
869     {
870       sprintf (args[ARG_STDERR], "%d", stderr_pipe[1]);
871      new_argv[ARG_STDERR] = args[ARG_STDERR];
872     }
873   else if (flags & G_SPAWN_STDERR_TO_DEV_NULL)
874     {
875       new_argv[ARG_STDERR] = "z";
876     }
877   else
878     {
879       new_argv[ARG_STDERR] = "-";
880     }
882   if (working_directory && *working_directory)
883     new_argv[ARG_WORKING_DIRECTORY] = protect_argv_string (working_directory);
884   else
885     new_argv[ARG_WORKING_DIRECTORY] = g_strdup ("-");
887   if (!(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
888     new_argv[ARG_CLOSE_DESCRIPTORS] = "y";
889   else
890     new_argv[ARG_CLOSE_DESCRIPTORS] = "-";
892   if (flags & G_SPAWN_SEARCH_PATH)
893     new_argv[ARG_USE_PATH] = "y";
894   else
895     new_argv[ARG_USE_PATH] = "-";
897   if (exit_status == NULL)
898     new_argv[ARG_WAIT] = "-";
899   else
900     new_argv[ARG_WAIT] = "w";
902   for (i = 0; i <= argc; i++)
903     new_argv[ARG_PROGRAM + i] = protected_argv[i];
905   //SETUP_DEBUG();
907   if (debug)
908     {
909       g_print ("calling %s with argv:\n", helper_process);
910       for (i = 0; i < argc + 1 + ARG_COUNT; i++)
911         g_print ("argv[%d]: %s\n", i, (new_argv[i] ? new_argv[i] : "NULL"));
912     }
914   if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
915     {
916       if (conv_error_index == ARG_WORKING_DIRECTORY)
917         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
918                      _("Invalid working directory: %s"),
919                      conv_error->message);
920       else
921         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
922                      _("Invalid string in argument vector at %d: %s"),
923                      conv_error_index - ARG_PROGRAM, conv_error->message);
924       g_error_free (conv_error);
925       g_strfreev (protected_argv);
926       g_free (new_argv[0]);
927       g_free (new_argv[ARG_WORKING_DIRECTORY]);
928       g_free (new_argv);
929       g_free (helper_process);
931       goto cleanup_and_fail;
932     }
934   if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
935     {
936       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
937                    _("Invalid string in environment: %s"),
938                    conv_error->message);
939       g_error_free (conv_error);
940       g_strfreev (protected_argv);
941       g_free (new_argv[0]);
942       g_free (new_argv[ARG_WORKING_DIRECTORY]);
943       g_free (new_argv);
944       g_free (helper_process);
945       g_strfreev ((gchar **) wargv);
947       goto cleanup_and_fail;
948     }
950   if (child_setup)
951     (* child_setup) (user_data);
953   whelper = (wchar_t *)g_utf8_to_utf16 (helper_process, -1, NULL, NULL, NULL);
954   g_free (helper_process);
956   if (wenvp != NULL)
957     rc = _wspawnvpe (P_NOWAIT, whelper, (const wchar_t **) wargv, (const wchar_t **) wenvp);
958   else
959     rc = _wspawnvp (P_NOWAIT, whelper, (const wchar_t **) wargv);
961   saved_errno = errno;
963   g_free (whelper);
964   g_strfreev ((gchar **) wargv);
965   g_strfreev ((gchar **) wenvp);
967   /* Close the other process's ends of the pipes in this process,
968    * otherwise the reader will never get EOF.
969    */
970   close_and_invalidate (&child_err_report_pipe[1]);
971   close_and_invalidate (&helper_sync_pipe[0]);
972   close_and_invalidate (&stdin_pipe[0]);
973   close_and_invalidate (&stdout_pipe[1]);
974   close_and_invalidate (&stderr_pipe[1]);
976   g_strfreev (protected_argv);
978   g_free (new_argv[0]);
979   g_free (new_argv[ARG_WORKING_DIRECTORY]);
980   g_free (new_argv);
982   /* Check if gspawn-win32-helper couldn't be run */
983   if (rc == -1 && saved_errno != 0)
984     {
985       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
986                    _("Failed to execute helper program (%s)"),
987                    g_strerror (saved_errno));
988       goto cleanup_and_fail;
989     }
991   if (exit_status != NULL)
992     {
993       /* Synchronous case. Pass helper's report pipe back to caller,
994        * which takes care of reading it after the grandchild has
995        * finished.
996        */
997       g_assert (err_report != NULL);
998       *err_report = child_err_report_pipe[0];
999       write (helper_sync_pipe[1], " ", 1);
1000       close_and_invalidate (&helper_sync_pipe[1]);
1001     }
1002   else
1003     {
1004       /* Asynchronous case. We read the helper's report right away. */
1005       if (!read_helper_report (child_err_report_pipe[0], helper_report, error))
1006         goto cleanup_and_fail;
1008       close_and_invalidate (&child_err_report_pipe[0]);
1010       switch (helper_report[0])
1011         {
1012         case CHILD_NO_ERROR:
1013           if (child_handle && do_return_handle)
1014             {
1015               /* rc is our HANDLE for gspawn-win32-helper. It has
1016                * told us the HANDLE of its child. Duplicate that into
1017                * a HANDLE valid in this process.
1018                */
1019               if (!DuplicateHandle ((HANDLE) rc, (HANDLE) helper_report[1],
1020                                     GetCurrentProcess (), (LPHANDLE) child_handle,
1021                                     0, TRUE, DUPLICATE_SAME_ACCESS))
1022                 {
1023                   char *emsg = g_win32_error_message (GetLastError ());
1024                   g_print("%s\n", emsg);
1025                   *child_handle = 0;
1026                 }
1027             }
1028           else if (child_handle)
1029             *child_handle = 0;
1030           write (helper_sync_pipe[1], " ", 1);
1031           close_and_invalidate (&helper_sync_pipe[1]);
1032           break;
1034         default:
1035           write (helper_sync_pipe[1], " ", 1);
1036           close_and_invalidate (&helper_sync_pipe[1]);
1037           set_child_error (helper_report, working_directory, error);
1038           goto cleanup_and_fail;
1039         }
1040     }
1042   /* Success against all odds! return the information */
1044   if (standard_input)
1045     *standard_input = stdin_pipe[1];
1046   if (standard_output)
1047     *standard_output = stdout_pipe[0];
1048   if (standard_error)
1049     *standard_error = stderr_pipe[0];
1050   if (rc != -1)
1051     CloseHandle ((HANDLE) rc);
1053   return TRUE;
1055   cleanup_and_fail:
1057   if (rc != -1)
1058     CloseHandle ((HANDLE) rc);
1059   if (child_err_report_pipe[0] != -1)
1060     close (child_err_report_pipe[0]);
1061   if (child_err_report_pipe[1] != -1)
1062     close (child_err_report_pipe[1]);
1063   if (helper_sync_pipe[0] != -1)
1064     close (helper_sync_pipe[0]);
1065   if (helper_sync_pipe[1] != -1)
1066     close (helper_sync_pipe[1]);
1067   if (stdin_pipe[0] != -1)
1068     close (stdin_pipe[0]);
1069   if (stdin_pipe[1] != -1)
1070     close (stdin_pipe[1]);
1071   if (stdout_pipe[0] != -1)
1072     close (stdout_pipe[0]);
1073   if (stdout_pipe[1] != -1)
1074     close (stdout_pipe[1]);
1075   if (stderr_pipe[0] != -1)
1076     close (stderr_pipe[0]);
1077   if (stderr_pipe[1] != -1)
1078     close (stderr_pipe[1]);
1080   return FALSE;
1083 gboolean
1084 my_spawn_async_with_pipes_utf8 (const gchar          *working_directory,
1085                                gchar               **argv,
1086                                gchar               **envp,
1087                                GSpawnFlags           flags,
1088                                GSpawnChildSetupFunc  child_setup,
1089                                gpointer              user_data,
1090                                GPid                 *child_handle,
1091                                gint                 *standard_input,
1092                                gint                 *standard_output,
1093                                gint                 *standard_error,
1094                                GError              **error)
1096   g_return_val_if_fail (argv != NULL, FALSE);
1097   g_return_val_if_fail (standard_output == NULL ||
1098                         !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
1099   g_return_val_if_fail (standard_error == NULL ||
1100                         !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
1101   /* can't inherit stdin if we have an input pipe. */
1102   g_return_val_if_fail (standard_input == NULL ||
1103                         !(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
1105   return do_spawn_with_pipes (NULL,
1106                               (flags & G_SPAWN_DO_NOT_REAP_CHILD),
1107                               working_directory,
1108                               argv,
1109                               envp,
1110                               flags,
1111                               child_setup,
1112                               user_data,
1113                               child_handle,
1114                               standard_input,
1115                               standard_output,
1116                               standard_error,
1117                               NULL,
1118                               error);
1121 typedef GPid Pid;
1123 // _WRAP_ENUM(SpawnFlags, GSpawnFlags, NO_GTYPE)
1125 /* Helper callback to invoke the actual sigc++ slot.
1126  * We don't need to worry about (un)referencing, since the
1127  * child process gets its own copy of the parent's memory anyway.
1128  */
1129 static void child_setup_callback(void* user_data)
1131   #ifdef GLIBMM_EXCEPTIONS_ENABLED
1132   try
1133   {
1134   #endif //GLIBMM_EXCEPTIONS_ENABLED
1135     (*reinterpret_cast<sigc::slot<void>*>(user_data))();
1136   #ifdef GLIBMM_EXCEPTIONS_ENABLED
1137   }
1138   catch(...)
1139   {
1140     Glib::exception_handlers_invoke();
1141   }
1142   #endif //GLIBMM_EXCEPTIONS_ENABLED
1146 void my_spawn_async_with_pipes(const std::string& working_directory,
1147                             const Glib::ArrayHandle<std::string>& argv,
1148                             GSpawnFlags flags,
1149                             const sigc::slot<void>& child_setup,
1150                             Pid* child_pid,
1151                             int* standard_input,
1152                             int* standard_output,
1153                             int* standard_error)
1155   const bool setup_slot = !child_setup.empty();
1156   sigc::slot<void> child_setup_ = child_setup;
1157   GError* error = 0;
1159   my_spawn_async_with_pipes_utf8(
1160       working_directory.c_str(),
1161       const_cast<char**>(argv.data()), 0,
1162       static_cast<GSpawnFlags>(unsigned(flags)),
1163       (setup_slot) ? &child_setup_callback : 0,
1164       (setup_slot) ? &child_setup_         : 0,
1165       child_pid,
1166       standard_input, standard_output, standard_error,
1167       &error);
1169   if(error)
1170     Glib::Error::throw_exception(error);
1173 #endif
1175 void
1176 Inkscape::IO::spawn_async_with_pipes( const std::string& working_directory,
1177                                       const Glib::ArrayHandle<std::string>& argv,
1178                                       Glib::SpawnFlags flags,
1179                                       const sigc::slot<void>& child_setup,
1180                                       Glib::Pid* child_pid,
1181                                       int* standard_input,
1182                                       int* standard_output,
1183                                       int* standard_error)
1185 #ifndef BYPASS_GLIB_SPAWN
1186     Glib::spawn_async_with_pipes(working_directory,
1187                                  argv,
1188                                  flags,
1189                                  child_setup,
1190                                  child_pid,
1191                                  standard_input,
1192                                  standard_output,
1193                                  standard_error);
1194 #else
1195     my_spawn_async_with_pipes(working_directory,
1196                               argv,
1197                               static_cast<GSpawnFlags>(flags),
1198                               child_setup,
1199                               child_pid,
1200                               standard_input,
1201                               standard_output,
1202                               standard_error);
1203 #endif
1207 gchar* Inkscape::IO::sanitizeString( gchar const * str )
1209     gchar *result = NULL;
1210     if ( str ) {
1211         if ( g_utf8_validate(str, -1, NULL) ) {
1212             result = g_strdup(str);
1213         } else {
1214             guchar scratch[8];
1215             Glib::ustring buf;
1216             guchar const *ptr = (guchar const*)str;
1217             while ( *ptr )
1218             {
1219                 if ( *ptr == '\\' )
1220                 {
1221                     buf.append("\\\\");
1222                 } else if ( *ptr < 0x80 ) {
1223                     buf += (char)(*ptr);
1224                 } else {
1225                     g_snprintf((gchar*)scratch, sizeof(scratch), "\\x%02x", *ptr);
1226                     buf.append((const char*)scratch);
1227                 }
1228                 ptr++;
1229             }
1230             result = g_strdup(buf.c_str());
1231         }
1232     }
1233     return result;
1236 /*
1237   Local Variables:
1238   mode:c++
1239   c-file-style:"stroustrup"
1240   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1241   indent-tabs-mode:nil
1242   fill-column:99
1243   End:
1244 */
1245 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :