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