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 }\
100 }
105 void Inkscape::IO::dump_fopen_call( char const *utf8name, char const *id )
106 {
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
131 }
133 FILE *Inkscape::IO::fopen_utf8name( char const *utf8name, char const *mode )
134 {
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;
163 }
166 int Inkscape::IO::mkdir_utf8name( char const *utf8name )
167 {
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;
195 }
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)
213 {
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 * */
225 std::string basename_template (prefix);
226 basename_template += "XXXXXX"; // this sillyness shouldn't be in the interface
228 GError* error = 0;
229 gchar *buf_name_used;
231 gint fileno = g_file_open_tmp(basename_template.c_str(), &buf_name_used, &error);
233 if(error)
234 Glib::Error::throw_exception(error);
236 name_used = g_strdup(buf_name_used);
237 g_free(buf_name_used);
238 return fileno;
239 #endif
240 }
242 bool Inkscape::IO::file_test( char const *utf8name, GFileTest test )
243 {
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;
269 }
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)
274 {
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 }
283 }
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)
292 {
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 }
303 }
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 )
311 {
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;
335 }
337 #ifdef BYPASS_GLIB_SPAWN
338 /*
339 this code was taken from the original glib sources
340 */
341 #define GSPAWN_HELPER
343 enum
344 {
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)
370 {
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);
378 }
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)
388 {
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;
431 }
434 static void
435 set_child_error (gint report[2],
436 const gchar *working_directory,
437 GError **error)
438 {
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 }
455 }
457 static gchar *
458 protect_argv_string (const gchar *string)
459 {
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;
509 }
512 static gint
513 protect_argv (gchar **argv,
514 gchar ***new_argv)
515 {
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;
540 }
543 static gboolean
544 utf8_charv_to_wcharv (char **utf8_charv,
545 wchar_t ***wcharv,
546 int *error_index,
547 GError **error)
548 {
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;
578 }
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)
587 {
588 if (*fd < 0)
589 return;
591 close (*fd);
592 *fd = -1;
593 }
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)
607 {
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;
695 }
697 static gboolean
698 make_pipe (gint p[2],
699 GError **error)
700 {
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;
710 }
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)
728 {
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;
1080 }
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)
1094 {
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);
1118 }
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)
1129 {
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
1142 }
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)
1153 {
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);
1170 }
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)
1183 {
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
1203 }
1206 gchar* Inkscape::IO::sanitizeString( gchar const * str )
1207 {
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;
1233 }
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 :