]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesel.c
Forgot to free GList on destroy, and to destroy fileop dialog boxes.
[~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_clist_set_column_width (GTK_CLIST (filesel->dir_list), 0, 150);
429   gtk_signal_connect (GTK_OBJECT (filesel->dir_list), "select_row",
430                       (GtkSignalFunc) gtk_file_selection_dir_button, 
431                       (gpointer) filesel);
432   gtk_clist_set_policy (GTK_CLIST (filesel->dir_list), GTK_POLICY_ALWAYS, GTK_POLICY_AUTOMATIC);
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_clist_set_column_width (GTK_CLIST (filesel->file_list), 0, 150);
441   gtk_signal_connect (GTK_OBJECT (filesel->file_list), "select_row",
442                       (GtkSignalFunc) gtk_file_selection_file_button, 
443                       (gpointer) filesel);
444   gtk_clist_set_policy (GTK_CLIST (filesel->file_list), GTK_POLICY_ALWAYS, GTK_POLICY_AUTOMATIC);
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     list = filesel->history_list;
587     while (list) {
588       callback_arg = list->data;
589       g_free (callback_arg->directory);
590       list = list->next;
591     }
592     g_list_free (filesel->history_list);
593   }
594   
595   cmpl_free_state (filesel->cmpl_state);
596
597   if (GTK_OBJECT_CLASS (parent_class)->destroy)
598     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
599 }
600
601 /* Begin file operations callbacks */
602
603 static void
604 gtk_file_selection_fileop_error (gchar *error_message)
605 {
606   GtkWidget *label;
607   GtkWidget *vbox;
608   GtkWidget *button;
609   GtkWidget *dialog;
610   
611   g_return_if_fail (error_message != NULL);
612   
613   /* main dialog */
614   dialog = gtk_dialog_new ();
615   /*
616   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
617                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
618                       (gpointer) fs);
619   */
620   gtk_window_set_title (GTK_WINDOW (dialog), "Error");
621   gtk_window_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
622   
623   vbox = gtk_vbox_new(FALSE, 0);
624   gtk_container_border_width(GTK_CONTAINER(vbox), 8);
625   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
626                      FALSE, FALSE, 0);
627   gtk_widget_show(vbox);
628
629   label = gtk_label_new(error_message);
630   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
631   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
632   gtk_widget_show(label);
633
634   /* yes, we free it */
635   g_free (error_message);
636   
637   /* close button */
638   button = gtk_button_new_with_label ("Close");
639   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
640                              (GtkSignalFunc) gtk_widget_destroy, 
641                              (gpointer) dialog);
642   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
643                      button, TRUE, TRUE, 0);
644   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
645   gtk_widget_grab_default(button);
646   gtk_widget_show (button);
647
648   gtk_widget_show (dialog);
649 }
650
651 static void
652 gtk_file_selection_fileop_destroy (GtkWidget *widget, gpointer data)
653 {
654   GtkFileSelection *fs = data;
655
656   g_return_if_fail (fs != NULL);
657   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
658   
659   fs->fileop_dialog = NULL;
660 }
661
662
663 static void
664 gtk_file_selection_create_dir_confirmed (GtkWidget *widget, gpointer data)
665 {
666   GtkFileSelection *fs = data;
667   gchar *dirname;
668   gchar *path;
669   gchar *full_path;
670   gchar *buf;
671   CompletionState *cmpl_state;
672   
673   g_return_if_fail (fs != NULL);
674   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
675
676   dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
677   cmpl_state = (CompletionState*) fs->cmpl_state;
678   path = cmpl_reference_position (cmpl_state);
679   
680   full_path = g_strconcat (path, "/", dirname, NULL);
681   if ( (mkdir (full_path, 0755) < 0) ) 
682     {
683       buf = g_strconcat ("Error creating directory \"", dirname, "\":  ", 
684                          g_strerror(errno), NULL);
685       gtk_file_selection_fileop_error (buf);
686     }
687   g_free (full_path);
688   
689   gtk_widget_destroy (fs->fileop_dialog);
690   gtk_file_selection_populate (fs, "", FALSE);
691 }
692   
693 static void
694 gtk_file_selection_create_dir (GtkWidget *widget, gpointer data)
695 {
696   GtkFileSelection *fs = data;
697   GtkWidget *label;
698   GtkWidget *dialog;
699   GtkWidget *vbox;
700   GtkWidget *button;
701
702   g_return_if_fail (fs != NULL);
703   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
704
705   if (fs->fileop_dialog)
706           return;
707   
708   /* main dialog */
709   fs->fileop_dialog = dialog = gtk_dialog_new ();
710   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
711                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
712                       (gpointer) fs);
713   gtk_window_set_title (GTK_WINDOW (dialog), "Create Directory");
714   gtk_window_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
715   gtk_widget_show (dialog);
716   
717   vbox = gtk_vbox_new(FALSE, 0);
718   gtk_container_border_width(GTK_CONTAINER(vbox), 8);
719   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
720                      FALSE, FALSE, 0);
721   gtk_widget_show(vbox);
722   
723   label = gtk_label_new("Directory name:");
724   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
725   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
726   gtk_widget_show(label);
727
728   /*  The directory entry widget  */
729   fs->fileop_entry = gtk_entry_new ();
730   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
731                       TRUE, TRUE, 5);
732   GTK_WIDGET_SET_FLAGS(fs->fileop_entry, GTK_CAN_DEFAULT);
733   gtk_widget_show (fs->fileop_entry);
734   
735   /* buttons */
736   button = gtk_button_new_with_label ("Create");
737   gtk_signal_connect (GTK_OBJECT (button), "clicked",
738                       (GtkSignalFunc) gtk_file_selection_create_dir_confirmed, 
739                       (gpointer) fs);
740   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
741                      button, TRUE, TRUE, 0);
742   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
743   gtk_widget_show(button);
744   
745   button = gtk_button_new_with_label ("Cancel");
746   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
747                              (GtkSignalFunc) gtk_widget_destroy, 
748                              (gpointer) dialog);
749   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
750                      button, TRUE, TRUE, 0);
751   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
752   gtk_widget_grab_default(button);
753   gtk_widget_show (button);
754 }
755
756 static void
757 gtk_file_selection_delete_file_confirmed (GtkWidget *widget, gpointer data)
758 {
759   GtkFileSelection *fs = data;
760   CompletionState *cmpl_state;
761   gchar *path;
762   gchar *full_path;
763   gchar *buf;
764   
765   g_return_if_fail (fs != NULL);
766   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
767
768   cmpl_state = (CompletionState*) fs->cmpl_state;
769   path = cmpl_reference_position (cmpl_state);
770   
771   full_path = g_strconcat (path, "/", fs->fileop_file, NULL);
772   if ( (unlink (full_path) < 0) ) 
773     {
774       buf = g_strconcat ("Error deleting file \"", fs->fileop_file, "\":  ", 
775                          g_strerror(errno), NULL);
776       gtk_file_selection_fileop_error (buf);
777     }
778   g_free (full_path);
779   
780   gtk_widget_destroy (fs->fileop_dialog);
781   gtk_file_selection_populate (fs, "", FALSE);
782 }
783
784 static void
785 gtk_file_selection_delete_file (GtkWidget *widget, gpointer data)
786 {
787   GtkFileSelection *fs = data;
788   GtkWidget *label;
789   GtkWidget *vbox;
790   GtkWidget *button;
791   GtkWidget *dialog;
792   gchar *filename;
793   gchar *buf;
794   
795   g_return_if_fail (fs != NULL);
796   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
797
798   if (fs->fileop_dialog)
799           return;
800
801   filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
802   if (strlen(filename) < 1)
803           return;
804
805   fs->fileop_file = filename;
806   
807   /* main dialog */
808   fs->fileop_dialog = dialog = gtk_dialog_new ();
809   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
810                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
811                       (gpointer) fs);
812   gtk_window_set_title (GTK_WINDOW (dialog), "Delete File");
813   gtk_window_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
814   
815   vbox = gtk_vbox_new(FALSE, 0);
816   gtk_container_border_width(GTK_CONTAINER(vbox), 8);
817   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
818                      FALSE, FALSE, 0);
819   gtk_widget_show(vbox);
820
821   buf = g_strconcat ("Really delete file \"", filename, "\" ?", NULL);
822   label = gtk_label_new(buf);
823   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
824   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
825   gtk_widget_show(label);
826   g_free(buf);
827   
828   /* buttons */
829   button = gtk_button_new_with_label ("Delete");
830   gtk_signal_connect (GTK_OBJECT (button), "clicked",
831                       (GtkSignalFunc) gtk_file_selection_delete_file_confirmed, 
832                       (gpointer) fs);
833   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
834                      button, TRUE, TRUE, 0);
835   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
836   gtk_widget_show(button);
837   
838   button = gtk_button_new_with_label ("Cancel");
839   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
840                              (GtkSignalFunc) gtk_widget_destroy, 
841                              (gpointer) dialog);
842   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
843                      button, TRUE, TRUE, 0);
844   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
845   gtk_widget_grab_default(button);
846   gtk_widget_show (button);
847
848   gtk_widget_show (dialog);
849 }
850
851 static void
852 gtk_file_selection_rename_file_confirmed (GtkWidget *widget, gpointer data)
853 {
854   GtkFileSelection *fs = data;
855   gchar *buf;
856   gchar *file;
857   gchar *path;
858   gchar *new_filename;
859   gchar *old_filename;
860   CompletionState *cmpl_state;
861   
862   g_return_if_fail (fs != NULL);
863   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
864
865   file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
866   cmpl_state = (CompletionState*) fs->cmpl_state;
867   path = cmpl_reference_position (cmpl_state);
868   
869   new_filename = g_strconcat (path, "/", file, NULL);
870   old_filename = g_strconcat (path, "/", fs->fileop_file, NULL);
871
872   if ( (rename (old_filename, new_filename)) < 0) 
873     {
874       buf = g_strconcat ("Error renaming file \"", file, "\":  ", 
875                          g_strerror(errno), NULL);
876       gtk_file_selection_fileop_error (buf);
877     }
878   g_free (new_filename);
879   g_free (old_filename);
880   
881   gtk_widget_destroy (fs->fileop_dialog);
882   gtk_file_selection_populate (fs, "", FALSE);
883 }
884   
885 static void
886 gtk_file_selection_rename_file (GtkWidget *widget, gpointer data)
887 {
888   GtkFileSelection *fs = data;
889   GtkWidget *label;
890   GtkWidget *dialog;
891   GtkWidget *vbox;
892   GtkWidget *button;
893   gchar *buf;
894   
895   g_return_if_fail (fs != NULL);
896   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
897
898   if (fs->fileop_dialog)
899           return;
900
901   fs->fileop_file = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
902   if (strlen(fs->fileop_file) < 1)
903           return;
904   
905   /* main dialog */
906   fs->fileop_dialog = dialog = gtk_dialog_new ();
907   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
908                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
909                       (gpointer) fs);
910   gtk_window_set_title (GTK_WINDOW (dialog), "Rename File");
911   gtk_window_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
912   gtk_widget_show (dialog);
913   
914   vbox = gtk_vbox_new(FALSE, 0);
915   gtk_container_border_width(GTK_CONTAINER(vbox), 8);
916   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
917                      FALSE, FALSE, 0);
918   gtk_widget_show(vbox);
919   
920   buf = g_strconcat ("Rename file \"", fs->fileop_file, "\" to:", NULL);
921   label = gtk_label_new(buf);
922   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
923   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
924   gtk_widget_show(label);
925   g_free(buf);
926
927   /* New filename entry */
928   fs->fileop_entry = gtk_entry_new ();
929   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
930                       TRUE, TRUE, 5);
931   GTK_WIDGET_SET_FLAGS(fs->fileop_entry, GTK_CAN_DEFAULT);
932   gtk_widget_show (fs->fileop_entry);
933   
934   gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
935   gtk_entry_select_region (GTK_ENTRY (fs->fileop_entry),
936                            0, strlen (fs->fileop_file));
937
938   /* buttons */
939   button = gtk_button_new_with_label ("Rename");
940   gtk_signal_connect (GTK_OBJECT (button), "clicked",
941                       (GtkSignalFunc) gtk_file_selection_rename_file_confirmed, 
942                       (gpointer) fs);
943   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
944                      button, TRUE, TRUE, 0);
945   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
946   gtk_widget_show(button);
947   
948   button = gtk_button_new_with_label ("Cancel");
949   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
950                              (GtkSignalFunc) gtk_widget_destroy, 
951                              (gpointer) dialog);
952   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
953                      button, TRUE, TRUE, 0);
954   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
955   gtk_widget_grab_default(button);
956   gtk_widget_show (button);
957 }
958
959
960 static gint
961 gtk_file_selection_key_press (GtkWidget   *widget,
962                               GdkEventKey *event,
963                               gpointer     user_data)
964 {
965   GtkFileSelection *fs;
966   char *text;
967
968   g_return_val_if_fail (widget != NULL, FALSE);
969   g_return_val_if_fail (event != NULL, FALSE);
970
971   if (event->keyval == GDK_Tab)
972     {
973       gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
974
975       fs = GTK_FILE_SELECTION (user_data);
976       text = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
977       gtk_file_selection_populate (fs, text, TRUE);
978
979       return TRUE;
980     }
981
982   return FALSE;
983 }
984
985
986 static void
987 gtk_file_selection_history_callback (GtkWidget *widget, gpointer data)
988 {
989   GtkFileSelection *fs = data;
990   HistoryCallbackArg *callback_arg;
991   GList *list;
992
993   g_return_if_fail (fs != NULL);
994   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
995
996   list = fs->history_list;
997   
998   while (list) {
999     callback_arg = list->data;
1000     
1001     if (callback_arg->menu_item == widget)
1002       {
1003         gtk_file_selection_populate (fs, callback_arg->directory, FALSE);
1004         break;
1005       }
1006     
1007     list = list->next;
1008   }
1009 }
1010
1011 static void 
1012 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
1013                                         gchar *current_directory)
1014 {
1015   HistoryCallbackArg *callback_arg;
1016   GtkWidget *menu_item;
1017   GList *list;
1018   gchar *current_dir;
1019   gchar *directory;
1020   gint dir_len;
1021   gint i;
1022   
1023   g_return_if_fail (fs != NULL);
1024   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1025   g_return_if_fail (current_directory != NULL);
1026   
1027   list = fs->history_list;
1028
1029   if (fs->history_menu) 
1030     {
1031       while (list) {
1032         callback_arg = list->data;
1033         g_free (callback_arg->directory);
1034         list = list->next;
1035       }
1036       g_list_free (fs->history_list);
1037       fs->history_list = NULL;
1038       
1039       gtk_widget_destroy (fs->history_menu);
1040     }
1041   
1042   fs->history_menu = gtk_menu_new();
1043
1044   current_dir = g_strdup(current_directory);
1045
1046   dir_len = strlen (current_dir);
1047
1048   for (i = dir_len; i >= 0; i--)
1049     {
1050       /* the i == dir_len is to catch the full path for the first 
1051        * entry. */
1052       if ( (current_dir[i] == '/') || (i == dir_len))
1053         {
1054           /* another small hack to catch the full path */
1055           if (i != dir_len) 
1056                   current_dir[i + 1] = '\0';
1057           menu_item = gtk_menu_item_new_with_label (current_dir);
1058           directory = g_strdup (current_dir);
1059           
1060           callback_arg = g_new (HistoryCallbackArg, 1);
1061           callback_arg->menu_item = menu_item;
1062           
1063           /* since the autocompletion gets confused if you don't 
1064            * supply a trailing '/' on a dir entry, set the full
1065            * (current) path to "" which just refreshes the filesel */
1066           if (dir_len == i) {
1067             callback_arg->directory = g_strdup ("");
1068           } else {
1069             callback_arg->directory = directory;
1070           }
1071           
1072           fs->history_list = g_list_append (fs->history_list, callback_arg);
1073           
1074           gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
1075                               (GtkSignalFunc) gtk_file_selection_history_callback,
1076                               (gpointer) fs);
1077           gtk_menu_append (GTK_MENU (fs->history_menu), menu_item);
1078           gtk_widget_show (menu_item);
1079         }
1080     }
1081
1082   gtk_option_menu_set_menu (GTK_OPTION_MENU (fs->history_pulldown), 
1083                             fs->history_menu);
1084   g_free (current_dir);
1085 }
1086
1087
1088
1089 static void
1090 gtk_file_selection_file_button (GtkWidget *widget,
1091                                gint row, 
1092                                gint column, 
1093                                GdkEventButton *bevent,
1094                                gpointer user_data)
1095 {
1096   GtkFileSelection *fs = NULL;
1097   gchar *filename;
1098   
1099   g_return_if_fail (GTK_IS_CLIST (widget));
1100
1101   fs = GTK_FILE_SELECTION (user_data);
1102   g_return_if_fail (fs != NULL);
1103   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1104
1105   filename = gtk_clist_get_row_data (GTK_CLIST (fs->file_list), row);
1106   
1107   if (bevent && filename) {
1108   
1109     switch (bevent->type)
1110       {
1111       case GDK_BUTTON_PRESS:
1112         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1113         break;
1114       
1115       case GDK_2BUTTON_PRESS:
1116         gtk_button_clicked (GTK_BUTTON (fs->ok_button));
1117         break;
1118         
1119       default:
1120         break;
1121       }
1122   }
1123 }
1124
1125 static void
1126 gtk_file_selection_dir_button (GtkWidget *widget,
1127                                gint row, 
1128                                gint column, 
1129                                GdkEventButton *bevent,
1130                                gpointer user_data)
1131 {
1132   GtkFileSelection *fs = NULL;
1133   gchar *filename;
1134   
1135   g_return_if_fail (GTK_IS_CLIST (widget));
1136
1137   fs = GTK_FILE_SELECTION (user_data);
1138   g_return_if_fail (fs != NULL);
1139   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1140
1141   filename = gtk_clist_get_row_data (GTK_CLIST (fs->dir_list), row);
1142   
1143   if (bevent && filename) {
1144   
1145     switch (bevent->type)
1146       {
1147       case GDK_BUTTON_PRESS:
1148         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1149         break;
1150       
1151       case GDK_2BUTTON_PRESS:
1152         gtk_file_selection_populate (fs, filename, FALSE);
1153         break;
1154         
1155       default:
1156         break;
1157       }
1158   }
1159 }
1160
1161 static void
1162 gtk_file_selection_populate (GtkFileSelection *fs,
1163                              gchar            *rel_path,
1164                              gint              try_complete)
1165 {
1166   CompletionState *cmpl_state;
1167   PossibleCompletion* poss;
1168   gchar* filename;
1169   gint row;
1170   gchar* rem_path = rel_path;
1171   gchar* sel_text;
1172   gchar* text[2];
1173   gint did_recurse = FALSE;
1174   gint possible_count = 0;
1175   gint selection_index = -1;
1176   
1177   g_return_if_fail (fs != NULL);
1178   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1179   
1180   cmpl_state = (CompletionState*) fs->cmpl_state;
1181   poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
1182
1183   if (!cmpl_state_okay (cmpl_state))
1184     {
1185       /* Something went wrong. */
1186       gtk_file_selection_abort (fs);
1187       return;
1188     }
1189
1190   g_assert (cmpl_state->reference_dir);
1191
1192   gtk_clist_freeze (GTK_CLIST (fs->dir_list));
1193   gtk_clist_clear (GTK_CLIST (fs->dir_list));
1194   gtk_clist_freeze (GTK_CLIST (fs->file_list));
1195   gtk_clist_clear (GTK_CLIST (fs->file_list));
1196
1197   /* Set the dir_list to include ./ and ../ */
1198   text[1] = NULL;
1199   text[0] = "./";
1200   row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1201   gtk_clist_set_row_data (GTK_CLIST (fs->dir_list), row, "./");
1202
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   while (poss)
1208     {
1209       if (cmpl_is_a_completion (poss))
1210         {
1211           possible_count += 1;
1212
1213           filename = g_strdup (cmpl_this_completion (poss));
1214
1215           text[0] = filename;
1216           
1217           if (cmpl_is_directory (poss))
1218             {
1219               if (strcmp (filename, "./") != 0 &&
1220                   strcmp (filename, "../") != 0)
1221                 {
1222                   row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1223                   gtk_clist_set_row_data (GTK_CLIST (fs->dir_list), row, 
1224                                           filename);
1225                 }
1226             }
1227           else
1228             {
1229               row = gtk_clist_append (GTK_CLIST (fs->file_list), text);
1230               gtk_clist_set_row_data (GTK_CLIST (fs->file_list), row, 
1231                                       filename);
1232             }
1233         }
1234
1235       poss = cmpl_next_completion (cmpl_state);
1236     }
1237
1238   gtk_clist_thaw (GTK_CLIST (fs->dir_list));
1239   gtk_clist_thaw (GTK_CLIST (fs->file_list));
1240
1241   /* File lists are set. */
1242
1243   g_assert (cmpl_state->reference_dir);
1244
1245   if (try_complete)
1246     {
1247
1248       /* User is trying to complete filenames, so advance the user's input
1249        * string to the updated_text, which is the common leading substring
1250        * of all possible completions, and if its a directory attempt
1251        * attempt completions in it. */
1252
1253       if (cmpl_updated_text (cmpl_state)[0])
1254         {
1255
1256           if (cmpl_updated_dir (cmpl_state))
1257             {
1258               gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
1259
1260               did_recurse = TRUE;
1261
1262               gtk_file_selection_populate (fs, dir_name, TRUE);
1263
1264               g_free (dir_name);
1265             }
1266           else
1267             {
1268               if (fs->selection_entry)
1269                       gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
1270                                           cmpl_updated_text (cmpl_state));
1271             }
1272         }
1273       else
1274         {
1275           selection_index = cmpl_last_valid_char (cmpl_state) -
1276                             (strlen (rel_path) - strlen (rem_path));
1277           if (fs->selection_entry)
1278             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
1279         }
1280     }
1281   else
1282     {
1283       if (fs->selection_entry)
1284         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
1285     }
1286
1287   if (!did_recurse)
1288     {
1289       if (fs->selection_entry)
1290         gtk_entry_set_position (GTK_ENTRY (fs->selection_entry), selection_index);
1291
1292       if (fs->selection_entry)
1293         {
1294           sel_text = g_new (char, strlen (cmpl_reference_position (cmpl_state)) +
1295                             sizeof ("Selection: "));
1296           strcpy (sel_text, "Selection: ");
1297           strcat (sel_text, cmpl_reference_position (cmpl_state));
1298
1299           gtk_label_set (GTK_LABEL (fs->selection_text), sel_text);
1300           g_free (sel_text);
1301         }
1302
1303       if (fs->history_pulldown) 
1304         {
1305           gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
1306         }
1307       
1308     }
1309 }
1310
1311 static void
1312 gtk_file_selection_abort (GtkFileSelection *fs)
1313 {
1314   gchar err_buf[256];
1315
1316   sprintf (err_buf, "Directory unreadable: %s", cmpl_strerror (cmpl_errno));
1317
1318   /*  BEEP gdk_beep();  */
1319
1320   if (fs->selection_entry)
1321     gtk_label_set (GTK_LABEL (fs->selection_text), err_buf);
1322 }
1323
1324 /**********************************************************************/
1325 /*                        External Interface                          */
1326 /**********************************************************************/
1327
1328 /* The four completion state selectors
1329  */
1330 static gchar*
1331 cmpl_updated_text (CompletionState* cmpl_state)
1332 {
1333   return cmpl_state->updated_text;
1334 }
1335
1336 static gint
1337 cmpl_updated_dir (CompletionState* cmpl_state)
1338 {
1339   return cmpl_state->re_complete;
1340 }
1341
1342 static gchar*
1343 cmpl_reference_position (CompletionState* cmpl_state)
1344 {
1345   return cmpl_state->reference_dir->fullname;
1346 }
1347
1348 static gint
1349 cmpl_last_valid_char (CompletionState* cmpl_state)
1350 {
1351   return cmpl_state->last_valid_char;
1352 }
1353
1354 static gchar*
1355 cmpl_completion_fullname (gchar* text, CompletionState* cmpl_state)
1356 {
1357   if (text[0] == '/')
1358     {
1359       strcpy (cmpl_state->updated_text, text);
1360     }
1361   else if (text[0] == '~')
1362     {
1363       CompletionDir* dir;
1364       char* slash;
1365
1366       dir = open_user_dir (text, cmpl_state);
1367
1368       if (!dir)
1369         {
1370           /* spencer says just return ~something, so
1371            * for now just do it. */
1372           strcpy (cmpl_state->updated_text, text);
1373         }
1374       else
1375         {
1376
1377           strcpy (cmpl_state->updated_text, dir->fullname);
1378
1379           slash = strchr (text, '/');
1380
1381           if (slash)
1382             strcat (cmpl_state->updated_text, slash);
1383         }
1384     }
1385   else
1386     {
1387       strcpy (cmpl_state->updated_text, cmpl_state->reference_dir->fullname);
1388       strcat (cmpl_state->updated_text, "/");
1389       strcat (cmpl_state->updated_text, text);
1390     }
1391
1392   return cmpl_state->updated_text;
1393 }
1394
1395 /* The three completion selectors
1396  */
1397 static gchar*
1398 cmpl_this_completion (PossibleCompletion* pc)
1399 {
1400   return pc->text;
1401 }
1402
1403 static gint
1404 cmpl_is_directory (PossibleCompletion* pc)
1405 {
1406   return pc->is_directory;
1407 }
1408
1409 static gint
1410 cmpl_is_a_completion (PossibleCompletion* pc)
1411 {
1412   return pc->is_a_completion;
1413 }
1414
1415 /**********************************************************************/
1416 /*                       Construction, deletion                       */
1417 /**********************************************************************/
1418
1419 static CompletionState*
1420 cmpl_init_state (void)
1421 {
1422   gchar getcwd_buf[2*MAXPATHLEN];
1423   CompletionState *new_state;
1424
1425   new_state = g_new (CompletionState, 1);
1426
1427   if (!getcwd (getcwd_buf, MAXPATHLEN))
1428     {
1429       cmpl_errno = errno;
1430       return NULL;
1431     }
1432
1433   new_state->reference_dir = NULL;
1434   new_state->completion_dir = NULL;
1435   new_state->active_completion_dir = NULL;
1436
1437   if ((new_state->user_home_dir = getenv("HOME")) != NULL)
1438     {
1439       /* if this fails, get_pwdb will fill it in. */
1440       new_state->user_home_dir = g_strdup(new_state->user_home_dir);
1441     }
1442
1443   new_state->directory_storage = NULL;
1444   new_state->directory_sent_storage = NULL;
1445   new_state->last_valid_char = 0;
1446   new_state->updated_text = g_new (gchar, MAXPATHLEN);
1447   new_state->updated_text_alloc = MAXPATHLEN;
1448   new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
1449   new_state->the_completion.text_alloc = MAXPATHLEN;
1450   new_state->user_dir_name_buffer = NULL;
1451   new_state->user_directories = NULL;
1452
1453   new_state->reference_dir =  open_dir (getcwd_buf, new_state);
1454
1455   if (!new_state->reference_dir)
1456     return NULL;
1457
1458   return new_state;
1459 }
1460
1461 static void
1462 cmpl_free_dir_list(GList* dp0)
1463 {
1464   GList *dp = dp0;
1465
1466   while (dp) {
1467     free_dir (dp->data);
1468     dp = dp->next;
1469   }
1470
1471   g_list_free(dp0);
1472 }
1473
1474 static void
1475 cmpl_free_dir_sent_list(GList* dp0)
1476 {
1477   GList *dp = dp0;
1478
1479   while (dp) {
1480     free_dir_sent (dp->data);
1481     dp = dp->next;
1482   }
1483
1484   g_list_free(dp0);
1485 }
1486
1487 static void
1488 cmpl_free_state (CompletionState* cmpl_state)
1489 {
1490   cmpl_free_dir_list(cmpl_state->directory_storage);
1491   cmpl_free_dir_sent_list(cmpl_state->directory_sent_storage);
1492
1493   if (cmpl_state->user_dir_name_buffer)
1494     g_free (cmpl_state->user_dir_name_buffer);
1495   if (cmpl_state->user_directories)
1496     g_free (cmpl_state->user_directories);
1497   if (cmpl_state->the_completion.text)
1498     g_free (cmpl_state->the_completion.text);
1499   if (cmpl_state->updated_text)
1500     g_free (cmpl_state->updated_text);
1501
1502   g_free (cmpl_state);
1503 }
1504
1505 static void
1506 free_dir(CompletionDir* dir)
1507 {
1508   g_free(dir->fullname);
1509   g_free(dir);
1510 }
1511
1512 static void
1513 free_dir_sent(CompletionDirSent* sent)
1514 {
1515   g_free(sent->name_buffer);
1516   g_free(sent->entries);
1517   g_free(sent);
1518 }
1519
1520 static void
1521 prune_memory_usage(CompletionState *cmpl_state)
1522 {
1523   GList* cdsl = cmpl_state->directory_sent_storage;
1524   GList* cdl = cmpl_state->directory_storage;
1525   GList* cdl0 = cdl;
1526   gint len = 0;
1527
1528   for(; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
1529     cdsl = cdsl->next;
1530
1531   if (cdsl) {
1532     cmpl_free_dir_sent_list(cdsl->next);
1533     cdsl->next = NULL;
1534   }
1535
1536   cmpl_state->directory_storage = NULL;
1537   while (cdl) {
1538     if (cdl->data == cmpl_state->reference_dir)
1539       cmpl_state->directory_storage = g_list_prepend(NULL, cdl->data);
1540     else
1541       free_dir (cdl->data);
1542     cdl = cdl->next;
1543   }
1544
1545   g_list_free(cdl0);
1546 }
1547
1548 /**********************************************************************/
1549 /*                        The main entrances.                         */
1550 /**********************************************************************/
1551
1552 static PossibleCompletion*
1553 cmpl_completion_matches (gchar* text_to_complete,
1554                          gchar** remaining_text,
1555                          CompletionState* cmpl_state)
1556 {
1557   gchar* first_slash;
1558   PossibleCompletion *poss;
1559
1560   prune_memory_usage(cmpl_state);
1561
1562   g_assert(text_to_complete);
1563
1564   cmpl_state->user_completion_index = -1;
1565   cmpl_state->last_completion_text = text_to_complete;
1566   cmpl_state->the_completion.text[0] = 0;
1567   cmpl_state->last_valid_char = 0;
1568   cmpl_state->updated_text_len = -1;
1569   cmpl_state->updated_text[0] = 0;
1570   cmpl_state->re_complete = FALSE;
1571
1572   first_slash = strchr(text_to_complete, '/');
1573
1574   if(text_to_complete[0] == '~' && !first_slash)
1575     {
1576       /* Text starts with ~ and there is no slash, show all the
1577        * home directory completions.
1578        */
1579       poss = attempt_homedir_completion(text_to_complete, cmpl_state);
1580
1581       update_cmpl(poss, cmpl_state);
1582
1583       return poss;
1584     }
1585
1586   cmpl_state->reference_dir =
1587     open_ref_dir(text_to_complete, remaining_text, cmpl_state);
1588
1589   if(!cmpl_state->reference_dir)
1590     return NULL;
1591
1592   cmpl_state->completion_dir =
1593     find_completion_dir(*remaining_text, remaining_text, cmpl_state);
1594
1595   cmpl_state->last_valid_char = *remaining_text - text_to_complete;
1596
1597   if(!cmpl_state->completion_dir)
1598     return NULL;
1599
1600   cmpl_state->completion_dir->cmpl_index = -1;
1601   cmpl_state->completion_dir->cmpl_parent = NULL;
1602   cmpl_state->completion_dir->cmpl_text = *remaining_text;
1603
1604   cmpl_state->active_completion_dir = cmpl_state->completion_dir;
1605
1606   cmpl_state->reference_dir = cmpl_state->completion_dir;
1607
1608   poss = attempt_file_completion(cmpl_state);
1609
1610   update_cmpl(poss, cmpl_state);
1611
1612   return poss;
1613 }
1614
1615 static PossibleCompletion*
1616 cmpl_next_completion (CompletionState* cmpl_state)
1617 {
1618   PossibleCompletion* poss = NULL;
1619
1620   cmpl_state->the_completion.text[0] = 0;
1621
1622   if(cmpl_state->user_completion_index >= 0)
1623     poss = attempt_homedir_completion(cmpl_state->last_completion_text, cmpl_state);
1624   else
1625     poss = attempt_file_completion(cmpl_state);
1626
1627   update_cmpl(poss, cmpl_state);
1628
1629   return poss;
1630 }
1631
1632 /**********************************************************************/
1633 /*                       Directory Operations                         */
1634 /**********************************************************************/
1635
1636 /* Open the directory where completion will begin from, if possible. */
1637 static CompletionDir*
1638 open_ref_dir(gchar* text_to_complete,
1639              gchar** remaining_text,
1640              CompletionState* cmpl_state)
1641 {
1642   gchar* first_slash;
1643   CompletionDir *new_dir;
1644
1645   first_slash = strchr(text_to_complete, '/');
1646
1647   if (text_to_complete[0] == '/' || !cmpl_state->reference_dir)
1648     {
1649       new_dir = open_dir("/", cmpl_state);
1650
1651       if(new_dir)
1652         *remaining_text = text_to_complete + 1;
1653     }
1654   else if (text_to_complete[0] == '~')
1655     {
1656       new_dir = open_user_dir(text_to_complete, cmpl_state);
1657
1658       if(new_dir)
1659         {
1660           if(first_slash)
1661             *remaining_text = first_slash + 1;
1662           else
1663             *remaining_text = text_to_complete + strlen(text_to_complete);
1664         }
1665       else
1666         {
1667           return NULL;
1668         }
1669     }
1670   else
1671     {
1672       *remaining_text = text_to_complete;
1673
1674       new_dir = open_dir(cmpl_state->reference_dir->fullname, cmpl_state);
1675     }
1676
1677   if(new_dir)
1678     {
1679       new_dir->cmpl_index = -1;
1680       new_dir->cmpl_parent = NULL;
1681     }
1682
1683   return new_dir;
1684 }
1685
1686 /* open a directory by user name */
1687 static CompletionDir*
1688 open_user_dir(gchar* text_to_complete,
1689               CompletionState *cmpl_state)
1690 {
1691   gchar *first_slash;
1692   gint cmp_len;
1693
1694   g_assert(text_to_complete && text_to_complete[0] == '~');
1695
1696   first_slash = strchr(text_to_complete, '/');
1697
1698   if (first_slash)
1699     cmp_len = first_slash - text_to_complete - 1;
1700   else
1701     cmp_len = strlen(text_to_complete + 1);
1702
1703   if(!cmp_len)
1704     {
1705       /* ~/ */
1706       if (!cmpl_state->user_home_dir &&
1707           !get_pwdb(cmpl_state))
1708         return NULL;
1709       return open_dir(cmpl_state->user_home_dir, cmpl_state);
1710     }
1711   else
1712     {
1713       /* ~user/ */
1714       char* copy = g_new(char, cmp_len + 1);
1715       struct passwd *pwd;
1716       strncpy(copy, text_to_complete + 1, cmp_len);
1717       copy[cmp_len] = 0;
1718       pwd = getpwnam(copy);
1719       g_free(copy);
1720       if (!pwd)
1721         {
1722           cmpl_errno = errno;
1723           return NULL;
1724         }
1725
1726       return open_dir(pwd->pw_dir, cmpl_state);
1727     }
1728 }
1729
1730 /* open a directory relative the the current relative directory */
1731 static CompletionDir*
1732 open_relative_dir(gchar* dir_name,
1733                   CompletionDir* dir,
1734                   CompletionState *cmpl_state)
1735 {
1736   gchar path_buf[2*MAXPATHLEN];
1737
1738   if(dir->fullname_len + strlen(dir_name) + 2 >= MAXPATHLEN)
1739     {
1740       cmpl_errno = CMPL_ERRNO_TOO_LONG;
1741       return NULL;
1742     }
1743
1744   strcpy(path_buf, dir->fullname);
1745
1746   if(dir->fullname_len > 1)
1747     {
1748       path_buf[dir->fullname_len] = '/';
1749       strcpy(path_buf + dir->fullname_len + 1, dir_name);
1750     }
1751   else
1752     {
1753       strcpy(path_buf + dir->fullname_len, dir_name);
1754     }
1755
1756   return open_dir(path_buf, cmpl_state);
1757 }
1758
1759 /* after the cache lookup fails, really open a new directory */
1760 static CompletionDirSent*
1761 open_new_dir(gchar* dir_name, struct stat* sbuf)
1762 {
1763   CompletionDirSent* sent;
1764   DIR* directory;
1765   gchar *buffer_ptr;
1766   struct dirent *dirent_ptr;
1767   gint buffer_size = 0;
1768   gint entry_count = 0;
1769   gint i;
1770   struct stat ent_sbuf;
1771   char path_buf[MAXPATHLEN*2];
1772   gint path_buf_len;
1773
1774   sent = g_new(CompletionDirSent, 1);
1775   sent->mtime = sbuf->st_mtime;
1776   sent->inode = sbuf->st_ino;
1777
1778   path_buf_len = strlen(dir_name);
1779
1780   if (path_buf_len > MAXPATHLEN)
1781     {
1782       cmpl_errno = CMPL_ERRNO_TOO_LONG;
1783       return NULL;
1784     }
1785
1786   strcpy(path_buf, dir_name);
1787
1788   directory = opendir(dir_name);
1789
1790   if(!directory)
1791     {
1792       cmpl_errno = errno;
1793       return NULL;
1794     }
1795
1796   while((dirent_ptr = readdir(directory)) != NULL)
1797     {
1798       int entry_len = strlen(dirent_ptr->d_name);
1799       buffer_size += entry_len + 1;
1800       entry_count += 1;
1801
1802       if(path_buf_len + entry_len + 2 >= MAXPATHLEN)
1803         {
1804           cmpl_errno = CMPL_ERRNO_TOO_LONG;
1805           closedir(directory);
1806           return NULL;
1807         }
1808     }
1809
1810   sent->name_buffer = g_new(gchar, buffer_size);
1811   sent->entries = g_new(CompletionDirEntry, entry_count);
1812   sent->entry_count = entry_count;
1813
1814   buffer_ptr = sent->name_buffer;
1815
1816   rewinddir(directory);
1817
1818   for(i = 0; i < entry_count; i += 1)
1819     {
1820       dirent_ptr = readdir(directory);
1821
1822       if(!dirent_ptr)
1823         {
1824           cmpl_errno = errno;
1825           closedir(directory);
1826           return NULL;
1827         }
1828
1829       strcpy(buffer_ptr, dirent_ptr->d_name);
1830       sent->entries[i].entry_name = buffer_ptr;
1831       buffer_ptr += strlen(dirent_ptr->d_name);
1832       *buffer_ptr = 0;
1833       buffer_ptr += 1;
1834
1835       path_buf[path_buf_len] = '/';
1836       strcpy(path_buf + path_buf_len + 1, dirent_ptr->d_name);
1837
1838       if(stat(path_buf, &ent_sbuf) >= 0 && S_ISDIR(ent_sbuf.st_mode))
1839         sent->entries[i].is_dir = 1;
1840       else
1841         /* stat may fail, and we don't mind, since it could be a
1842          * dangling symlink. */
1843         sent->entries[i].is_dir = 0;
1844     }
1845
1846   qsort(sent->entries, sent->entry_count, sizeof(CompletionDirEntry), compare_cmpl_dir);
1847
1848   closedir(directory);
1849
1850   return sent;
1851 }
1852
1853 /* open a directory by absolute pathname */
1854 static CompletionDir*
1855 open_dir(gchar* dir_name, CompletionState* cmpl_state)
1856 {
1857   struct stat sbuf;
1858   CompletionDirSent *sent;
1859   GList* cdsl;
1860
1861   if(stat(dir_name, &sbuf) < 0)
1862     {
1863       cmpl_errno = errno;
1864       return NULL;
1865     }
1866
1867   cdsl = cmpl_state->directory_sent_storage;
1868
1869   while (cdsl)
1870     {
1871       sent = cdsl->data;
1872
1873       if(sent->inode == sbuf.st_ino &&
1874          sent->mtime == sbuf.st_mtime)
1875         return attach_dir(sent, dir_name, cmpl_state);
1876
1877       cdsl = cdsl->next;
1878     }
1879
1880   sent = open_new_dir(dir_name, &sbuf);
1881
1882   if (sent) {
1883     cmpl_state->directory_sent_storage =
1884       g_list_prepend(cmpl_state->directory_sent_storage, sent);
1885
1886     return attach_dir(sent, dir_name, cmpl_state);
1887   }
1888
1889   return NULL;
1890 }
1891
1892 static CompletionDir*
1893 attach_dir(CompletionDirSent* sent, gchar* dir_name, CompletionState *cmpl_state)
1894 {
1895   CompletionDir* new_dir;
1896
1897   new_dir = g_new(CompletionDir, 1);
1898
1899   cmpl_state->directory_storage =
1900     g_list_prepend(cmpl_state->directory_storage, new_dir);
1901
1902   new_dir->sent = sent;
1903   new_dir->fullname = g_strdup(dir_name);
1904   new_dir->fullname_len = strlen(dir_name);
1905
1906   return new_dir;
1907 }
1908
1909 static gint
1910 correct_dir_fullname(CompletionDir* cmpl_dir)
1911 {
1912   gint length = strlen(cmpl_dir->fullname);
1913   struct stat sbuf;
1914
1915   if (strcmp(cmpl_dir->fullname + length - 2, "/.") == 0)
1916     {
1917       if (length == 2) 
1918         {
1919           strcpy(cmpl_dir->fullname, "/");
1920           cmpl_dir->fullname_len = 1;
1921           return TRUE;
1922         } else {
1923           cmpl_dir->fullname[length - 2] = 0;
1924         }
1925     }
1926   else if (strcmp(cmpl_dir->fullname + length - 3, "/./") == 0)
1927     cmpl_dir->fullname[length - 2] = 0;
1928   else if (strcmp(cmpl_dir->fullname + length - 3, "/..") == 0)
1929     {
1930       if(length == 3)
1931         {
1932           strcpy(cmpl_dir->fullname, "/");
1933           cmpl_dir->fullname_len = 1;
1934           return TRUE;
1935         }
1936
1937       if(stat(cmpl_dir->fullname, &sbuf) < 0)
1938         {
1939           cmpl_errno = errno;
1940           return FALSE;
1941         }
1942
1943       cmpl_dir->fullname[length - 2] = 0;
1944
1945       if(!correct_parent(cmpl_dir, &sbuf))
1946         return FALSE;
1947     }
1948   else if (strcmp(cmpl_dir->fullname + length - 4, "/../") == 0)
1949     {
1950       if(length == 4)
1951         {
1952           strcpy(cmpl_dir->fullname, "/");
1953           cmpl_dir->fullname_len = 1;
1954           return TRUE;
1955         }
1956
1957       if(stat(cmpl_dir->fullname, &sbuf) < 0)
1958         {
1959           cmpl_errno = errno;
1960           return FALSE;
1961         }
1962
1963       cmpl_dir->fullname[length - 3] = 0;
1964
1965       if(!correct_parent(cmpl_dir, &sbuf))
1966         return FALSE;
1967     }
1968
1969   cmpl_dir->fullname_len = strlen(cmpl_dir->fullname);
1970
1971   return TRUE;
1972 }
1973
1974 static gint
1975 correct_parent(CompletionDir* cmpl_dir, struct stat *sbuf)
1976 {
1977   struct stat parbuf;
1978   gchar *last_slash;
1979   gchar *new_name;
1980   gchar c = 0;
1981
1982   last_slash = strrchr(cmpl_dir->fullname, '/');
1983
1984   g_assert(last_slash);
1985
1986   if(last_slash != cmpl_dir->fullname)
1987     { /* last_slash[0] = 0; */ }
1988   else
1989     {
1990       c = last_slash[1];
1991       last_slash[1] = 0;
1992     }
1993
1994   if (stat(cmpl_dir->fullname, &parbuf) < 0)
1995     {
1996       cmpl_errno = errno;
1997       return FALSE;
1998     }
1999
2000   if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
2001     /* it wasn't a link */
2002     return TRUE;
2003
2004   if(c)
2005     last_slash[1] = c;
2006   /* else
2007     last_slash[0] = '/'; */
2008
2009   /* it was a link, have to figure it out the hard way */
2010
2011   new_name = find_parent_dir_fullname(cmpl_dir->fullname);
2012
2013   if (!new_name)
2014     return FALSE;
2015
2016   g_free(cmpl_dir->fullname);
2017
2018   cmpl_dir->fullname = new_name;
2019
2020   return TRUE;
2021 }
2022
2023 static gchar*
2024 find_parent_dir_fullname(gchar* dirname)
2025 {
2026   gchar buffer[MAXPATHLEN];
2027   gchar buffer2[MAXPATHLEN];
2028
2029   if(!getcwd(buffer, MAXPATHLEN))
2030     {
2031       cmpl_errno = errno;
2032       return NULL;
2033     }
2034
2035   if(chdir(dirname) != 0 || chdir("..") != 0)
2036     {
2037       cmpl_errno = errno;
2038       return NULL;
2039     }
2040
2041   if(!getcwd(buffer2, MAXPATHLEN))
2042     {
2043       chdir(buffer);
2044       cmpl_errno = errno;
2045
2046       return NULL;
2047     }
2048
2049   if(chdir(buffer) != 0)
2050     {
2051       cmpl_errno = errno;
2052       return NULL;
2053     }
2054
2055   return g_strdup(buffer2);
2056 }
2057
2058 /**********************************************************************/
2059 /*                        Completion Operations                       */
2060 /**********************************************************************/
2061
2062 static PossibleCompletion*
2063 attempt_homedir_completion(gchar* text_to_complete,
2064                            CompletionState *cmpl_state)
2065 {
2066   gint index, length;
2067
2068   if (!cmpl_state->user_dir_name_buffer &&
2069       !get_pwdb(cmpl_state))
2070     return NULL;
2071   length = strlen(text_to_complete) - 1;
2072
2073   cmpl_state->user_completion_index += 1;
2074
2075   while(cmpl_state->user_completion_index < cmpl_state->user_directories_len)
2076     {
2077       index = first_diff_index(text_to_complete + 1,
2078                                cmpl_state->user_directories
2079                                [cmpl_state->user_completion_index].login);
2080
2081       switch(index)
2082         {
2083         case PATTERN_MATCH:
2084           break;
2085         default:
2086           if(cmpl_state->last_valid_char < (index + 1))
2087             cmpl_state->last_valid_char = index + 1;
2088           cmpl_state->user_completion_index += 1;
2089           continue;
2090         }
2091
2092       cmpl_state->the_completion.is_a_completion = 1;
2093       cmpl_state->the_completion.is_directory = 1;
2094
2095       append_completion_text("~", cmpl_state);
2096
2097       append_completion_text(cmpl_state->
2098                               user_directories[cmpl_state->user_completion_index].login,
2099                              cmpl_state);
2100
2101       return append_completion_text("/", cmpl_state);
2102     }
2103
2104   if(text_to_complete[1] ||
2105      cmpl_state->user_completion_index > cmpl_state->user_directories_len)
2106     {
2107       cmpl_state->user_completion_index = -1;
2108       return NULL;
2109     }
2110   else
2111     {
2112       cmpl_state->user_completion_index += 1;
2113       cmpl_state->the_completion.is_a_completion = 1;
2114       cmpl_state->the_completion.is_directory = 1;
2115
2116       return append_completion_text("~/", cmpl_state);
2117     }
2118 }
2119
2120 /* returns the index (>= 0) of the first differing character,
2121  * PATTERN_MATCH if the completion matches */
2122 static gint
2123 first_diff_index(gchar* pat, gchar* text)
2124 {
2125   gint diff = 0;
2126
2127   while(*pat && *text && *text == *pat)
2128     {
2129       pat += 1;
2130       text += 1;
2131       diff += 1;
2132     }
2133
2134   if(*pat)
2135     return diff;
2136
2137   return PATTERN_MATCH;
2138 }
2139
2140 static PossibleCompletion*
2141 append_completion_text(gchar* text, CompletionState* cmpl_state)
2142 {
2143   gint len, i = 1;
2144
2145   if(!cmpl_state->the_completion.text)
2146     return NULL;
2147
2148   len = strlen(text) + strlen(cmpl_state->the_completion.text) + 1;
2149
2150   if(cmpl_state->the_completion.text_alloc > len)
2151     {
2152       strcat(cmpl_state->the_completion.text, text);
2153       return &cmpl_state->the_completion;
2154     }
2155
2156   while(i < len) { i <<= 1; }
2157
2158   cmpl_state->the_completion.text_alloc = i;
2159
2160   cmpl_state->the_completion.text = (gchar*)g_realloc(cmpl_state->the_completion.text, i);
2161
2162   if(!cmpl_state->the_completion.text)
2163     return NULL;
2164   else
2165     {
2166       strcat(cmpl_state->the_completion.text, text);
2167       return &cmpl_state->the_completion;
2168     }
2169 }
2170
2171 static CompletionDir*
2172 find_completion_dir(gchar* text_to_complete,
2173                     gchar** remaining_text,
2174                     CompletionState* cmpl_state)
2175 {
2176   gchar* first_slash = strchr(text_to_complete, '/');
2177   CompletionDir* dir = cmpl_state->reference_dir;
2178   *remaining_text = text_to_complete;
2179
2180   while(first_slash)
2181     {
2182       gint len = first_slash - *remaining_text;
2183       gint found = 0;
2184       gint found_index = -1;
2185       gint i;
2186       gchar* pat_buf = g_new (gchar, len + 1);
2187
2188       strncpy(pat_buf, *remaining_text, len);
2189       pat_buf[len] = 0;
2190
2191       for(i = 0; i < dir->sent->entry_count; i += 1)
2192         {
2193           if(dir->sent->entries[i].is_dir &&
2194              fnmatch(pat_buf, dir->sent->entries[i].entry_name,
2195                      FNMATCH_FLAGS)!= FNM_NOMATCH)
2196             {
2197               if(found)
2198                 {
2199                   g_free (pat_buf);
2200                   return dir;
2201                 }
2202               else
2203                 {
2204                   found = 1;
2205                   found_index = i;
2206                 }
2207             }
2208         }
2209
2210       if(found)
2211         {
2212           CompletionDir* next = open_relative_dir(dir->sent->entries[found_index].entry_name,
2213                                                   dir, cmpl_state);
2214
2215           if(!next)
2216             {
2217               g_free (pat_buf);
2218               return NULL;
2219             }
2220
2221           next->cmpl_parent = dir;
2222
2223           dir = next;
2224
2225           if(!correct_dir_fullname(dir))
2226             {
2227               g_free(pat_buf);
2228               return NULL;
2229             }
2230
2231           *remaining_text = first_slash + 1;
2232           first_slash = strchr(*remaining_text, '/');
2233         }
2234       else
2235         {
2236           g_free (pat_buf);
2237           return NULL;
2238         }
2239
2240       g_free (pat_buf);
2241     }
2242
2243   return dir;
2244 }
2245
2246 static void
2247 update_cmpl(PossibleCompletion* poss, CompletionState* cmpl_state)
2248 {
2249   gint cmpl_len;
2250
2251   if(!poss || !cmpl_is_a_completion(poss))
2252     return;
2253
2254   cmpl_len = strlen(cmpl_this_completion(poss));
2255
2256   if(cmpl_state->updated_text_alloc < cmpl_len + 1)
2257     {
2258       cmpl_state->updated_text =
2259         (gchar*)g_realloc(cmpl_state->updated_text,
2260                           cmpl_state->updated_text_alloc);
2261       cmpl_state->updated_text_alloc = 2*cmpl_len;
2262     }
2263
2264   if(cmpl_state->updated_text_len < 0)
2265     {
2266       strcpy(cmpl_state->updated_text, cmpl_this_completion(poss));
2267       cmpl_state->updated_text_len = cmpl_len;
2268       cmpl_state->re_complete = cmpl_is_directory(poss);
2269     }
2270   else if(cmpl_state->updated_text_len == 0)
2271     {
2272       cmpl_state->re_complete = FALSE;
2273     }
2274   else
2275     {
2276       gint first_diff =
2277         first_diff_index(cmpl_state->updated_text,
2278                          cmpl_this_completion(poss));
2279
2280       cmpl_state->re_complete = FALSE;
2281
2282       if(first_diff == PATTERN_MATCH)
2283         return;
2284
2285       if(first_diff > cmpl_state->updated_text_len)
2286         strcpy(cmpl_state->updated_text, cmpl_this_completion(poss));
2287
2288       cmpl_state->updated_text_len = first_diff;
2289       cmpl_state->updated_text[first_diff] = 0;
2290     }
2291 }
2292
2293 static PossibleCompletion*
2294 attempt_file_completion(CompletionState *cmpl_state)
2295 {
2296   gchar *pat_buf, *first_slash;
2297   CompletionDir *dir = cmpl_state->active_completion_dir;
2298
2299   dir->cmpl_index += 1;
2300
2301   if(dir->cmpl_index == dir->sent->entry_count)
2302     {
2303       if(dir->cmpl_parent == NULL)
2304         {
2305           cmpl_state->active_completion_dir = NULL;
2306
2307           return NULL;
2308         }
2309       else
2310         {
2311           cmpl_state->active_completion_dir = dir->cmpl_parent;
2312
2313           return attempt_file_completion(cmpl_state);
2314         }
2315     }
2316
2317   g_assert(dir->cmpl_text);
2318
2319   first_slash = strchr(dir->cmpl_text, '/');
2320
2321   if(first_slash)
2322     {
2323       gint len = first_slash - dir->cmpl_text;
2324
2325       pat_buf = g_new (gchar, len + 1);
2326       strncpy(pat_buf, dir->cmpl_text, len);
2327       pat_buf[len] = 0;
2328     }
2329   else
2330     {
2331       gint len = strlen(dir->cmpl_text);
2332
2333       pat_buf = g_new (gchar, len + 2);
2334       strcpy(pat_buf, dir->cmpl_text);
2335       strcpy(pat_buf + len, "*");
2336     }
2337
2338   if(first_slash)
2339     {
2340       if(dir->sent->entries[dir->cmpl_index].is_dir)
2341         {
2342           if(fnmatch(pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
2343                      FNMATCH_FLAGS) != FNM_NOMATCH)
2344             {
2345               CompletionDir* new_dir;
2346
2347               new_dir = open_relative_dir(dir->sent->entries[dir->cmpl_index].entry_name,
2348                                           dir, cmpl_state);
2349
2350               if(!new_dir)
2351                 {
2352                   g_free (pat_buf);
2353                   return NULL;
2354                 }
2355
2356               new_dir->cmpl_parent = dir;
2357
2358               new_dir->cmpl_index = -1;
2359               new_dir->cmpl_text = first_slash + 1;
2360
2361               cmpl_state->active_completion_dir = new_dir;
2362
2363               g_free (pat_buf);
2364               return attempt_file_completion(cmpl_state);
2365             }
2366           else
2367             {
2368               g_free (pat_buf);
2369               return attempt_file_completion(cmpl_state);
2370             }
2371         }
2372       else
2373         {
2374           g_free (pat_buf);
2375           return attempt_file_completion(cmpl_state);
2376         }
2377     }
2378   else
2379     {
2380       if(dir->cmpl_parent != NULL)
2381         {
2382           append_completion_text(dir->fullname +
2383                                  strlen(cmpl_state->completion_dir->fullname) + 1,
2384                                  cmpl_state);
2385           append_completion_text("/", cmpl_state);
2386         }
2387
2388       append_completion_text(dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
2389
2390       cmpl_state->the_completion.is_a_completion =
2391         (fnmatch(pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
2392                  FNMATCH_FLAGS) != FNM_NOMATCH);
2393
2394       cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
2395       if(dir->sent->entries[dir->cmpl_index].is_dir)
2396         append_completion_text("/", cmpl_state);
2397
2398       g_free (pat_buf);
2399       return &cmpl_state->the_completion;
2400     }
2401 }
2402
2403
2404 static gint
2405 get_pwdb(CompletionState* cmpl_state)
2406 {
2407   struct passwd *pwd_ptr;
2408   gchar* buf_ptr, *home_dir = NULL;
2409   gint len = 0, i, count = 0;
2410
2411   if(cmpl_state->user_dir_name_buffer)
2412     return TRUE;
2413   setpwent ();
2414
2415   while ((pwd_ptr = getpwent()) != NULL)
2416     {
2417       len += strlen(pwd_ptr->pw_name);
2418       len += strlen(pwd_ptr->pw_dir);
2419       len += 2;
2420       count += 1;
2421     }
2422
2423   if (!cmpl_state->user_home_dir)
2424     {
2425       /* the loser doesn't have $HOME set */
2426       setpwent ();
2427
2428       pwd_ptr = getpwuid(getuid());
2429       if(!pwd_ptr)
2430         {
2431           cmpl_errno = errno;
2432           goto error;
2433         }
2434       home_dir = pwd_ptr->pw_dir;
2435
2436       len += strlen(home_dir);
2437       len += 1;
2438     }
2439
2440   setpwent ();
2441
2442   cmpl_state->user_dir_name_buffer = g_new(gchar, len);
2443   cmpl_state->user_directories = g_new(CompletionUserDir, count);
2444   cmpl_state->user_directories_len = count;
2445
2446   buf_ptr = cmpl_state->user_dir_name_buffer;
2447
2448   if (!cmpl_state->user_home_dir)
2449     {
2450       strcpy(buf_ptr, home_dir);
2451       cmpl_state->user_home_dir = buf_ptr;
2452       buf_ptr += strlen(buf_ptr);
2453       buf_ptr += 1;
2454     }
2455
2456   for(i = 0; i < count; i += 1)
2457     {
2458       pwd_ptr = getpwent();
2459       if(!pwd_ptr)
2460         {
2461           cmpl_errno = errno;
2462           goto error;
2463         }
2464
2465       strcpy(buf_ptr, pwd_ptr->pw_name);
2466       cmpl_state->user_directories[i].login = buf_ptr;
2467       buf_ptr += strlen(buf_ptr);
2468       buf_ptr += 1;
2469       strcpy(buf_ptr, pwd_ptr->pw_dir);
2470       cmpl_state->user_directories[i].homedir = buf_ptr;
2471       buf_ptr += strlen(buf_ptr);
2472       buf_ptr += 1;
2473     }
2474
2475   qsort(cmpl_state->user_directories,
2476         cmpl_state->user_directories_len,
2477         sizeof(CompletionUserDir),
2478         compare_user_dir);
2479
2480   endpwent();
2481
2482   return TRUE;
2483
2484 error:
2485
2486   if(cmpl_state->user_dir_name_buffer)
2487     g_free(cmpl_state->user_dir_name_buffer);
2488   if(cmpl_state->user_directories)
2489     g_free(cmpl_state->user_directories);
2490
2491   cmpl_state->user_dir_name_buffer = NULL;
2492   cmpl_state->user_directories = NULL;
2493
2494   return FALSE;
2495 }
2496
2497 static gint
2498 compare_user_dir(const void* a, const void* b)
2499 {
2500   return strcmp((((CompletionUserDir*)a))->login,
2501                 (((CompletionUserDir*)b))->login);
2502 }
2503
2504 static gint
2505 compare_cmpl_dir(const void* a, const void* b)
2506 {
2507   return strcmp((((CompletionDirEntry*)a))->entry_name,
2508                 (((CompletionDirEntry*)b))->entry_name);
2509 }
2510
2511 static gint
2512 cmpl_state_okay(CompletionState* cmpl_state)
2513 {
2514   return  cmpl_state && cmpl_state->reference_dir;
2515 }
2516
2517 static gchar*
2518 cmpl_strerror(gint err)
2519 {
2520   if(err == CMPL_ERRNO_TOO_LONG)
2521     return "Name too long";
2522   else
2523     return g_strerror (err);
2524 }