Improve error reporting. Slight refactoring.
authorTor Lillqvist <tml@novell.com>
Fri, 26 Aug 2005 01:43:11 +0000 (01:43 +0000)
committerTor Lillqvist <tml@src.gnome.org>
Fri, 26 Aug 2005 01:43:11 +0000 (01:43 +0000)
2005-08-26  Tor Lillqvist  <tml@novell.com>

* glib/gspawn-win32.c: Improve error reporting. Slight refactoring.

glib/gspawn-win32.c

index 423aa54..ff42dea 100644 (file)
@@ -108,6 +108,60 @@ enum {
   ARG_COUNT = ARG_PROGRAM
 };
 
+static gchar *
+protect_argv_string (const gchar *string)
+{
+  const gchar *p = string;
+  gchar *retval, *q;
+  gint len = 0;
+  gboolean need_dblquotes = FALSE;
+  while (*p)
+    {
+      if (*p == ' ' || *p == '\t')
+       need_dblquotes = TRUE;
+      else if (*p == '"')
+       len++;
+      else if (*p == '\\')
+       {
+         const gchar *pp = p;
+         while (*pp && *pp == '\\')
+           pp++;
+         if (*pp == '"')
+           len++;
+       }
+      len++;
+      p++;
+    }
+  
+  q = retval = g_malloc (len + need_dblquotes*2 + 1);
+  p = string;
+
+  if (need_dblquotes)
+    *q++ = '"';
+  
+  while (*p)
+    {
+      if (*p == '"')
+       *q++ = '\\';
+      else if (*p == '\\')
+       {
+         const gchar *pp = p;
+         while (*pp && *pp == '\\')
+           pp++;
+         if (*pp == '"')
+           *q++ = '\\';
+       }
+      *q++ = *p;
+      p++;
+    }
+  
+  if (need_dblquotes)
+    *q++ = '"';
+  *q++ = '\0';
+
+  return retval;
+}
+
 static gint
 protect_argv (gchar  **argv,
              gchar ***new_argv)
@@ -131,55 +185,8 @@ protect_argv (gchar  **argv,
    * without any quoting.
    */
   for (i = 0; i < argc; i++)
-    {
-      gchar *p = argv[i];
-      gchar *q;
-      gint len = 0;
-      gboolean need_dblquotes = FALSE;
-      while (*p)
-       {
-         if (*p == ' ' || *p == '\t')
-           need_dblquotes = TRUE;
-         else if (*p == '"')
-           len++;
-         else if (*p == '\\')
-           {
-             gchar *pp = p;
-             while (*pp && *pp == '\\')
-               pp++;
-             if (*pp == '"')
-               len++;
-           }
-         len++;
-         p++;
-       }
-
-      q = (*new_argv)[i] = g_malloc (len + need_dblquotes*2 + 1);
-      p = argv[i];
-
-      if (need_dblquotes)
-       *q++ = '"';
-
-      while (*p)
-       {
-         if (*p == '"')
-           *q++ = '\\';
-         else if (*p == '\\')
-           {
-             gchar *pp = p;
-             while (*pp && *pp == '\\')
-               pp++;
-             if (*pp == '"')
-               *q++ = '\\';
-           }
-         *q++ = *p;
-         p++;
-       }
+    (*new_argv)[i] = protect_argv_string (argv[i]);
 
-      if (need_dblquotes)
-       *q++ = '"';
-      *q++ = '\0';
-    }
   (*new_argv)[argc] = NULL;
 
   return argc;
@@ -265,9 +272,7 @@ read_data (GString     *str,
     goto again;
   else if (giostatus == G_IO_STATUS_ERROR)
     {
-      g_set_error (error,
-                   G_SPAWN_ERROR,
-                   G_SPAWN_ERROR_READ,
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ,
                    _("Failed to read data from child process"));
       
       return READ_FAILED;
@@ -282,9 +287,7 @@ make_pipe (gint     p[2],
 {
   if (pipe (p) < 0)
     {
-      g_set_error (error,
-                   G_SPAWN_ERROR,
-                   G_SPAWN_ERROR_FAILED,
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
                    _("Failed to create pipe for communicating with child process (%s)"),
                    g_strerror (errno));
       return FALSE;
@@ -322,9 +325,7 @@ read_helper_report (int      fd,
         {
           /* Some weird shit happened, bail out */
               
-          g_set_error (error,
-                       G_SPAWN_ERROR,
-                       G_SPAWN_ERROR_FAILED,
+          g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
                        _("Failed to read from child pipe (%s)"),
                        g_strerror (errno));
 
@@ -350,17 +351,13 @@ set_child_error (gint         report[2],
   switch (report[0])
     {
     case CHILD_CHDIR_FAILED:
-      g_set_error (error,
-                  G_SPAWN_ERROR,
-                  G_SPAWN_ERROR_CHDIR,
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
                   _("Failed to change to directory '%s' (%s)"),
                   working_directory,
                   g_strerror (report[1]));
       break;
     case CHILD_SPAWN_FAILED:
-      g_set_error (error,
-                  G_SPAWN_ERROR,
-                  G_SPAWN_ERROR_FAILED,
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
                   _("Failed to execute child process (%s)"),
                   g_strerror (report[1]));
       break;
@@ -369,11 +366,15 @@ set_child_error (gint         report[2],
     }
 }
 
-static wchar_t **
-utf8_charv_to_wcharv (gchar **utf8_charv)
+static gboolean
+utf8_charv_to_wcharv (char     **utf8_charv,
+                     wchar_t ***wcharv,
+                     int       *error_index,
+                     GError   **error)
 {
   wchar_t **retval = NULL;
 
+  *wcharv = NULL;
   if (utf8_charv != NULL)
     {
       int n = 0, i;
@@ -383,17 +384,34 @@ utf8_charv_to_wcharv (gchar **utf8_charv)
       retval = g_new (wchar_t *, n + 1);
 
       for (i = 0; i < n; i++)
-       retval[i] = g_utf8_to_utf16 (utf8_charv[i], -1, NULL, NULL, NULL);
+       {
+         retval[i] = g_utf8_to_utf16 (utf8_charv[i], -1, NULL, NULL, error);
+         if (retval[i] == NULL)
+           {
+             if (error_index)
+               *error_index = i;
+             while (i)
+               g_free (retval[--i]);
+             g_free (retval);
+             return FALSE;
+           }
+       }
+           
       retval[n] = NULL;
     }
-  return retval;
+  *wcharv = retval;
+  return TRUE;
 }
 
-static char **
-utf8_charv_to_cp_charv (gchar **utf8_charv)
+static gboolean
+utf8_charv_to_cp_charv (char   **utf8_charv,
+                       gchar ***cp_charv,
+                       int     *error_index,
+                       GError **error)
 {
   char **retval = NULL;
 
+  *cp_charv = NULL;
   if (utf8_charv != NULL)
     {
       int n = 0, i;
@@ -403,10 +421,184 @@ utf8_charv_to_cp_charv (gchar **utf8_charv)
       retval = g_new (char *, n + 1);
 
       for (i = 0; i < n; i++)
-       retval[i] = g_locale_from_utf8 (utf8_charv[i], -1, NULL, NULL, NULL);
+       {
+         retval[i] = g_locale_from_utf8 (utf8_charv[i], -1, NULL, NULL, error);
+         if (retval[i] == NULL)
+           {
+             if (error_index)
+               *error_index = i;
+             while (i)
+               g_free (retval[--i]);
+             g_free (retval);
+             return FALSE;
+           }
+       }
       retval[n] = NULL;
     }
-  return retval;
+
+  *cp_charv = retval;
+  return TRUE;
+}
+
+static gboolean
+do_spawn_directly (gboolean              dont_wait,
+                  gboolean              dont_return_handle,
+                  GSpawnFlags           flags,
+                  gchar               **argv,
+                  char                **envp,
+                  char                **protected_argv,
+                  GSpawnChildSetupFunc  child_setup,
+                  gpointer              user_data,
+                  GPid                 *child_handle,
+                  gint                 *exit_status,
+                  GError              **error)     
+{
+  int mode = dont_wait ? P_NOWAIT : P_WAIT;
+  char **new_argv;
+  int rc = -1;
+  int saved_errno;
+  GError *conv_error = NULL;
+  gint conv_error_index;
+
+  new_argv = (flags & G_SPAWN_FILE_AND_ARGV_ZERO) ? protected_argv + 1 : protected_argv;
+  if (G_WIN32_HAVE_WIDECHAR_API ())
+    {
+      wchar_t *wargv0, **wargv, **wenvp;
+      
+      wargv0 = g_utf8_to_utf16 (argv[0], -1, NULL, NULL, &conv_error);
+      if (wargv0 == NULL)
+       {
+         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                      _("Invalid program name: %s"),
+                      conv_error->message);
+         g_error_free (conv_error);
+         
+         return FALSE;
+       }
+      
+      if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
+       {
+         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                      _("Invalid string in argument vector at %d: %s"),
+                      conv_error_index, conv_error->message);
+         g_error_free (conv_error);
+         g_free (wargv0);
+         
+         return FALSE;
+       }
+      
+      if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
+       {
+         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                      _("Invalid string in environment: %s"),
+                      conv_error->message);
+         g_error_free (conv_error);
+         g_free (wargv0);
+         g_strfreev ((gchar **) wargv);
+         
+         return FALSE;
+       }
+      
+      if (child_setup)
+       (* child_setup) (user_data);
+      
+      if (flags & G_SPAWN_SEARCH_PATH)
+       if (wenvp != NULL)
+         rc = _wspawnvpe (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
+       else
+         rc = _wspawnvp (mode, wargv0, (const wchar_t **) wargv);
+      else
+       if (wenvp != NULL)
+         rc = _wspawnve (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
+       else
+         rc = _wspawnv (mode, wargv0, (const wchar_t **) wargv);
+      
+      g_free (wargv0);
+      g_strfreev ((gchar **) wargv);
+      g_strfreev ((gchar **) wenvp);
+    }
+  else
+    {
+      char *cpargv0, **cpargv, **cpenvp;
+      
+      cpargv0 = g_locale_from_utf8 (argv[0], -1, NULL, NULL, &conv_error);
+      if (cpargv0 == NULL)
+       {
+         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                      _("Invalid program name: %s"),
+                      conv_error->message);
+         g_error_free (conv_error);
+
+         return FALSE;
+       }
+
+      if  (!utf8_charv_to_cp_charv (new_argv, &cpargv, &conv_error_index, &conv_error))
+       {
+         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                      _("Invalid string in argument vector at %d: %s"),
+                      conv_error_index, conv_error->message);
+         g_error_free (conv_error);
+         g_free (cpargv0);
+
+         return FALSE;
+       }
+
+      if (!utf8_charv_to_cp_charv (envp, &cpenvp, NULL, &conv_error))
+       {
+         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                      _("Invalid string in environment: %s"),
+                      conv_error->message);
+         g_error_free (conv_error);
+         g_free (cpargv0);
+         g_strfreev (cpargv);
+
+         return FALSE;
+       }
+
+      if (child_setup)
+       (* child_setup) (user_data);
+
+      if (flags & G_SPAWN_SEARCH_PATH)
+       if (cpenvp != NULL)
+         rc = spawnvpe (mode, cpargv0, (const char **) cpargv, (const char **) cpenvp);
+       else
+         rc = spawnvp (mode, cpargv0, (const char **) cpargv);
+      else
+       if (envp != NULL)
+         rc = spawnve (mode, cpargv0, (const char **) cpargv, (const char **) cpenvp);
+       else
+         rc = spawnv (mode, cpargv0, (const char **) cpargv);
+
+      g_free (cpargv0);
+      g_strfreev (cpargv);
+      g_strfreev (cpenvp);
+    }
+
+  saved_errno = errno;
+
+  if (rc == -1 && saved_errno != 0)
+    {
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                  _("Failed to execute child process (%s)"),
+                  g_strerror (saved_errno));
+      return FALSE;
+    }
+
+  if (dont_wait)
+    {
+      if (child_handle && !dont_return_handle)
+       *child_handle = (GPid) rc;
+      else
+       {
+         CloseHandle ((HANDLE) rc);
+         if (child_handle)
+           *child_handle = 0;
+       }
+    }
+  else if (exit_status)
+    *exit_status = rc;
+
+  return TRUE;
 }
 
 static gboolean
@@ -438,9 +630,18 @@ do_spawn_with_pipes (gboolean              dont_wait,
   int stderr_pipe[2] = { -1, -1 };
   int child_err_report_pipe[2] = { -1, -1 };
   int helper_report[2];
+  static gboolean warned_about_child_setup = FALSE;
+  GError *conv_error = NULL;
+  gint conv_error_index;
   
   SETUP_DEBUG();
 
+  if (child_setup && !warned_about_child_setup)
+    {
+      warned_about_child_setup = TRUE;
+      g_warning ("passing a child setup function to the g_spawn functions is pointless and dangerous on Win32");
+    }
+
   argc = protect_argv (argv, &protected_argv);
 
   if (!standard_input && !standard_output && !standard_error &&
@@ -451,103 +652,15 @@ do_spawn_with_pipes (gboolean              dont_wait,
       (flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
     {
       /* We can do without the helper process */
-      int mode = dont_wait ? P_NOWAIT : P_WAIT;
-
-      if (debug)
-       g_print ("doing without " HELPER_PROCESS "\n");
-
-      /* Call user function just before we spawn the program. Dunno
-       * what's the usefulness of this. A child setup function used on
-       * Unix probably isn't of much use as such on Win32, anyhow.
-       */
-      if (child_setup)
-       {
-         static gboolean warned = FALSE;
-         if (!warned)
-           {
-             warned = TRUE;
-             g_warning ("passing a child setup function to the g_spawn functions is pointless and dangerous on Win32");
-           }
-         (* child_setup) (user_data);
-       }
-
-      new_argv = (flags & G_SPAWN_FILE_AND_ARGV_ZERO) ? protected_argv + 1 : protected_argv;
-      if (G_WIN32_HAVE_WIDECHAR_API ())
-       {
-         wchar_t *wargv0 = g_utf8_to_utf16 (argv[0], -1, NULL, NULL, NULL);
-         wchar_t **wargv = utf8_charv_to_wcharv (new_argv);
-         wchar_t **wenvp = utf8_charv_to_wcharv (envp);
-         
-         if (flags & G_SPAWN_SEARCH_PATH)
-           if (wenvp != NULL)
-             rc = _wspawnvpe (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
-           else
-             rc = _wspawnvp (mode, wargv0, (const wchar_t **) wargv);
-         else
-           if (wenvp != NULL)
-             rc = _wspawnve (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
-           else
-             rc = _wspawnv (mode, wargv0, (const wchar_t **) wargv);
-
-         g_free (wargv0);
-         g_strfreev ((gchar **) wargv);
-         g_strfreev ((gchar **) wenvp);
-       }
-      else
-       {
-         char *cpargv0 = g_locale_from_utf8 (argv[0], -1, NULL, NULL, NULL);
-         char **cpargv = utf8_charv_to_cp_charv (new_argv);
-         char **cpenvp = utf8_charv_to_cp_charv (envp);
-         
-         if (flags & G_SPAWN_SEARCH_PATH)
-           if (cpenvp != NULL)
-             rc = spawnvpe (mode, cpargv0, (const char **) cpargv, (const char **) cpenvp);
-           else
-             rc = spawnvp (mode, cpargv0, (const char **) cpargv);
-         else
-           if (envp != NULL)
-             rc = spawnve (mode, cpargv0, (const char **) cpargv, (const char **) cpenvp);
-           else
-             rc = spawnv (mode, cpargv0, (const char **) cpargv);
-
-         g_free (cpargv0);
-         g_strfreev (cpargv);
-         g_strfreev (cpenvp);
-       }
-
-      saved_errno = errno;
-
+      gboolean retval =
+       do_spawn_directly (dont_wait, dont_return_handle, flags,
+                          argv, envp, protected_argv,
+                          child_setup, user_data, child_handle,
+                          exit_status, error);
       g_strfreev (protected_argv);
-
-      if (rc == -1 && saved_errno != 0)
-       {
-         g_set_error (error,
-                      G_SPAWN_ERROR,
-                      G_SPAWN_ERROR_FAILED,
-                      _("Failed to execute child process (%s)"),
-                      g_strerror (errno));
-         goto cleanup_and_fail;
-       }
-
-      if (dont_wait)
-       {
-         if (child_handle && !dont_return_handle)
-           *child_handle = (GPid) rc;
-         else
-           {
-             CloseHandle (rc);
-             if (child_handle)
-               *child_handle = 0;
-           }
-       }
-      else if (exit_status)
-       *exit_status = rc;
-      
-      return TRUE;
+      return retval;
     }
 
-  new_argv = g_new (char *, argc + 1 + ARG_COUNT);
-
   if (standard_input && !make_pipe (stdin_pipe, error))
     goto cleanup_and_fail;
   
@@ -560,6 +673,7 @@ do_spawn_with_pipes (gboolean              dont_wait,
   if (!make_pipe (child_err_report_pipe, error))
     goto cleanup_and_fail;
   
+  new_argv = g_new (char *, argc + 1 + ARG_COUNT);
   new_argv[0] = HELPER_PROCESS;
   _g_sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_pipe[1]);
   new_argv[ARG_CHILD_ERR_REPORT] = args[ARG_CHILD_ERR_REPORT];
@@ -617,8 +731,7 @@ do_spawn_with_pipes (gboolean              dont_wait,
     }
   
   if (working_directory && *working_directory)
-    /* The g_strdup() to lose the constness */
-    new_argv[ARG_WORKING_DIRECTORY] = g_strdup (working_directory);
+    new_argv[ARG_WORKING_DIRECTORY] = protect_argv_string (working_directory);
   else
     new_argv[ARG_WORKING_DIRECTORY] = g_strdup ("-");
   
@@ -647,14 +760,47 @@ do_spawn_with_pipes (gboolean              dont_wait,
        g_print ("argv[%d]: %s\n", i, (new_argv[i] ? new_argv[i] : "NULL"));
     }
 
-  if (child_setup)
-    (* child_setup) (user_data);
-
   if (G_WIN32_HAVE_WIDECHAR_API ())
     {
       wchar_t *whelper = g_utf8_to_utf16 (HELPER_PROCESS, -1, NULL, NULL, NULL);
-      wchar_t **wargv = utf8_charv_to_wcharv (new_argv);
-      wchar_t **wenvp = utf8_charv_to_wcharv (envp);
+      wchar_t **wargv, **wenvp;
+
+      if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
+       {
+         if (conv_error_index == ARG_WORKING_DIRECTORY)
+           g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
+                        _("Invalid working directory: %s"),
+                        conv_error->message);
+         else
+           g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                        _("Invalid string in argument vector at %d: %s"),
+                        conv_error_index - ARG_PROGRAM, conv_error->message);
+         g_error_free (conv_error);
+         g_strfreev (protected_argv);
+         g_free (new_argv[ARG_WORKING_DIRECTORY]);
+         g_free (new_argv);
+         g_free (whelper);
+         
+         return FALSE;
+       }
+      
+      if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
+       {
+         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                      _("Invalid string in environment: %s"),
+                      conv_error->message);
+         g_error_free (conv_error);
+         g_strfreev (protected_argv);
+         g_free (new_argv[ARG_WORKING_DIRECTORY]);
+         g_free (new_argv);
+         g_free (whelper);
+         g_strfreev ((gchar **) wargv);
+         
+         return FALSE;
+       }
+
+      if (child_setup)
+       (* child_setup) (user_data);
 
       if (wenvp != NULL)
        /* Let's hope envp hasn't mucked with PATH so that
@@ -672,8 +818,42 @@ do_spawn_with_pipes (gboolean              dont_wait,
     }
   else
     {
-      char **cpargv = utf8_charv_to_cp_charv (new_argv);
-      char **cpenvp = utf8_charv_to_cp_charv (envp);
+      char **cpargv, **cpenvp;
+
+      if (!utf8_charv_to_cp_charv (new_argv, &cpargv, &conv_error_index, &conv_error))
+       {
+         if (conv_error_index == ARG_WORKING_DIRECTORY)
+           g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
+                        _("Invalid working directory: %s"),
+                        conv_error->message);
+         else
+           g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                        _("Invalid string in argument vector at %d: %s"),
+                        conv_error_index - ARG_PROGRAM, conv_error->message);
+         g_error_free (conv_error);
+         g_strfreev (protected_argv);
+         g_free (new_argv[ARG_WORKING_DIRECTORY]);
+         g_free (new_argv);
+         
+         return FALSE;
+       }
+       
+      if (!utf8_charv_to_cp_charv (envp, &cpenvp, NULL, &conv_error))
+       {
+         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                      _("Invalid string in environment: %s"),
+                      conv_error->message);
+         g_error_free (conv_error);
+         g_strfreev (protected_argv);
+         g_free (new_argv[ARG_WORKING_DIRECTORY]);
+         g_free (new_argv);
+         g_strfreev (cpargv);
+         
+         return FALSE;
+       }
+
+      if (child_setup)
+       (* child_setup) (user_data);
 
       if (cpenvp != NULL)
        rc = spawnvpe (P_NOWAIT, HELPER_PROCESS, (const char **) cpargv, (const char **) cpenvp);
@@ -702,10 +882,9 @@ do_spawn_with_pipes (gboolean              dont_wait,
   /* Check if gspawn-win32-helper couldn't be run */
   if (rc == -1 && saved_errno != 0)
     {
-      g_set_error (error,
-                  G_SPAWN_ERROR,
-                  G_SPAWN_ERROR_FAILED,
-                  _("Failed to execute helper program"));
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                  _("Failed to execute helper program (%s)"),
+                  g_strerror (saved_errno));
       goto cleanup_and_fail;
     }
 
@@ -900,9 +1079,7 @@ g_spawn_sync_utf8 (const gchar          *working_directory,
         {
           failed = TRUE;
 
-          g_set_error (error,
-                       G_SPAWN_ERROR,
-                       G_SPAWN_ERROR_READ,
+          g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ,
                        _("Unexpected error in g_io_channel_win32_poll() reading data from a child process"));
          
           break;
@@ -1159,11 +1336,17 @@ setup_utf8_copies (const gchar *working_directory,
     *utf8_working_directory = NULL;
   else
     {
-      *utf8_working_directory = g_locale_to_utf8 (working_directory, -1, NULL, NULL, NULL);
+      GError *conv_error = NULL;
+      
+      *utf8_working_directory = g_locale_to_utf8 (working_directory, -1, NULL, NULL, &conv_error);
       if (*utf8_working_directory == NULL)
-       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
-                    "Invalid characters in working directory");
-      return FALSE;
+       {
+         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
+                      _("Invalid working directory: %s"),
+                      conv_error->message);
+         g_error_free (conv_error);
+         return FALSE;
+       }
     }
 
   argc = 0;
@@ -1172,11 +1355,15 @@ setup_utf8_copies (const gchar *working_directory,
   *utf8_argv = g_new (gchar *, argc + 1);
   for (i = 0; i < argc; i++)
     {
-      (*utf8_argv)[i] = g_locale_to_utf8 (argv[i], -1, NULL, NULL, NULL);
+      GError *conv_error = NULL;
+
+      (*utf8_argv)[i] = g_locale_to_utf8 (argv[i], -1, NULL, NULL, &conv_error);
       if ((*utf8_argv)[i] == NULL)
-       {       
+       {
          g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
-                      "Invalid characters in argument vector");
+                      _("Invalid string in argument vector at %d: %s"),
+                      i, conv_error->message);
+         g_error_free (conv_error);
          
          g_strfreev (*utf8_argv);
          *utf8_argv = NULL;
@@ -1201,11 +1388,15 @@ setup_utf8_copies (const gchar *working_directory,
       *utf8_envp = g_new (gchar *, envc + 1);
       for (i = 0; i < envc; i++)
        {
-         (*utf8_envp)[i] = g_locale_to_utf8 (envp[i], -1, NULL, NULL, NULL);
+         GError *conv_error = NULL;
+
+         (*utf8_envp)[i] = g_locale_to_utf8 (envp[i], -1, NULL, NULL, &conv_error);
          if ((*utf8_envp)[i] == NULL)
            {   
              g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
-                          "Invalid characters in environment");
+                          _("Invalid string in environment: %s"),
+                          conv_error->message);
+             g_error_free (conv_error);
 
              g_strfreev (*utf8_envp);
              *utf8_envp = NULL;