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