]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesel.c
fa330d0cc0fe0e3e0a9d1ecc205053d6ab069632
[~andy/gtk] / gtk / gtkfilesel.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "config.h"
28
29 #include <stdio.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #ifdef HAVE_SYS_PARAM_H
33 #include <sys/param.h>
34 #endif
35 #ifdef HAVE_DIRENT_H
36 #include <dirent.h>
37 #endif
38 #include <stdlib.h>
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42 #include <string.h>
43 #include <errno.h>
44 #ifdef HAVE_PWD_H
45 #include <pwd.h>
46 #endif
47
48 #include "fnmatch.h"
49
50 #include "gdk/gdkkeysyms.h"
51 #include "gtkbutton.h"
52 #include "gtkentry.h"
53 #include "gtkfilesel.h"
54 #include "gtkhbox.h"
55 #include "gtkhbbox.h"
56 #include "gtklabel.h"
57 #include "gtklist.h"
58 #include "gtklistitem.h"
59 #include "gtkmain.h"
60 #include "gtkscrolledwindow.h"
61 #include "gtkstock.h"
62 #include "gtksignal.h"
63 #include "gtkvbox.h"
64 #include "gtkmenu.h"
65 #include "gtkmenuitem.h"
66 #include "gtkoptionmenu.h"
67 #include "gtkclist.h"
68 #include "gtkdialog.h"
69 #include "gtkmessagedialog.h"
70 #include "gtkintl.h"
71 #include "gtkdnd.h"
72 #include "gtkeventbox.h"
73
74 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
75 #define STRICT
76 #include <windows.h>
77
78 #ifdef G_OS_WIN32
79 #include <direct.h>
80 #include <io.h>
81 #define mkdir(p,m) _mkdir(p)
82 #ifndef S_ISDIR
83 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
84 #endif
85
86 #endif /* G_OS_WIN32 */
87
88 #endif /* G_OS_WIN32 || G_WITH_CYGWIN */
89
90 #define DIR_LIST_WIDTH   180
91 #define DIR_LIST_HEIGHT  180
92 #define FILE_LIST_WIDTH  180
93 #define FILE_LIST_HEIGHT 180
94
95 /* The Hurd doesn't define either PATH_MAX or MAXPATHLEN, so we put this
96  * in here, since the rest of the code in the file does require some
97  * fixed maximum.
98  */
99 #ifndef MAXPATHLEN
100 #  ifdef PATH_MAX
101 #    define MAXPATHLEN PATH_MAX
102 #  else
103 #    define MAXPATHLEN 2048
104 #  endif
105 #endif
106
107 /* I've put this here so it doesn't get confused with the 
108  * file completion interface */
109 typedef struct _HistoryCallbackArg HistoryCallbackArg;
110
111 struct _HistoryCallbackArg
112 {
113   gchar *directory;
114   GtkWidget *menu_item;
115 };
116
117
118 typedef struct _CompletionState    CompletionState;
119 typedef struct _CompletionDir      CompletionDir;
120 typedef struct _CompletionDirSent  CompletionDirSent;
121 typedef struct _CompletionDirEntry CompletionDirEntry;
122 typedef struct _CompletionUserDir  CompletionUserDir;
123 typedef struct _PossibleCompletion PossibleCompletion;
124
125 /* Non-external file completion decls and structures */
126
127 /* A contant telling PRCS how many directories to cache.  Its actually
128  * kept in a list, so the geometry isn't important. */
129 #define CMPL_DIRECTORY_CACHE_SIZE 10
130
131 /* A constant used to determine whether a substring was an exact
132  * match by first_diff_index()
133  */
134 #define PATTERN_MATCH -1
135 /* The arguments used by all fnmatch() calls below
136  */
137 #define FNMATCH_FLAGS (FNM_PATHNAME | FNM_PERIOD)
138
139 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
140 #define CMPL_ERRNO_DID_NOT_CONVERT ((1<<16)-2)
141
142 /* This structure contains all the useful information about a directory
143  * for the purposes of filename completion.  These structures are cached
144  * in the CompletionState struct.  CompletionDir's are reference counted.
145  */
146 struct _CompletionDirSent
147 {
148   ino_t inode;
149   time_t mtime;
150   dev_t device;
151
152   gint entry_count;
153   struct _CompletionDirEntry *entries;
154 };
155
156 struct _CompletionDir
157 {
158   CompletionDirSent *sent;
159
160   gchar *fullname;
161   gint fullname_len;
162
163   struct _CompletionDir *cmpl_parent;
164   gint cmpl_index;
165   gchar *cmpl_text;
166 };
167
168 /* This structure contains pairs of directory entry names with a flag saying
169  * whether or not they are a valid directory.  NOTE: This information is used
170  * to provide the caller with information about whether to update its completions
171  * or try to open a file.  Since directories are cached by the directory mtime,
172  * a symlink which points to an invalid file (which will not be a directory),
173  * will not be reevaluated if that file is created, unless the containing
174  * directory is touched.  I consider this case to be worth ignoring (josh).
175  */
176 struct _CompletionDirEntry
177 {
178   gboolean is_dir;
179   gchar *entry_name;
180 };
181
182 struct _CompletionUserDir
183 {
184   gchar *login;
185   gchar *homedir;
186 };
187
188 struct _PossibleCompletion
189 {
190   /* accessible fields, all are accessed externally by functions
191    * declared above
192    */
193   gchar *text;
194   gint is_a_completion;
195   gboolean is_directory;
196
197   /* Private fields
198    */
199   gint text_alloc;
200 };
201
202 struct _CompletionState
203 {
204   gint last_valid_char;
205   gchar *updated_text;
206   gint updated_text_len;
207   gint updated_text_alloc;
208   gboolean re_complete;
209
210   gchar *user_dir_name_buffer;
211   gint user_directories_len;
212
213   gchar *last_completion_text;
214
215   gint user_completion_index; /* if >= 0, currently completing ~user */
216
217   struct _CompletionDir *completion_dir; /* directory completing from */
218   struct _CompletionDir *active_completion_dir;
219
220   struct _PossibleCompletion the_completion;
221
222   struct _CompletionDir *reference_dir; /* initial directory */
223
224   GList* directory_storage;
225   GList* directory_sent_storage;
226
227   struct _CompletionUserDir *user_directories;
228 };
229
230 enum {
231   PROP_0,
232   PROP_SHOW_FILEOPS,
233   PROP_FILENAME
234 };
235
236 /* File completion functions which would be external, were they used
237  * outside of this file.
238  */
239
240 static CompletionState*    cmpl_init_state        (void);
241 static void                cmpl_free_state        (CompletionState *cmpl_state);
242 static gint                cmpl_state_okay        (CompletionState* cmpl_state);
243 static const gchar*        cmpl_strerror          (gint);
244
245 static PossibleCompletion* cmpl_completion_matches(gchar           *text_to_complete,
246                                                    gchar          **remaining_text,
247                                                    CompletionState *cmpl_state);
248
249 /* Returns a name for consideration, possibly a completion, this name
250  * will be invalid after the next call to cmpl_next_completion.
251  */
252 static char*               cmpl_this_completion   (PossibleCompletion*);
253
254 /* True if this completion matches the given text.  Otherwise, this
255  * output can be used to have a list of non-completions.
256  */
257 static gint                cmpl_is_a_completion   (PossibleCompletion*);
258
259 /* True if the completion is a directory
260  */
261 static gboolean            cmpl_is_directory      (PossibleCompletion*);
262
263 /* Obtains the next completion, or NULL
264  */
265 static PossibleCompletion* cmpl_next_completion   (CompletionState*);
266
267 /* Updating completions: the return value of cmpl_updated_text() will
268  * be text_to_complete completed as much as possible after the most
269  * recent call to cmpl_completion_matches.  For the present
270  * application, this is the suggested replacement for the user's input
271  * string.  You must CALL THIS AFTER ALL cmpl_text_completions have
272  * been received.
273  */
274 static gchar*              cmpl_updated_text       (CompletionState* cmpl_state);
275
276 /* After updating, to see if the completion was a directory, call
277  * this.  If it was, you should consider re-calling completion_matches.
278  */
279 static gboolean            cmpl_updated_dir        (CompletionState* cmpl_state);
280
281 /* Current location: if using file completion, return the current
282  * directory, from which file completion begins.  More specifically,
283  * the cwd concatenated with all exact completions up to the last
284  * directory delimiter('/').
285  */
286 static gchar*              cmpl_reference_position (CompletionState* cmpl_state);
287
288 /* backing up: if cmpl_completion_matches returns NULL, you may query
289  * the index of the last completable character into cmpl_updated_text.
290  */
291 static gint                cmpl_last_valid_char    (CompletionState* cmpl_state);
292
293 /* When the user selects a non-directory, call cmpl_completion_fullname
294  * to get the full name of the selected file.
295  */
296 static gchar*              cmpl_completion_fullname (const gchar*, CompletionState* cmpl_state);
297
298
299 /* Directory operations. */
300 static CompletionDir* open_ref_dir         (gchar* text_to_complete,
301                                             gchar** remaining_text,
302                                             CompletionState* cmpl_state);
303 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
304 static gboolean       check_dir            (gchar *dir_name, 
305                                             struct stat *result, 
306                                             gboolean *stat_subdirs);
307 #endif
308 static CompletionDir* open_dir             (gchar* dir_name,
309                                             CompletionState* cmpl_state);
310 #ifdef HAVE_PWD_H
311 static CompletionDir* open_user_dir        (const gchar* text_to_complete,
312                                             CompletionState *cmpl_state);
313 #endif
314 static CompletionDir* open_relative_dir    (gchar* dir_name, CompletionDir* dir,
315                                             CompletionState *cmpl_state);
316 static CompletionDirSent* open_new_dir     (gchar* dir_name, 
317                                             struct stat* sbuf,
318                                             gboolean stat_subdirs);
319 static gint           correct_dir_fullname (CompletionDir* cmpl_dir);
320 static gint           correct_parent       (CompletionDir* cmpl_dir,
321                                             struct stat *sbuf);
322 static gchar*         find_parent_dir_fullname    (gchar* dirname);
323 static CompletionDir* attach_dir           (CompletionDirSent* sent,
324                                             gchar* dir_name,
325                                             CompletionState *cmpl_state);
326 static void           free_dir_sent (CompletionDirSent* sent);
327 static void           free_dir      (CompletionDir  *dir);
328 static void           prune_memory_usage(CompletionState *cmpl_state);
329
330 /* Completion operations */
331 static PossibleCompletion* attempt_homedir_completion(gchar* text_to_complete,
332                                                       CompletionState *cmpl_state);
333 static PossibleCompletion* attempt_file_completion(CompletionState *cmpl_state);
334 static CompletionDir* find_completion_dir(gchar* text_to_complete,
335                                           gchar** remaining_text,
336                                           CompletionState* cmpl_state);
337 static PossibleCompletion* append_completion_text(gchar* text,
338                                                   CompletionState* cmpl_state);
339 #ifdef HAVE_PWD_H
340 static gint get_pwdb(CompletionState* cmpl_state);
341 static gint compare_user_dir(const void* a, const void* b);
342 #endif
343 static gint first_diff_index(gchar* pat, gchar* text);
344 static gint compare_cmpl_dir(const void* a, const void* b);
345 static void update_cmpl(PossibleCompletion* poss,
346                         CompletionState* cmpl_state);
347
348 static void gtk_file_selection_class_init    (GtkFileSelectionClass *klass);
349 static void gtk_file_selection_set_property  (GObject         *object,
350                                               guint            prop_id,
351                                               const GValue    *value,
352                                               GParamSpec      *pspec);
353 static void gtk_file_selection_get_property  (GObject         *object,
354                                               guint            prop_id,
355                                               GValue          *value,
356                                               GParamSpec      *pspec);
357 static void gtk_file_selection_init          (GtkFileSelection      *filesel);
358 static void gtk_file_selection_finalize      (GObject               *object);
359 static void gtk_file_selection_destroy       (GtkObject             *object);
360 static gint gtk_file_selection_key_press     (GtkWidget             *widget,
361                                               GdkEventKey           *event,
362                                               gpointer               user_data);
363 static gint gtk_file_selection_insert_text   (GtkWidget             *widget,
364                                               const gchar           *new_text,
365                                               gint                   new_text_length,
366                                               gint                  *position,
367                                               gpointer               user_data);
368
369 static void gtk_file_selection_file_button (GtkWidget *widget,
370                                             gint row, 
371                                             gint column, 
372                                             GdkEventButton *bevent,
373                                             gpointer user_data);
374
375 static void gtk_file_selection_dir_button (GtkWidget *widget,
376                                            gint row, 
377                                            gint column, 
378                                            GdkEventButton *bevent,
379                                            gpointer data);
380
381 static void gtk_file_selection_populate      (GtkFileSelection      *fs,
382                                               gchar                 *rel_path,
383                                               gint                   try_complete);
384 static void gtk_file_selection_abort         (GtkFileSelection      *fs);
385
386 static void gtk_file_selection_update_history_menu (GtkFileSelection       *fs,
387                                                     gchar                  *current_dir);
388
389 static void gtk_file_selection_create_dir (GtkWidget *widget, gpointer data);
390 static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data);
391 static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
392
393
394
395 static GtkWindowClass *parent_class = NULL;
396
397 /* Saves errno when something cmpl does fails. */
398 static gint cmpl_errno;
399
400 #ifdef G_WITH_CYGWIN
401 /*
402  * Take the path currently in the file selection
403  * entry field and translate as necessary from
404  * a WIN32 style to CYGWIN32 style path.  For
405  * instance translate:
406  * x:\somepath\file.jpg
407  * to:
408  * //x/somepath/file.jpg
409  *
410  * Replace the path in the selection text field.
411  * Return a boolean value concerning whether a
412  * translation had to be made.
413  */
414 int
415 translate_win32_path (GtkFileSelection *filesel)
416 {
417   int updated = 0;
418   gchar *path;
419
420   /*
421    * Retrieve the current path
422    */
423   path = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
424
425   /*
426    * Translate only if this looks like a DOS-ish
427    * path... First handle any drive letters.
428    */
429   if (isalpha (path[0]) && (path[1] == ':')) {
430     /*
431      * This part kind of stinks... It isn't possible
432      * to know if there is enough space in the current
433      * string for the extra character required in this
434      * conversion.  Assume that there isn't enough space
435      * and use the set function on the text field to
436      * set the newly created string.
437      */
438     gchar *newPath = g_strdup_printf ("//%c/%s", path[0], (path + 3));
439     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath);
440
441     path = newPath;
442     updated = 1;
443   }
444
445   /*
446    * Now, replace backslashes with forward slashes 
447    * if necessary.
448    */
449   if (strchr (path, '\\'))
450     {
451       int index;
452       for (index = 0; path[index] != '\0'; index++)
453         if (path[index] == '\\')
454           path[index] = '/';
455       
456       updated = 1;
457     }
458     
459   return updated;
460 }
461 #endif
462
463 GtkType
464 gtk_file_selection_get_type (void)
465 {
466   static GtkType file_selection_type = 0;
467
468   if (!file_selection_type)
469     {
470       static const GtkTypeInfo filesel_info =
471       {
472         "GtkFileSelection",
473         sizeof (GtkFileSelection),
474         sizeof (GtkFileSelectionClass),
475         (GtkClassInitFunc) gtk_file_selection_class_init,
476         (GtkObjectInitFunc) gtk_file_selection_init,
477         /* reserved_1 */ NULL,
478         /* reserved_2 */ NULL,
479         (GtkClassInitFunc) NULL,
480       };
481
482       file_selection_type = gtk_type_unique (GTK_TYPE_DIALOG, &filesel_info);
483     }
484
485   return file_selection_type;
486 }
487
488 static void
489 gtk_file_selection_class_init (GtkFileSelectionClass *class)
490 {
491   GObjectClass *gobject_class;
492   GtkObjectClass *object_class;
493
494   gobject_class = (GObjectClass*) class;
495   object_class = (GtkObjectClass*) class;
496
497   parent_class = gtk_type_class (GTK_TYPE_DIALOG);
498
499   gobject_class->finalize = gtk_file_selection_finalize;
500   gobject_class->set_property = gtk_file_selection_set_property;
501   gobject_class->get_property = gtk_file_selection_get_property;
502    
503   g_object_class_install_property (gobject_class,
504                                    PROP_FILENAME,
505                                    g_param_spec_string ("filename",
506                                                         _("Filename"),
507                                                         _("The currently selected filename."),
508                                                         NULL,
509                                                         G_PARAM_READABLE | G_PARAM_WRITABLE));
510   g_object_class_install_property (gobject_class,
511                                    PROP_SHOW_FILEOPS,
512                                    g_param_spec_boolean ("show_fileops",
513                                                          _("Show file operations"),
514                                                          _("Whether buttons for creating/manipulating files should be displayed."),
515                                                          FALSE,
516                                                          G_PARAM_READABLE |
517                                                          G_PARAM_WRITABLE));
518   object_class->destroy = gtk_file_selection_destroy;
519 }
520
521 static void gtk_file_selection_set_property (GObject         *object,
522                                              guint            prop_id,
523                                              const GValue    *value,
524                                              GParamSpec      *pspec)
525 {
526   GtkFileSelection *filesel;
527
528   filesel = GTK_FILE_SELECTION (object);
529
530   switch (prop_id)
531     {
532     case PROP_FILENAME:
533       gtk_file_selection_set_filename (filesel,
534                                        g_value_get_string (value));
535       break;
536       
537     case PROP_SHOW_FILEOPS:
538       if (g_value_get_boolean (value))
539          gtk_file_selection_show_fileop_buttons (filesel);
540       else
541          gtk_file_selection_hide_fileop_buttons (filesel);
542       break;
543     default:
544       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
545       break;
546     }
547 }
548
549 static void gtk_file_selection_get_property (GObject         *object,
550                                              guint            prop_id,
551                                              GValue          *value,
552                                              GParamSpec      *pspec)
553 {
554   GtkFileSelection *filesel;
555
556   filesel = GTK_FILE_SELECTION (object);
557
558   switch (prop_id)
559     {
560     case PROP_FILENAME:
561       g_value_set_string (value,
562                           gtk_file_selection_get_filename(filesel));
563       break;
564
565     case PROP_SHOW_FILEOPS:
566       /* This is a little bit hacky, but doing otherwise would require
567        * adding a field to the object.
568        */
569       g_value_set_boolean (value, (filesel->fileop_c_dir && 
570                                    filesel->fileop_del_file &&
571                                    filesel->fileop_ren_file));
572       break;
573     default:
574       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
575       break;
576     }
577 }
578
579 static gboolean
580 grab_default (GtkWidget *widget)
581 {
582   gtk_widget_grab_default (widget);
583   return FALSE;
584 }
585      
586 static void
587 gtk_file_selection_init (GtkFileSelection *filesel)
588 {
589   GtkWidget *entry_vbox;
590   GtkWidget *label;
591   GtkWidget *list_hbox;
592   GtkWidget *confirm_area;
593   GtkWidget *pulldown_hbox;
594   GtkWidget *scrolled_win;
595   GtkWidget *eventbox;
596   GtkDialog *dialog;
597   
598   char *dir_title [2];
599   char *file_title [2];
600
601   dialog = GTK_DIALOG (filesel);
602
603   filesel->cmpl_state = cmpl_init_state ();
604
605   /* The dialog-sized vertical box  */
606   filesel->main_vbox = dialog->vbox;
607   gtk_container_set_border_width (GTK_CONTAINER (filesel), 10);
608
609   /* The horizontal box containing create, rename etc. buttons */
610   filesel->button_area = gtk_hbutton_box_new ();
611   gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel->button_area), GTK_BUTTONBOX_START);
612   gtk_box_set_spacing (GTK_BOX (filesel->button_area), 0);
613   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->button_area, 
614                       FALSE, FALSE, 0);
615   gtk_widget_show (filesel->button_area);
616   
617   gtk_file_selection_show_fileop_buttons (filesel);
618
619   /* hbox for pulldown menu */
620   pulldown_hbox = gtk_hbox_new (TRUE, 5);
621   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), pulldown_hbox, FALSE, FALSE, 0);
622   gtk_widget_show (pulldown_hbox);
623   
624   /* Pulldown menu */
625   filesel->history_pulldown = gtk_option_menu_new ();
626   gtk_widget_show (filesel->history_pulldown);
627   gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown, 
628                       FALSE, FALSE, 0);
629     
630   /*  The horizontal box containing the directory and file listboxes  */
631   list_hbox = gtk_hbox_new (FALSE, 5);
632   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0);
633   gtk_widget_show (list_hbox);
634
635   /* The directories clist */
636   dir_title[0] = _("Directories");
637   dir_title[1] = NULL;
638   filesel->dir_list = gtk_clist_new_with_titles (1, (gchar**) dir_title);
639   gtk_widget_set_usize (filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
640   gtk_signal_connect (GTK_OBJECT (filesel->dir_list), "select_row",
641                       (GtkSignalFunc) gtk_file_selection_dir_button, 
642                       (gpointer) filesel);
643   gtk_clist_set_column_auto_resize (GTK_CLIST (filesel->dir_list), 0, TRUE);
644   gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list));
645
646   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
647   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
648   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
649                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
650   gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
651   gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
652   gtk_widget_show (filesel->dir_list);
653   gtk_widget_show (scrolled_win);
654
655   /* The files clist */
656   file_title[0] = _("Files");
657   file_title[1] = NULL;
658   filesel->file_list = gtk_clist_new_with_titles (1, (gchar**) file_title);
659   gtk_widget_set_usize (filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
660   gtk_signal_connect (GTK_OBJECT (filesel->file_list), "select_row",
661                       (GtkSignalFunc) gtk_file_selection_file_button, 
662                       (gpointer) filesel);
663   gtk_clist_set_column_auto_resize (GTK_CLIST (filesel->file_list), 0, TRUE);
664   gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list));
665
666   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
667   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
668   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
669                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
670   gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
671   gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
672   gtk_widget_show (filesel->file_list);
673   gtk_widget_show (scrolled_win);
674
675   /* action area for packing buttons into. */
676   filesel->action_area = gtk_hbox_new (TRUE, 0);
677   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->action_area, 
678                       FALSE, FALSE, 0);
679   gtk_widget_show (filesel->action_area);
680   
681   /*  The OK/Cancel button area */
682   confirm_area = dialog->action_area;
683
684   /*  The OK button  */
685   filesel->ok_button = gtk_dialog_add_button (dialog,
686                                               GTK_STOCK_OK,
687                                               GTK_RESPONSE_OK);
688   
689   gtk_widget_grab_default (filesel->ok_button);
690
691   /*  The Cancel button  */
692   filesel->cancel_button = gtk_dialog_add_button (dialog,
693                                                   GTK_STOCK_CANCEL,
694                                                   GTK_RESPONSE_CANCEL);
695
696   /*  The selection entry widget  */
697   entry_vbox = gtk_vbox_new (FALSE, 2);
698   gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 2);
699   gtk_widget_show (entry_vbox);
700   
701   eventbox = gtk_event_box_new ();
702   filesel->selection_text = label = gtk_label_new ("");
703   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
704   gtk_container_add (GTK_CONTAINER (eventbox), label);
705   gtk_box_pack_start (GTK_BOX (entry_vbox), eventbox, FALSE, FALSE, 0);
706   gtk_widget_show (label);
707   gtk_widget_show (eventbox);
708
709   filesel->selection_entry = gtk_entry_new ();
710   gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event",
711                       (GtkSignalFunc) gtk_file_selection_key_press, filesel);
712   gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "insert_text",
713                       (GtkSignalFunc) gtk_file_selection_insert_text, NULL);
714   gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event",
715                              (GtkSignalFunc) grab_default,
716                              GTK_OBJECT (filesel->ok_button));
717   gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "activate",
718                              (GtkSignalFunc) gtk_button_clicked,
719                              GTK_OBJECT (filesel->ok_button));
720   gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
721   gtk_widget_show (filesel->selection_entry);
722
723   if (!cmpl_state_okay (filesel->cmpl_state))
724     {
725       gchar err_buf[256];
726
727       sprintf (err_buf, _("Directory unreadable: %s"), cmpl_strerror (cmpl_errno));
728
729       gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
730     }
731   else
732     {
733       gtk_file_selection_populate (filesel, "", FALSE);
734     }
735
736   gtk_widget_grab_focus (filesel->selection_entry);
737 }
738
739 static gchar *
740 uri_list_extract_first_uri (const gchar* uri_list)
741 {
742   const gchar *p, *q;
743   
744   g_return_val_if_fail (uri_list != NULL, NULL);
745   
746   p = uri_list;
747   /* We don't actually try to validate the URI according to RFC
748    * 2396, or even check for allowed characters - we just ignore
749    * comments and trim whitespace off the ends.  We also
750    * allow LF delimination as well as the specified CRLF.
751    *
752    * We do allow comments like specified in RFC 2483.
753    */
754   while (p)
755     {
756       if (*p != '#')
757         {
758           while (g_ascii_isspace(*p))
759             p++;
760           
761           q = p;
762           while (*q && (*q != '\n') && (*q != '\r'))
763             q++;
764           
765           if (q > p)
766             {
767               q--;
768               while (q > p && g_ascii_isspace (*q))
769                 q--;
770
771               if (q > p)
772                 return g_strndup (p, q - p + 1);
773             }
774         }
775       p = strchr (p, '\n');
776       if (p)
777         p++;
778     }
779   return NULL;
780 }
781
782 static void
783 dnd_really_drop  (GtkWidget *dialog, gint response_id, GtkFileSelection *fs)
784 {
785   gchar *filename;
786   
787   if (response_id == GTK_RESPONSE_YES)
788     {
789       filename = g_object_get_data (G_OBJECT (dialog), "gtk-fs-dnd-filename");
790
791       gtk_file_selection_set_filename (fs, filename);
792     }
793   
794   gtk_widget_destroy (dialog);
795 }
796
797
798 static void
799 filenames_dropped (GtkWidget        *widget,
800                    GdkDragContext   *context,
801                    gint              x,
802                    gint              y,
803                    GtkSelectionData *selection_data,
804                    guint             info,
805                    guint             time)
806 {
807   char *uri = NULL;
808   char *filename = NULL;
809   char *hostname;
810   char this_hostname[257];
811   int res;
812   GError *error = NULL;
813         
814   if (!selection_data->data)
815     return;
816
817   uri = uri_list_extract_first_uri ((char *)selection_data->data);
818   
819   if (!uri)
820     return;
821
822   filename = g_filename_from_uri (uri, &hostname, &error);
823   g_free (uri);
824   
825   if (!filename)
826     {
827       g_warning ("Error getting dropped filename: %s\n",
828                  error->message);
829       g_error_free (error);
830       return;
831     }
832
833   res = gethostname (this_hostname, 256);
834   this_hostname[256] = 0;
835   
836   if ((hostname == NULL) ||
837       (res == 0 && strcmp (hostname, this_hostname) == 0) ||
838       (strcmp (hostname, "localhost") == 0))
839     gtk_file_selection_set_filename (GTK_FILE_SELECTION (widget),
840                                      filename);
841   else
842     {
843       GtkWidget *dialog;
844       
845       dialog = gtk_message_dialog_new (GTK_WINDOW (widget),
846                                        GTK_DIALOG_DESTROY_WITH_PARENT,
847                                        GTK_MESSAGE_QUESTION,
848                                        GTK_BUTTONS_YES_NO,
849                                        _("The file \"%s\" resides on another machine (called %s) and may not be availible to this program.\n"
850                                          "Are you sure that you want to select it?"), filename, hostname);
851
852       g_object_set_data_full (G_OBJECT (dialog), "gtk-fs-dnd-filename", g_strdup (filename), g_free);
853       
854       g_signal_connect_data (dialog, "response",
855                              (GCallback) dnd_really_drop, 
856                              widget, NULL, 0);
857       
858       gtk_widget_show (dialog);
859     }
860
861   g_free (hostname);
862   g_free (filename);
863 }
864
865 enum
866 {
867   TARGET_URILIST,
868   TARGET_UTF8_STRING,
869   TARGET_STRING,
870   TARGET_TEXT,
871   TARGET_COMPOUND_TEXT
872 };
873
874
875 static void
876 filenames_drag_get (GtkWidget        *widget,
877                     GdkDragContext   *context,
878                     GtkSelectionData *selection_data,
879                     guint             info,
880                     guint             time,
881                     GtkFileSelection *filesel)
882 {
883   const gchar *file;
884   gchar *uri_list;
885   char hostname[256];
886   int res;
887   GError *error;
888
889   file = gtk_file_selection_get_filename (filesel);
890
891   if (file)
892     {
893       if (info == TARGET_URILIST)
894         {
895           res = gethostname (hostname, 256);
896           
897           error = NULL;
898           uri_list = g_filename_to_uri (file, (!res)?hostname:NULL, &error);
899           if (!uri_list)
900             {
901               g_warning ("Error getting filename: %s\n",
902                          error->message);
903               g_error_free (error);
904               return;
905             }
906           
907           gtk_selection_data_set (selection_data,
908                                   selection_data->target, 8,
909                                   (void *)uri_list, strlen((char *)uri_list));
910           g_free (uri_list);
911         }
912       else
913         {
914           g_print ("Setting text: '%s'\n", file);
915           gtk_selection_data_set_text (selection_data, file);
916         }
917     }
918 }
919
920 static void
921 file_selection_setup_dnd (GtkFileSelection *filesel)
922 {
923   GtkWidget *eventbox;
924   static GtkTargetEntry drop_types[] = {
925     { "text/uri-list", 0, TARGET_URILIST}
926   };
927   static gint n_drop_types = sizeof(drop_types)/sizeof(drop_types[0]);
928   static GtkTargetEntry drag_types[] = {
929     { "text/uri-list", 0, TARGET_URILIST},
930     { "UTF8_STRING", 0, TARGET_UTF8_STRING },
931     { "STRING", 0, 0 },
932     { "TEXT",   0, 0 }, 
933     { "COMPOUND_TEXT", 0, 0 }
934   };
935   static gint n_drag_types = sizeof(drag_types)/sizeof(drag_types[0]);
936
937   gtk_drag_dest_set (GTK_WIDGET (filesel),
938                      GTK_DEST_DEFAULT_ALL,
939                      drop_types, n_drop_types,
940                      GDK_ACTION_COPY);
941
942   gtk_signal_connect (GTK_OBJECT(filesel), "drag_data_received",
943                       GTK_SIGNAL_FUNC(filenames_dropped), NULL);
944
945   eventbox = gtk_widget_get_parent (filesel->selection_text);
946   gtk_drag_source_set (eventbox,
947                        GDK_BUTTON1_MASK,
948                        drag_types, n_drag_types,
949                        GDK_ACTION_COPY);
950
951   gtk_signal_connect (GTK_OBJECT (eventbox),
952                       "drag_data_get",
953                       GTK_SIGNAL_FUNC (filenames_drag_get),
954                       filesel);
955 }
956
957 GtkWidget*
958 gtk_file_selection_new (const gchar *title)
959 {
960   GtkFileSelection *filesel;
961
962   filesel = gtk_type_new (GTK_TYPE_FILE_SELECTION);
963   gtk_window_set_title (GTK_WINDOW (filesel), title);
964   gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE);
965
966   file_selection_setup_dnd (filesel);
967   
968   return GTK_WIDGET (filesel);
969 }
970
971 void
972 gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
973 {
974   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
975     
976   /* delete, create directory, and rename */
977   if (!filesel->fileop_c_dir) 
978     {
979       filesel->fileop_c_dir = gtk_button_new_with_label (_("Create Dir"));
980       gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked",
981                           (GtkSignalFunc) gtk_file_selection_create_dir, 
982                           (gpointer) filesel);
983       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
984                           filesel->fileop_c_dir, TRUE, TRUE, 0);
985       gtk_widget_show (filesel->fileop_c_dir);
986     }
987         
988   if (!filesel->fileop_del_file) 
989     {
990       filesel->fileop_del_file = gtk_button_new_with_label (_("Delete File"));
991       gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked",
992                           (GtkSignalFunc) gtk_file_selection_delete_file, 
993                           (gpointer) filesel);
994       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
995                           filesel->fileop_del_file, TRUE, TRUE, 0);
996       gtk_widget_show (filesel->fileop_del_file);
997     }
998
999   if (!filesel->fileop_ren_file)
1000     {
1001       filesel->fileop_ren_file = gtk_button_new_with_label (_("Rename File"));
1002       gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked",
1003                           (GtkSignalFunc) gtk_file_selection_rename_file, 
1004                           (gpointer) filesel);
1005       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
1006                           filesel->fileop_ren_file, TRUE, TRUE, 0);
1007       gtk_widget_show (filesel->fileop_ren_file);
1008     }
1009   g_object_notify (G_OBJECT (filesel), "show_fileops");
1010   gtk_widget_queue_resize (GTK_WIDGET (filesel));
1011 }
1012
1013 void       
1014 gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
1015 {
1016   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1017     
1018   if (filesel->fileop_ren_file)
1019     {
1020       gtk_widget_destroy (filesel->fileop_ren_file);
1021       filesel->fileop_ren_file = NULL;
1022     }
1023
1024   if (filesel->fileop_del_file)
1025     {
1026       gtk_widget_destroy (filesel->fileop_del_file);
1027       filesel->fileop_del_file = NULL;
1028     }
1029
1030   if (filesel->fileop_c_dir)
1031     {
1032       gtk_widget_destroy (filesel->fileop_c_dir);
1033       filesel->fileop_c_dir = NULL;
1034     }
1035   g_object_notify (G_OBJECT (filesel), "show_fileops");
1036 }
1037
1038
1039
1040 void
1041 gtk_file_selection_set_filename (GtkFileSelection *filesel,
1042                                  const gchar      *filename)
1043 {
1044   gchar *buf;
1045   const char *name, *last_slash;
1046
1047   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1048   g_return_if_fail (filename != NULL);
1049
1050   last_slash = strrchr (filename, G_DIR_SEPARATOR);
1051
1052   if (!last_slash)
1053     {
1054       buf = g_strdup ("");
1055       name = filename;
1056     }
1057   else
1058     {
1059       buf = g_strdup (filename);
1060       buf[last_slash - filename + 1] = 0;
1061       name = last_slash + 1;
1062     }
1063
1064   gtk_file_selection_populate (filesel, buf, FALSE);
1065
1066   if (filesel->selection_entry)
1067     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
1068   g_free (buf);
1069   g_object_notify (G_OBJECT (filesel), "filename");
1070 }
1071
1072 /**
1073  * gtk_file_selection_get_filename:
1074  * @filesel: a #GtkFileSelection
1075  * 
1076  * This function returns the selected filename in the C runtime's
1077  * multibyte string encoding, which may or may not be the same as that
1078  * used by GTK+ (UTF-8). To convert to UTF-8, call g_filename_to_utf8().
1079  * The returned string points to a statically allocated buffer and
1080  * should be copied if you plan to keep it around.
1081  * 
1082  * Return value: currently-selected filename in locale's encoding
1083  **/
1084 G_CONST_RETURN gchar*
1085 gtk_file_selection_get_filename (GtkFileSelection *filesel)
1086 {
1087   static gchar nothing[2] = "";
1088   static gchar something[MAXPATHLEN*2];
1089   char *sys_filename;
1090   const char *text;
1091
1092   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
1093
1094 #ifdef G_WITH_CYGWIN
1095   translate_win32_path (filesel);
1096 #endif
1097   text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
1098   if (text)
1099     {
1100       sys_filename = g_filename_from_utf8 (cmpl_completion_fullname (text, filesel->cmpl_state), -1, NULL, NULL, NULL);
1101       if (!sys_filename)
1102         return nothing;
1103       strncpy (something, sys_filename, sizeof (something));
1104       g_free (sys_filename);
1105       return something;
1106     }
1107
1108   return nothing;
1109 }
1110
1111 void
1112 gtk_file_selection_complete (GtkFileSelection *filesel,
1113                              const gchar      *pattern)
1114 {
1115   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1116   g_return_if_fail (pattern != NULL);
1117
1118   if (filesel->selection_entry)
1119     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
1120   gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE);
1121 }
1122
1123 static void
1124 gtk_file_selection_destroy (GtkObject *object)
1125 {
1126   GtkFileSelection *filesel;
1127   GList *list;
1128   HistoryCallbackArg *callback_arg;
1129   
1130   g_return_if_fail (GTK_IS_FILE_SELECTION (object));
1131   
1132   filesel = GTK_FILE_SELECTION (object);
1133   
1134   if (filesel->fileop_dialog)
1135     {
1136       gtk_widget_destroy (filesel->fileop_dialog);
1137       filesel->fileop_dialog = NULL;
1138     }
1139   
1140   if (filesel->history_list)
1141     {
1142       list = filesel->history_list;
1143       while (list)
1144         {
1145           callback_arg = list->data;
1146           g_free (callback_arg->directory);
1147           g_free (callback_arg);
1148           list = list->next;
1149         }
1150       g_list_free (filesel->history_list);
1151       filesel->history_list = NULL;
1152     }
1153
1154   if (filesel->cmpl_state)
1155     {
1156       cmpl_free_state (filesel->cmpl_state);
1157       filesel->cmpl_state = NULL;
1158     }
1159   
1160   GTK_OBJECT_CLASS (parent_class)->destroy (object);
1161 }
1162
1163 static void
1164 gtk_file_selection_finalize (GObject *object)
1165 {
1166   GtkFileSelection *filesel = GTK_FILE_SELECTION (object);
1167
1168   g_free (filesel->fileop_file);
1169 }
1170
1171 /* Begin file operations callbacks */
1172
1173 static void
1174 gtk_file_selection_fileop_error (GtkFileSelection *fs,
1175                                  gchar            *error_message)
1176 {
1177   GtkWidget *dialog;
1178     
1179   g_return_if_fail (error_message != NULL);
1180
1181   /* main dialog */
1182   dialog = gtk_message_dialog_new (GTK_WINDOW (fs),
1183                                    GTK_DIALOG_DESTROY_WITH_PARENT,
1184                                    GTK_MESSAGE_ERROR,
1185                                    GTK_BUTTONS_CLOSE,
1186                                    "%s", error_message);
1187
1188   /* yes, we free it */
1189   g_free (error_message);
1190
1191   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1192
1193   gtk_signal_connect_object (GTK_OBJECT (dialog), "response",
1194                              (GtkSignalFunc) gtk_widget_destroy, 
1195                              (gpointer) dialog);
1196
1197   gtk_widget_show (dialog);
1198 }
1199
1200 static void
1201 gtk_file_selection_fileop_destroy (GtkWidget *widget,
1202                                    gpointer   data)
1203 {
1204   GtkFileSelection *fs = data;
1205
1206   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1207   
1208   fs->fileop_dialog = NULL;
1209 }
1210
1211
1212 static void
1213 gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
1214                                          gpointer   data)
1215 {
1216   GtkFileSelection *fs = data;
1217   const gchar *dirname;
1218   gchar *path;
1219   gchar *full_path;
1220   gchar *sys_full_path;
1221   gchar *buf;
1222   GError *error = NULL;
1223   CompletionState *cmpl_state;
1224   
1225   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1226
1227   dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1228   cmpl_state = (CompletionState*) fs->cmpl_state;
1229   path = cmpl_reference_position (cmpl_state);
1230   
1231   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
1232   sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1233   if (error)
1234     {
1235       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1236         buf = g_strdup_printf (_("The directory name \"%s\" contains symbols that are not allowed in filenames"), dirname);
1237       else
1238         buf = g_strdup_printf (_("Error creating directory \"%s\": %s\n%s"), dirname, error->message,
1239                                _("You probably used symbols not allowed in filenames."));
1240       gtk_file_selection_fileop_error (fs, buf);
1241       g_error_free (error);
1242       goto out;
1243     }
1244
1245   if (mkdir (sys_full_path, 0755) < 0) 
1246     {
1247       buf = g_strdup_printf (_("Error creating directory \"%s\": %s\n"), dirname,
1248                              g_strerror (errno));
1249       gtk_file_selection_fileop_error (fs, buf);
1250     }
1251
1252  out:
1253   g_free (full_path);
1254   g_free (sys_full_path);
1255   
1256   gtk_widget_destroy (fs->fileop_dialog);
1257   gtk_file_selection_populate (fs, "", FALSE);
1258 }
1259   
1260 static void
1261 gtk_file_selection_create_dir (GtkWidget *widget,
1262                                gpointer   data)
1263 {
1264   GtkFileSelection *fs = data;
1265   GtkWidget *label;
1266   GtkWidget *dialog;
1267   GtkWidget *vbox;
1268   GtkWidget *button;
1269
1270   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1271
1272   if (fs->fileop_dialog)
1273     return;
1274   
1275   /* main dialog */
1276   dialog = gtk_dialog_new ();
1277   fs->fileop_dialog = dialog;
1278   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1279                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
1280                       (gpointer) fs);
1281   gtk_window_set_title (GTK_WINDOW (dialog), _("Create Directory"));
1282   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1283
1284   /* If file dialog is grabbed, grab option dialog */
1285   /* When option dialog is closed, file dialog will be grabbed again */
1286   if (GTK_WINDOW (fs)->modal)
1287       gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1288
1289   vbox = gtk_vbox_new (FALSE, 0);
1290   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1291   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1292                      FALSE, FALSE, 0);
1293   gtk_widget_show( vbox);
1294   
1295   label = gtk_label_new_with_mnemonic (_("_Directory name:"));
1296   gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.0);
1297   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1298   gtk_widget_show (label);
1299
1300   /*  The directory entry widget  */
1301   fs->fileop_entry = gtk_entry_new ();
1302   gtk_label_set_mnemonic_widget (GTK_LABEL (label), fs->fileop_entry);
1303   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
1304                       TRUE, TRUE, 5);
1305   GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1306   gtk_widget_show (fs->fileop_entry);
1307   
1308   /* buttons */
1309   button = gtk_button_new_with_label (_("Create"));
1310   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1311                       (GtkSignalFunc) gtk_file_selection_create_dir_confirmed, 
1312                       (gpointer) fs);
1313   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1314                      button, TRUE, TRUE, 0);
1315   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1316   gtk_widget_show (button);
1317   
1318   button = gtk_button_new_with_label (_("Cancel"));
1319   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1320                              (GtkSignalFunc) gtk_widget_destroy, 
1321                              (gpointer) dialog);
1322   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1323                      button, TRUE, TRUE, 0);
1324   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1325   gtk_widget_grab_default (button);
1326   gtk_widget_show (button);
1327
1328   gtk_widget_show (dialog);
1329 }
1330
1331 static void
1332 gtk_file_selection_delete_file_confirmed (GtkWidget *widget,
1333                                           gpointer   data)
1334 {
1335   GtkFileSelection *fs = data;
1336   CompletionState *cmpl_state;
1337   gchar *path;
1338   gchar *full_path;
1339   gchar *sys_full_path;
1340   GError *error = NULL;
1341   gchar *buf;
1342   
1343   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1344
1345   cmpl_state = (CompletionState*) fs->cmpl_state;
1346   path = cmpl_reference_position (cmpl_state);
1347   
1348   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1349   sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1350   if (error)
1351     {
1352       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1353         buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
1354                                fs->fileop_file);
1355       else
1356         buf = g_strdup_printf (_("Error deleting file \"%s\": %s\n%s"),
1357                                fs->fileop_file, error->message,
1358                                _("It probably contains symbols not allowed in filenames."));
1359       
1360       gtk_file_selection_fileop_error (fs, buf);
1361       g_error_free (error);
1362       goto out;
1363     }
1364
1365   if (unlink (sys_full_path) < 0) 
1366     {
1367       buf = g_strdup_printf (_("Error deleting file \"%s\": %s"),
1368                              fs->fileop_file, g_strerror (errno));
1369       gtk_file_selection_fileop_error (fs, buf);
1370     }
1371   
1372  out:
1373   g_free (full_path);
1374   g_free (sys_full_path);
1375   
1376   gtk_widget_destroy (fs->fileop_dialog);
1377   gtk_file_selection_populate (fs, "", FALSE);
1378 }
1379
1380 static void
1381 gtk_file_selection_delete_file (GtkWidget *widget,
1382                                 gpointer   data)
1383 {
1384   GtkFileSelection *fs = data;
1385   GtkWidget *label;
1386   GtkWidget *vbox;
1387   GtkWidget *button;
1388   GtkWidget *dialog;
1389   const gchar *filename;
1390   gchar *buf;
1391   
1392   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1393
1394   if (fs->fileop_dialog)
1395           return;
1396
1397 #ifdef G_WITH_CYGWIN
1398   translate_win32_path (fs);
1399 #endif
1400
1401   filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1402   if (strlen (filename) < 1)
1403     return;
1404
1405   g_free (fs->fileop_file);
1406   fs->fileop_file = g_strdup (filename);
1407   
1408   /* main dialog */
1409   fs->fileop_dialog = dialog = gtk_dialog_new ();
1410   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1411                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
1412                       (gpointer) fs);
1413   gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
1414   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1415
1416   /* If file dialog is grabbed, grab option dialog */
1417   /* When option dialog is closed, file dialog will be grabbed again */
1418   if (GTK_WINDOW (fs)->modal)
1419       gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1420   
1421   vbox = gtk_vbox_new (FALSE, 0);
1422   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1423   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1424                      FALSE, FALSE, 0);
1425   gtk_widget_show (vbox);
1426
1427   buf = g_strconcat ("Really delete file \"", filename, "\" ?", NULL);
1428   label = gtk_label_new (buf);
1429   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1430   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1431   gtk_widget_show (label);
1432   g_free (buf);
1433   
1434   /* buttons */
1435   button = gtk_button_new_with_label (_("Delete"));
1436   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1437                       (GtkSignalFunc) gtk_file_selection_delete_file_confirmed, 
1438                       (gpointer) fs);
1439   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1440                       button, TRUE, TRUE, 0);
1441   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1442   gtk_widget_show (button);
1443   
1444   button = gtk_button_new_with_label (_("Cancel"));
1445   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1446                              (GtkSignalFunc) gtk_widget_destroy, 
1447                              (gpointer) dialog);
1448   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1449                       button, TRUE, TRUE, 0);
1450   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1451   gtk_widget_grab_default (button);
1452   gtk_widget_show (button);
1453
1454   gtk_widget_show (dialog);
1455
1456 }
1457
1458 static void
1459 gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
1460                                           gpointer   data)
1461 {
1462   GtkFileSelection *fs = data;
1463   gchar *buf;
1464   const gchar *file;
1465   gchar *path;
1466   gchar *new_filename;
1467   gchar *old_filename;
1468   gchar *sys_new_filename;
1469   gchar *sys_old_filename;
1470   CompletionState *cmpl_state;
1471   GError *error = NULL;
1472   
1473   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1474
1475   file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1476   cmpl_state = (CompletionState*) fs->cmpl_state;
1477   path = cmpl_reference_position (cmpl_state);
1478   
1479   new_filename = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
1480   old_filename = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1481
1482   sys_new_filename = g_filename_from_utf8 (new_filename, -1, NULL, NULL, &error);
1483   if (error)
1484     {
1485       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1486         buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), new_filename);
1487       else
1488         buf = g_strdup_printf (_("Error renaming file to \"%s\": %s\n%s"),
1489                                new_filename, error->message,
1490                                _("You probably used symbols not allowed in filenames."));
1491       gtk_file_selection_fileop_error (fs, buf);
1492       g_error_free (error);
1493       goto out1;
1494     }
1495
1496   sys_old_filename = g_filename_from_utf8 (old_filename, -1, NULL, NULL, &error);
1497   if (error)
1498     {
1499       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1500         buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), old_filename);
1501       else
1502         buf = g_strdup_printf (_("Error renaming file \"%s\": %s\n%s"),
1503                                old_filename, error->message,
1504                                _("It probably contains symbols not allowed in filenames."));
1505       gtk_file_selection_fileop_error (fs, buf);
1506       g_error_free (error);
1507       goto out2;
1508     }
1509   
1510   if (rename (sys_old_filename, sys_new_filename) < 0) 
1511     {
1512       buf = g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"),
1513                              sys_old_filename, sys_new_filename,
1514                              g_strerror (errno));
1515       gtk_file_selection_fileop_error (fs, buf);
1516     }
1517   
1518  out2:
1519   g_free (sys_old_filename);
1520
1521  out1:
1522   g_free (new_filename);
1523   g_free (old_filename);
1524   g_free (sys_new_filename);
1525   
1526   gtk_widget_destroy (fs->fileop_dialog);
1527   gtk_file_selection_populate (fs, "", FALSE);
1528 }
1529   
1530 static void
1531 gtk_file_selection_rename_file (GtkWidget *widget,
1532                                 gpointer   data)
1533 {
1534   GtkFileSelection *fs = data;
1535   GtkWidget *label;
1536   GtkWidget *dialog;
1537   GtkWidget *vbox;
1538   GtkWidget *button;
1539   gchar *buf;
1540   
1541   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1542
1543   if (fs->fileop_dialog)
1544           return;
1545
1546   g_free (fs->fileop_file);
1547   fs->fileop_file = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1548   if (strlen (fs->fileop_file) < 1)
1549     return;
1550   
1551   /* main dialog */
1552   fs->fileop_dialog = dialog = gtk_dialog_new ();
1553   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1554                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
1555                       (gpointer) fs);
1556   gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
1557   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1558
1559   /* If file dialog is grabbed, grab option dialog */
1560   /* When option dialog  closed, file dialog will be grabbed again */
1561   if (GTK_WINDOW (fs)->modal)
1562     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1563   
1564   vbox = gtk_vbox_new (FALSE, 0);
1565   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1566   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1567                       FALSE, FALSE, 0);
1568   gtk_widget_show(vbox);
1569   
1570   buf = g_strconcat ("Rename file \"", fs->fileop_file, "\" to:", NULL);
1571   label = gtk_label_new(buf);
1572   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1573   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1574   gtk_widget_show (label);
1575   g_free (buf);
1576
1577   /* New filename entry */
1578   fs->fileop_entry = gtk_entry_new ();
1579   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
1580                       TRUE, TRUE, 5);
1581   GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1582   gtk_widget_show (fs->fileop_entry);
1583   
1584   gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
1585   gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
1586                               0, strlen (fs->fileop_file));
1587
1588   /* buttons */
1589   button = gtk_button_new_with_label (_("Rename"));
1590   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1591                       (GtkSignalFunc) gtk_file_selection_rename_file_confirmed, 
1592                       (gpointer) fs);
1593   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1594                       button, TRUE, TRUE, 0);
1595   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1596   gtk_widget_show (button);
1597   
1598   button = gtk_button_new_with_label (_("Cancel"));
1599   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1600                              (GtkSignalFunc) gtk_widget_destroy, 
1601                              (gpointer) dialog);
1602   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1603                       button, TRUE, TRUE, 0);
1604   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1605   gtk_widget_grab_default (button);
1606   gtk_widget_show (button);
1607
1608   gtk_widget_show (dialog);
1609 }
1610
1611 static gint
1612 gtk_file_selection_insert_text (GtkWidget   *widget,
1613                                 const gchar *new_text,
1614                                 gint         new_text_length,
1615                                 gint        *position,
1616                                 gpointer     user_data)
1617 {
1618   gchar *filename;
1619
1620   filename = g_filename_from_utf8 (new_text, new_text_length, NULL, NULL, NULL);
1621
1622   if (!filename)
1623     {
1624       gdk_beep ();
1625       gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "insert_text");
1626       return FALSE;
1627     }
1628   
1629   g_free (filename);
1630   
1631   return TRUE;
1632 }
1633
1634 static gint
1635 gtk_file_selection_key_press (GtkWidget   *widget,
1636                               GdkEventKey *event,
1637                               gpointer     user_data)
1638 {
1639   GtkFileSelection *fs;
1640   char *text;
1641
1642   g_return_val_if_fail (widget != NULL, FALSE);
1643   g_return_val_if_fail (event != NULL, FALSE);
1644
1645   if (event->keyval == GDK_Tab ||
1646       event->keyval == GDK_ISO_Left_Tab ||
1647       event->keyval == GDK_KP_Tab)
1648     {
1649       fs = GTK_FILE_SELECTION (user_data);
1650 #ifdef G_WITH_CYGWIN
1651       translate_win32_path (fs);
1652 #endif
1653       text = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1654
1655       gtk_file_selection_populate (fs, text, TRUE);
1656
1657       g_free (text);
1658
1659       return TRUE;
1660     }
1661
1662   return FALSE;
1663 }
1664
1665
1666 static void
1667 gtk_file_selection_history_callback (GtkWidget *widget,
1668                                      gpointer   data)
1669 {
1670   GtkFileSelection *fs = data;
1671   HistoryCallbackArg *callback_arg;
1672   GList *list;
1673
1674   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1675
1676   list = fs->history_list;
1677   
1678   while (list) {
1679     callback_arg = list->data;
1680     
1681     if (callback_arg->menu_item == widget)
1682       {
1683         gtk_file_selection_populate (fs, callback_arg->directory, FALSE);
1684         break;
1685       }
1686     
1687     list = list->next;
1688   }
1689 }
1690
1691 static void 
1692 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
1693                                         gchar            *current_directory)
1694 {
1695   HistoryCallbackArg *callback_arg;
1696   GtkWidget *menu_item;
1697   GList *list;
1698   gchar *current_dir;
1699   gint dir_len;
1700   gint i;
1701   
1702   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1703   g_return_if_fail (current_directory != NULL);
1704   
1705   list = fs->history_list;
1706
1707   if (fs->history_menu) 
1708     {
1709       while (list) {
1710         callback_arg = list->data;
1711         g_free (callback_arg->directory);
1712         g_free (callback_arg);
1713         list = list->next;
1714       }
1715       g_list_free (fs->history_list);
1716       fs->history_list = NULL;
1717       
1718       gtk_widget_destroy (fs->history_menu);
1719     }
1720   
1721   fs->history_menu = gtk_menu_new ();
1722
1723   current_dir = g_strdup (current_directory);
1724
1725   dir_len = strlen (current_dir);
1726
1727   for (i = dir_len; i >= 0; i--)
1728     {
1729       /* the i == dir_len is to catch the full path for the first 
1730        * entry. */
1731       if ( (current_dir[i] == G_DIR_SEPARATOR) || (i == dir_len))
1732         {
1733           /* another small hack to catch the full path */
1734           if (i != dir_len) 
1735                   current_dir[i + 1] = '\0';
1736 #ifdef G_WITH_CYGWIN
1737           if (!strcmp (current_dir, "//"))
1738             continue;
1739 #endif
1740           menu_item = gtk_menu_item_new_with_label (current_dir);
1741           
1742           callback_arg = g_new (HistoryCallbackArg, 1);
1743           callback_arg->menu_item = menu_item;
1744           
1745           /* since the autocompletion gets confused if you don't 
1746            * supply a trailing '/' on a dir entry, set the full
1747            * (current) path to "" which just refreshes the filesel */
1748           if (dir_len == i)
1749             {
1750               callback_arg->directory = g_strdup ("");
1751             }
1752           else
1753             {
1754               callback_arg->directory = g_strdup (current_dir);
1755             }
1756           
1757           fs->history_list = g_list_append (fs->history_list, callback_arg);
1758           
1759           gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
1760                               (GtkSignalFunc) gtk_file_selection_history_callback,
1761                               (gpointer) fs);
1762           gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item);
1763           gtk_widget_show (menu_item);
1764         }
1765     }
1766
1767   gtk_option_menu_set_menu (GTK_OPTION_MENU (fs->history_pulldown), 
1768                             fs->history_menu);
1769   g_free (current_dir);
1770 }
1771
1772 static void
1773 gtk_file_selection_file_button (GtkWidget      *widget,
1774                                 gint            row, 
1775                                 gint            column, 
1776                                 GdkEventButton *bevent,
1777                                 gpointer        user_data)
1778 {
1779   GtkFileSelection *fs = NULL;
1780   gchar *filename, *temp = NULL;
1781   
1782   g_return_if_fail (GTK_IS_CLIST (widget));
1783
1784   fs = user_data;
1785   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1786   
1787   gtk_clist_get_text (GTK_CLIST (fs->file_list), row, 0, &temp);
1788   filename = g_strdup (temp);
1789
1790 #ifdef G_WITH_CYGWIN
1791   /* Check to see if the selection was a drive selector */
1792   if (isalpha (filename[0]) && (filename[1] == ':'))
1793     {
1794       /* It is... map it to a CYGWIN32 drive */
1795       gchar *temp_filename = g_strdup_printf ("//%c/", tolower (filename[0]));
1796       g_free(filename);
1797       filename = temp_filename;
1798     }
1799 #endif /* G_WITH_CYGWIN */
1800
1801   if (filename)
1802     {
1803       if (bevent)
1804         switch (bevent->type)
1805           {
1806           case GDK_2BUTTON_PRESS:
1807             gtk_button_clicked (GTK_BUTTON (fs->ok_button));
1808             break;
1809             
1810           default:
1811             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1812             break;
1813           }
1814       else
1815         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1816
1817       g_free (filename);
1818     }
1819 }
1820
1821 static void
1822 gtk_file_selection_dir_button (GtkWidget      *widget,
1823                                gint            row, 
1824                                gint            column, 
1825                                GdkEventButton *bevent,
1826                                gpointer        user_data)
1827 {
1828   GtkFileSelection *fs = NULL;
1829   gchar *filename, *temp = NULL;
1830
1831   g_return_if_fail (GTK_IS_CLIST (widget));
1832
1833   fs = GTK_FILE_SELECTION (user_data);
1834   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1835
1836   gtk_clist_get_text (GTK_CLIST (fs->dir_list), row, 0, &temp);
1837   filename = g_strdup (temp);
1838
1839   if (filename)
1840     {
1841       if (bevent)
1842         switch (bevent->type)
1843           {
1844           case GDK_2BUTTON_PRESS:
1845             gtk_file_selection_populate (fs, filename, FALSE);
1846             break;
1847           
1848           default:
1849             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1850             break;
1851           }
1852       else
1853         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1854
1855       g_free (filename);
1856     }
1857 }
1858
1859 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
1860
1861 static void
1862 win32_gtk_add_drives_to_dir_list (GtkWidget *the_dir_list)
1863 {
1864   gchar *text[2], *textPtr;
1865   gchar buffer[128];
1866   char volumeNameBuf[128];
1867   char formatBuffer[128];
1868
1869   text[1] = NULL;
1870
1871   /* Get the Drives string */
1872   GetLogicalDriveStrings (sizeof (buffer), buffer);
1873
1874   /* Add the drives as necessary */
1875   textPtr = buffer;
1876   while (*textPtr != '\0') {
1877     /* Get the volume information for this drive */
1878     if ((tolower (textPtr[0]) != 'a') && (tolower (textPtr[0]) != 'b'))
1879       {
1880         /* Ignore floppies (?) */
1881         DWORD maxComponentLength, flags;
1882
1883         GetVolumeInformation (textPtr,
1884                               volumeNameBuf, sizeof(volumeNameBuf),
1885                               NULL, &maxComponentLength,
1886                               &flags, NULL, 0);
1887         /* Build the actual displayable string */
1888
1889         sprintf (formatBuffer, "%c:\\", toupper(textPtr[0]));
1890 #if 0 /* HB: removed to allow drive change AND directory update with one click */
1891         if (strlen (volumeNameBuf) > 0)
1892           sprintf (formatBuffer, "%s (%s)", formatBuffer, volumeNameBuf);
1893 #endif
1894         /* Add to the list */
1895         text[0] = formatBuffer;
1896         gtk_clist_append (GTK_CLIST (the_dir_list), text);
1897       }
1898     textPtr += (strlen (textPtr) + 1);
1899   }
1900 }
1901 #endif
1902
1903 static void
1904 gtk_file_selection_populate (GtkFileSelection *fs,
1905                              gchar            *rel_path,
1906                              gint              try_complete)
1907 {
1908   CompletionState *cmpl_state;
1909   PossibleCompletion* poss;
1910   gchar* filename;
1911   gchar* rem_path = rel_path;
1912   gchar* sel_text;
1913   gchar* text[2];
1914   gint did_recurse = FALSE;
1915   gint possible_count = 0;
1916   gint selection_index = -1;
1917   
1918   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1919   
1920   cmpl_state = (CompletionState*) fs->cmpl_state;
1921   poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
1922
1923   if (!cmpl_state_okay (cmpl_state))
1924     {
1925       /* Something went wrong. */
1926       gtk_file_selection_abort (fs);
1927       return;
1928     }
1929
1930   g_assert (cmpl_state->reference_dir);
1931
1932   gtk_clist_freeze (GTK_CLIST (fs->dir_list));
1933   gtk_clist_clear (GTK_CLIST (fs->dir_list));
1934   gtk_clist_freeze (GTK_CLIST (fs->file_list));
1935   gtk_clist_clear (GTK_CLIST (fs->file_list));
1936
1937   /* Set the dir_list to include ./ and ../ */
1938   text[1] = NULL;
1939   text[0] = "." G_DIR_SEPARATOR_S;
1940   gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1941
1942   text[0] = ".." G_DIR_SEPARATOR_S;
1943   gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1944
1945   while (poss)
1946     {
1947       if (cmpl_is_a_completion (poss))
1948         {
1949           possible_count += 1;
1950
1951           filename = cmpl_this_completion (poss);
1952
1953           text[0] = filename;
1954           
1955           if (cmpl_is_directory (poss))
1956             {
1957               if (strcmp (filename, "." G_DIR_SEPARATOR_S) != 0 &&
1958                   strcmp (filename, ".." G_DIR_SEPARATOR_S) != 0)
1959                 gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1960             }
1961           else
1962             {
1963               gtk_clist_append (GTK_CLIST (fs->file_list), text);
1964             }
1965         }
1966
1967       poss = cmpl_next_completion (cmpl_state);
1968     }
1969
1970 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
1971   /* For Windows, add drives as potential selections */
1972   win32_gtk_add_drives_to_dir_list (fs->dir_list);
1973 #endif
1974
1975   gtk_clist_thaw (GTK_CLIST (fs->dir_list));
1976   gtk_clist_thaw (GTK_CLIST (fs->file_list));
1977
1978   /* File lists are set. */
1979
1980   g_assert (cmpl_state->reference_dir);
1981
1982   if (try_complete)
1983     {
1984
1985       /* User is trying to complete filenames, so advance the user's input
1986        * string to the updated_text, which is the common leading substring
1987        * of all possible completions, and if its a directory attempt
1988        * attempt completions in it. */
1989
1990       if (cmpl_updated_text (cmpl_state)[0])
1991         {
1992
1993           if (cmpl_updated_dir (cmpl_state))
1994             {
1995               gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
1996
1997               did_recurse = TRUE;
1998
1999               gtk_file_selection_populate (fs, dir_name, TRUE);
2000
2001               g_free (dir_name);
2002             }
2003           else
2004             {
2005               if (fs->selection_entry)
2006                       gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
2007                                           cmpl_updated_text (cmpl_state));
2008             }
2009         }
2010       else
2011         {
2012           selection_index = cmpl_last_valid_char (cmpl_state) -
2013                             (strlen (rel_path) - strlen (rem_path));
2014           if (fs->selection_entry)
2015             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
2016         }
2017     }
2018   else
2019     {
2020       if (fs->selection_entry)
2021         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2022     }
2023
2024   if (!did_recurse)
2025     {
2026       if (fs->selection_entry)
2027         gtk_entry_set_position (GTK_ENTRY (fs->selection_entry), selection_index);
2028
2029       if (fs->selection_entry)
2030         {
2031           sel_text = g_strconcat (_("Selection: "),
2032                                   cmpl_reference_position (cmpl_state),
2033                                   NULL);
2034
2035           gtk_label_set_text (GTK_LABEL (fs->selection_text), sel_text);
2036           g_free (sel_text);
2037         }
2038
2039       if (fs->history_pulldown) 
2040         {
2041           gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
2042         }
2043       
2044     }
2045 }
2046
2047 static void
2048 gtk_file_selection_abort (GtkFileSelection *fs)
2049 {
2050   gchar err_buf[256];
2051
2052   sprintf (err_buf, _("Directory unreadable: %s"), cmpl_strerror (cmpl_errno));
2053
2054   /*  BEEP gdk_beep();  */
2055
2056   if (fs->selection_entry)
2057     gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
2058 }
2059
2060 /**********************************************************************/
2061 /*                        External Interface                          */
2062 /**********************************************************************/
2063
2064 /* The four completion state selectors
2065  */
2066 static gchar*
2067 cmpl_updated_text (CompletionState *cmpl_state)
2068 {
2069   return cmpl_state->updated_text;
2070 }
2071
2072 static gboolean
2073 cmpl_updated_dir (CompletionState *cmpl_state)
2074 {
2075   return cmpl_state->re_complete;
2076 }
2077
2078 static gchar*
2079 cmpl_reference_position (CompletionState *cmpl_state)
2080 {
2081   return cmpl_state->reference_dir->fullname;
2082 }
2083
2084 static gint
2085 cmpl_last_valid_char (CompletionState *cmpl_state)
2086 {
2087   return cmpl_state->last_valid_char;
2088 }
2089
2090 static gchar*
2091 cmpl_completion_fullname (const gchar     *text,
2092                           CompletionState *cmpl_state)
2093 {
2094   static char nothing[2] = "";
2095
2096   if (!cmpl_state_okay (cmpl_state))
2097     {
2098       return nothing;
2099     }
2100   else if (g_path_is_absolute (text))
2101     {
2102       strcpy (cmpl_state->updated_text, text);
2103     }
2104 #ifdef HAVE_PWD_H
2105   else if (text[0] == '~')
2106     {
2107       CompletionDir* dir;
2108       char* slash;
2109
2110       dir = open_user_dir (text, cmpl_state);
2111
2112       if (!dir)
2113         {
2114           /* spencer says just return ~something, so
2115            * for now just do it. */
2116           strcpy (cmpl_state->updated_text, text);
2117         }
2118       else
2119         {
2120
2121           strcpy (cmpl_state->updated_text, dir->fullname);
2122
2123           slash = strchr (text, G_DIR_SEPARATOR);
2124
2125           if (slash)
2126             strcat (cmpl_state->updated_text, slash);
2127         }
2128     }
2129 #endif
2130   else
2131     {
2132       strcpy (cmpl_state->updated_text, cmpl_state->reference_dir->fullname);
2133       if (cmpl_state->updated_text[strlen (cmpl_state->updated_text) - 1] != G_DIR_SEPARATOR)
2134         strcat (cmpl_state->updated_text, G_DIR_SEPARATOR_S);
2135       strcat (cmpl_state->updated_text, text);
2136     }
2137
2138   return cmpl_state->updated_text;
2139 }
2140
2141 /* The three completion selectors
2142  */
2143 static gchar*
2144 cmpl_this_completion (PossibleCompletion* pc)
2145 {
2146   return pc->text;
2147 }
2148
2149 static gboolean
2150 cmpl_is_directory (PossibleCompletion* pc)
2151 {
2152   return pc->is_directory;
2153 }
2154
2155 static gint
2156 cmpl_is_a_completion (PossibleCompletion* pc)
2157 {
2158   return pc->is_a_completion;
2159 }
2160
2161 /**********************************************************************/
2162 /*                       Construction, deletion                       */
2163 /**********************************************************************/
2164
2165 static CompletionState*
2166 cmpl_init_state (void)
2167 {
2168   gchar *sys_getcwd_buf;
2169   gchar *utf8_cwd;
2170   CompletionState *new_state;
2171
2172   new_state = g_new (CompletionState, 1);
2173
2174   /* g_get_current_dir() returns a string in the "system" charset */
2175   sys_getcwd_buf = g_get_current_dir ();
2176   utf8_cwd = g_filename_to_utf8 (sys_getcwd_buf, -1, NULL, NULL, NULL);
2177   g_free (sys_getcwd_buf);
2178
2179 tryagain:
2180
2181   new_state->reference_dir = NULL;
2182   new_state->completion_dir = NULL;
2183   new_state->active_completion_dir = NULL;
2184   new_state->directory_storage = NULL;
2185   new_state->directory_sent_storage = NULL;
2186   new_state->last_valid_char = 0;
2187   new_state->updated_text = g_new (gchar, MAXPATHLEN);
2188   new_state->updated_text_alloc = MAXPATHLEN;
2189   new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
2190   new_state->the_completion.text_alloc = MAXPATHLEN;
2191   new_state->user_dir_name_buffer = NULL;
2192   new_state->user_directories = NULL;
2193
2194   new_state->reference_dir = open_dir (utf8_cwd, new_state);
2195
2196   if (!new_state->reference_dir)
2197     {
2198       /* Directories changing from underneath us, grumble */
2199       strcpy (utf8_cwd, G_DIR_SEPARATOR_S);
2200       goto tryagain;
2201     }
2202
2203   g_free (utf8_cwd);
2204   return new_state;
2205 }
2206
2207 static void
2208 cmpl_free_dir_list (GList* dp0)
2209 {
2210   GList *dp = dp0;
2211
2212   while (dp)
2213     {
2214       free_dir (dp->data);
2215       dp = dp->next;
2216     }
2217
2218   g_list_free (dp0);
2219 }
2220
2221 static void
2222 cmpl_free_dir_sent_list (GList* dp0)
2223 {
2224   GList *dp = dp0;
2225
2226   while (dp)
2227     {
2228       free_dir_sent (dp->data);
2229       dp = dp->next;
2230     }
2231
2232   g_list_free (dp0);
2233 }
2234
2235 static void
2236 cmpl_free_state (CompletionState* cmpl_state)
2237 {
2238   g_return_if_fail (cmpl_state != NULL);
2239
2240   cmpl_free_dir_list (cmpl_state->directory_storage);
2241   cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
2242
2243   if (cmpl_state->user_dir_name_buffer)
2244     g_free (cmpl_state->user_dir_name_buffer);
2245   if (cmpl_state->user_directories)
2246     g_free (cmpl_state->user_directories);
2247   if (cmpl_state->the_completion.text)
2248     g_free (cmpl_state->the_completion.text);
2249   if (cmpl_state->updated_text)
2250     g_free (cmpl_state->updated_text);
2251
2252   g_free (cmpl_state);
2253 }
2254
2255 static void
2256 free_dir (CompletionDir* dir)
2257 {
2258   g_free (dir->fullname);
2259   g_free (dir);
2260 }
2261
2262 static void
2263 free_dir_sent (CompletionDirSent* sent)
2264 {
2265   gint i;
2266   for (i = 0; i < sent->entry_count; i++)
2267     g_free (sent->entries[i].entry_name);
2268   g_free (sent->entries);
2269   g_free (sent);
2270 }
2271
2272 static void
2273 prune_memory_usage (CompletionState *cmpl_state)
2274 {
2275   GList* cdsl = cmpl_state->directory_sent_storage;
2276   GList* cdl = cmpl_state->directory_storage;
2277   GList* cdl0 = cdl;
2278   gint len = 0;
2279
2280   for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
2281     cdsl = cdsl->next;
2282
2283   if (cdsl)
2284     {
2285       cmpl_free_dir_sent_list (cdsl->next);
2286       cdsl->next = NULL;
2287     }
2288
2289   cmpl_state->directory_storage = NULL;
2290   while (cdl)
2291     {
2292       if (cdl->data == cmpl_state->reference_dir)
2293         cmpl_state->directory_storage = g_list_prepend (NULL, cdl->data);
2294       else
2295         free_dir (cdl->data);
2296       cdl = cdl->next;
2297     }
2298
2299   g_list_free (cdl0);
2300 }
2301
2302 /**********************************************************************/
2303 /*                        The main entrances.                         */
2304 /**********************************************************************/
2305
2306 static PossibleCompletion*
2307 cmpl_completion_matches (gchar           *text_to_complete,
2308                          gchar          **remaining_text,
2309                          CompletionState *cmpl_state)
2310 {
2311   gchar* first_slash;
2312   PossibleCompletion *poss;
2313
2314   prune_memory_usage (cmpl_state);
2315
2316   g_assert (text_to_complete != NULL);
2317
2318   cmpl_state->user_completion_index = -1;
2319   cmpl_state->last_completion_text = text_to_complete;
2320   cmpl_state->the_completion.text[0] = 0;
2321   cmpl_state->last_valid_char = 0;
2322   cmpl_state->updated_text_len = -1;
2323   cmpl_state->updated_text[0] = 0;
2324   cmpl_state->re_complete = FALSE;
2325
2326 #ifdef HAVE_PWD_H
2327   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2328
2329   if (text_to_complete[0] == '~' && !first_slash)
2330     {
2331       /* Text starts with ~ and there is no slash, show all the
2332        * home directory completions.
2333        */
2334       poss = attempt_homedir_completion (text_to_complete, cmpl_state);
2335
2336       update_cmpl (poss, cmpl_state);
2337
2338       return poss;
2339     }
2340 #endif
2341   cmpl_state->reference_dir =
2342     open_ref_dir (text_to_complete, remaining_text, cmpl_state);
2343
2344   if (!cmpl_state->reference_dir)
2345     return NULL;
2346
2347   cmpl_state->completion_dir =
2348     find_completion_dir (*remaining_text, remaining_text, cmpl_state);
2349
2350   cmpl_state->last_valid_char = *remaining_text - text_to_complete;
2351
2352   if (!cmpl_state->completion_dir)
2353     return NULL;
2354
2355   cmpl_state->completion_dir->cmpl_index = -1;
2356   cmpl_state->completion_dir->cmpl_parent = NULL;
2357   cmpl_state->completion_dir->cmpl_text = *remaining_text;
2358
2359   cmpl_state->active_completion_dir = cmpl_state->completion_dir;
2360
2361   cmpl_state->reference_dir = cmpl_state->completion_dir;
2362
2363   poss = attempt_file_completion (cmpl_state);
2364
2365   update_cmpl (poss, cmpl_state);
2366
2367   return poss;
2368 }
2369
2370 static PossibleCompletion*
2371 cmpl_next_completion (CompletionState* cmpl_state)
2372 {
2373   PossibleCompletion* poss = NULL;
2374
2375   cmpl_state->the_completion.text[0] = 0;
2376
2377 #ifdef HAVE_PWD_H
2378   if (cmpl_state->user_completion_index >= 0)
2379     poss = attempt_homedir_completion (cmpl_state->last_completion_text, cmpl_state);
2380   else
2381     poss = attempt_file_completion (cmpl_state);
2382 #else
2383   poss = attempt_file_completion (cmpl_state);
2384 #endif
2385
2386   update_cmpl (poss, cmpl_state);
2387
2388   return poss;
2389 }
2390
2391 /**********************************************************************/
2392 /*                       Directory Operations                         */
2393 /**********************************************************************/
2394
2395 /* Open the directory where completion will begin from, if possible. */
2396 static CompletionDir*
2397 open_ref_dir (gchar           *text_to_complete,
2398               gchar          **remaining_text,
2399               CompletionState *cmpl_state)
2400 {
2401   gchar* first_slash;
2402   CompletionDir *new_dir;
2403
2404   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2405
2406 #ifdef G_WITH_CYGWIN
2407   if (text_to_complete[0] == '/' && text_to_complete[1] == '/')
2408     {
2409       char root_dir[5];
2410       sprintf (root_dir, "//%c", text_to_complete[2]);
2411
2412       new_dir = open_dir (root_dir, cmpl_state);
2413
2414       if (new_dir) {
2415         *remaining_text = text_to_complete + 4;
2416       }
2417     }
2418 #else
2419   if (FALSE)
2420     ;
2421 #endif
2422 #ifdef HAVE_PWD_H
2423   else if (text_to_complete[0] == '~')
2424     {
2425       new_dir = open_user_dir (text_to_complete, cmpl_state);
2426
2427       if (new_dir)
2428         {
2429           if (first_slash)
2430             *remaining_text = first_slash + 1;
2431           else
2432             *remaining_text = text_to_complete + strlen (text_to_complete);
2433         }
2434       else
2435         {
2436           return NULL;
2437         }
2438     }
2439 #endif
2440   else if (g_path_is_absolute (text_to_complete) || !cmpl_state->reference_dir)
2441     {
2442       gchar *tmp = g_strdup (text_to_complete);
2443       gchar *p;
2444
2445       p = tmp;
2446       while (*p && *p != '*' && *p != '?')
2447         p++;
2448
2449       *p = '\0';
2450       p = strrchr (tmp, G_DIR_SEPARATOR);
2451       if (p)
2452         {
2453           if (p == tmp)
2454             p++;
2455       
2456           *p = '\0';
2457
2458           new_dir = open_dir (tmp, cmpl_state);
2459
2460           if (new_dir)
2461             *remaining_text = text_to_complete + 
2462               ((p == tmp + 1) ? (p - tmp) : (p + 1 - tmp));
2463         }
2464       else
2465         {
2466           /* If no possible candidates, use the cwd */
2467           gchar *sys_curdir = g_get_current_dir ();
2468           gchar *utf8_curdir = g_filename_to_utf8 (sys_curdir, -1, NULL, NULL, NULL);
2469
2470           g_free (sys_curdir);
2471
2472           new_dir = open_dir (utf8_curdir, cmpl_state);
2473
2474           if (new_dir)
2475             *remaining_text = text_to_complete;
2476
2477           g_free (utf8_curdir);
2478         }
2479
2480       g_free (tmp);
2481     }
2482   else
2483     {
2484       *remaining_text = text_to_complete;
2485
2486       new_dir = open_dir (cmpl_state->reference_dir->fullname, cmpl_state);
2487     }
2488
2489   if (new_dir)
2490     {
2491       new_dir->cmpl_index = -1;
2492       new_dir->cmpl_parent = NULL;
2493     }
2494
2495   return new_dir;
2496 }
2497
2498 #ifdef HAVE_PWD_H
2499
2500 /* open a directory by user name */
2501 static CompletionDir*
2502 open_user_dir (const gchar     *text_to_complete,
2503                CompletionState *cmpl_state)
2504 {
2505   CompletionDir *result;
2506   gchar *first_slash;
2507   gint cmp_len;
2508
2509   g_assert (text_to_complete && text_to_complete[0] == '~');
2510
2511   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2512
2513   if (first_slash)
2514     cmp_len = first_slash - text_to_complete - 1;
2515   else
2516     cmp_len = strlen (text_to_complete + 1);
2517
2518   if (!cmp_len)
2519     {
2520       /* ~/ */
2521       const gchar *homedir = g_get_home_dir ();
2522       gchar *utf8_homedir = g_filename_to_utf8 (homedir, -1, NULL, NULL, NULL);
2523
2524       if (utf8_homedir)
2525         result = open_dir (utf8_homedir, cmpl_state);
2526       else
2527         result = NULL;
2528       
2529       g_free (utf8_homedir);
2530     }
2531   else
2532     {
2533       /* ~user/ */
2534       gchar* copy = g_new (char, cmp_len + 1);
2535       gchar *utf8_dir;
2536       struct passwd *pwd;
2537
2538       strncpy (copy, text_to_complete + 1, cmp_len);
2539       copy[cmp_len] = 0;
2540       pwd = getpwnam (copy);
2541       g_free (copy);
2542       if (!pwd)
2543         {
2544           cmpl_errno = errno;
2545           return NULL;
2546         }
2547       utf8_dir = g_filename_to_utf8 (pwd->pw_dir, -1, NULL, NULL, NULL);
2548       result = open_dir (utf8_dir, cmpl_state);
2549       g_free (utf8_dir);
2550     }
2551   return result;
2552 }
2553
2554 #endif
2555
2556 /* open a directory relative the the current relative directory */
2557 static CompletionDir*
2558 open_relative_dir (gchar           *dir_name,
2559                    CompletionDir   *dir,
2560                    CompletionState *cmpl_state)
2561 {
2562   CompletionDir *result;
2563   GString *path;
2564
2565   path = g_string_sized_new (dir->fullname_len + strlen (dir_name) + 10);
2566   g_string_assign (path, dir->fullname);
2567
2568   if (dir->fullname_len > 1
2569       && path->str[dir->fullname_len - 1] != G_DIR_SEPARATOR)
2570     g_string_append_c (path, G_DIR_SEPARATOR);
2571   g_string_append (path, dir_name);
2572
2573   result = open_dir (path->str, cmpl_state);
2574
2575   g_string_free (path, TRUE);
2576
2577   return result;
2578 }
2579
2580 /* after the cache lookup fails, really open a new directory */
2581 static CompletionDirSent*
2582 open_new_dir (gchar       *dir_name,
2583               struct stat *sbuf,
2584               gboolean     stat_subdirs)
2585 {
2586   CompletionDirSent *sent;
2587   DIR *directory;
2588   struct dirent *dirent_ptr;
2589   gint entry_count = 0;
2590   gint n_entries = 0;
2591   gint i;
2592   struct stat ent_sbuf;
2593   GString *path;
2594   gchar *sys_dir_name;
2595
2596   sent = g_new (CompletionDirSent, 1);
2597   sent->mtime = sbuf->st_mtime;
2598   sent->inode = sbuf->st_ino;
2599   sent->device = sbuf->st_dev;
2600
2601   path = g_string_sized_new (2*MAXPATHLEN + 10);
2602
2603   sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
2604   if (!sys_dir_name)
2605     {
2606       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
2607       return NULL;
2608     }
2609   
2610   directory = opendir (sys_dir_name);
2611   if (!directory)
2612     {
2613       cmpl_errno = errno;
2614       g_free (sys_dir_name);
2615       return NULL;
2616     }
2617
2618   while ((dirent_ptr = readdir (directory)) != NULL)
2619     entry_count++;
2620
2621   sent->entries = g_new (CompletionDirEntry, entry_count);
2622   sent->entry_count = entry_count;
2623
2624   rewinddir (directory);
2625
2626   for (i = 0; i < entry_count; i += 1)
2627     {
2628       dirent_ptr = readdir (directory);
2629
2630       if (!dirent_ptr)
2631         {
2632           cmpl_errno = errno;
2633           closedir (directory);
2634           g_free (sys_dir_name);
2635           return NULL;
2636         }
2637
2638       sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent_ptr->d_name, -1, NULL, NULL, NULL);
2639       if (!g_utf8_validate (sent->entries[n_entries].entry_name, -1, NULL))
2640         {
2641           g_warning (_("The filename %s couldn't be converted to UTF-8. Try setting the environment variable G_BROKEN_FILENAMES."), dirent_ptr->d_name);
2642           continue;
2643         }
2644
2645       g_string_assign (path, sys_dir_name);
2646       if (path->str[path->len-1] != G_DIR_SEPARATOR)
2647         {
2648           g_string_append_c (path, G_DIR_SEPARATOR);
2649         }
2650       g_string_append (path, dirent_ptr->d_name);
2651
2652       if (stat_subdirs)
2653         {
2654           /* Here we know path->str is a "system charset" string */
2655           if (stat (path->str, &ent_sbuf) >= 0 && S_ISDIR (ent_sbuf.st_mode))
2656             sent->entries[n_entries].is_dir = TRUE;
2657           else
2658             /* stat may fail, and we don't mind, since it could be a
2659              * dangling symlink. */
2660             sent->entries[n_entries].is_dir = FALSE;
2661         }
2662       else
2663         sent->entries[n_entries].is_dir = 1;
2664
2665       n_entries++;
2666     }
2667   sent->entry_count = n_entries;
2668   
2669   g_free (sys_dir_name);
2670   g_string_free (path, TRUE);
2671   qsort (sent->entries, sent->entry_count, sizeof (CompletionDirEntry), compare_cmpl_dir);
2672
2673   closedir (directory);
2674
2675   return sent;
2676 }
2677
2678 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
2679
2680 static gboolean
2681 check_dir (gchar       *dir_name,
2682            struct stat *result,
2683            gboolean    *stat_subdirs)
2684 {
2685   /* A list of directories that we know only contain other directories.
2686    * Trying to stat every file in these directories would be very
2687    * expensive.
2688    */
2689
2690   static struct {
2691     gchar *name;
2692     gboolean present;
2693     struct stat statbuf;
2694   } no_stat_dirs[] = {
2695     { "/afs", FALSE, { 0 } },
2696     { "/net", FALSE, { 0 } }
2697   };
2698
2699   static const gint n_no_stat_dirs = G_N_ELEMENTS (no_stat_dirs);
2700   static gboolean initialized = FALSE;
2701   gchar *sys_dir_name;
2702   gint i;
2703
2704   if (!initialized)
2705     {
2706       initialized = TRUE;
2707       for (i = 0; i < n_no_stat_dirs; i++)
2708         {
2709           if (stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
2710             no_stat_dirs[i].present = TRUE;
2711         }
2712     }
2713
2714   sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
2715   if (!sys_dir_name)
2716     {
2717       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
2718       return FALSE;
2719     }
2720   
2721   if (stat (sys_dir_name, result) < 0)
2722     {
2723       g_free (sys_dir_name);
2724       cmpl_errno = errno;
2725       return FALSE;
2726     }
2727   g_free (sys_dir_name);
2728
2729   *stat_subdirs = TRUE;
2730   for (i = 0; i < n_no_stat_dirs; i++)
2731     {
2732       if (no_stat_dirs[i].present &&
2733           (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
2734           (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
2735         {
2736           *stat_subdirs = FALSE;
2737           break;
2738         }
2739     }
2740
2741   return TRUE;
2742 }
2743
2744 #endif
2745
2746 /* open a directory by absolute pathname */
2747 static CompletionDir*
2748 open_dir (gchar           *dir_name,
2749           CompletionState *cmpl_state)
2750 {
2751   struct stat sbuf;
2752   gboolean stat_subdirs;
2753   CompletionDirSent *sent;
2754   GList* cdsl;
2755
2756 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
2757   if (!check_dir (dir_name, &sbuf, &stat_subdirs))
2758     return NULL;
2759
2760   cdsl = cmpl_state->directory_sent_storage;
2761
2762   while (cdsl)
2763     {
2764       sent = cdsl->data;
2765
2766       if (sent->inode == sbuf.st_ino &&
2767           sent->mtime == sbuf.st_mtime &&
2768           sent->device == sbuf.st_dev)
2769         return attach_dir (sent, dir_name, cmpl_state);
2770
2771       cdsl = cdsl->next;
2772     }
2773 #else
2774   stat_subdirs = TRUE;
2775 #endif
2776
2777   sent = open_new_dir (dir_name, &sbuf, stat_subdirs);
2778
2779   if (sent)
2780     {
2781       cmpl_state->directory_sent_storage =
2782         g_list_prepend (cmpl_state->directory_sent_storage, sent);
2783
2784       return attach_dir (sent, dir_name, cmpl_state);
2785     }
2786
2787   return NULL;
2788 }
2789
2790 static CompletionDir*
2791 attach_dir (CompletionDirSent *sent,
2792             gchar             *dir_name,
2793             CompletionState   *cmpl_state)
2794 {
2795   CompletionDir* new_dir;
2796
2797   new_dir = g_new (CompletionDir, 1);
2798
2799   cmpl_state->directory_storage =
2800     g_list_prepend (cmpl_state->directory_storage, new_dir);
2801
2802   new_dir->sent = sent;
2803   new_dir->fullname = g_strdup (dir_name);
2804   new_dir->fullname_len = strlen (dir_name);
2805
2806   return new_dir;
2807 }
2808
2809 static gint
2810 correct_dir_fullname (CompletionDir* cmpl_dir)
2811 {
2812   gint length = strlen (cmpl_dir->fullname);
2813   gchar *first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
2814   gchar *sys_filename;
2815   struct stat sbuf;
2816
2817   /* Does it end with /. (\.) ? */
2818   if (length >= 2 &&
2819       strcmp (cmpl_dir->fullname + length - 2, G_DIR_SEPARATOR_S ".") == 0)
2820     {
2821       /* Is it just the root directory (on a drive) ? */
2822       if (cmpl_dir->fullname + length - 2 == first_slash)
2823         {
2824           cmpl_dir->fullname[length - 1] = 0;
2825           cmpl_dir->fullname_len = length - 1;
2826           return TRUE;
2827         }
2828       else
2829         {
2830           cmpl_dir->fullname[length - 2] = 0;
2831         }
2832     }
2833
2834   /* Ends with /./ (\.\)? */
2835   else if (length >= 3 &&
2836            strcmp (cmpl_dir->fullname + length - 3,
2837                    G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) == 0)
2838     cmpl_dir->fullname[length - 2] = 0;
2839
2840   /* Ends with /.. (\..) ? */
2841   else if (length >= 3 &&
2842            strcmp (cmpl_dir->fullname + length - 3,
2843                    G_DIR_SEPARATOR_S "..") == 0)
2844     {
2845       /* Is it just /.. (X:\..)? */
2846       if (cmpl_dir->fullname + length - 3 == first_slash)
2847         {
2848           cmpl_dir->fullname[length - 2] = 0;
2849           cmpl_dir->fullname_len = length - 2;
2850           return TRUE;
2851         }
2852
2853       sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
2854       if (!sys_filename)
2855         {
2856           cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
2857           return FALSE;
2858         }
2859       
2860       if (stat (sys_filename, &sbuf) < 0)
2861         {
2862           g_free (sys_filename);
2863           cmpl_errno = errno;
2864           return FALSE;
2865         }
2866       g_free (sys_filename);
2867
2868       cmpl_dir->fullname[length - 3] = 0;
2869
2870       if (!correct_parent (cmpl_dir, &sbuf))
2871         return FALSE;
2872     }
2873
2874   /* Ends with /../ (\..\)? */
2875   else if (length >= 4 &&
2876            strcmp (cmpl_dir->fullname + length - 4,
2877                    G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) == 0)
2878     {
2879       /* Is it just /../ (X:\..\)? */
2880       if (cmpl_dir->fullname + length - 4 == first_slash)
2881         {
2882           cmpl_dir->fullname[length - 3] = 0;
2883           cmpl_dir->fullname_len = length - 3;
2884           return TRUE;
2885         }
2886
2887       sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
2888       if (!sys_filename)
2889         {
2890           cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
2891           return FALSE;
2892         }
2893       
2894       if (stat (sys_filename, &sbuf) < 0)
2895         {
2896           g_free (sys_filename);
2897           cmpl_errno = errno;
2898           return FALSE;
2899         }
2900       g_free (sys_filename);
2901
2902       cmpl_dir->fullname[length - 4] = 0;
2903
2904       if (!correct_parent (cmpl_dir, &sbuf))
2905         return FALSE;
2906     }
2907
2908   cmpl_dir->fullname_len = strlen (cmpl_dir->fullname);
2909
2910   return TRUE;
2911 }
2912
2913 static gint
2914 correct_parent (CompletionDir *cmpl_dir,
2915                 struct stat   *sbuf)
2916 {
2917   struct stat parbuf;
2918   gchar *last_slash;
2919   gchar *first_slash;
2920   gchar *new_name;
2921   gchar *sys_filename;
2922   gchar c = 0;
2923
2924   last_slash = strrchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
2925   g_assert (last_slash);
2926   first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
2927
2928   /* Clever (?) way to check for top-level directory that works also on
2929    * Win32, where there is a drive letter and colon prefixed...
2930    */
2931   if (last_slash != first_slash)
2932     {
2933       last_slash[0] = 0;
2934     }
2935   else
2936     {
2937       c = last_slash[1];
2938       last_slash[1] = 0;
2939     }
2940
2941   sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
2942   if (!sys_filename)
2943     {
2944       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
2945       if (!c)
2946         last_slash[0] = G_DIR_SEPARATOR;
2947       return FALSE;
2948     }
2949   
2950   if (stat (sys_filename, &parbuf) < 0)
2951     {
2952       g_free (sys_filename);
2953       cmpl_errno = errno;
2954       if (!c)
2955         last_slash[0] = G_DIR_SEPARATOR;
2956       return FALSE;
2957     }
2958   g_free (sys_filename);
2959
2960 #ifndef G_OS_WIN32              /* No inode numbers on Win32 */
2961   if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
2962     /* it wasn't a link */
2963     return TRUE;
2964
2965   if (c)
2966     last_slash[1] = c;
2967   else
2968     last_slash[0] = G_DIR_SEPARATOR;
2969
2970   /* it was a link, have to figure it out the hard way */
2971
2972   new_name = find_parent_dir_fullname (cmpl_dir->fullname);
2973
2974   if (!new_name)
2975     return FALSE;
2976
2977   g_free (cmpl_dir->fullname);
2978
2979   cmpl_dir->fullname = new_name;
2980 #endif
2981
2982   return TRUE;
2983 }
2984
2985 #ifndef G_OS_WIN32
2986
2987 static gchar*
2988 find_parent_dir_fullname (gchar* dirname)
2989 {
2990   gchar *sys_orig_dir;
2991   gchar *result;
2992   gchar *sys_cwd;
2993   gchar *sys_dirname;
2994
2995   sys_orig_dir = g_get_current_dir ();
2996   sys_dirname = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
2997   if (!sys_dirname)
2998     {
2999       g_free (sys_orig_dir);
3000       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3001       return NULL;
3002     }
3003   
3004   if (chdir (sys_dirname) != 0 || chdir ("..") != 0)
3005     {
3006       g_free (sys_dirname);
3007       g_free (sys_orig_dir);
3008       cmpl_errno = errno;
3009       return NULL;
3010     }
3011   g_free (sys_dirname);
3012
3013   sys_cwd = g_get_current_dir ();
3014   result = g_filename_to_utf8 (sys_cwd, -1, NULL, NULL, NULL);
3015   g_free (sys_cwd);
3016
3017   if (chdir (sys_orig_dir) != 0)
3018     {
3019       cmpl_errno = errno;
3020       g_free (sys_orig_dir);
3021       return NULL;
3022     }
3023
3024   g_free (sys_orig_dir);
3025   return result;
3026 }
3027
3028 #endif
3029
3030 /**********************************************************************/
3031 /*                        Completion Operations                       */
3032 /**********************************************************************/
3033
3034 #ifdef HAVE_PWD_H
3035
3036 static PossibleCompletion*
3037 attempt_homedir_completion (gchar           *text_to_complete,
3038                             CompletionState *cmpl_state)
3039 {
3040   gint index, length;
3041
3042   if (!cmpl_state->user_dir_name_buffer &&
3043       !get_pwdb (cmpl_state))
3044     return NULL;
3045   length = strlen (text_to_complete) - 1;
3046
3047   cmpl_state->user_completion_index += 1;
3048
3049   while (cmpl_state->user_completion_index < cmpl_state->user_directories_len)
3050     {
3051       index = first_diff_index (text_to_complete + 1,
3052                                 cmpl_state->user_directories
3053                                 [cmpl_state->user_completion_index].login);
3054
3055       switch (index)
3056         {
3057         case PATTERN_MATCH:
3058           break;
3059         default:
3060           if (cmpl_state->last_valid_char < (index + 1))
3061             cmpl_state->last_valid_char = index + 1;
3062           cmpl_state->user_completion_index += 1;
3063           continue;
3064         }
3065
3066       cmpl_state->the_completion.is_a_completion = 1;
3067       cmpl_state->the_completion.is_directory = TRUE;
3068
3069       append_completion_text ("~", cmpl_state);
3070
3071       append_completion_text (cmpl_state->
3072                               user_directories[cmpl_state->user_completion_index].login,
3073                               cmpl_state);
3074
3075       return append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3076     }
3077
3078   if (text_to_complete[1]
3079       || cmpl_state->user_completion_index > cmpl_state->user_directories_len)
3080     {
3081       cmpl_state->user_completion_index = -1;
3082       return NULL;
3083     }
3084   else
3085     {
3086       cmpl_state->user_completion_index += 1;
3087       cmpl_state->the_completion.is_a_completion = 1;
3088       cmpl_state->the_completion.is_directory = TRUE;
3089
3090       return append_completion_text ("~" G_DIR_SEPARATOR_S, cmpl_state);
3091     }
3092 }
3093
3094 #endif
3095
3096 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
3097 #define FOLD(c) (tolower(c))
3098 #else
3099 #define FOLD(c) (c)
3100 #endif
3101
3102 /* returns the index (>= 0) of the first differing character,
3103  * PATTERN_MATCH if the completion matches */
3104 static gint
3105 first_diff_index (gchar *pat,
3106                   gchar *text)
3107 {
3108   gint diff = 0;
3109
3110   while (*pat && *text && FOLD (*text) == FOLD (*pat))
3111     {
3112       pat += 1;
3113       text += 1;
3114       diff += 1;
3115     }
3116
3117   if (*pat)
3118     return diff;
3119
3120   return PATTERN_MATCH;
3121 }
3122
3123 static PossibleCompletion*
3124 append_completion_text (gchar           *text,
3125                         CompletionState *cmpl_state)
3126 {
3127   gint len, i = 1;
3128
3129   if (!cmpl_state->the_completion.text)
3130     return NULL;
3131
3132   len = strlen (text) + strlen (cmpl_state->the_completion.text) + 1;
3133
3134   if (cmpl_state->the_completion.text_alloc > len)
3135     {
3136       strcat (cmpl_state->the_completion.text, text);
3137       return &cmpl_state->the_completion;
3138     }
3139
3140   while (i < len)
3141     i <<= 1;
3142
3143   cmpl_state->the_completion.text_alloc = i;
3144
3145   cmpl_state->the_completion.text = (gchar*) g_realloc (cmpl_state->the_completion.text, i);
3146
3147   if (!cmpl_state->the_completion.text)
3148     return NULL;
3149   else
3150     {
3151       strcat (cmpl_state->the_completion.text, text);
3152       return &cmpl_state->the_completion;
3153     }
3154 }
3155
3156 static CompletionDir*
3157 find_completion_dir (gchar          *text_to_complete,
3158                     gchar          **remaining_text,
3159                     CompletionState *cmpl_state)
3160 {
3161   gchar* first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
3162   CompletionDir* dir = cmpl_state->reference_dir;
3163   CompletionDir* next;
3164   *remaining_text = text_to_complete;
3165
3166   while (first_slash)
3167     {
3168       gint len = first_slash - *remaining_text;
3169       gint found = 0;
3170       gchar *found_name = NULL;         /* Quiet gcc */
3171       gint i;
3172       gchar* pat_buf = g_new (gchar, len + 1);
3173
3174       strncpy (pat_buf, *remaining_text, len);
3175       pat_buf[len] = 0;
3176
3177       for (i = 0; i < dir->sent->entry_count; i += 1)
3178         {
3179           if (dir->sent->entries[i].is_dir &&
3180              fnmatch (pat_buf, dir->sent->entries[i].entry_name,
3181                       FNMATCH_FLAGS)!= FNM_NOMATCH)
3182             {
3183               if (found)
3184                 {
3185                   g_free (pat_buf);
3186                   return dir;
3187                 }
3188               else
3189                 {
3190                   found = 1;
3191                   found_name = dir->sent->entries[i].entry_name;
3192                 }
3193             }
3194         }
3195
3196       if (!found)
3197         {
3198           /* Perhaps we are trying to open an automount directory */
3199           found_name = pat_buf;
3200         }
3201
3202       next = open_relative_dir (found_name, dir, cmpl_state);
3203       
3204       if (!next)
3205         {
3206           g_free (pat_buf);
3207           return NULL;
3208         }
3209       
3210       next->cmpl_parent = dir;
3211       
3212       dir = next;
3213       
3214       if (!correct_dir_fullname (dir))
3215         {
3216           g_free (pat_buf);
3217           return NULL;
3218         }
3219       
3220       *remaining_text = first_slash + 1;
3221       first_slash = strchr (*remaining_text, G_DIR_SEPARATOR);
3222
3223       g_free (pat_buf);
3224     }
3225
3226   return dir;
3227 }
3228
3229 static void
3230 update_cmpl (PossibleCompletion *poss,
3231              CompletionState    *cmpl_state)
3232 {
3233   gint cmpl_len;
3234
3235   if (!poss || !cmpl_is_a_completion (poss))
3236     return;
3237
3238   cmpl_len = strlen (cmpl_this_completion (poss));
3239
3240   if (cmpl_state->updated_text_alloc < cmpl_len + 1)
3241     {
3242       cmpl_state->updated_text =
3243         (gchar*)g_realloc (cmpl_state->updated_text,
3244                            cmpl_state->updated_text_alloc);
3245       cmpl_state->updated_text_alloc = 2*cmpl_len;
3246     }
3247
3248   if (cmpl_state->updated_text_len < 0)
3249     {
3250       strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3251       cmpl_state->updated_text_len = cmpl_len;
3252       cmpl_state->re_complete = cmpl_is_directory (poss);
3253     }
3254   else if (cmpl_state->updated_text_len == 0)
3255     {
3256       cmpl_state->re_complete = FALSE;
3257     }
3258   else
3259     {
3260       gint first_diff =
3261         first_diff_index (cmpl_state->updated_text,
3262                           cmpl_this_completion (poss));
3263
3264       cmpl_state->re_complete = FALSE;
3265
3266       if (first_diff == PATTERN_MATCH)
3267         return;
3268
3269       if (first_diff > cmpl_state->updated_text_len)
3270         strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3271
3272       cmpl_state->updated_text_len = first_diff;
3273       cmpl_state->updated_text[first_diff] = 0;
3274     }
3275 }
3276
3277 static PossibleCompletion*
3278 attempt_file_completion (CompletionState *cmpl_state)
3279 {
3280   gchar *pat_buf, *first_slash;
3281   CompletionDir *dir = cmpl_state->active_completion_dir;
3282
3283   dir->cmpl_index += 1;
3284
3285   if (dir->cmpl_index == dir->sent->entry_count)
3286     {
3287       if (dir->cmpl_parent == NULL)
3288         {
3289           cmpl_state->active_completion_dir = NULL;
3290
3291           return NULL;
3292         }
3293       else
3294         {
3295           cmpl_state->active_completion_dir = dir->cmpl_parent;
3296
3297           return attempt_file_completion (cmpl_state);
3298         }
3299     }
3300
3301   g_assert (dir->cmpl_text);
3302
3303   first_slash = strchr (dir->cmpl_text, G_DIR_SEPARATOR);
3304
3305   if (first_slash)
3306     {
3307       gint len = first_slash - dir->cmpl_text;
3308
3309       pat_buf = g_new (gchar, len + 1);
3310       strncpy (pat_buf, dir->cmpl_text, len);
3311       pat_buf[len] = 0;
3312     }
3313   else
3314     {
3315       gint len = strlen (dir->cmpl_text);
3316
3317       pat_buf = g_new (gchar, len + 2);
3318       strcpy (pat_buf, dir->cmpl_text);
3319       /* Don't append a * if the user entered one herself.
3320        * This way one can complete *.h and don't get matches
3321        * on any .help files, for instance.
3322        */
3323       if (strchr (pat_buf, '*') == NULL)
3324         strcpy (pat_buf + len, "*");
3325     }
3326
3327   if (first_slash)
3328     {
3329       if (dir->sent->entries[dir->cmpl_index].is_dir)
3330         {
3331           if (fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3332                        FNMATCH_FLAGS) != FNM_NOMATCH)
3333             {
3334               CompletionDir* new_dir;
3335
3336               new_dir = open_relative_dir (dir->sent->entries[dir->cmpl_index].entry_name,
3337                                            dir, cmpl_state);
3338
3339               if (!new_dir)
3340                 {
3341                   g_free (pat_buf);
3342                   return NULL;
3343                 }
3344
3345               new_dir->cmpl_parent = dir;
3346
3347               new_dir->cmpl_index = -1;
3348               new_dir->cmpl_text = first_slash + 1;
3349
3350               cmpl_state->active_completion_dir = new_dir;
3351
3352               g_free (pat_buf);
3353               return attempt_file_completion (cmpl_state);
3354             }
3355           else
3356             {
3357               g_free (pat_buf);
3358               return attempt_file_completion (cmpl_state);
3359             }
3360         }
3361       else
3362         {
3363           g_free (pat_buf);
3364           return attempt_file_completion (cmpl_state);
3365         }
3366     }
3367   else
3368     {
3369       if (dir->cmpl_parent != NULL)
3370         {
3371           append_completion_text (dir->fullname +
3372                                   strlen (cmpl_state->completion_dir->fullname) + 1,
3373                                   cmpl_state);
3374           append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3375         }
3376
3377       append_completion_text (dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
3378
3379       cmpl_state->the_completion.is_a_completion =
3380         fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3381                  FNMATCH_FLAGS) != FNM_NOMATCH;
3382
3383       cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
3384       if (dir->sent->entries[dir->cmpl_index].is_dir)
3385         append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3386
3387       g_free (pat_buf);
3388       return &cmpl_state->the_completion;
3389     }
3390 }
3391
3392 #ifdef HAVE_PWD_H
3393
3394 static gint
3395 get_pwdb (CompletionState* cmpl_state)
3396 {
3397   struct passwd *pwd_ptr;
3398   gchar* buf_ptr;
3399   gchar *utf8;
3400   gint len = 0, i, count = 0;
3401
3402   if (cmpl_state->user_dir_name_buffer)
3403     return TRUE;
3404   setpwent ();
3405
3406   while ((pwd_ptr = getpwent ()) != NULL)
3407     {
3408       utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3409       len += strlen (utf8);
3410       g_free (utf8);
3411       utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3412       len += strlen (utf8);
3413       g_free (utf8);
3414       len += 2;
3415       count += 1;
3416     }
3417
3418   setpwent ();
3419
3420   cmpl_state->user_dir_name_buffer = g_new (gchar, len);
3421   cmpl_state->user_directories = g_new (CompletionUserDir, count);
3422   cmpl_state->user_directories_len = count;
3423
3424   buf_ptr = cmpl_state->user_dir_name_buffer;
3425
3426   for (i = 0; i < count; i += 1)
3427     {
3428       pwd_ptr = getpwent ();
3429       if (!pwd_ptr)
3430         {
3431           cmpl_errno = errno;
3432           goto error;
3433         }
3434
3435       utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3436       strcpy (buf_ptr, utf8);
3437       g_free (utf8);
3438
3439       cmpl_state->user_directories[i].login = buf_ptr;
3440
3441       buf_ptr += strlen (buf_ptr);
3442       buf_ptr += 1;
3443
3444       utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3445       strcpy (buf_ptr, utf8);
3446       g_free (utf8);
3447
3448       cmpl_state->user_directories[i].homedir = buf_ptr;
3449
3450       buf_ptr += strlen (buf_ptr);
3451       buf_ptr += 1;
3452     }
3453
3454   qsort (cmpl_state->user_directories,
3455          cmpl_state->user_directories_len,
3456          sizeof (CompletionUserDir),
3457          compare_user_dir);
3458
3459   endpwent ();
3460
3461   return TRUE;
3462
3463 error:
3464
3465   if (cmpl_state->user_dir_name_buffer)
3466     g_free (cmpl_state->user_dir_name_buffer);
3467   if (cmpl_state->user_directories)
3468     g_free (cmpl_state->user_directories);
3469
3470   cmpl_state->user_dir_name_buffer = NULL;
3471   cmpl_state->user_directories = NULL;
3472
3473   return FALSE;
3474 }
3475
3476 static gint
3477 compare_user_dir (const void *a,
3478                   const void *b)
3479 {
3480   return strcmp ((((CompletionUserDir*)a))->login,
3481                  (((CompletionUserDir*)b))->login);
3482 }
3483
3484 #endif
3485
3486 static gint
3487 compare_cmpl_dir (const void *a,
3488                   const void *b)
3489 {
3490 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3491   return strcmp ((((CompletionDirEntry*)a))->entry_name,
3492                  (((CompletionDirEntry*)b))->entry_name);
3493 #else
3494   return g_strcasecmp ((((CompletionDirEntry*)a))->entry_name,
3495                        (((CompletionDirEntry*)b))->entry_name);
3496 #endif
3497 }
3498
3499 static gint
3500 cmpl_state_okay (CompletionState* cmpl_state)
3501 {
3502   return  cmpl_state && cmpl_state->reference_dir;
3503 }
3504
3505 static const gchar*
3506 cmpl_strerror (gint err)
3507 {
3508   if (err == CMPL_ERRNO_TOO_LONG)
3509     return _("Name too long");
3510   else if (err == CMPL_ERRNO_DID_NOT_CONVERT)
3511     return _("Couldn't convert filename");
3512   else
3513     return g_strerror (err);
3514 }