]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkfilesel.c
Add more test when converting filenames from/to utf-8. Don't allow
[~andy/gtk] / gtk / gtkfilesel.c
index 18f8896cf573342895ae8a6f437f2f68d7e8f958..cbb3b374a7d8597ca999c18a4f2577511d074ca5 100644 (file)
@@ -58,6 +58,7 @@
 #include "gtklistitem.h"
 #include "gtkmain.h"
 #include "gtkscrolledwindow.h"
+#include "gtkstock.h"
 #include "gtksignal.h"
 #include "gtkvbox.h"
 #include "gtkmenu.h"
@@ -65,6 +66,7 @@
 #include "gtkoptionmenu.h"
 #include "gtkclist.h"
 #include "gtkdialog.h"
+#include "gtkmessagedialog.h"
 #include "gtkintl.h"
 
 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
 #define FILE_LIST_WIDTH  180
 #define FILE_LIST_HEIGHT 180
 
+/* The Hurd doesn't define either PATH_MAX or MAXPATHLEN, so we put this
+ * in here, since the rest of the code in the file does require some
+ * fixed maximum.
+ */
+#ifndef MAXPATHLEN
+#  ifdef PATH_MAX
+#    define MAXPATHLEN PATH_MAX
+#  else
+#    define MAXPATHLEN 2048
+#  endif
+#endif
+
 /* I've put this here so it doesn't get confused with the 
  * file completion interface */
 typedef struct _HistoryCallbackArg HistoryCallbackArg;
@@ -121,6 +135,7 @@ typedef struct _PossibleCompletion PossibleCompletion;
 #define FNMATCH_FLAGS (FNM_PATHNAME | FNM_PERIOD)
 
 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
+#define CMPL_ERRNO_DID_NOT_CONVERT ((1<<16)-2)
 
 /* This structure contains all the useful information about a directory
  * for the purposes of filename completion.  These structures are cached
@@ -329,6 +344,11 @@ static void gtk_file_selection_destroy       (GtkObject             *object);
 static gint gtk_file_selection_key_press     (GtkWidget             *widget,
                                              GdkEventKey           *event,
                                              gpointer               user_data);
+static gint gtk_file_selection_insert_text   (GtkWidget             *widget,
+                                             const gchar           *new_text,
+                                             gint                   new_text_length,
+                                             gint                  *position,
+                                             gpointer               user_data);
 
 static void gtk_file_selection_file_button (GtkWidget *widget,
                                            gint row, 
@@ -443,7 +463,7 @@ gtk_file_selection_get_type (void)
         (GtkClassInitFunc) NULL,
       };
 
-      file_selection_type = gtk_type_unique (GTK_TYPE_WINDOW, &filesel_info);
+      file_selection_type = gtk_type_unique (GTK_TYPE_DIALOG, &filesel_info);
     }
 
   return file_selection_type;
@@ -456,7 +476,7 @@ gtk_file_selection_class_init (GtkFileSelectionClass *class)
 
   object_class = (GtkObjectClass*) class;
 
-  parent_class = gtk_type_class (GTK_TYPE_WINDOW);
+  parent_class = gtk_type_class (GTK_TYPE_DIALOG);
 
   object_class->destroy = gtk_file_selection_destroy;
 }
@@ -470,22 +490,23 @@ gtk_file_selection_init (GtkFileSelection *filesel)
   GtkWidget *confirm_area;
   GtkWidget *pulldown_hbox;
   GtkWidget *scrolled_win;
-
+  GtkDialog *dialog;
+  
   char *dir_title [2];
   char *file_title [2];
-  
+
+  dialog = GTK_DIALOG (filesel);
+
   filesel->cmpl_state = cmpl_init_state ();
 
   /* The dialog-sized vertical box  */
-  filesel->main_vbox = gtk_vbox_new (FALSE, 10);
+  filesel->main_vbox = dialog->vbox;
   gtk_container_set_border_width (GTK_CONTAINER (filesel), 10);
-  gtk_container_add (GTK_CONTAINER (filesel), filesel->main_vbox);
-  gtk_widget_show (filesel->main_vbox);
 
   /* The horizontal box containing create, rename etc. buttons */
   filesel->button_area = gtk_hbutton_box_new ();
   gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel->button_area), GTK_BUTTONBOX_START);
-  gtk_button_box_set_spacing (GTK_BUTTON_BOX (filesel->button_area), 0);
+  gtk_box_set_spacing (GTK_BOX (filesel->button_area), 0);
   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->button_area, 
                      FALSE, FALSE, 0);
   gtk_widget_show (filesel->button_area);
@@ -555,28 +576,23 @@ gtk_file_selection_init (GtkFileSelection *filesel)
   gtk_widget_show (filesel->action_area);
   
   /*  The OK/Cancel button area */
-  confirm_area = gtk_hbutton_box_new ();
-  gtk_button_box_set_layout (GTK_BUTTON_BOX (confirm_area), GTK_BUTTONBOX_END);
-  gtk_button_box_set_spacing (GTK_BUTTON_BOX (confirm_area), 5);
-  gtk_box_pack_end (GTK_BOX (filesel->main_vbox), confirm_area, FALSE, FALSE, 0);
-  gtk_widget_show (confirm_area);
+  confirm_area = dialog->action_area;
 
   /*  The OK button  */
-  filesel->ok_button = gtk_button_new_with_label (_("OK"));
-  GTK_WIDGET_SET_FLAGS (filesel->ok_button, GTK_CAN_DEFAULT);
-  gtk_box_pack_start (GTK_BOX (confirm_area), filesel->ok_button, TRUE, TRUE, 0);
+  filesel->ok_button = gtk_dialog_add_button (dialog,
+                                              GTK_STOCK_BUTTON_OK,
+                                              GTK_RESPONSE_OK);
+  
   gtk_widget_grab_default (filesel->ok_button);
-  gtk_widget_show (filesel->ok_button);
 
   /*  The Cancel button  */
-  filesel->cancel_button = gtk_button_new_with_label (_("Cancel"));
-  GTK_WIDGET_SET_FLAGS (filesel->cancel_button, GTK_CAN_DEFAULT);
-  gtk_box_pack_start (GTK_BOX (confirm_area), filesel->cancel_button, TRUE, TRUE, 0);
-  gtk_widget_show (filesel->cancel_button);
+  filesel->cancel_button = gtk_dialog_add_button (dialog,
+                                                  GTK_STOCK_BUTTON_CANCEL,
+                                                  GTK_RESPONSE_CANCEL);
 
   /*  The selection entry widget  */
   entry_vbox = gtk_vbox_new (FALSE, 2);
-  gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 0);
+  gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 2);
   gtk_widget_show (entry_vbox);
 
   filesel->selection_text = label = gtk_label_new ("");
@@ -587,6 +603,8 @@ gtk_file_selection_init (GtkFileSelection *filesel)
   filesel->selection_entry = gtk_entry_new ();
   gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event",
                      (GtkSignalFunc) gtk_file_selection_key_press, filesel);
+  gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "insert_text",
+                     (GtkSignalFunc) gtk_file_selection_insert_text, NULL);
   gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event",
                             (GtkSignalFunc) gtk_widget_grab_default,
                             GTK_OBJECT (filesel->ok_button));
@@ -619,6 +637,7 @@ gtk_file_selection_new (const gchar *title)
 
   filesel = gtk_type_new (GTK_TYPE_FILE_SELECTION);
   gtk_window_set_title (GTK_WINDOW (filesel), title);
+  gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE);
 
   return GTK_WIDGET (filesel);
 }
@@ -730,7 +749,7 @@ gtk_file_selection_get_filename (GtkFileSelection *filesel)
 {
   static gchar nothing[2] = "";
   static gchar something[MAXPATHLEN*2];
-  char *filename;
+  char *sys_filename;
   char *text;
 
   g_return_val_if_fail (filesel != NULL, nothing);
@@ -742,9 +761,11 @@ gtk_file_selection_get_filename (GtkFileSelection *filesel)
   text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
   if (text)
     {
-      filename = g_filename_from_utf8 (cmpl_completion_fullname (text, filesel->cmpl_state));
-      strncpy (something, filename, sizeof (something));
-      g_free (filename);
+      sys_filename = g_filename_from_utf8 (cmpl_completion_fullname (text, filesel->cmpl_state), -1, NULL, NULL, NULL);
+      if (sys_filename)
+       return nothing;
+      strncpy (something, sys_filename, sizeof (something));
+      g_free (sys_filename);
       return something;
     }
 
@@ -810,52 +831,25 @@ static void
 gtk_file_selection_fileop_error (GtkFileSelection *fs,
                                 gchar            *error_message)
 {
-  GtkWidget *label;
-  GtkWidget *vbox;
-  GtkWidget *button;
   GtkWidget *dialog;
-  
+    
   g_return_if_fail (error_message != NULL);
-  
-  /* main dialog */
-  dialog = gtk_dialog_new ();
-  /*
-  gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
-                     (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
-                     (gpointer) fs);
-  */
-  gtk_window_set_title (GTK_WINDOW (dialog), _("Error"));
-  gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
-  
-  /* If file dialog is grabbed, make this dialog modal too */
-  /* When error dialog is closed, file dialog will be grabbed again */
-  if (GTK_WINDOW (fs)->modal)
-      gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
-
-  vbox = gtk_vbox_new (FALSE, 0);
-  gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
-  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
-                    FALSE, FALSE, 0);
-  gtk_widget_show (vbox);
 
-  label = gtk_label_new (error_message);
-  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
-  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
-  gtk_widget_show (label);
+  /* main dialog */
+  dialog = gtk_message_dialog_new (GTK_WINDOW (fs),
+                                  GTK_DIALOG_DESTROY_WITH_PARENT,
+                                  GTK_MESSAGE_ERROR,
+                                  GTK_BUTTONS_CLOSE,
+                                  "%s", error_message);
 
   /* yes, we free it */
   g_free (error_message);
-  
-  /* close button */
-  button = gtk_button_new_with_label (_("Close"));
-  gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+
+  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+
+  gtk_signal_connect_object (GTK_OBJECT (dialog), "response",
                             (GtkSignalFunc) gtk_widget_destroy, 
                             (gpointer) dialog);
-  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
-                    button, TRUE, TRUE, 0);
-  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
-  gtk_widget_grab_default (button);
-  gtk_widget_show (button);
 
   gtk_widget_show (dialog);
 }
@@ -881,8 +875,9 @@ gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
   gchar *dirname;
   gchar *path;
   gchar *full_path;
-  gchar *xpath;
+  gchar *sys_full_path;
   gchar *buf;
+  GError *error = NULL;
   CompletionState *cmpl_state;
   
   g_return_if_fail (fs != NULL);
@@ -893,15 +888,29 @@ gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
   path = cmpl_reference_position (cmpl_state);
   
   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
-  xpath = g_filename_from_utf8 (full_path);
-  if (mkdir (xpath, 0755) < 0) 
+  sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
+  if (error)
+    {
+      if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
+       buf = g_strdup_printf (_("The directory name \"%s\" contains symbols that are not allowed in filenames"), dirname);
+      else
+       buf = g_strdup_printf (_("Error creating directory \"%s\": %s\n%s"), dirname, error->message,
+                              _("You probably used symbols not allowed in filenames."));
+      gtk_file_selection_fileop_error (fs, buf);
+      g_error_free (error);
+      goto out;
+    }
+
+  if (mkdir (sys_full_path, 0755) < 0) 
     {
-      buf = g_strconcat ("Error creating directory \"", dirname, "\":  ", 
-                        g_strerror (errno), NULL);
+      buf = g_strdup_printf (_("Error creating directory \"%s\": %s\n"), dirname,
+                            g_strerror (errno));
       gtk_file_selection_fileop_error (fs, buf);
     }
+
+ out:
   g_free (full_path);
-  g_free (xpath);
+  g_free (sys_full_path);
   
   gtk_widget_destroy (fs->fileop_dialog);
   gtk_file_selection_populate (fs, "", FALSE);
@@ -986,7 +995,8 @@ gtk_file_selection_delete_file_confirmed (GtkWidget *widget,
   CompletionState *cmpl_state;
   gchar *path;
   gchar *full_path;
-  gchar *xpath;
+  gchar *sys_full_path;
+  GError *error = NULL;
   gchar *buf;
   
   g_return_if_fail (fs != NULL);
@@ -996,15 +1006,32 @@ gtk_file_selection_delete_file_confirmed (GtkWidget *widget,
   path = cmpl_reference_position (cmpl_state);
   
   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
-  xpath = g_filename_from_utf8 (full_path);
-  if (unlink (xpath) < 0) 
+  sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
+  if (error)
+    {
+      if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
+       buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
+                              fs->fileop_file);
+      else
+       buf = g_strdup_printf (_("Error deleting file \"%s\": %s\n%s"),
+                              fs->fileop_file, error->message,
+                              _("It probably contains symbols not allowed in filenames."));
+      
+      gtk_file_selection_fileop_error (fs, buf);
+      g_error_free (error);
+      goto out;
+    }
+
+  if (unlink (sys_full_path) < 0) 
     {
-      buf = g_strconcat ("Error deleting file \"", fs->fileop_file, "\":  ", 
-                        g_strerror (errno), NULL);
+      buf = g_strdup_printf (_("Error deleting file \"%s\": %s"),
+                            fs->fileop_file, g_strerror (errno));
       gtk_file_selection_fileop_error (fs, buf);
     }
+  
+ out:
   g_free (full_path);
-  g_free (xpath);
+  g_free (sys_full_path);
   
   gtk_widget_destroy (fs->fileop_dialog);
   gtk_file_selection_populate (fs, "", FALSE);
@@ -1098,7 +1125,10 @@ gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
   gchar *path;
   gchar *new_filename;
   gchar *old_filename;
+  gchar *sys_new_filename;
+  gchar *sys_old_filename;
   CompletionState *cmpl_state;
+  GError *error = NULL;
   
   g_return_if_fail (fs != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
@@ -1110,14 +1140,49 @@ gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
   new_filename = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
   old_filename = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
 
-  if (rename (old_filename, new_filename) < 0) 
+  sys_new_filename = g_filename_from_utf8 (new_filename, -1, NULL, NULL, &error);
+  if (error)
+    {
+      if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
+       buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), new_filename);
+      else
+       buf = g_strdup_printf (_("Error renaming file to \"%s\": %s\n%s"),
+                              new_filename, error->message,
+                              _("You probably used symbols not allowed in filenames."));
+      gtk_file_selection_fileop_error (fs, buf);
+      g_error_free (error);
+      goto out1;
+    }
+
+  sys_old_filename = g_filename_from_utf8 (old_filename, -1, NULL, NULL, &error);
+  if (error)
     {
-      buf = g_strconcat ("Error renaming file \"", file, "\":  ", 
-                        g_strerror (errno), NULL);
+      if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
+       buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), old_filename);
+      else
+       buf = g_strdup_printf (_("Error renaming file \"%s\": %s\n%s"),
+                              old_filename, error->message,
+                              _("It probably contains symbols not allowed in filenames."));
       gtk_file_selection_fileop_error (fs, buf);
+      g_error_free (error);
+      goto out2;
     }
+  
+  if (rename (sys_old_filename, sys_new_filename) < 0) 
+    {
+      buf = g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"),
+                            sys_old_filename, sys_new_filename,
+                            g_strerror (errno));
+      gtk_file_selection_fileop_error (fs, buf);
+    }
+  
+ out2:
+  g_free (sys_old_filename);
+
+ out1:
   g_free (new_filename);
   g_free (old_filename);
+  g_free (sys_new_filename);
   
   gtk_widget_destroy (fs->fileop_dialog);
   gtk_file_selection_populate (fs, "", FALSE);
@@ -1204,6 +1269,28 @@ gtk_file_selection_rename_file (GtkWidget *widget,
   gtk_widget_show (dialog);
 }
 
+static gint
+gtk_file_selection_insert_text (GtkWidget   *widget,
+                               const gchar *new_text,
+                               gint         new_text_length,
+                               gint        *position,
+                               gpointer     user_data)
+{
+  gchar *filename;
+
+  filename = g_filename_from_utf8 (new_text, new_text_length, NULL, NULL, NULL);
+
+  if (!filename)
+    {
+      gdk_beep ();
+      gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "insert_text");
+      return FALSE;
+    }
+  
+  g_free (filename);
+  
+  return TRUE;
+}
 
 static gint
 gtk_file_selection_key_press (GtkWidget   *widget,
@@ -1337,7 +1424,7 @@ gtk_file_selection_update_history_menu (GtkFileSelection *fs,
          gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
                              (GtkSignalFunc) gtk_file_selection_history_callback,
                              (gpointer) fs);
-         gtk_menu_append (GTK_MENU (fs->history_menu), menu_item);
+         gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item);
          gtk_widget_show (menu_item);
        }
     }
@@ -1745,16 +1832,16 @@ cmpl_is_a_completion (PossibleCompletion* pc)
 static CompletionState*
 cmpl_init_state (void)
 {
-  gchar *getcwd_buf;
+  gchar *sys_getcwd_buf;
   gchar *utf8_cwd;
   CompletionState *new_state;
 
   new_state = g_new (CompletionState, 1);
 
-  /* g_get_current_dir() returns a GSystemCodepageString */
-  getcwd_buf = g_get_current_dir ();
-  utf8_cwd = g_filename_to_utf8 (getcwd_buf);
-  g_free (getcwd_buf);
+  /* g_get_current_dir() returns a string in the "system" charset */
+  sys_getcwd_buf = g_get_current_dir ();
+  utf8_cwd = g_filename_to_utf8 (sys_getcwd_buf, -1, NULL, NULL, NULL);
+  g_free (sys_getcwd_buf);
 
 tryagain:
 
@@ -1776,7 +1863,7 @@ tryagain:
   if (!new_state->reference_dir)
     {
       /* Directories changing from underneath us, grumble */
-      strcpy (getcwd_buf, G_DIR_SEPARATOR_S);
+      strcpy (utf8_cwd, G_DIR_SEPARATOR_S);
       goto tryagain;
     }
 
@@ -2044,10 +2131,10 @@ open_ref_dir (gchar           *text_to_complete,
       else
        {
          /* If no possible candidates, use the cwd */
-         gchar *curdir = g_get_current_dir ();
-         gchar *utf8_curdir = g_filename_to_utf8 (curdir);
+         gchar *sys_curdir = g_get_current_dir ();
+         gchar *utf8_curdir = g_filename_to_utf8 (sys_curdir, -1, NULL, NULL, NULL);
 
-         g_free (curdir);
+         g_free (sys_curdir);
 
          new_dir = open_dir (utf8_curdir, cmpl_state);
 
@@ -2099,7 +2186,7 @@ open_user_dir (gchar           *text_to_complete,
     {
       /* ~/ */
       gchar *homedir = g_get_home_dir ();
-      gchar *utf8_home = g_filename_to_utf8 (homedir);
+      gchar *utf8_homedir = g_filename_to_utf8 (homedir, -1, NULL, NULL, NULL);
 
       g_free (homedir);
 
@@ -2126,7 +2213,7 @@ open_user_dir (gchar           *text_to_complete,
          cmpl_errno = errno;
          return NULL;
        }
-      utf8_dir = g_filename_to_utf8 (pwd->pw_dir);
+      utf8_dir = g_filename_to_utf8 (pwd->pw_dir, -1, NULL, NULL, NULL);
       result = open_dir (utf8_dir, cmpl_state);
       g_free (utf8_dir);
     }
@@ -2169,10 +2256,11 @@ open_new_dir (gchar       *dir_name,
   DIR *directory;
   struct dirent *dirent_ptr;
   gint entry_count = 0;
+  gint n_entries = 0;
   gint i;
   struct stat ent_sbuf;
   GString *path;
-  gchar *xdir;
+  gchar *sys_dir_name;
 
   sent = g_new (CompletionDirSent, 1);
   sent->mtime = sbuf->st_mtime;
@@ -2181,13 +2269,18 @@ open_new_dir (gchar       *dir_name,
 
   path = g_string_sized_new (2*MAXPATHLEN + 10);
 
-  xdir = g_filename_from_utf8 (dir_name);
-  directory = opendir (xdir);
-
+  sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
+  if (!sys_dir_name)
+    {
+      cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
+      return NULL;
+    }
+  
+  directory = opendir (sys_dir_name);
   if (!directory)
     {
       cmpl_errno = errno;
-      g_free (xdir);
+      g_free (sys_dir_name);
       return NULL;
     }
 
@@ -2207,13 +2300,19 @@ open_new_dir (gchar       *dir_name,
        {
          cmpl_errno = errno;
          closedir (directory);
-         g_free (xdir);
+         g_free (sys_dir_name);
          return NULL;
        }
 
-      sent->entries[i].entry_name = g_filename_to_utf8 (dirent_ptr->d_name);
+      sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent_ptr->d_name, -1, NULL, NULL, NULL);
+      if (!g_utf8_validate (sent->entries[n_entries].entry_name, -1, NULL))
+       {
+         g_warning (_("The filename %s couldn't be converted to UTF-8. Try setting the environment variable G_BROKEN_FILENAMES."), dirent_ptr->d_name);
+         continue;
+       }
+      n_entries++;
 
-      g_string_assign (path, xdir);
+      g_string_assign (path, sys_dir_name);
       if (path->str[path->len-1] != G_DIR_SEPARATOR)
        {
          g_string_append_c (path, G_DIR_SEPARATOR);
@@ -2222,19 +2321,20 @@ open_new_dir (gchar       *dir_name,
 
       if (stat_subdirs)
        {
-         /* Here we know path->str is a GSystemCodepageString */
+         /* Here we know path->str is a "system charset" string */
          if (stat (path->str, &ent_sbuf) >= 0 && S_ISDIR (ent_sbuf.st_mode))
-           sent->entries[i].is_dir = TRUE;
+           sent->entries[n_entries].is_dir = TRUE;
          else
            /* stat may fail, and we don't mind, since it could be a
             * dangling symlink. */
-           sent->entries[i].is_dir = FALSE;
+           sent->entries[n_entries].is_dir = FALSE;
        }
       else
-       sent->entries[i].is_dir = 1;
+       sent->entries[n_entries].is_dir = 1;
     }
-
-  g_free (xdir);
+  sent->entry_count = n_entries;
+  
+  g_free (sys_dir_name);
   g_string_free (path, TRUE);
   qsort (sent->entries, sent->entry_count, sizeof (CompletionDirEntry), compare_cmpl_dir);
 
@@ -2266,7 +2366,7 @@ check_dir (gchar       *dir_name,
 
   static const gint n_no_stat_dirs = G_N_ELEMENTS (no_stat_dirs);
   static gboolean initialized = FALSE;
-  gchar *xdir;
+  gchar *sys_dir_name;
   gint i;
 
   if (!initialized)
@@ -2279,14 +2379,20 @@ check_dir (gchar       *dir_name,
        }
     }
 
-  xdir = g_filename_from_utf8 (dir_name);
-  if (stat (xdir, result) < 0)
+  sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
+  if (!sys_dir_name)
     {
-      g_free (xdir);
+      cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
+      return FALSE;
+    }
+  
+  if (stat (sys_dir_name, result) < 0)
+    {
+      g_free (sys_dir_name);
       cmpl_errno = errno;
       return FALSE;
     }
-  g_free (xdir);
+  g_free (sys_dir_name);
 
   *stat_subdirs = TRUE;
   for (i = 0; i < n_no_stat_dirs; i++)
@@ -2373,7 +2479,7 @@ correct_dir_fullname (CompletionDir* cmpl_dir)
 {
   gint length = strlen (cmpl_dir->fullname);
   gchar *first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
-  gchar *xfilename;
+  gchar *sys_filename;
   struct stat sbuf;
 
   /* Does it end with /. (\.) ? */
@@ -2412,14 +2518,20 @@ correct_dir_fullname (CompletionDir* cmpl_dir)
          return TRUE;
        }
 
-      xfilename = g_filename_from_utf8 (cmpl_dir->fullname);
-      if (stat (xfilename, &sbuf) < 0)
+      sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
+      if (!sys_filename)
        {
-         g_free (xfilename);
+         cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
+         return FALSE;
+       }
+      
+      if (stat (sys_filename, &sbuf) < 0)
+       {
+         g_free (sys_filename);
          cmpl_errno = errno;
          return FALSE;
        }
-      g_free (xfilename);
+      g_free (sys_filename);
 
       cmpl_dir->fullname[length - 3] = 0;
 
@@ -2440,14 +2552,20 @@ correct_dir_fullname (CompletionDir* cmpl_dir)
          return TRUE;
        }
 
-      xfilename = g_filename_from_utf8 (cmpl_dir->fullname);
-      if (stat (xfilename, &sbuf) < 0)
+      sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
+      if (!sys_filename)
        {
-         g_free (xfilename);
+         cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
+         return FALSE;
+       }
+      
+      if (stat (sys_filename, &sbuf) < 0)
+       {
+         g_free (sys_filename);
          cmpl_errno = errno;
          return FALSE;
        }
-      g_free (xfilename);
+      g_free (sys_filename);
 
       cmpl_dir->fullname[length - 4] = 0;
 
@@ -2468,7 +2586,7 @@ correct_parent (CompletionDir *cmpl_dir,
   gchar *last_slash;
   gchar *first_slash;
   gchar *new_name;
-  gchar *xfilename;
+  gchar *sys_filename;
   gchar c = 0;
 
   last_slash = strrchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
@@ -2488,16 +2606,24 @@ correct_parent (CompletionDir *cmpl_dir,
       last_slash[1] = 0;
     }
 
-  xfilename = g_filename_from_utf8 (cmpl_dir->fullname);
-  if (stat (xfilename, &parbuf) < 0)
+  sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
+  if (!sys_filename)
+    {
+      cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
+      if (!c)
+       last_slash[0] = G_DIR_SEPARATOR;
+      return FALSE;
+    }
+  
+  if (stat (sys_filename, &parbuf) < 0)
     {
-      g_free (xfilename);
+      g_free (sys_filename);
       cmpl_errno = errno;
       if (!c)
        last_slash[0] = G_DIR_SEPARATOR;
       return FALSE;
     }
-  g_free (xfilename);
+  g_free (sys_filename);
 
 #ifndef G_OS_WIN32             /* No inode numbers on Win32 */
   if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
@@ -2529,33 +2655,41 @@ correct_parent (CompletionDir *cmpl_dir,
 static gchar*
 find_parent_dir_fullname (gchar* dirname)
 {
-  gchar *orig_dir;
+  gchar *sys_orig_dir;
   gchar *result;
-  gchar *xcwd;
-  gchar *xdir;
+  gchar *sys_cwd;
+  gchar *sys_dirname;
 
-  orig_dir = g_get_current_dir ();
-
-  xdir = g_filename_from_utf8 (dirname);
-  if (chdir (xdir) != 0 || chdir ("..") != 0)
+  sys_orig_dir = g_get_current_dir ();
+  sys_dirname = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
+  if (!sys_dirname)
+    {
+      g_free (sys_orig_dir);
+      cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
+      return NULL;
+    }
+  
+  if (chdir (sys_dirname) != 0 || chdir ("..") != 0)
     {
-      g_free (xdir);
+      g_free (sys_dirname);
+      g_free (sys_orig_dir);
       cmpl_errno = errno;
       return NULL;
     }
-  g_free (xdir);
+  g_free (sys_dirname);
 
-  xcwd = g_get_current_dir ();
-  result = g_filename_to_utf8 (cwd);
-  g_free (xcwd);
+  sys_cwd = g_get_current_dir ();
+  result = g_filename_to_utf8 (sys_cwd, -1, NULL, NULL, NULL);
+  g_free (sys_cwd);
 
-  if (chdir (orig_dir) != 0)
+  if (chdir (sys_orig_dir) != 0)
     {
       cmpl_errno = errno;
+      g_free (sys_orig_dir);
       return NULL;
     }
 
-  g_free (orig_dir);
+  g_free (sys_orig_dir);
   return result;
 }
 
@@ -2939,10 +3073,10 @@ get_pwdb (CompletionState* cmpl_state)
 
   while ((pwd_ptr = getpwent ()) != NULL)
     {
-      utf8 = g_filename_to_utf8 (pwd_ptr->pw_name);
+      utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
       len += strlen (utf8);
       g_free (utf8);
-      utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir);
+      utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
       len += strlen (utf8);
       g_free (utf8);
       len += 2;
@@ -2966,7 +3100,7 @@ get_pwdb (CompletionState* cmpl_state)
          goto error;
        }
 
-      utf8 = g_filename_to_utf8 (pwd_ptr->pw_name);
+      utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
       strcpy (buf_ptr, utf8);
       g_free (utf8);
 
@@ -2975,7 +3109,7 @@ get_pwdb (CompletionState* cmpl_state)
       buf_ptr += strlen (buf_ptr);
       buf_ptr += 1;
 
-      utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir);
+      utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
       strcpy (buf_ptr, utf8);
       g_free (utf8);
 
@@ -3040,7 +3174,9 @@ static gchar*
 cmpl_strerror (gint err)
 {
   if (err == CMPL_ERRNO_TOO_LONG)
-    return "Name too long";
+    return _("Name too long");
+  else if (err == CMPL_ERRNO_DID_NOT_CONVERT)
+    return _("Couldn't convert filename");
   else
     return g_strerror (err);
 }