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