]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesel.c
Apply property patch from Lee Mallabone
[~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
576 static void
577 gtk_file_selection_init (GtkFileSelection *filesel)
578 {
579   GtkWidget *entry_vbox;
580   GtkWidget *label;
581   GtkWidget *list_hbox;
582   GtkWidget *confirm_area;
583   GtkWidget *pulldown_hbox;
584   GtkWidget *scrolled_win;
585   GtkDialog *dialog;
586   
587   char *dir_title [2];
588   char *file_title [2];
589
590   dialog = GTK_DIALOG (filesel);
591
592   filesel->cmpl_state = cmpl_init_state ();
593
594   /* The dialog-sized vertical box  */
595   filesel->main_vbox = dialog->vbox;
596   gtk_container_set_border_width (GTK_CONTAINER (filesel), 10);
597
598   /* The horizontal box containing create, rename etc. buttons */
599   filesel->button_area = gtk_hbutton_box_new ();
600   gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel->button_area), GTK_BUTTONBOX_START);
601   gtk_box_set_spacing (GTK_BOX (filesel->button_area), 0);
602   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->button_area, 
603                       FALSE, FALSE, 0);
604   gtk_widget_show (filesel->button_area);
605   
606   gtk_file_selection_show_fileop_buttons (filesel);
607
608   /* hbox for pulldown menu */
609   pulldown_hbox = gtk_hbox_new (TRUE, 5);
610   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), pulldown_hbox, FALSE, FALSE, 0);
611   gtk_widget_show (pulldown_hbox);
612   
613   /* Pulldown menu */
614   filesel->history_pulldown = gtk_option_menu_new ();
615   gtk_widget_show (filesel->history_pulldown);
616   gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown, 
617                       FALSE, FALSE, 0);
618     
619   /*  The horizontal box containing the directory and file listboxes  */
620   list_hbox = gtk_hbox_new (FALSE, 5);
621   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0);
622   gtk_widget_show (list_hbox);
623
624   /* The directories clist */
625   dir_title[0] = _("Directories");
626   dir_title[1] = NULL;
627   filesel->dir_list = gtk_clist_new_with_titles (1, (gchar**) dir_title);
628   gtk_widget_set_usize (filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
629   gtk_signal_connect (GTK_OBJECT (filesel->dir_list), "select_row",
630                       (GtkSignalFunc) gtk_file_selection_dir_button, 
631                       (gpointer) filesel);
632   gtk_clist_set_column_auto_resize (GTK_CLIST (filesel->dir_list), 0, TRUE);
633   gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list));
634
635   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
636   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
637   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
638                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
639   gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
640   gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
641   gtk_widget_show (filesel->dir_list);
642   gtk_widget_show (scrolled_win);
643
644   /* The files clist */
645   file_title[0] = _("Files");
646   file_title[1] = NULL;
647   filesel->file_list = gtk_clist_new_with_titles (1, (gchar**) file_title);
648   gtk_widget_set_usize (filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
649   gtk_signal_connect (GTK_OBJECT (filesel->file_list), "select_row",
650                       (GtkSignalFunc) gtk_file_selection_file_button, 
651                       (gpointer) filesel);
652   gtk_clist_set_column_auto_resize (GTK_CLIST (filesel->file_list), 0, TRUE);
653   gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list));
654
655   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
656   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
657   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
658                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
659   gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
660   gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
661   gtk_widget_show (filesel->file_list);
662   gtk_widget_show (scrolled_win);
663
664   /* action area for packing buttons into. */
665   filesel->action_area = gtk_hbox_new (TRUE, 0);
666   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->action_area, 
667                       FALSE, FALSE, 0);
668   gtk_widget_show (filesel->action_area);
669   
670   /*  The OK/Cancel button area */
671   confirm_area = dialog->action_area;
672
673   /*  The OK button  */
674   filesel->ok_button = gtk_dialog_add_button (dialog,
675                                               GTK_STOCK_BUTTON_OK,
676                                               GTK_RESPONSE_OK);
677   
678   gtk_widget_grab_default (filesel->ok_button);
679
680   /*  The Cancel button  */
681   filesel->cancel_button = gtk_dialog_add_button (dialog,
682                                                   GTK_STOCK_BUTTON_CANCEL,
683                                                   GTK_RESPONSE_CANCEL);
684
685   /*  The selection entry widget  */
686   entry_vbox = gtk_vbox_new (FALSE, 2);
687   gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 2);
688   gtk_widget_show (entry_vbox);
689
690   filesel->selection_text = label = gtk_label_new ("");
691   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
692   gtk_box_pack_start (GTK_BOX (entry_vbox), label, FALSE, FALSE, 0);
693   gtk_widget_show (label);
694
695   filesel->selection_entry = gtk_entry_new ();
696   gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event",
697                       (GtkSignalFunc) gtk_file_selection_key_press, filesel);
698   gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "insert_text",
699                       (GtkSignalFunc) gtk_file_selection_insert_text, NULL);
700   gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event",
701                              (GtkSignalFunc) gtk_widget_grab_default,
702                              GTK_OBJECT (filesel->ok_button));
703   gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "activate",
704                              (GtkSignalFunc) gtk_button_clicked,
705                              GTK_OBJECT (filesel->ok_button));
706   gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
707   gtk_widget_show (filesel->selection_entry);
708
709   if (!cmpl_state_okay (filesel->cmpl_state))
710     {
711       gchar err_buf[256];
712
713       sprintf (err_buf, _("Directory unreadable: %s"), cmpl_strerror (cmpl_errno));
714
715       gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
716     }
717   else
718     {
719       gtk_file_selection_populate (filesel, "", FALSE);
720     }
721
722   gtk_widget_grab_focus (filesel->selection_entry);
723 }
724
725 GtkWidget*
726 gtk_file_selection_new (const gchar *title)
727 {
728   GtkFileSelection *filesel;
729
730   filesel = gtk_type_new (GTK_TYPE_FILE_SELECTION);
731   gtk_window_set_title (GTK_WINDOW (filesel), title);
732   gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE);
733
734   return GTK_WIDGET (filesel);
735 }
736
737 void
738 gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
739 {
740   g_return_if_fail (filesel != NULL);
741   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
742     
743   /* delete, create directory, and rename */
744   if (!filesel->fileop_c_dir) 
745     {
746       filesel->fileop_c_dir = gtk_button_new_with_label (_("Create Dir"));
747       gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked",
748                           (GtkSignalFunc) gtk_file_selection_create_dir, 
749                           (gpointer) filesel);
750       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
751                           filesel->fileop_c_dir, TRUE, TRUE, 0);
752       gtk_widget_show (filesel->fileop_c_dir);
753     }
754         
755   if (!filesel->fileop_del_file) 
756     {
757       filesel->fileop_del_file = gtk_button_new_with_label (_("Delete File"));
758       gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked",
759                           (GtkSignalFunc) gtk_file_selection_delete_file, 
760                           (gpointer) filesel);
761       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
762                           filesel->fileop_del_file, TRUE, TRUE, 0);
763       gtk_widget_show (filesel->fileop_del_file);
764     }
765
766   if (!filesel->fileop_ren_file)
767     {
768       filesel->fileop_ren_file = gtk_button_new_with_label (_("Rename File"));
769       gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked",
770                           (GtkSignalFunc) gtk_file_selection_rename_file, 
771                           (gpointer) filesel);
772       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
773                           filesel->fileop_ren_file, TRUE, TRUE, 0);
774       gtk_widget_show (filesel->fileop_ren_file);
775     }
776   g_object_notify (G_OBJECT (filesel), "show_fileops");
777   gtk_widget_queue_resize (GTK_WIDGET (filesel));
778 }
779
780 void       
781 gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
782 {
783   g_return_if_fail (filesel != NULL);
784   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
785     
786   if (filesel->fileop_ren_file)
787     {
788       gtk_widget_destroy (filesel->fileop_ren_file);
789       filesel->fileop_ren_file = NULL;
790     }
791
792   if (filesel->fileop_del_file)
793     {
794       gtk_widget_destroy (filesel->fileop_del_file);
795       filesel->fileop_del_file = NULL;
796     }
797
798   if (filesel->fileop_c_dir)
799     {
800       gtk_widget_destroy (filesel->fileop_c_dir);
801       filesel->fileop_c_dir = NULL;
802     }
803   g_object_notify (G_OBJECT (filesel), "show_fileops");
804 }
805
806
807
808 void
809 gtk_file_selection_set_filename (GtkFileSelection *filesel,
810                                  const gchar      *filename)
811 {
812   gchar *buf;
813   const char *name, *last_slash;
814
815   g_return_if_fail (filesel != NULL);
816   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
817   g_return_if_fail (filename != NULL);
818
819   last_slash = strrchr (filename, G_DIR_SEPARATOR);
820
821   if (!last_slash)
822     {
823       buf = g_strdup ("");
824       name = filename;
825     }
826   else
827     {
828       buf = g_strdup (filename);
829       buf[last_slash - filename + 1] = 0;
830       name = last_slash + 1;
831     }
832
833   gtk_file_selection_populate (filesel, buf, FALSE);
834
835   if (filesel->selection_entry)
836     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
837   g_free (buf);
838   g_object_notify (G_OBJECT (filesel), "filename");
839 }
840
841 /**
842  * gtk_file_selection_get_filename:
843  * @filesel: a #GtkFileSelection
844  * 
845  * This function returns the selected filename in the C runtime's
846  * multibyte string encoding, which may or may not be the same as that
847  * used by GTK+ (UTF-8). To convert to UTF-8, call g_filename_to_utf8().
848  * The returned string points to a statically allocated buffer and
849  * should be copied if you plan to keep it around.
850  * 
851  * Return value: currently-selected filename in locale's encoding
852  **/
853 G_CONST_RETURN gchar*
854 gtk_file_selection_get_filename (GtkFileSelection *filesel)
855 {
856   static gchar nothing[2] = "";
857   static gchar something[MAXPATHLEN*2];
858   char *sys_filename;
859   char *text;
860
861   g_return_val_if_fail (filesel != NULL, nothing);
862   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
863
864 #ifdef G_WITH_CYGWIN
865   translate_win32_path (filesel);
866 #endif
867   text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
868   if (text)
869     {
870       sys_filename = g_filename_from_utf8 (cmpl_completion_fullname (text, filesel->cmpl_state), -1, NULL, NULL, NULL);
871       if (sys_filename)
872         return nothing;
873       strncpy (something, sys_filename, sizeof (something));
874       g_free (sys_filename);
875       return something;
876     }
877
878   return nothing;
879 }
880
881 void
882 gtk_file_selection_complete (GtkFileSelection *filesel,
883                              const gchar      *pattern)
884 {
885   g_return_if_fail (filesel != NULL);
886   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
887   g_return_if_fail (pattern != NULL);
888
889   if (filesel->selection_entry)
890     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
891   gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE);
892 }
893
894 static void
895 gtk_file_selection_destroy (GtkObject *object)
896 {
897   GtkFileSelection *filesel;
898   GList *list;
899   HistoryCallbackArg *callback_arg;
900   
901   g_return_if_fail (GTK_IS_FILE_SELECTION (object));
902   
903   filesel = GTK_FILE_SELECTION (object);
904   
905   if (filesel->fileop_dialog)
906     {
907       gtk_widget_destroy (filesel->fileop_dialog);
908       filesel->fileop_dialog = NULL;
909     }
910   
911   if (filesel->history_list)
912     {
913       list = filesel->history_list;
914       while (list)
915         {
916           callback_arg = list->data;
917           g_free (callback_arg->directory);
918           g_free (callback_arg);
919           list = list->next;
920         }
921       g_list_free (filesel->history_list);
922       filesel->history_list = NULL;
923     }
924
925   if (filesel->cmpl_state)
926     {
927       cmpl_free_state (filesel->cmpl_state);
928       filesel->cmpl_state = NULL;
929     }
930   
931   GTK_OBJECT_CLASS (parent_class)->destroy (object);
932 }
933
934 /* Begin file operations callbacks */
935
936 static void
937 gtk_file_selection_fileop_error (GtkFileSelection *fs,
938                                  gchar            *error_message)
939 {
940   GtkWidget *dialog;
941     
942   g_return_if_fail (error_message != NULL);
943
944   /* main dialog */
945   dialog = gtk_message_dialog_new (GTK_WINDOW (fs),
946                                    GTK_DIALOG_DESTROY_WITH_PARENT,
947                                    GTK_MESSAGE_ERROR,
948                                    GTK_BUTTONS_CLOSE,
949                                    "%s", error_message);
950
951   /* yes, we free it */
952   g_free (error_message);
953
954   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
955
956   gtk_signal_connect_object (GTK_OBJECT (dialog), "response",
957                              (GtkSignalFunc) gtk_widget_destroy, 
958                              (gpointer) dialog);
959
960   gtk_widget_show (dialog);
961 }
962
963 static void
964 gtk_file_selection_fileop_destroy (GtkWidget *widget,
965                                    gpointer   data)
966 {
967   GtkFileSelection *fs = data;
968
969   g_return_if_fail (fs != NULL);
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 (fs != NULL);
990   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
991
992   dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
993   cmpl_state = (CompletionState*) fs->cmpl_state;
994   path = cmpl_reference_position (cmpl_state);
995   
996   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
997   sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
998   if (error)
999     {
1000       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1001         buf = g_strdup_printf (_("The directory name \"%s\" contains symbols that are not allowed in filenames"), dirname);
1002       else
1003         buf = g_strdup_printf (_("Error creating directory \"%s\": %s\n%s"), dirname, error->message,
1004                                _("You probably used symbols not allowed in filenames."));
1005       gtk_file_selection_fileop_error (fs, buf);
1006       g_error_free (error);
1007       goto out;
1008     }
1009
1010   if (mkdir (sys_full_path, 0755) < 0) 
1011     {
1012       buf = g_strdup_printf (_("Error creating directory \"%s\": %s\n"), dirname,
1013                              g_strerror (errno));
1014       gtk_file_selection_fileop_error (fs, buf);
1015     }
1016
1017  out:
1018   g_free (full_path);
1019   g_free (sys_full_path);
1020   
1021   gtk_widget_destroy (fs->fileop_dialog);
1022   gtk_file_selection_populate (fs, "", FALSE);
1023 }
1024   
1025 static void
1026 gtk_file_selection_create_dir (GtkWidget *widget,
1027                                gpointer   data)
1028 {
1029   GtkFileSelection *fs = data;
1030   GtkWidget *label;
1031   GtkWidget *dialog;
1032   GtkWidget *vbox;
1033   GtkWidget *button;
1034
1035   g_return_if_fail (fs != NULL);
1036   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1037
1038   if (fs->fileop_dialog)
1039     return;
1040   
1041   /* main dialog */
1042   dialog = gtk_dialog_new ();
1043   fs->fileop_dialog = dialog;
1044   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1045                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
1046                       (gpointer) fs);
1047   gtk_window_set_title (GTK_WINDOW (dialog), _("Create Directory"));
1048   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1049
1050   /* If file dialog is grabbed, grab option dialog */
1051   /* When option dialog is closed, file dialog will be grabbed again */
1052   if (GTK_WINDOW (fs)->modal)
1053       gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1054
1055   vbox = gtk_vbox_new (FALSE, 0);
1056   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1057   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1058                      FALSE, FALSE, 0);
1059   gtk_widget_show( vbox);
1060   
1061   label = gtk_label_new (_("Directory name:"));
1062   gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.0);
1063   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1064   gtk_widget_show (label);
1065
1066   /*  The directory entry widget  */
1067   fs->fileop_entry = gtk_entry_new ();
1068   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
1069                       TRUE, TRUE, 5);
1070   GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1071   gtk_widget_show (fs->fileop_entry);
1072   
1073   /* buttons */
1074   button = gtk_button_new_with_label (_("Create"));
1075   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1076                       (GtkSignalFunc) gtk_file_selection_create_dir_confirmed, 
1077                       (gpointer) fs);
1078   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1079                      button, TRUE, TRUE, 0);
1080   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1081   gtk_widget_show (button);
1082   
1083   button = gtk_button_new_with_label (_("Cancel"));
1084   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1085                              (GtkSignalFunc) gtk_widget_destroy, 
1086                              (gpointer) dialog);
1087   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1088                      button, TRUE, TRUE, 0);
1089   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1090   gtk_widget_grab_default (button);
1091   gtk_widget_show (button);
1092
1093   gtk_widget_show (dialog);
1094 }
1095
1096 static void
1097 gtk_file_selection_delete_file_confirmed (GtkWidget *widget,
1098                                           gpointer   data)
1099 {
1100   GtkFileSelection *fs = data;
1101   CompletionState *cmpl_state;
1102   gchar *path;
1103   gchar *full_path;
1104   gchar *sys_full_path;
1105   GError *error = NULL;
1106   gchar *buf;
1107   
1108   g_return_if_fail (fs != NULL);
1109   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1110
1111   cmpl_state = (CompletionState*) fs->cmpl_state;
1112   path = cmpl_reference_position (cmpl_state);
1113   
1114   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1115   sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1116   if (error)
1117     {
1118       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1119         buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
1120                                fs->fileop_file);
1121       else
1122         buf = g_strdup_printf (_("Error deleting file \"%s\": %s\n%s"),
1123                                fs->fileop_file, error->message,
1124                                _("It probably contains symbols not allowed in filenames."));
1125       
1126       gtk_file_selection_fileop_error (fs, buf);
1127       g_error_free (error);
1128       goto out;
1129     }
1130
1131   if (unlink (sys_full_path) < 0) 
1132     {
1133       buf = g_strdup_printf (_("Error deleting file \"%s\": %s"),
1134                              fs->fileop_file, g_strerror (errno));
1135       gtk_file_selection_fileop_error (fs, buf);
1136     }
1137   
1138  out:
1139   g_free (full_path);
1140   g_free (sys_full_path);
1141   
1142   gtk_widget_destroy (fs->fileop_dialog);
1143   gtk_file_selection_populate (fs, "", FALSE);
1144 }
1145
1146 static void
1147 gtk_file_selection_delete_file (GtkWidget *widget,
1148                                 gpointer   data)
1149 {
1150   GtkFileSelection *fs = data;
1151   GtkWidget *label;
1152   GtkWidget *vbox;
1153   GtkWidget *button;
1154   GtkWidget *dialog;
1155   gchar *filename;
1156   gchar *buf;
1157   
1158   g_return_if_fail (fs != NULL);
1159   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1160
1161   if (fs->fileop_dialog)
1162           return;
1163
1164 #ifdef G_WITH_CYGWIN
1165   translate_win32_path (fs);
1166 #endif
1167
1168   filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1169   if (strlen (filename) < 1)
1170     return;
1171
1172   fs->fileop_file = filename;
1173   
1174   /* main dialog */
1175   fs->fileop_dialog = dialog = gtk_dialog_new ();
1176   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1177                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
1178                       (gpointer) fs);
1179   gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
1180   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1181
1182   /* If file dialog is grabbed, grab option dialog */
1183   /* When option dialog is closed, file dialog will be grabbed again */
1184   if (GTK_WINDOW (fs)->modal)
1185       gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1186   
1187   vbox = gtk_vbox_new (FALSE, 0);
1188   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1189   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1190                      FALSE, FALSE, 0);
1191   gtk_widget_show (vbox);
1192
1193   buf = g_strconcat ("Really delete file \"", filename, "\" ?", NULL);
1194   label = gtk_label_new (buf);
1195   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1196   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1197   gtk_widget_show (label);
1198   g_free (buf);
1199   
1200   /* buttons */
1201   button = gtk_button_new_with_label (_("Delete"));
1202   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1203                       (GtkSignalFunc) gtk_file_selection_delete_file_confirmed, 
1204                       (gpointer) fs);
1205   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1206                       button, TRUE, TRUE, 0);
1207   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1208   gtk_widget_show (button);
1209   
1210   button = gtk_button_new_with_label (_("Cancel"));
1211   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1212                              (GtkSignalFunc) gtk_widget_destroy, 
1213                              (gpointer) dialog);
1214   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1215                       button, TRUE, TRUE, 0);
1216   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1217   gtk_widget_grab_default (button);
1218   gtk_widget_show (button);
1219
1220   gtk_widget_show (dialog);
1221
1222 }
1223
1224 static void
1225 gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
1226                                           gpointer   data)
1227 {
1228   GtkFileSelection *fs = data;
1229   gchar *buf;
1230   gchar *file;
1231   gchar *path;
1232   gchar *new_filename;
1233   gchar *old_filename;
1234   gchar *sys_new_filename;
1235   gchar *sys_old_filename;
1236   CompletionState *cmpl_state;
1237   GError *error = NULL;
1238   
1239   g_return_if_fail (fs != NULL);
1240   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1241
1242   file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1243   cmpl_state = (CompletionState*) fs->cmpl_state;
1244   path = cmpl_reference_position (cmpl_state);
1245   
1246   new_filename = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
1247   old_filename = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1248
1249   sys_new_filename = g_filename_from_utf8 (new_filename, -1, NULL, NULL, &error);
1250   if (error)
1251     {
1252       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1253         buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), new_filename);
1254       else
1255         buf = g_strdup_printf (_("Error renaming file to \"%s\": %s\n%s"),
1256                                new_filename, error->message,
1257                                _("You probably used symbols not allowed in filenames."));
1258       gtk_file_selection_fileop_error (fs, buf);
1259       g_error_free (error);
1260       goto out1;
1261     }
1262
1263   sys_old_filename = g_filename_from_utf8 (old_filename, -1, NULL, NULL, &error);
1264   if (error)
1265     {
1266       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1267         buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), old_filename);
1268       else
1269         buf = g_strdup_printf (_("Error renaming file \"%s\": %s\n%s"),
1270                                old_filename, error->message,
1271                                _("It probably contains symbols not allowed in filenames."));
1272       gtk_file_selection_fileop_error (fs, buf);
1273       g_error_free (error);
1274       goto out2;
1275     }
1276   
1277   if (rename (sys_old_filename, sys_new_filename) < 0) 
1278     {
1279       buf = g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"),
1280                              sys_old_filename, sys_new_filename,
1281                              g_strerror (errno));
1282       gtk_file_selection_fileop_error (fs, buf);
1283     }
1284   
1285  out2:
1286   g_free (sys_old_filename);
1287
1288  out1:
1289   g_free (new_filename);
1290   g_free (old_filename);
1291   g_free (sys_new_filename);
1292   
1293   gtk_widget_destroy (fs->fileop_dialog);
1294   gtk_file_selection_populate (fs, "", FALSE);
1295 }
1296   
1297 static void
1298 gtk_file_selection_rename_file (GtkWidget *widget,
1299                                 gpointer   data)
1300 {
1301   GtkFileSelection *fs = data;
1302   GtkWidget *label;
1303   GtkWidget *dialog;
1304   GtkWidget *vbox;
1305   GtkWidget *button;
1306   gchar *buf;
1307   
1308   g_return_if_fail (fs != NULL);
1309   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1310
1311   if (fs->fileop_dialog)
1312           return;
1313
1314   fs->fileop_file = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1315   if (strlen (fs->fileop_file) < 1)
1316     return;
1317   
1318   /* main dialog */
1319   fs->fileop_dialog = dialog = gtk_dialog_new ();
1320   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1321                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
1322                       (gpointer) fs);
1323   gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
1324   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1325
1326   /* If file dialog is grabbed, grab option dialog */
1327   /* When option dialog  closed, file dialog will be grabbed again */
1328   if (GTK_WINDOW (fs)->modal)
1329     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1330   
1331   vbox = gtk_vbox_new (FALSE, 0);
1332   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1333   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1334                       FALSE, FALSE, 0);
1335   gtk_widget_show(vbox);
1336   
1337   buf = g_strconcat ("Rename file \"", fs->fileop_file, "\" to:", NULL);
1338   label = gtk_label_new(buf);
1339   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1340   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1341   gtk_widget_show (label);
1342   g_free (buf);
1343
1344   /* New filename entry */
1345   fs->fileop_entry = gtk_entry_new ();
1346   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
1347                       TRUE, TRUE, 5);
1348   GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1349   gtk_widget_show (fs->fileop_entry);
1350   
1351   gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
1352   gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
1353                               0, strlen (fs->fileop_file));
1354
1355   /* buttons */
1356   button = gtk_button_new_with_label (_("Rename"));
1357   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1358                       (GtkSignalFunc) gtk_file_selection_rename_file_confirmed, 
1359                       (gpointer) fs);
1360   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1361                       button, TRUE, TRUE, 0);
1362   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1363   gtk_widget_show (button);
1364   
1365   button = gtk_button_new_with_label (_("Cancel"));
1366   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1367                              (GtkSignalFunc) gtk_widget_destroy, 
1368                              (gpointer) dialog);
1369   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1370                       button, TRUE, TRUE, 0);
1371   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1372   gtk_widget_grab_default (button);
1373   gtk_widget_show (button);
1374
1375   gtk_widget_show (dialog);
1376 }
1377
1378 static gint
1379 gtk_file_selection_insert_text (GtkWidget   *widget,
1380                                 const gchar *new_text,
1381                                 gint         new_text_length,
1382                                 gint        *position,
1383                                 gpointer     user_data)
1384 {
1385   gchar *filename;
1386
1387   filename = g_filename_from_utf8 (new_text, new_text_length, NULL, NULL, NULL);
1388
1389   if (!filename)
1390     {
1391       gdk_beep ();
1392       gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "insert_text");
1393       return FALSE;
1394     }
1395   
1396   g_free (filename);
1397   
1398   return TRUE;
1399 }
1400
1401 static gint
1402 gtk_file_selection_key_press (GtkWidget   *widget,
1403                               GdkEventKey *event,
1404                               gpointer     user_data)
1405 {
1406   GtkFileSelection *fs;
1407   char *text;
1408
1409   g_return_val_if_fail (widget != NULL, FALSE);
1410   g_return_val_if_fail (event != NULL, FALSE);
1411
1412   if (event->keyval == GDK_Tab)
1413     {
1414       fs = GTK_FILE_SELECTION (user_data);
1415 #ifdef G_WITH_CYGWIN
1416       translate_win32_path (fs);
1417 #endif
1418       text = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1419
1420       text = g_strdup (text);
1421
1422       gtk_file_selection_populate (fs, text, TRUE);
1423
1424       g_free (text);
1425
1426       gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
1427
1428       return TRUE;
1429     }
1430
1431   return FALSE;
1432 }
1433
1434
1435 static void
1436 gtk_file_selection_history_callback (GtkWidget *widget,
1437                                      gpointer   data)
1438 {
1439   GtkFileSelection *fs = data;
1440   HistoryCallbackArg *callback_arg;
1441   GList *list;
1442
1443   g_return_if_fail (fs != NULL);
1444   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1445
1446   list = fs->history_list;
1447   
1448   while (list) {
1449     callback_arg = list->data;
1450     
1451     if (callback_arg->menu_item == widget)
1452       {
1453         gtk_file_selection_populate (fs, callback_arg->directory, FALSE);
1454         break;
1455       }
1456     
1457     list = list->next;
1458   }
1459 }
1460
1461 static void 
1462 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
1463                                         gchar            *current_directory)
1464 {
1465   HistoryCallbackArg *callback_arg;
1466   GtkWidget *menu_item;
1467   GList *list;
1468   gchar *current_dir;
1469   gint dir_len;
1470   gint i;
1471   
1472   g_return_if_fail (fs != NULL);
1473   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1474   g_return_if_fail (current_directory != NULL);
1475   
1476   list = fs->history_list;
1477
1478   if (fs->history_menu) 
1479     {
1480       while (list) {
1481         callback_arg = list->data;
1482         g_free (callback_arg->directory);
1483         g_free (callback_arg);
1484         list = list->next;
1485       }
1486       g_list_free (fs->history_list);
1487       fs->history_list = NULL;
1488       
1489       gtk_widget_destroy (fs->history_menu);
1490     }
1491   
1492   fs->history_menu = gtk_menu_new ();
1493
1494   current_dir = g_strdup (current_directory);
1495
1496   dir_len = strlen (current_dir);
1497
1498   for (i = dir_len; i >= 0; i--)
1499     {
1500       /* the i == dir_len is to catch the full path for the first 
1501        * entry. */
1502       if ( (current_dir[i] == G_DIR_SEPARATOR) || (i == dir_len))
1503         {
1504           /* another small hack to catch the full path */
1505           if (i != dir_len) 
1506                   current_dir[i + 1] = '\0';
1507 #ifdef G_WITH_CYGWIN
1508           if (!strcmp (current_dir, "//"))
1509             continue;
1510 #endif
1511           menu_item = gtk_menu_item_new_with_label (current_dir);
1512           
1513           callback_arg = g_new (HistoryCallbackArg, 1);
1514           callback_arg->menu_item = menu_item;
1515           
1516           /* since the autocompletion gets confused if you don't 
1517            * supply a trailing '/' on a dir entry, set the full
1518            * (current) path to "" which just refreshes the filesel */
1519           if (dir_len == i)
1520             {
1521               callback_arg->directory = g_strdup ("");
1522             }
1523           else
1524             {
1525               callback_arg->directory = g_strdup (current_dir);
1526             }
1527           
1528           fs->history_list = g_list_append (fs->history_list, callback_arg);
1529           
1530           gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
1531                               (GtkSignalFunc) gtk_file_selection_history_callback,
1532                               (gpointer) fs);
1533           gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item);
1534           gtk_widget_show (menu_item);
1535         }
1536     }
1537
1538   gtk_option_menu_set_menu (GTK_OPTION_MENU (fs->history_pulldown), 
1539                             fs->history_menu);
1540   g_free (current_dir);
1541 }
1542
1543 static void
1544 gtk_file_selection_file_button (GtkWidget      *widget,
1545                                 gint            row, 
1546                                 gint            column, 
1547                                 GdkEventButton *bevent,
1548                                 gpointer        user_data)
1549 {
1550   GtkFileSelection *fs = NULL;
1551   gchar *filename, *temp = NULL;
1552   
1553   g_return_if_fail (GTK_IS_CLIST (widget));
1554
1555   fs = user_data;
1556   g_return_if_fail (fs != NULL);
1557   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1558   
1559   gtk_clist_get_text (GTK_CLIST (fs->file_list), row, 0, &temp);
1560   filename = g_strdup (temp);
1561
1562 #ifdef G_WITH_CYGWIN
1563   /* Check to see if the selection was a drive selector */
1564   if (isalpha (filename[0]) && (filename[1] == ':')) {
1565     /* It is... map it to a CYGWIN32 drive */
1566     gchar *temp_filename = g_strdup_printf ("//%c/", tolower (filename[0]));
1567     g_free(filename);
1568     filename = temp_filename;
1569   }
1570 #endif /* G_WITH_CYGWIN */
1571
1572   if (filename)
1573     {
1574       if (bevent)
1575         switch (bevent->type)
1576           {
1577           case GDK_2BUTTON_PRESS:
1578             gtk_button_clicked (GTK_BUTTON (fs->ok_button));
1579             break;
1580             
1581           default:
1582             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1583             break;
1584           }
1585       else
1586         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1587
1588       g_free (filename);
1589     }
1590 }
1591
1592 static void
1593 gtk_file_selection_dir_button (GtkWidget      *widget,
1594                                gint            row, 
1595                                gint            column, 
1596                                GdkEventButton *bevent,
1597                                gpointer        user_data)
1598 {
1599   GtkFileSelection *fs = NULL;
1600   gchar *filename, *temp = NULL;
1601
1602   g_return_if_fail (GTK_IS_CLIST (widget));
1603
1604   fs = GTK_FILE_SELECTION (user_data);
1605   g_return_if_fail (fs != NULL);
1606   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1607
1608   gtk_clist_get_text (GTK_CLIST (fs->dir_list), row, 0, &temp);
1609   filename = g_strdup (temp);
1610
1611   if (filename)
1612     {
1613       if (bevent)
1614         switch (bevent->type)
1615           {
1616           case GDK_2BUTTON_PRESS:
1617             gtk_file_selection_populate (fs, filename, FALSE);
1618             break;
1619           
1620           default:
1621             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1622             break;
1623           }
1624       else
1625         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1626
1627       g_free (filename);
1628     }
1629 }
1630
1631 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
1632
1633 static void
1634 win32_gtk_add_drives_to_dir_list (GtkWidget *the_dir_list)
1635 {
1636   gchar *text[2], *textPtr;
1637   gchar buffer[128];
1638   char volumeNameBuf[128];
1639   char formatBuffer[128];
1640
1641   text[1] = NULL;
1642
1643   /* Get the Drives string */
1644   GetLogicalDriveStrings (sizeof (buffer), buffer);
1645
1646   /* Add the drives as necessary */
1647   textPtr = buffer;
1648   while (*textPtr != '\0') {
1649     /* Get the volume information for this drive */
1650     if ((tolower (textPtr[0]) != 'a') && (tolower (textPtr[0]) != 'b'))
1651       {
1652         /* Ignore floppies (?) */
1653         DWORD maxComponentLength, flags;
1654
1655         GetVolumeInformation (textPtr,
1656                               volumeNameBuf, sizeof(volumeNameBuf),
1657                               NULL, &maxComponentLength,
1658                               &flags, NULL, 0);
1659         /* Build the actual displayable string */
1660
1661         sprintf (formatBuffer, "%c:\\", toupper(textPtr[0]));
1662 #if 0 /* HB: removed to allow drive change AND directory update with one click */
1663         if (strlen (volumeNameBuf) > 0)
1664           sprintf (formatBuffer, "%s (%s)", formatBuffer, volumeNameBuf);
1665 #endif
1666         /* Add to the list */
1667         text[0] = formatBuffer;
1668         gtk_clist_append (GTK_CLIST (the_dir_list), text);
1669       }
1670     textPtr += (strlen (textPtr) + 1);
1671   }
1672 }
1673 #endif
1674
1675 static void
1676 gtk_file_selection_populate (GtkFileSelection *fs,
1677                              gchar            *rel_path,
1678                              gint              try_complete)
1679 {
1680   CompletionState *cmpl_state;
1681   PossibleCompletion* poss;
1682   gchar* filename;
1683   gchar* rem_path = rel_path;
1684   gchar* sel_text;
1685   gchar* text[2];
1686   gint did_recurse = FALSE;
1687   gint possible_count = 0;
1688   gint selection_index = -1;
1689   
1690   g_return_if_fail (fs != NULL);
1691   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1692   
1693   cmpl_state = (CompletionState*) fs->cmpl_state;
1694   poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
1695
1696   if (!cmpl_state_okay (cmpl_state))
1697     {
1698       /* Something went wrong. */
1699       gtk_file_selection_abort (fs);
1700       return;
1701     }
1702
1703   g_assert (cmpl_state->reference_dir);
1704
1705   gtk_clist_freeze (GTK_CLIST (fs->dir_list));
1706   gtk_clist_clear (GTK_CLIST (fs->dir_list));
1707   gtk_clist_freeze (GTK_CLIST (fs->file_list));
1708   gtk_clist_clear (GTK_CLIST (fs->file_list));
1709
1710   /* Set the dir_list to include ./ and ../ */
1711   text[1] = NULL;
1712   text[0] = "." G_DIR_SEPARATOR_S;
1713   gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1714
1715   text[0] = ".." G_DIR_SEPARATOR_S;
1716   gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1717
1718   while (poss)
1719     {
1720       if (cmpl_is_a_completion (poss))
1721         {
1722           possible_count += 1;
1723
1724           filename = cmpl_this_completion (poss);
1725
1726           text[0] = filename;
1727           
1728           if (cmpl_is_directory (poss))
1729             {
1730               if (strcmp (filename, "." G_DIR_SEPARATOR_S) != 0 &&
1731                   strcmp (filename, ".." G_DIR_SEPARATOR_S) != 0)
1732                 gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1733             }
1734           else
1735             {
1736               gtk_clist_append (GTK_CLIST (fs->file_list), text);
1737             }
1738         }
1739
1740       poss = cmpl_next_completion (cmpl_state);
1741     }
1742
1743 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
1744   /* For Windows, add drives as potential selections */
1745   win32_gtk_add_drives_to_dir_list (fs->dir_list);
1746 #endif
1747
1748   gtk_clist_thaw (GTK_CLIST (fs->dir_list));
1749   gtk_clist_thaw (GTK_CLIST (fs->file_list));
1750
1751   /* File lists are set. */
1752
1753   g_assert (cmpl_state->reference_dir);
1754
1755   if (try_complete)
1756     {
1757
1758       /* User is trying to complete filenames, so advance the user's input
1759        * string to the updated_text, which is the common leading substring
1760        * of all possible completions, and if its a directory attempt
1761        * attempt completions in it. */
1762
1763       if (cmpl_updated_text (cmpl_state)[0])
1764         {
1765
1766           if (cmpl_updated_dir (cmpl_state))
1767             {
1768               gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
1769
1770               did_recurse = TRUE;
1771
1772               gtk_file_selection_populate (fs, dir_name, TRUE);
1773
1774               g_free (dir_name);
1775             }
1776           else
1777             {
1778               if (fs->selection_entry)
1779                       gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
1780                                           cmpl_updated_text (cmpl_state));
1781             }
1782         }
1783       else
1784         {
1785           selection_index = cmpl_last_valid_char (cmpl_state) -
1786                             (strlen (rel_path) - strlen (rem_path));
1787           if (fs->selection_entry)
1788             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
1789         }
1790     }
1791   else
1792     {
1793       if (fs->selection_entry)
1794         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
1795     }
1796
1797   if (!did_recurse)
1798     {
1799       if (fs->selection_entry)
1800         gtk_entry_set_position (GTK_ENTRY (fs->selection_entry), selection_index);
1801
1802       if (fs->selection_entry)
1803         {
1804           sel_text = g_strconcat (_("Selection: "),
1805                                   cmpl_reference_position (cmpl_state),
1806                                   NULL);
1807
1808           gtk_label_set_text (GTK_LABEL (fs->selection_text), sel_text);
1809           g_free (sel_text);
1810         }
1811
1812       if (fs->history_pulldown) 
1813         {
1814           gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
1815         }
1816       
1817     }
1818 }
1819
1820 static void
1821 gtk_file_selection_abort (GtkFileSelection *fs)
1822 {
1823   gchar err_buf[256];
1824
1825   sprintf (err_buf, _("Directory unreadable: %s"), cmpl_strerror (cmpl_errno));
1826
1827   /*  BEEP gdk_beep();  */
1828
1829   if (fs->selection_entry)
1830     gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
1831 }
1832
1833 /**********************************************************************/
1834 /*                        External Interface                          */
1835 /**********************************************************************/
1836
1837 /* The four completion state selectors
1838  */
1839 static gchar*
1840 cmpl_updated_text (CompletionState *cmpl_state)
1841 {
1842   return cmpl_state->updated_text;
1843 }
1844
1845 static gboolean
1846 cmpl_updated_dir (CompletionState *cmpl_state)
1847 {
1848   return cmpl_state->re_complete;
1849 }
1850
1851 static gchar*
1852 cmpl_reference_position (CompletionState *cmpl_state)
1853 {
1854   return cmpl_state->reference_dir->fullname;
1855 }
1856
1857 static gint
1858 cmpl_last_valid_char (CompletionState *cmpl_state)
1859 {
1860   return cmpl_state->last_valid_char;
1861 }
1862
1863 static gchar*
1864 cmpl_completion_fullname (gchar           *text,
1865                           CompletionState *cmpl_state)
1866 {
1867   static char nothing[2] = "";
1868
1869   if (!cmpl_state_okay (cmpl_state))
1870     {
1871       return nothing;
1872     }
1873   else if (g_path_is_absolute (text))
1874     {
1875       strcpy (cmpl_state->updated_text, text);
1876     }
1877 #ifdef HAVE_PWD_H
1878   else if (text[0] == '~')
1879     {
1880       CompletionDir* dir;
1881       char* slash;
1882
1883       dir = open_user_dir (text, cmpl_state);
1884
1885       if (!dir)
1886         {
1887           /* spencer says just return ~something, so
1888            * for now just do it. */
1889           strcpy (cmpl_state->updated_text, text);
1890         }
1891       else
1892         {
1893
1894           strcpy (cmpl_state->updated_text, dir->fullname);
1895
1896           slash = strchr (text, G_DIR_SEPARATOR);
1897
1898           if (slash)
1899             strcat (cmpl_state->updated_text, slash);
1900         }
1901     }
1902 #endif
1903   else
1904     {
1905       strcpy (cmpl_state->updated_text, cmpl_state->reference_dir->fullname);
1906       if (cmpl_state->updated_text[strlen (cmpl_state->updated_text) - 1] != G_DIR_SEPARATOR)
1907         strcat (cmpl_state->updated_text, G_DIR_SEPARATOR_S);
1908       strcat (cmpl_state->updated_text, text);
1909     }
1910
1911   return cmpl_state->updated_text;
1912 }
1913
1914 /* The three completion selectors
1915  */
1916 static gchar*
1917 cmpl_this_completion (PossibleCompletion* pc)
1918 {
1919   return pc->text;
1920 }
1921
1922 static gboolean
1923 cmpl_is_directory (PossibleCompletion* pc)
1924 {
1925   return pc->is_directory;
1926 }
1927
1928 static gint
1929 cmpl_is_a_completion (PossibleCompletion* pc)
1930 {
1931   return pc->is_a_completion;
1932 }
1933
1934 /**********************************************************************/
1935 /*                       Construction, deletion                       */
1936 /**********************************************************************/
1937
1938 static CompletionState*
1939 cmpl_init_state (void)
1940 {
1941   gchar *sys_getcwd_buf;
1942   gchar *utf8_cwd;
1943   CompletionState *new_state;
1944
1945   new_state = g_new (CompletionState, 1);
1946
1947   /* g_get_current_dir() returns a string in the "system" charset */
1948   sys_getcwd_buf = g_get_current_dir ();
1949   utf8_cwd = g_filename_to_utf8 (sys_getcwd_buf, -1, NULL, NULL, NULL);
1950   g_free (sys_getcwd_buf);
1951
1952 tryagain:
1953
1954   new_state->reference_dir = NULL;
1955   new_state->completion_dir = NULL;
1956   new_state->active_completion_dir = NULL;
1957   new_state->directory_storage = NULL;
1958   new_state->directory_sent_storage = NULL;
1959   new_state->last_valid_char = 0;
1960   new_state->updated_text = g_new (gchar, MAXPATHLEN);
1961   new_state->updated_text_alloc = MAXPATHLEN;
1962   new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
1963   new_state->the_completion.text_alloc = MAXPATHLEN;
1964   new_state->user_dir_name_buffer = NULL;
1965   new_state->user_directories = NULL;
1966
1967   new_state->reference_dir = open_dir (utf8_cwd, new_state);
1968
1969   if (!new_state->reference_dir)
1970     {
1971       /* Directories changing from underneath us, grumble */
1972       strcpy (utf8_cwd, G_DIR_SEPARATOR_S);
1973       goto tryagain;
1974     }
1975
1976   g_free (utf8_cwd);
1977   return new_state;
1978 }
1979
1980 static void
1981 cmpl_free_dir_list (GList* dp0)
1982 {
1983   GList *dp = dp0;
1984
1985   while (dp)
1986     {
1987       free_dir (dp->data);
1988       dp = dp->next;
1989     }
1990
1991   g_list_free (dp0);
1992 }
1993
1994 static void
1995 cmpl_free_dir_sent_list (GList* dp0)
1996 {
1997   GList *dp = dp0;
1998
1999   while (dp)
2000     {
2001       free_dir_sent (dp->data);
2002       dp = dp->next;
2003     }
2004
2005   g_list_free (dp0);
2006 }
2007
2008 static void
2009 cmpl_free_state (CompletionState* cmpl_state)
2010 {
2011   g_return_if_fail (cmpl_state != NULL);
2012
2013   cmpl_free_dir_list (cmpl_state->directory_storage);
2014   cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
2015
2016   if (cmpl_state->user_dir_name_buffer)
2017     g_free (cmpl_state->user_dir_name_buffer);
2018   if (cmpl_state->user_directories)
2019     g_free (cmpl_state->user_directories);
2020   if (cmpl_state->the_completion.text)
2021     g_free (cmpl_state->the_completion.text);
2022   if (cmpl_state->updated_text)
2023     g_free (cmpl_state->updated_text);
2024
2025   g_free (cmpl_state);
2026 }
2027
2028 static void
2029 free_dir (CompletionDir* dir)
2030 {
2031   g_free (dir->fullname);
2032   g_free (dir);
2033 }
2034
2035 static void
2036 free_dir_sent (CompletionDirSent* sent)
2037 {
2038   gint i;
2039   for (i = 0; i < sent->entry_count; i++)
2040     g_free (sent->entries[i].entry_name);
2041   g_free (sent->entries);
2042   g_free (sent);
2043 }
2044
2045 static void
2046 prune_memory_usage (CompletionState *cmpl_state)
2047 {
2048   GList* cdsl = cmpl_state->directory_sent_storage;
2049   GList* cdl = cmpl_state->directory_storage;
2050   GList* cdl0 = cdl;
2051   gint len = 0;
2052
2053   for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
2054     cdsl = cdsl->next;
2055
2056   if (cdsl)
2057     {
2058       cmpl_free_dir_sent_list (cdsl->next);
2059       cdsl->next = NULL;
2060     }
2061
2062   cmpl_state->directory_storage = NULL;
2063   while (cdl)
2064     {
2065       if (cdl->data == cmpl_state->reference_dir)
2066         cmpl_state->directory_storage = g_list_prepend (NULL, cdl->data);
2067       else
2068         free_dir (cdl->data);
2069       cdl = cdl->next;
2070     }
2071
2072   g_list_free (cdl0);
2073 }
2074
2075 /**********************************************************************/
2076 /*                        The main entrances.                         */
2077 /**********************************************************************/
2078
2079 static PossibleCompletion*
2080 cmpl_completion_matches (gchar           *text_to_complete,
2081                          gchar          **remaining_text,
2082                          CompletionState *cmpl_state)
2083 {
2084   gchar* first_slash;
2085   PossibleCompletion *poss;
2086
2087   prune_memory_usage (cmpl_state);
2088
2089   g_assert (text_to_complete != NULL);
2090
2091   cmpl_state->user_completion_index = -1;
2092   cmpl_state->last_completion_text = text_to_complete;
2093   cmpl_state->the_completion.text[0] = 0;
2094   cmpl_state->last_valid_char = 0;
2095   cmpl_state->updated_text_len = -1;
2096   cmpl_state->updated_text[0] = 0;
2097   cmpl_state->re_complete = FALSE;
2098
2099 #ifdef HAVE_PWD_H
2100   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2101
2102   if (text_to_complete[0] == '~' && !first_slash)
2103     {
2104       /* Text starts with ~ and there is no slash, show all the
2105        * home directory completions.
2106        */
2107       poss = attempt_homedir_completion (text_to_complete, cmpl_state);
2108
2109       update_cmpl (poss, cmpl_state);
2110
2111       return poss;
2112     }
2113 #endif
2114   cmpl_state->reference_dir =
2115     open_ref_dir (text_to_complete, remaining_text, cmpl_state);
2116
2117   if (!cmpl_state->reference_dir)
2118     return NULL;
2119
2120   cmpl_state->completion_dir =
2121     find_completion_dir (*remaining_text, remaining_text, cmpl_state);
2122
2123   cmpl_state->last_valid_char = *remaining_text - text_to_complete;
2124
2125   if (!cmpl_state->completion_dir)
2126     return NULL;
2127
2128   cmpl_state->completion_dir->cmpl_index = -1;
2129   cmpl_state->completion_dir->cmpl_parent = NULL;
2130   cmpl_state->completion_dir->cmpl_text = *remaining_text;
2131
2132   cmpl_state->active_completion_dir = cmpl_state->completion_dir;
2133
2134   cmpl_state->reference_dir = cmpl_state->completion_dir;
2135
2136   poss = attempt_file_completion (cmpl_state);
2137
2138   update_cmpl (poss, cmpl_state);
2139
2140   return poss;
2141 }
2142
2143 static PossibleCompletion*
2144 cmpl_next_completion (CompletionState* cmpl_state)
2145 {
2146   PossibleCompletion* poss = NULL;
2147
2148   cmpl_state->the_completion.text[0] = 0;
2149
2150 #ifdef HAVE_PWD_H
2151   if (cmpl_state->user_completion_index >= 0)
2152     poss = attempt_homedir_completion (cmpl_state->last_completion_text, cmpl_state);
2153   else
2154     poss = attempt_file_completion (cmpl_state);
2155 #else
2156   poss = attempt_file_completion (cmpl_state);
2157 #endif
2158
2159   update_cmpl (poss, cmpl_state);
2160
2161   return poss;
2162 }
2163
2164 /**********************************************************************/
2165 /*                       Directory Operations                         */
2166 /**********************************************************************/
2167
2168 /* Open the directory where completion will begin from, if possible. */
2169 static CompletionDir*
2170 open_ref_dir (gchar           *text_to_complete,
2171               gchar          **remaining_text,
2172               CompletionState *cmpl_state)
2173 {
2174   gchar* first_slash;
2175   CompletionDir *new_dir;
2176
2177   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2178
2179 #ifdef G_WITH_CYGWIN
2180   if (text_to_complete[0] == '/' && text_to_complete[1] == '/')
2181     {
2182       char root_dir[5];
2183       sprintf (root_dir, "//%c", text_to_complete[2]);
2184
2185       new_dir = open_dir (root_dir, cmpl_state);
2186
2187       if (new_dir) {
2188         *remaining_text = text_to_complete + 4;
2189       }
2190     }
2191 #else
2192   if (FALSE)
2193     ;
2194 #endif
2195 #ifdef HAVE_PWD_H
2196   else if (text_to_complete[0] == '~')
2197     {
2198       new_dir = open_user_dir (text_to_complete, cmpl_state);
2199
2200       if (new_dir)
2201         {
2202           if (first_slash)
2203             *remaining_text = first_slash + 1;
2204           else
2205             *remaining_text = text_to_complete + strlen (text_to_complete);
2206         }
2207       else
2208         {
2209           return NULL;
2210         }
2211     }
2212 #endif
2213   else if (g_path_is_absolute (text_to_complete) || !cmpl_state->reference_dir)
2214     {
2215       gchar *tmp = g_strdup (text_to_complete);
2216       gchar *p;
2217
2218       p = tmp;
2219       while (*p && *p != '*' && *p != '?')
2220         p++;
2221
2222       *p = '\0';
2223       p = strrchr (tmp, G_DIR_SEPARATOR);
2224       if (p)
2225         {
2226           if (p == tmp)
2227             p++;
2228       
2229           *p = '\0';
2230
2231           new_dir = open_dir (tmp, cmpl_state);
2232
2233           if (new_dir)
2234             *remaining_text = text_to_complete + 
2235               ((p == tmp + 1) ? (p - tmp) : (p + 1 - tmp));
2236         }
2237       else
2238         {
2239           /* If no possible candidates, use the cwd */
2240           gchar *sys_curdir = g_get_current_dir ();
2241           gchar *utf8_curdir = g_filename_to_utf8 (sys_curdir, -1, NULL, NULL, NULL);
2242
2243           g_free (sys_curdir);
2244
2245           new_dir = open_dir (utf8_curdir, cmpl_state);
2246
2247           if (new_dir)
2248             *remaining_text = text_to_complete;
2249
2250           g_free (utf8_curdir);
2251         }
2252
2253       g_free (tmp);
2254     }
2255   else
2256     {
2257       *remaining_text = text_to_complete;
2258
2259       new_dir = open_dir (cmpl_state->reference_dir->fullname, cmpl_state);
2260     }
2261
2262   if (new_dir)
2263     {
2264       new_dir->cmpl_index = -1;
2265       new_dir->cmpl_parent = NULL;
2266     }
2267
2268   return new_dir;
2269 }
2270
2271 #ifdef HAVE_PWD_H
2272
2273 /* open a directory by user name */
2274 static CompletionDir*
2275 open_user_dir (gchar           *text_to_complete,
2276                CompletionState *cmpl_state)
2277 {
2278   CompletionDir *result;
2279   gchar *first_slash;
2280   gint cmp_len;
2281
2282   g_assert (text_to_complete && text_to_complete[0] == '~');
2283
2284   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2285
2286   if (first_slash)
2287     cmp_len = first_slash - text_to_complete - 1;
2288   else
2289     cmp_len = strlen (text_to_complete + 1);
2290
2291   if (!cmp_len)
2292     {
2293       /* ~/ */
2294       gchar *homedir = g_get_home_dir ();
2295       gchar *utf8_homedir = g_filename_to_utf8 (homedir, -1, NULL, NULL, NULL);
2296
2297       g_free (homedir);
2298
2299       if (utf8_homedir)
2300         result = open_dir (utf8_homedir, cmpl_state);
2301       else
2302         result = NULL;
2303       
2304       g_free (utf8_homedir);
2305     }
2306   else
2307     {
2308       /* ~user/ */
2309       gchar* copy = g_new (char, cmp_len + 1);
2310       gchar *utf8_dir;
2311       struct passwd *pwd;
2312
2313       strncpy (copy, text_to_complete + 1, cmp_len);
2314       copy[cmp_len] = 0;
2315       pwd = getpwnam (copy);
2316       g_free (copy);
2317       if (!pwd)
2318         {
2319           cmpl_errno = errno;
2320           return NULL;
2321         }
2322       utf8_dir = g_filename_to_utf8 (pwd->pw_dir, -1, NULL, NULL, NULL);
2323       result = open_dir (utf8_dir, cmpl_state);
2324       g_free (utf8_dir);
2325     }
2326   return result;
2327 }
2328
2329 #endif
2330
2331 /* open a directory relative the the current relative directory */
2332 static CompletionDir*
2333 open_relative_dir (gchar           *dir_name,
2334                    CompletionDir   *dir,
2335                    CompletionState *cmpl_state)
2336 {
2337   CompletionDir *result;
2338   GString *path;
2339
2340   path = g_string_sized_new (dir->fullname_len + strlen (dir_name) + 10);
2341   g_string_assign (path, dir->fullname);
2342
2343   if (dir->fullname_len > 1
2344       && path->str[dir->fullname_len - 1] != G_DIR_SEPARATOR)
2345     g_string_append_c (path, G_DIR_SEPARATOR);
2346   g_string_append (path, dir_name);
2347
2348   result = open_dir (path->str, cmpl_state);
2349
2350   g_string_free (path, TRUE);
2351
2352   return result;
2353 }
2354
2355 /* after the cache lookup fails, really open a new directory */
2356 static CompletionDirSent*
2357 open_new_dir (gchar       *dir_name,
2358               struct stat *sbuf,
2359               gboolean     stat_subdirs)
2360 {
2361   CompletionDirSent *sent;
2362   DIR *directory;
2363   struct dirent *dirent_ptr;
2364   gint entry_count = 0;
2365   gint n_entries = 0;
2366   gint i;
2367   struct stat ent_sbuf;
2368   GString *path;
2369   gchar *sys_dir_name;
2370
2371   sent = g_new (CompletionDirSent, 1);
2372   sent->mtime = sbuf->st_mtime;
2373   sent->inode = sbuf->st_ino;
2374   sent->device = sbuf->st_dev;
2375
2376   path = g_string_sized_new (2*MAXPATHLEN + 10);
2377
2378   sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
2379   if (!sys_dir_name)
2380     {
2381       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
2382       return NULL;
2383     }
2384   
2385   directory = opendir (sys_dir_name);
2386   if (!directory)
2387     {
2388       cmpl_errno = errno;
2389       g_free (sys_dir_name);
2390       return NULL;
2391     }
2392
2393   while ((dirent_ptr = readdir (directory)) != NULL)
2394     entry_count++;
2395
2396   sent->entries = g_new (CompletionDirEntry, entry_count);
2397   sent->entry_count = entry_count;
2398
2399   rewinddir (directory);
2400
2401   for (i = 0; i < entry_count; i += 1)
2402     {
2403       dirent_ptr = readdir (directory);
2404
2405       if (!dirent_ptr)
2406         {
2407           cmpl_errno = errno;
2408           closedir (directory);
2409           g_free (sys_dir_name);
2410           return NULL;
2411         }
2412
2413       sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent_ptr->d_name, -1, NULL, NULL, NULL);
2414       if (!g_utf8_validate (sent->entries[n_entries].entry_name, -1, NULL))
2415         {
2416           g_warning (_("The filename %s couldn't be converted to UTF-8. Try setting the environment variable G_BROKEN_FILENAMES."), dirent_ptr->d_name);
2417           continue;
2418         }
2419       n_entries++;
2420
2421       g_string_assign (path, sys_dir_name);
2422       if (path->str[path->len-1] != G_DIR_SEPARATOR)
2423         {
2424           g_string_append_c (path, G_DIR_SEPARATOR);
2425         }
2426       g_string_append (path, dirent_ptr->d_name);
2427
2428       if (stat_subdirs)
2429         {
2430           /* Here we know path->str is a "system charset" string */
2431           if (stat (path->str, &ent_sbuf) >= 0 && S_ISDIR (ent_sbuf.st_mode))
2432             sent->entries[n_entries].is_dir = TRUE;
2433           else
2434             /* stat may fail, and we don't mind, since it could be a
2435              * dangling symlink. */
2436             sent->entries[n_entries].is_dir = FALSE;
2437         }
2438       else
2439         sent->entries[n_entries].is_dir = 1;
2440     }
2441   sent->entry_count = n_entries;
2442   
2443   g_free (sys_dir_name);
2444   g_string_free (path, TRUE);
2445   qsort (sent->entries, sent->entry_count, sizeof (CompletionDirEntry), compare_cmpl_dir);
2446
2447   closedir (directory);
2448
2449   return sent;
2450 }
2451
2452 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
2453
2454 static gboolean
2455 check_dir (gchar       *dir_name,
2456            struct stat *result,
2457            gboolean    *stat_subdirs)
2458 {
2459   /* A list of directories that we know only contain other directories.
2460    * Trying to stat every file in these directories would be very
2461    * expensive.
2462    */
2463
2464   static struct {
2465     gchar *name;
2466     gboolean present;
2467     struct stat statbuf;
2468   } no_stat_dirs[] = {
2469     { "/afs", FALSE, { 0 } },
2470     { "/net", FALSE, { 0 } }
2471   };
2472
2473   static const gint n_no_stat_dirs = G_N_ELEMENTS (no_stat_dirs);
2474   static gboolean initialized = FALSE;
2475   gchar *sys_dir_name;
2476   gint i;
2477
2478   if (!initialized)
2479     {
2480       initialized = TRUE;
2481       for (i = 0; i < n_no_stat_dirs; i++)
2482         {
2483           if (stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
2484             no_stat_dirs[i].present = TRUE;
2485         }
2486     }
2487
2488   sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
2489   if (!sys_dir_name)
2490     {
2491       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
2492       return FALSE;
2493     }
2494   
2495   if (stat (sys_dir_name, result) < 0)
2496     {
2497       g_free (sys_dir_name);
2498       cmpl_errno = errno;
2499       return FALSE;
2500     }
2501   g_free (sys_dir_name);
2502
2503   *stat_subdirs = TRUE;
2504   for (i = 0; i < n_no_stat_dirs; i++)
2505     {
2506       if (no_stat_dirs[i].present &&
2507           (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
2508           (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
2509         {
2510           *stat_subdirs = FALSE;
2511           break;
2512         }
2513     }
2514
2515   return TRUE;
2516 }
2517
2518 #endif
2519
2520 /* open a directory by absolute pathname */
2521 static CompletionDir*
2522 open_dir (gchar           *dir_name,
2523           CompletionState *cmpl_state)
2524 {
2525   struct stat sbuf;
2526   gboolean stat_subdirs;
2527   CompletionDirSent *sent;
2528   GList* cdsl;
2529
2530 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
2531   if (!check_dir (dir_name, &sbuf, &stat_subdirs))
2532     return NULL;
2533
2534   cdsl = cmpl_state->directory_sent_storage;
2535
2536   while (cdsl)
2537     {
2538       sent = cdsl->data;
2539
2540       if (sent->inode == sbuf.st_ino &&
2541           sent->mtime == sbuf.st_mtime &&
2542           sent->device == sbuf.st_dev)
2543         return attach_dir (sent, dir_name, cmpl_state);
2544
2545       cdsl = cdsl->next;
2546     }
2547 #else
2548   stat_subdirs = TRUE;
2549 #endif
2550
2551   sent = open_new_dir (dir_name, &sbuf, stat_subdirs);
2552
2553   if (sent)
2554     {
2555       cmpl_state->directory_sent_storage =
2556         g_list_prepend (cmpl_state->directory_sent_storage, sent);
2557
2558       return attach_dir (sent, dir_name, cmpl_state);
2559     }
2560
2561   return NULL;
2562 }
2563
2564 static CompletionDir*
2565 attach_dir (CompletionDirSent *sent,
2566             gchar             *dir_name,
2567             CompletionState   *cmpl_state)
2568 {
2569   CompletionDir* new_dir;
2570
2571   new_dir = g_new (CompletionDir, 1);
2572
2573   cmpl_state->directory_storage =
2574     g_list_prepend (cmpl_state->directory_storage, new_dir);
2575
2576   new_dir->sent = sent;
2577   new_dir->fullname = g_strdup (dir_name);
2578   new_dir->fullname_len = strlen (dir_name);
2579
2580   return new_dir;
2581 }
2582
2583 static gint
2584 correct_dir_fullname (CompletionDir* cmpl_dir)
2585 {
2586   gint length = strlen (cmpl_dir->fullname);
2587   gchar *first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
2588   gchar *sys_filename;
2589   struct stat sbuf;
2590
2591   /* Does it end with /. (\.) ? */
2592   if (length >= 2 &&
2593       strcmp (cmpl_dir->fullname + length - 2, G_DIR_SEPARATOR_S ".") == 0)
2594     {
2595       /* Is it just the root directory (on a drive) ? */
2596       if (cmpl_dir->fullname + length - 2 == first_slash)
2597         {
2598           cmpl_dir->fullname[length - 1] = 0;
2599           cmpl_dir->fullname_len = length - 1;
2600           return TRUE;
2601         }
2602       else
2603         {
2604           cmpl_dir->fullname[length - 2] = 0;
2605         }
2606     }
2607
2608   /* Ends with /./ (\.\)? */
2609   else if (length >= 3 &&
2610            strcmp (cmpl_dir->fullname + length - 3,
2611                    G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) == 0)
2612     cmpl_dir->fullname[length - 2] = 0;
2613
2614   /* Ends with /.. (\..) ? */
2615   else if (length >= 3 &&
2616            strcmp (cmpl_dir->fullname + length - 3,
2617                    G_DIR_SEPARATOR_S "..") == 0)
2618     {
2619       /* Is it just /.. (X:\..)? */
2620       if (cmpl_dir->fullname + length - 3 == first_slash)
2621         {
2622           cmpl_dir->fullname[length - 2] = 0;
2623           cmpl_dir->fullname_len = length - 2;
2624           return TRUE;
2625         }
2626
2627       sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
2628       if (!sys_filename)
2629         {
2630           cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
2631           return FALSE;
2632         }
2633       
2634       if (stat (sys_filename, &sbuf) < 0)
2635         {
2636           g_free (sys_filename);
2637           cmpl_errno = errno;
2638           return FALSE;
2639         }
2640       g_free (sys_filename);
2641
2642       cmpl_dir->fullname[length - 3] = 0;
2643
2644       if (!correct_parent (cmpl_dir, &sbuf))
2645         return FALSE;
2646     }
2647
2648   /* Ends with /../ (\..\)? */
2649   else if (length >= 4 &&
2650            strcmp (cmpl_dir->fullname + length - 4,
2651                    G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) == 0)
2652     {
2653       /* Is it just /../ (X:\..\)? */
2654       if (cmpl_dir->fullname + length - 4 == first_slash)
2655         {
2656           cmpl_dir->fullname[length - 3] = 0;
2657           cmpl_dir->fullname_len = length - 3;
2658           return TRUE;
2659         }
2660
2661       sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
2662       if (!sys_filename)
2663         {
2664           cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
2665           return FALSE;
2666         }
2667       
2668       if (stat (sys_filename, &sbuf) < 0)
2669         {
2670           g_free (sys_filename);
2671           cmpl_errno = errno;
2672           return FALSE;
2673         }
2674       g_free (sys_filename);
2675
2676       cmpl_dir->fullname[length - 4] = 0;
2677
2678       if (!correct_parent (cmpl_dir, &sbuf))
2679         return FALSE;
2680     }
2681
2682   cmpl_dir->fullname_len = strlen (cmpl_dir->fullname);
2683
2684   return TRUE;
2685 }
2686
2687 static gint
2688 correct_parent (CompletionDir *cmpl_dir,
2689                 struct stat   *sbuf)
2690 {
2691   struct stat parbuf;
2692   gchar *last_slash;
2693   gchar *first_slash;
2694   gchar *new_name;
2695   gchar *sys_filename;
2696   gchar c = 0;
2697
2698   last_slash = strrchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
2699   g_assert (last_slash);
2700   first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
2701
2702   /* Clever (?) way to check for top-level directory that works also on
2703    * Win32, where there is a drive letter and colon prefixed...
2704    */
2705   if (last_slash != first_slash)
2706     {
2707       last_slash[0] = 0;
2708     }
2709   else
2710     {
2711       c = last_slash[1];
2712       last_slash[1] = 0;
2713     }
2714
2715   sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
2716   if (!sys_filename)
2717     {
2718       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
2719       if (!c)
2720         last_slash[0] = G_DIR_SEPARATOR;
2721       return FALSE;
2722     }
2723   
2724   if (stat (sys_filename, &parbuf) < 0)
2725     {
2726       g_free (sys_filename);
2727       cmpl_errno = errno;
2728       if (!c)
2729         last_slash[0] = G_DIR_SEPARATOR;
2730       return FALSE;
2731     }
2732   g_free (sys_filename);
2733
2734 #ifndef G_OS_WIN32              /* No inode numbers on Win32 */
2735   if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
2736     /* it wasn't a link */
2737     return TRUE;
2738
2739   if (c)
2740     last_slash[1] = c;
2741   else
2742     last_slash[0] = G_DIR_SEPARATOR;
2743
2744   /* it was a link, have to figure it out the hard way */
2745
2746   new_name = find_parent_dir_fullname (cmpl_dir->fullname);
2747
2748   if (!new_name)
2749     return FALSE;
2750
2751   g_free (cmpl_dir->fullname);
2752
2753   cmpl_dir->fullname = new_name;
2754 #endif
2755
2756   return TRUE;
2757 }
2758
2759 #ifndef G_OS_WIN32
2760
2761 static gchar*
2762 find_parent_dir_fullname (gchar* dirname)
2763 {
2764   gchar *sys_orig_dir;
2765   gchar *result;
2766   gchar *sys_cwd;
2767   gchar *sys_dirname;
2768
2769   sys_orig_dir = g_get_current_dir ();
2770   sys_dirname = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
2771   if (!sys_dirname)
2772     {
2773       g_free (sys_orig_dir);
2774       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
2775       return NULL;
2776     }
2777   
2778   if (chdir (sys_dirname) != 0 || chdir ("..") != 0)
2779     {
2780       g_free (sys_dirname);
2781       g_free (sys_orig_dir);
2782       cmpl_errno = errno;
2783       return NULL;
2784     }
2785   g_free (sys_dirname);
2786
2787   sys_cwd = g_get_current_dir ();
2788   result = g_filename_to_utf8 (sys_cwd, -1, NULL, NULL, NULL);
2789   g_free (sys_cwd);
2790
2791   if (chdir (sys_orig_dir) != 0)
2792     {
2793       cmpl_errno = errno;
2794       g_free (sys_orig_dir);
2795       return NULL;
2796     }
2797
2798   g_free (sys_orig_dir);
2799   return result;
2800 }
2801
2802 #endif
2803
2804 /**********************************************************************/
2805 /*                        Completion Operations                       */
2806 /**********************************************************************/
2807
2808 #ifdef HAVE_PWD_H
2809
2810 static PossibleCompletion*
2811 attempt_homedir_completion (gchar           *text_to_complete,
2812                             CompletionState *cmpl_state)
2813 {
2814   gint index, length;
2815
2816   if (!cmpl_state->user_dir_name_buffer &&
2817       !get_pwdb (cmpl_state))
2818     return NULL;
2819   length = strlen (text_to_complete) - 1;
2820
2821   cmpl_state->user_completion_index += 1;
2822
2823   while (cmpl_state->user_completion_index < cmpl_state->user_directories_len)
2824     {
2825       index = first_diff_index (text_to_complete + 1,
2826                                 cmpl_state->user_directories
2827                                 [cmpl_state->user_completion_index].login);
2828
2829       switch (index)
2830         {
2831         case PATTERN_MATCH:
2832           break;
2833         default:
2834           if (cmpl_state->last_valid_char < (index + 1))
2835             cmpl_state->last_valid_char = index + 1;
2836           cmpl_state->user_completion_index += 1;
2837           continue;
2838         }
2839
2840       cmpl_state->the_completion.is_a_completion = 1;
2841       cmpl_state->the_completion.is_directory = TRUE;
2842
2843       append_completion_text ("~", cmpl_state);
2844
2845       append_completion_text (cmpl_state->
2846                               user_directories[cmpl_state->user_completion_index].login,
2847                               cmpl_state);
2848
2849       return append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
2850     }
2851
2852   if (text_to_complete[1]
2853       || cmpl_state->user_completion_index > cmpl_state->user_directories_len)
2854     {
2855       cmpl_state->user_completion_index = -1;
2856       return NULL;
2857     }
2858   else
2859     {
2860       cmpl_state->user_completion_index += 1;
2861       cmpl_state->the_completion.is_a_completion = 1;
2862       cmpl_state->the_completion.is_directory = TRUE;
2863
2864       return append_completion_text ("~" G_DIR_SEPARATOR_S, cmpl_state);
2865     }
2866 }
2867
2868 #endif
2869
2870 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
2871 #define FOLD(c) (tolower(c))
2872 #else
2873 #define FOLD(c) (c)
2874 #endif
2875
2876 /* returns the index (>= 0) of the first differing character,
2877  * PATTERN_MATCH if the completion matches */
2878 static gint
2879 first_diff_index (gchar *pat,
2880                   gchar *text)
2881 {
2882   gint diff = 0;
2883
2884   while (*pat && *text && FOLD (*text) == FOLD (*pat))
2885     {
2886       pat += 1;
2887       text += 1;
2888       diff += 1;
2889     }
2890
2891   if (*pat)
2892     return diff;
2893
2894   return PATTERN_MATCH;
2895 }
2896
2897 static PossibleCompletion*
2898 append_completion_text (gchar           *text,
2899                         CompletionState *cmpl_state)
2900 {
2901   gint len, i = 1;
2902
2903   if (!cmpl_state->the_completion.text)
2904     return NULL;
2905
2906   len = strlen (text) + strlen (cmpl_state->the_completion.text) + 1;
2907
2908   if (cmpl_state->the_completion.text_alloc > len)
2909     {
2910       strcat (cmpl_state->the_completion.text, text);
2911       return &cmpl_state->the_completion;
2912     }
2913
2914   while (i < len)
2915     i <<= 1;
2916
2917   cmpl_state->the_completion.text_alloc = i;
2918
2919   cmpl_state->the_completion.text = (gchar*) g_realloc (cmpl_state->the_completion.text, i);
2920
2921   if (!cmpl_state->the_completion.text)
2922     return NULL;
2923   else
2924     {
2925       strcat (cmpl_state->the_completion.text, text);
2926       return &cmpl_state->the_completion;
2927     }
2928 }
2929
2930 static CompletionDir*
2931 find_completion_dir (gchar          *text_to_complete,
2932                     gchar          **remaining_text,
2933                     CompletionState *cmpl_state)
2934 {
2935   gchar* first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2936   CompletionDir* dir = cmpl_state->reference_dir;
2937   CompletionDir* next;
2938   *remaining_text = text_to_complete;
2939
2940   while (first_slash)
2941     {
2942       gint len = first_slash - *remaining_text;
2943       gint found = 0;
2944       gchar *found_name = NULL;         /* Quiet gcc */
2945       gint i;
2946       gchar* pat_buf = g_new (gchar, len + 1);
2947
2948       strncpy (pat_buf, *remaining_text, len);
2949       pat_buf[len] = 0;
2950
2951       for (i = 0; i < dir->sent->entry_count; i += 1)
2952         {
2953           if (dir->sent->entries[i].is_dir &&
2954              fnmatch (pat_buf, dir->sent->entries[i].entry_name,
2955                       FNMATCH_FLAGS)!= FNM_NOMATCH)
2956             {
2957               if (found)
2958                 {
2959                   g_free (pat_buf);
2960                   return dir;
2961                 }
2962               else
2963                 {
2964                   found = 1;
2965                   found_name = dir->sent->entries[i].entry_name;
2966                 }
2967             }
2968         }
2969
2970       if (!found)
2971         {
2972           /* Perhaps we are trying to open an automount directory */
2973           found_name = pat_buf;
2974         }
2975
2976       next = open_relative_dir (found_name, dir, cmpl_state);
2977       
2978       if (!next)
2979         {
2980           g_free (pat_buf);
2981           return NULL;
2982         }
2983       
2984       next->cmpl_parent = dir;
2985       
2986       dir = next;
2987       
2988       if (!correct_dir_fullname (dir))
2989         {
2990           g_free (pat_buf);
2991           return NULL;
2992         }
2993       
2994       *remaining_text = first_slash + 1;
2995       first_slash = strchr (*remaining_text, G_DIR_SEPARATOR);
2996
2997       g_free (pat_buf);
2998     }
2999
3000   return dir;
3001 }
3002
3003 static void
3004 update_cmpl (PossibleCompletion *poss,
3005              CompletionState    *cmpl_state)
3006 {
3007   gint cmpl_len;
3008
3009   if (!poss || !cmpl_is_a_completion (poss))
3010     return;
3011
3012   cmpl_len = strlen (cmpl_this_completion (poss));
3013
3014   if (cmpl_state->updated_text_alloc < cmpl_len + 1)
3015     {
3016       cmpl_state->updated_text =
3017         (gchar*)g_realloc (cmpl_state->updated_text,
3018                            cmpl_state->updated_text_alloc);
3019       cmpl_state->updated_text_alloc = 2*cmpl_len;
3020     }
3021
3022   if (cmpl_state->updated_text_len < 0)
3023     {
3024       strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3025       cmpl_state->updated_text_len = cmpl_len;
3026       cmpl_state->re_complete = cmpl_is_directory (poss);
3027     }
3028   else if (cmpl_state->updated_text_len == 0)
3029     {
3030       cmpl_state->re_complete = FALSE;
3031     }
3032   else
3033     {
3034       gint first_diff =
3035         first_diff_index (cmpl_state->updated_text,
3036                           cmpl_this_completion (poss));
3037
3038       cmpl_state->re_complete = FALSE;
3039
3040       if (first_diff == PATTERN_MATCH)
3041         return;
3042
3043       if (first_diff > cmpl_state->updated_text_len)
3044         strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3045
3046       cmpl_state->updated_text_len = first_diff;
3047       cmpl_state->updated_text[first_diff] = 0;
3048     }
3049 }
3050
3051 static PossibleCompletion*
3052 attempt_file_completion (CompletionState *cmpl_state)
3053 {
3054   gchar *pat_buf, *first_slash;
3055   CompletionDir *dir = cmpl_state->active_completion_dir;
3056
3057   dir->cmpl_index += 1;
3058
3059   if (dir->cmpl_index == dir->sent->entry_count)
3060     {
3061       if (dir->cmpl_parent == NULL)
3062         {
3063           cmpl_state->active_completion_dir = NULL;
3064
3065           return NULL;
3066         }
3067       else
3068         {
3069           cmpl_state->active_completion_dir = dir->cmpl_parent;
3070
3071           return attempt_file_completion (cmpl_state);
3072         }
3073     }
3074
3075   g_assert (dir->cmpl_text);
3076
3077   first_slash = strchr (dir->cmpl_text, G_DIR_SEPARATOR);
3078
3079   if (first_slash)
3080     {
3081       gint len = first_slash - dir->cmpl_text;
3082
3083       pat_buf = g_new (gchar, len + 1);
3084       strncpy (pat_buf, dir->cmpl_text, len);
3085       pat_buf[len] = 0;
3086     }
3087   else
3088     {
3089       gint len = strlen (dir->cmpl_text);
3090
3091       pat_buf = g_new (gchar, len + 2);
3092       strcpy (pat_buf, dir->cmpl_text);
3093       /* Don't append a * if the user entered one herself.
3094        * This way one can complete *.h and don't get matches
3095        * on any .help files, for instance.
3096        */
3097       if (strchr (pat_buf, '*') == NULL)
3098         strcpy (pat_buf + len, "*");
3099     }
3100
3101   if (first_slash)
3102     {
3103       if (dir->sent->entries[dir->cmpl_index].is_dir)
3104         {
3105           if (fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3106                        FNMATCH_FLAGS) != FNM_NOMATCH)
3107             {
3108               CompletionDir* new_dir;
3109
3110               new_dir = open_relative_dir (dir->sent->entries[dir->cmpl_index].entry_name,
3111                                            dir, cmpl_state);
3112
3113               if (!new_dir)
3114                 {
3115                   g_free (pat_buf);
3116                   return NULL;
3117                 }
3118
3119               new_dir->cmpl_parent = dir;
3120
3121               new_dir->cmpl_index = -1;
3122               new_dir->cmpl_text = first_slash + 1;
3123
3124               cmpl_state->active_completion_dir = new_dir;
3125
3126               g_free (pat_buf);
3127               return attempt_file_completion (cmpl_state);
3128             }
3129           else
3130             {
3131               g_free (pat_buf);
3132               return attempt_file_completion (cmpl_state);
3133             }
3134         }
3135       else
3136         {
3137           g_free (pat_buf);
3138           return attempt_file_completion (cmpl_state);
3139         }
3140     }
3141   else
3142     {
3143       if (dir->cmpl_parent != NULL)
3144         {
3145           append_completion_text (dir->fullname +
3146                                   strlen (cmpl_state->completion_dir->fullname) + 1,
3147                                   cmpl_state);
3148           append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3149         }
3150
3151       append_completion_text (dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
3152
3153       cmpl_state->the_completion.is_a_completion =
3154         fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3155                  FNMATCH_FLAGS) != FNM_NOMATCH;
3156
3157       cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
3158       if (dir->sent->entries[dir->cmpl_index].is_dir)
3159         append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3160
3161       g_free (pat_buf);
3162       return &cmpl_state->the_completion;
3163     }
3164 }
3165
3166 #ifdef HAVE_PWD_H
3167
3168 static gint
3169 get_pwdb (CompletionState* cmpl_state)
3170 {
3171   struct passwd *pwd_ptr;
3172   gchar* buf_ptr;
3173   gchar *utf8;
3174   gint len = 0, i, count = 0;
3175
3176   if (cmpl_state->user_dir_name_buffer)
3177     return TRUE;
3178   setpwent ();
3179
3180   while ((pwd_ptr = getpwent ()) != NULL)
3181     {
3182       utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3183       len += strlen (utf8);
3184       g_free (utf8);
3185       utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3186       len += strlen (utf8);
3187       g_free (utf8);
3188       len += 2;
3189       count += 1;
3190     }
3191
3192   setpwent ();
3193
3194   cmpl_state->user_dir_name_buffer = g_new (gchar, len);
3195   cmpl_state->user_directories = g_new (CompletionUserDir, count);
3196   cmpl_state->user_directories_len = count;
3197
3198   buf_ptr = cmpl_state->user_dir_name_buffer;
3199
3200   for (i = 0; i < count; i += 1)
3201     {
3202       pwd_ptr = getpwent ();
3203       if (!pwd_ptr)
3204         {
3205           cmpl_errno = errno;
3206           goto error;
3207         }
3208
3209       utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3210       strcpy (buf_ptr, utf8);
3211       g_free (utf8);
3212
3213       cmpl_state->user_directories[i].login = buf_ptr;
3214
3215       buf_ptr += strlen (buf_ptr);
3216       buf_ptr += 1;
3217
3218       utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3219       strcpy (buf_ptr, utf8);
3220       g_free (utf8);
3221
3222       cmpl_state->user_directories[i].homedir = buf_ptr;
3223
3224       buf_ptr += strlen (buf_ptr);
3225       buf_ptr += 1;
3226     }
3227
3228   qsort (cmpl_state->user_directories,
3229          cmpl_state->user_directories_len,
3230          sizeof (CompletionUserDir),
3231          compare_user_dir);
3232
3233   endpwent ();
3234
3235   return TRUE;
3236
3237 error:
3238
3239   if (cmpl_state->user_dir_name_buffer)
3240     g_free (cmpl_state->user_dir_name_buffer);
3241   if (cmpl_state->user_directories)
3242     g_free (cmpl_state->user_directories);
3243
3244   cmpl_state->user_dir_name_buffer = NULL;
3245   cmpl_state->user_directories = NULL;
3246
3247   return FALSE;
3248 }
3249
3250 static gint
3251 compare_user_dir (const void *a,
3252                   const void *b)
3253 {
3254   return strcmp ((((CompletionUserDir*)a))->login,
3255                  (((CompletionUserDir*)b))->login);
3256 }
3257
3258 #endif
3259
3260 static gint
3261 compare_cmpl_dir (const void *a,
3262                   const void *b)
3263 {
3264 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3265   return strcmp ((((CompletionDirEntry*)a))->entry_name,
3266                  (((CompletionDirEntry*)b))->entry_name);
3267 #else
3268   return g_strcasecmp ((((CompletionDirEntry*)a))->entry_name,
3269                        (((CompletionDirEntry*)b))->entry_name);
3270 #endif
3271 }
3272
3273 static gint
3274 cmpl_state_okay (CompletionState* cmpl_state)
3275 {
3276   return  cmpl_state && cmpl_state->reference_dir;
3277 }
3278
3279 static gchar*
3280 cmpl_strerror (gint err)
3281 {
3282   if (err == CMPL_ERRNO_TOO_LONG)
3283     return _("Name too long");
3284   else if (err == CMPL_ERRNO_DID_NOT_CONVERT)
3285     return _("Couldn't convert filename");
3286   else
3287     return g_strerror (err);
3288 }