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