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 }\
101 }
106 void Inkscape::IO::dump_fopen_call( char const *utf8name, char const *id )
107 {
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
132 }
134 FILE *Inkscape::IO::fopen_utf8name( char const *utf8name, char const *mode )
135 {
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;
164 }
167 int Inkscape::IO::mkdir_utf8name( char const *utf8name )
168 {
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;
196 }
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)
214 {
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 * */
226 std::string basename_template (prefix);
227 basename_template += "XXXXXX"; // this sillyness shouldn't be in the interface
229 GError* error = 0;
230 gchar *buf_name_used;
232 gint fileno = g_file_open_tmp(basename_template.c_str(), &buf_name_used, &error);
234 if(error)
235 Glib::Error::throw_exception(error);
237 name_used = g_strdup(buf_name_used);
238 g_free(buf_name_used);
239 return fileno;
240 #endif
241 }
243 bool Inkscape::IO::file_test( char const *utf8name, GFileTest test )
244 {
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;
270 }
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)
275 {
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 }
284 }
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)
293 {
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 }
304 }
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 )
312 {
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;
336 }
338 #ifdef BYPASS_GLIB_SPAWN
339 /*
340 this code was taken from the original glib sources
341 */
342 #define GSPAWN_HELPER
344 enum
345 {
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)
371 {
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);
379 }
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)
389 {
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;
432 }
435 static void
436 set_child_error (gint report[2],
437 const gchar *working_directory,
438 GError **error)
439 {
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 }
456 }
458 static gchar *
459 protect_argv_string (const gchar *string)
460 {
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;
510 }
513 static gint
514 protect_argv (gchar **argv,
515 gchar ***new_argv)
516 {
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;
541 }
544 static gboolean
545 utf8_charv_to_wcharv (char **utf8_charv,
546 wchar_t ***wcharv,
547 int *error_index,
548 GError **error)
549 {
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;
579 }
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)
588 {
589 if (*fd < 0)
590 return;
592 close (*fd);
593 *fd = -1;
594 }
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)
608 {
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;
696 }
698 static gboolean
699 make_pipe (gint p[2],
700 GError **error)
701 {
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;
711 }
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)
729 {
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;
1081 }
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)
1095 {
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);
1119 }
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)
1130 {
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
1143 }
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)
1154 {
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);
1171 }
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)
1184 {
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
1204 }
1207 gchar* Inkscape::IO::sanitizeString( gchar const * str )
1208 {
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;
1234 }
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 :