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