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