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