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