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