1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
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.
19 #include <sys/types.h>
21 #include <sys/param.h>
30 #include "gdk/gdkkeysyms.h"
31 #include "gtkbutton.h"
33 #include "gtkfilesel.h"
37 #include "gtklistitem.h"
39 #include "gtkscrolledwindow.h"
40 #include "gtksignal.h"
44 #define DIR_LIST_WIDTH 160
45 #define DIR_LIST_HEIGHT 175
46 #define FILE_LIST_WIDTH 160
47 #define FILE_LIST_HEIGHT 175
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;
57 /* Non-external file completion decls and structures */
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
63 /* A constant used to determine whether a substring was an exact
64 * match by first_diff_index()
66 #define PATTERN_MATCH -1
67 /* The arguments used by all fnmatch() calls below
69 #define FNMATCH_FLAGS (FNM_PATHNAME | FNM_PERIOD)
71 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
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.
77 struct _CompletionDirSent
83 gchar *name_buffer; /* memory segment containing names of all entries */
85 struct _CompletionDirEntry *entries;
90 CompletionDirSent *sent;
95 struct _CompletionDir *cmpl_parent;
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).
108 struct _CompletionDirEntry
114 struct _CompletionUserDir
120 struct _PossibleCompletion
122 /* accessible fields, all are accessed externally by functions
126 gint is_a_completion;
134 struct _CompletionState
136 gint last_valid_char;
138 gint updated_text_len;
139 gint updated_text_alloc;
142 gchar *user_dir_name_buffer;
143 gint user_directories_len;
144 gchar *user_home_dir;
146 gchar *last_completion_text;
148 gint user_completion_index; /* if >= 0, currently completing ~user */
150 struct _CompletionDir *completion_dir; /* directory completing from */
151 struct _CompletionDir *active_completion_dir;
153 struct _PossibleCompletion the_completion;
155 struct _CompletionDir *reference_dir; /* initial directory */
157 GList* directory_storage;
158 GList* directory_sent_storage;
160 struct _CompletionUserDir *user_directories;
164 /* File completion functions which would be external, were they used
165 * outside of this file.
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);
173 static PossibleCompletion* cmpl_completion_matches(gchar *text_to_complete,
174 gchar **remaining_text,
175 CompletionState *cmpl_state);
177 /* Returns a name for consideration, possibly a completion, this name
178 * will be invalid after the next call to cmpl_next_completion.
180 static char* cmpl_this_completion (PossibleCompletion*);
182 /* True if this completion matches the given text. Otherwise, this
183 * output can be used to have a list of non-completions.
185 static gint cmpl_is_a_completion (PossibleCompletion*);
187 /* True if the completion is a directory
189 static gint cmpl_is_directory (PossibleCompletion*);
191 /* Obtains the next completion, or NULL
193 static PossibleCompletion* cmpl_next_completion (CompletionState*);
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
202 static gchar* cmpl_updated_text (CompletionState* cmpl_state);
204 /* After updating, to see if the completion was a directory, call
205 * this. If it was, you should consider re-calling completion_matches.
207 static gint cmpl_updated_dir (CompletionState* cmpl_state);
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('/').
214 static gchar* cmpl_reference_position (CompletionState* cmpl_state);
216 /* backing up: if cmpl_completion_matches returns NULL, you may query
217 * the index of the last completable character into cmpl_updated_text.
219 static gint cmpl_last_valid_char (CompletionState* cmpl_state);
221 /* When the user selects a non-directory, call cmpl_completion_fullname
222 * to get the full name of the selected file.
224 static gchar* cmpl_completion_fullname (gchar*, CompletionState* cmpl_state);
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,
241 static gchar* find_parent_dir_fullname (gchar* dirname);
242 static CompletionDir* attach_dir (CompletionDirSent* sent,
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);
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);
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,
271 static gint gtk_file_selection_file_button (GtkWidget *widget,
272 GdkEventButton *event,
274 static gint gtk_file_selection_dir_button (GtkWidget *widget,
275 GdkEventButton *event,
277 static void gtk_file_selection_file_list_changed (GtkList *gtklist,
279 static void gtk_file_selection_dir_list_changed (GtkList *gtklist,
281 static void gtk_file_selection_populate (GtkFileSelection *fs,
284 static void gtk_file_selection_abort (GtkFileSelection *fs);
285 static void gtk_file_selection_free_filename (GtkWidget *widget,
286 gpointer client_data);
289 static GtkWindowClass *parent_class = NULL;
291 static gchar *list_changed_key = "_gtk_selection_changed_handler_key";
293 /* Saves errno when something cmpl does fails. */
294 static gint cmpl_errno;
298 gtk_file_selection_get_type ()
300 static guint file_selection_type = 0;
302 if (!file_selection_type)
304 GtkTypeInfo filesel_info =
307 sizeof (GtkFileSelection),
308 sizeof (GtkFileSelectionClass),
309 (GtkClassInitFunc) gtk_file_selection_class_init,
310 (GtkObjectInitFunc) gtk_file_selection_init,
314 file_selection_type = gtk_type_unique (gtk_window_get_type (), &filesel_info);
317 return file_selection_type;
321 gtk_file_selection_class_init (GtkFileSelectionClass *class)
323 GtkObjectClass *object_class;
325 object_class = (GtkObjectClass*) class;
327 parent_class = gtk_type_class (gtk_window_get_type ());
329 object_class->destroy = gtk_file_selection_destroy;
333 gtk_file_selection_init (GtkFileSelection *filesel)
336 GtkWidget *file_vbox;
337 GtkWidget *entry_vbox;
340 GtkWidget *list_hbox;
341 GtkWidget *action_area;
344 filesel->cmpl_state = cmpl_init_state ();
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);
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);
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);
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);
368 listbox = gtk_scrolled_window_new (NULL, NULL);
369 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (listbox),
370 GTK_POLICY_AUTOMATIC,
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);
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),
382 (GtkSignalFunc) gtk_file_selection_dir_list_changed,
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);
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);
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);
399 listbox = gtk_scrolled_window_new (NULL, NULL);
400 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (listbox),
401 GTK_POLICY_AUTOMATIC,
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);
407 filesel->file_list = gtk_list_new ();
408 gtk_list_set_selection_mode (GTK_LIST (filesel->file_list), GTK_SELECTION_BROWSE);
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),
413 (GtkSignalFunc) gtk_file_selection_file_list_changed,
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);
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);
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);
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);
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);
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);
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);
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);
467 if (!cmpl_state_okay (filesel->cmpl_state))
471 sprintf (err_buf, "Directory unreadable: %s", cmpl_strerror (cmpl_errno));
473 gtk_label_set (GTK_LABEL (filesel->selection_text), err_buf);
477 gtk_file_selection_populate (filesel, "", FALSE);
480 gtk_widget_grab_focus (filesel->selection_entry);
484 gtk_file_selection_new (const gchar *title)
486 GtkFileSelection *filesel;
488 filesel = gtk_type_new (gtk_file_selection_get_type ());
489 gtk_window_set_title (GTK_WINDOW (filesel), title);
491 return GTK_WIDGET (filesel);
495 gtk_file_selection_set_filename (GtkFileSelection *filesel,
496 const gchar *filename)
498 char buf[MAXPATHLEN];
499 const char *name, *last_slash;
501 g_return_if_fail (filesel != NULL);
502 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
503 g_return_if_fail (filename != NULL);
505 last_slash = strrchr (filename, '/');
514 gint len = MIN (MAXPATHLEN - 1, last_slash - filename + 1);
516 strncpy (buf, filename, len);
519 name = last_slash + 1;
522 gtk_file_selection_populate (filesel, buf, FALSE);
524 if (filesel->selection_entry)
525 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
529 gtk_file_selection_get_filename (GtkFileSelection *filesel)
531 static char nothing[2] = "";
535 g_return_val_if_fail (filesel != NULL, nothing);
536 g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
538 text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
541 filename = cmpl_completion_fullname (text, filesel->cmpl_state);
549 gtk_file_selection_destroy (GtkObject *object)
551 GtkFileSelection *filesel;
553 g_return_if_fail (object != NULL);
554 g_return_if_fail (GTK_IS_FILE_SELECTION (object));
556 filesel = GTK_FILE_SELECTION (object);
558 cmpl_free_state (filesel->cmpl_state);
560 if (GTK_OBJECT_CLASS (parent_class)->destroy)
561 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
565 gtk_file_selection_key_press (GtkWidget *widget,
569 GtkFileSelection *fs;
572 g_return_val_if_fail (widget != NULL, FALSE);
573 g_return_val_if_fail (event != NULL, FALSE);
575 if (event->keyval == GDK_Tab)
577 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
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);
590 gtk_file_selection_file_button (GtkWidget *widget,
591 GdkEventButton *event,
594 GtkFileSelection *fs;
595 GtkWidget *event_widget;
599 g_return_val_if_fail (widget != NULL, FALSE);
600 g_return_val_if_fail (event != NULL, FALSE);
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);
606 event_widget = gtk_get_event_widget ((GdkEvent*) event);
608 if (GTK_IS_LIST_ITEM (event_widget))
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);
619 case GDK_2BUTTON_PRESS:
620 gtk_button_clicked (GTK_BUTTON (fs->ok_button));
633 gtk_file_selection_file_list_changed (GtkList *list,
636 GtkFileSelection *fs;
638 g_return_if_fail (func_data != NULL);
639 g_return_if_fail (GTK_IS_FILE_SELECTION (func_data));
641 fs = GTK_FILE_SELECTION (func_data);
643 /* only act on an appropriate selection
645 if (list->selection && list->selection->data)
649 item = list->selection->data;
651 if (GTK_IS_LIST_ITEM (item))
655 filename = gtk_object_get_user_data (GTK_OBJECT (item));
656 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
662 gtk_file_selection_dir_button (GtkWidget *widget,
663 GdkEventButton *event,
666 GtkFileSelection *fs;
667 GtkWidget *event_widget;
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);
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);
679 event_widget = gtk_get_event_widget ((GdkEvent*) event);
681 if (GTK_IS_LIST_ITEM (event_widget))
685 filename = gtk_object_get_user_data (GTK_OBJECT (event_widget));
687 key = (gint) gtk_object_get_data (GTK_OBJECT (widget), list_changed_key);
691 case GDK_BUTTON_PRESS:
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);
697 gtk_widget_grab_focus (fs->selection_entry);
698 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
702 case GDK_2BUTTON_PRESS:
703 gtk_file_selection_populate (fs, filename, FALSE);
716 gtk_file_selection_dir_list_changed (GtkList *list,
719 GtkFileSelection *fs;
721 g_return_if_fail (func_data != NULL);
722 g_return_if_fail (GTK_IS_FILE_SELECTION (func_data));
724 fs = GTK_FILE_SELECTION (func_data);
726 /* only act on an appropriate selection
728 if (list->selection && list->selection->data)
732 item = list->selection->data;
734 if (GTK_IS_LIST_ITEM (item))
738 filename = gtk_object_get_user_data (GTK_OBJECT (item));
741 gtk_file_selection_populate (fs, filename, FALSE);
747 gtk_file_selection_populate (GtkFileSelection *fs,
751 CompletionState *cmpl_state;
752 PossibleCompletion* poss;
753 GList *dir_list = NULL;
754 GList *file_list = NULL;
757 gchar* rem_path = rel_path;
759 gint did_recurse = FALSE;
760 gint possible_count = 0;
761 gint selection_index = -1;
762 gint dir_changed_key;
764 g_return_if_fail (fs != NULL);
765 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
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);
770 cmpl_state = (CompletionState*) fs->cmpl_state;
771 poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
773 if (!cmpl_state_okay (cmpl_state))
775 /* Something went wrong. */
776 gtk_file_selection_abort (fs);
777 gtk_signal_handler_unblock (GTK_OBJECT (fs->dir_list), dir_changed_key);
781 g_assert (cmpl_state->reference_dir);
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("../"));
790 if (cmpl_is_a_completion (poss))
794 filename = g_strdup (cmpl_this_completion (poss));
796 if (cmpl_is_directory (poss))
798 if (strcmp (filename, "./") != 0 &&
799 strcmp (filename, "../") != 0)
800 dir_list = g_list_prepend (dir_list, filename);
803 file_list = g_list_prepend (file_list, filename);
806 poss = cmpl_next_completion (cmpl_state);
809 /* File lists are set. */
811 g_assert (cmpl_state->reference_dir);
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. */
820 if (cmpl_updated_text (cmpl_state)[0])
822 if (cmpl_updated_dir (cmpl_state))
824 gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
828 gtk_file_selection_populate (fs, dir_name, TRUE);
834 if (fs->selection_entry)
835 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
836 cmpl_updated_text (cmpl_state));
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);
849 if (fs->selection_entry)
850 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
855 GList *file_label_list = NULL;
856 GList *dir_label_list = NULL;
858 /* This reverses the lists. */
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);
865 file_label_list = g_list_prepend (file_label_list, label);
866 file_list = file_list->next;
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);
875 dir_label_list = g_list_prepend (dir_label_list, label);
876 dir_list = dir_list->next;
879 gtk_container_disable_resize (GTK_CONTAINER (fs));
881 if (fs->selection_entry)
882 gtk_entry_set_position (GTK_ENTRY (fs->selection_entry), selection_index);
884 if (fs->selection_entry)
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));
891 gtk_label_set (GTK_LABEL (fs->selection_text), sel_text);
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);
902 gtk_list_append_items (GTK_LIST (fs->dir_list), dir_label_list);
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);
911 gtk_list_append_items (GTK_LIST (fs->file_list), file_label_list);
914 gtk_container_enable_resize (GTK_CONTAINER (fs));
918 GList *dir_list0 = dir_list;
919 GList *file_list0 = file_list;
923 GList *tmp = dir_list;
924 dir_list = dir_list->next;
932 GList *tmp = file_list;
933 file_list = file_list->next;
939 g_list_free (dir_list0);
940 g_list_free (file_list0);
943 gtk_signal_handler_unblock (GTK_OBJECT (fs->dir_list), dir_changed_key);
947 gtk_file_selection_abort (GtkFileSelection *fs)
951 sprintf (err_buf, "Directory unreadable: %s", cmpl_strerror (cmpl_errno));
953 /* BEEP gdk_beep(); */
955 if (fs->selection_entry)
956 gtk_label_set (GTK_LABEL (fs->selection_text), err_buf);
960 gtk_file_selection_free_filename (GtkWidget *widget,
961 gpointer client_data)
963 g_return_if_fail (widget != NULL);
965 g_free (gtk_object_get_user_data (GTK_OBJECT (widget)));
966 gtk_object_set_user_data (GTK_OBJECT (widget), NULL);
971 /**********************************************************************/
972 /* External Interface */
973 /**********************************************************************/
975 /* The four completion state selectors
978 cmpl_updated_text (CompletionState* cmpl_state)
980 return cmpl_state->updated_text;
984 cmpl_updated_dir (CompletionState* cmpl_state)
986 return cmpl_state->re_complete;
990 cmpl_reference_position (CompletionState* cmpl_state)
992 return cmpl_state->reference_dir->fullname;
996 cmpl_last_valid_char (CompletionState* cmpl_state)
998 return cmpl_state->last_valid_char;
1002 cmpl_completion_fullname (gchar* text, CompletionState* cmpl_state)
1006 strcpy (cmpl_state->updated_text, text);
1008 else if (text[0] == '~')
1013 dir = open_user_dir (text, cmpl_state);
1017 /* spencer says just return ~something, so
1018 * for now just do it. */
1019 strcpy (cmpl_state->updated_text, text);
1024 strcpy (cmpl_state->updated_text, dir->fullname);
1026 slash = strchr (text, '/');
1029 strcat (cmpl_state->updated_text, slash);
1034 strcpy (cmpl_state->updated_text, cmpl_state->reference_dir->fullname);
1035 strcat (cmpl_state->updated_text, "/");
1036 strcat (cmpl_state->updated_text, text);
1039 return cmpl_state->updated_text;
1042 /* The three completion selectors
1045 cmpl_this_completion (PossibleCompletion* pc)
1051 cmpl_is_directory (PossibleCompletion* pc)
1053 return pc->is_directory;
1057 cmpl_is_a_completion (PossibleCompletion* pc)
1059 return pc->is_a_completion;
1062 /**********************************************************************/
1063 /* Construction, deletion */
1064 /**********************************************************************/
1066 static CompletionState*
1067 cmpl_init_state (void)
1069 gchar getcwd_buf[2*MAXPATHLEN];
1070 CompletionState *new_state;
1072 new_state = g_new (CompletionState, 1);
1074 if (!getcwd (getcwd_buf, MAXPATHLEN))
1080 new_state->reference_dir = NULL;
1081 new_state->completion_dir = NULL;
1082 new_state->active_completion_dir = NULL;
1084 if ((new_state->user_home_dir = getenv("HOME")) != NULL)
1086 /* if this fails, get_pwdb will fill it in. */
1087 new_state->user_home_dir = g_strdup(new_state->user_home_dir);
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;
1100 new_state->reference_dir = open_dir (getcwd_buf, new_state);
1102 if (!new_state->reference_dir)
1109 cmpl_free_dir_list(GList* dp0)
1114 free_dir (dp->data);
1122 cmpl_free_dir_sent_list(GList* dp0)
1127 free_dir_sent (dp->data);
1135 cmpl_free_state (CompletionState* cmpl_state)
1137 cmpl_free_dir_list(cmpl_state->directory_storage);
1138 cmpl_free_dir_sent_list(cmpl_state->directory_sent_storage);
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);
1149 g_free (cmpl_state);
1153 free_dir(CompletionDir* dir)
1155 g_free(dir->fullname);
1160 free_dir_sent(CompletionDirSent* sent)
1162 g_free(sent->name_buffer);
1163 g_free(sent->entries);
1168 prune_memory_usage(CompletionState *cmpl_state)
1170 GList* cdsl = cmpl_state->directory_sent_storage;
1171 GList* cdl = cmpl_state->directory_storage;
1175 for(; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
1179 cmpl_free_dir_sent_list(cdsl->next);
1184 if (cdl->data == cmpl_state->reference_dir)
1185 cmpl_state->directory_storage = g_list_prepend(NULL, cdl->data);
1187 free_dir (cdl->data);
1194 /**********************************************************************/
1195 /* The main entrances. */
1196 /**********************************************************************/
1198 static PossibleCompletion*
1199 cmpl_completion_matches (gchar* text_to_complete,
1200 gchar** remaining_text,
1201 CompletionState* cmpl_state)
1204 PossibleCompletion *poss;
1206 prune_memory_usage(cmpl_state);
1208 g_assert(text_to_complete);
1210 cmpl_state->user_completion_index = -1;
1211 cmpl_state->last_completion_text = text_to_complete;
1212 cmpl_state->the_completion.text[0] = 0;
1213 cmpl_state->last_valid_char = 0;
1214 cmpl_state->updated_text_len = -1;
1215 cmpl_state->updated_text[0] = 0;
1216 cmpl_state->re_complete = FALSE;
1218 first_slash = strchr(text_to_complete, '/');
1220 if(text_to_complete[0] == '~' && !first_slash)
1222 /* Text starts with ~ and there is no slash, show all the
1223 * home directory completions.
1225 poss = attempt_homedir_completion(text_to_complete, cmpl_state);
1227 update_cmpl(poss, cmpl_state);
1232 cmpl_state->reference_dir =
1233 open_ref_dir(text_to_complete, remaining_text, cmpl_state);
1235 if(!cmpl_state->reference_dir)
1238 cmpl_state->completion_dir =
1239 find_completion_dir(*remaining_text, remaining_text, cmpl_state);
1241 cmpl_state->last_valid_char = *remaining_text - text_to_complete;
1243 if(!cmpl_state->completion_dir)
1246 cmpl_state->completion_dir->cmpl_index = -1;
1247 cmpl_state->completion_dir->cmpl_parent = NULL;
1248 cmpl_state->completion_dir->cmpl_text = *remaining_text;
1250 cmpl_state->active_completion_dir = cmpl_state->completion_dir;
1252 cmpl_state->reference_dir = cmpl_state->completion_dir;
1254 poss = attempt_file_completion(cmpl_state);
1256 update_cmpl(poss, cmpl_state);
1261 static PossibleCompletion*
1262 cmpl_next_completion (CompletionState* cmpl_state)
1264 PossibleCompletion* poss = NULL;
1266 cmpl_state->the_completion.text[0] = 0;
1268 if(cmpl_state->user_completion_index >= 0)
1269 poss = attempt_homedir_completion(cmpl_state->last_completion_text, cmpl_state);
1271 poss = attempt_file_completion(cmpl_state);
1273 update_cmpl(poss, cmpl_state);
1278 /**********************************************************************/
1279 /* Directory Operations */
1280 /**********************************************************************/
1282 /* Open the directory where completion will begin from, if possible. */
1283 static CompletionDir*
1284 open_ref_dir(gchar* text_to_complete,
1285 gchar** remaining_text,
1286 CompletionState* cmpl_state)
1289 CompletionDir *new_dir;
1291 first_slash = strchr(text_to_complete, '/');
1293 if (text_to_complete[0] == '/' || !cmpl_state->reference_dir)
1295 new_dir = open_dir("/", cmpl_state);
1298 *remaining_text = text_to_complete + 1;
1300 else if (text_to_complete[0] == '~')
1302 new_dir = open_user_dir(text_to_complete, cmpl_state);
1307 *remaining_text = first_slash + 1;
1309 *remaining_text = text_to_complete + strlen(text_to_complete);
1318 *remaining_text = text_to_complete;
1320 new_dir = open_dir(cmpl_state->reference_dir->fullname, cmpl_state);
1325 new_dir->cmpl_index = -1;
1326 new_dir->cmpl_parent = NULL;
1332 /* open a directory by user name */
1333 static CompletionDir*
1334 open_user_dir(gchar* text_to_complete,
1335 CompletionState *cmpl_state)
1340 g_assert(text_to_complete && text_to_complete[0] == '~');
1342 first_slash = strchr(text_to_complete, '/');
1345 cmp_len = first_slash - text_to_complete - 1;
1347 cmp_len = strlen(text_to_complete + 1);
1352 if (!cmpl_state->user_home_dir &&
1353 !get_pwdb(cmpl_state))
1355 return open_dir(cmpl_state->user_home_dir, cmpl_state);
1360 char* copy = g_new(char, cmp_len + 1);
1362 strncpy(copy, text_to_complete + 1, cmp_len);
1364 pwd = getpwnam(copy);
1372 return open_dir(pwd->pw_dir, cmpl_state);
1376 /* open a directory relative the the current relative directory */
1377 static CompletionDir*
1378 open_relative_dir(gchar* dir_name,
1380 CompletionState *cmpl_state)
1382 gchar path_buf[2*MAXPATHLEN];
1384 if(dir->fullname_len + strlen(dir_name) + 2 >= MAXPATHLEN)
1386 cmpl_errno = CMPL_ERRNO_TOO_LONG;
1390 strcpy(path_buf, dir->fullname);
1392 if(dir->fullname_len > 1)
1394 path_buf[dir->fullname_len] = '/';
1395 strcpy(path_buf + dir->fullname_len + 1, dir_name);
1399 strcpy(path_buf + dir->fullname_len, dir_name);
1402 return open_dir(path_buf, cmpl_state);
1405 /* after the cache lookup fails, really open a new directory */
1406 static CompletionDirSent*
1407 open_new_dir(gchar* dir_name, struct stat* sbuf)
1409 CompletionDirSent* sent;
1412 struct dirent *dirent_ptr;
1413 gint buffer_size = 0;
1414 gint entry_count = 0;
1416 struct stat ent_sbuf;
1417 char path_buf[MAXPATHLEN*2];
1420 sent = g_new(CompletionDirSent, 1);
1421 sent->mtime = sbuf->st_mtime;
1422 sent->inode = sbuf->st_ino;
1424 path_buf_len = strlen(dir_name);
1426 if (path_buf_len > MAXPATHLEN)
1428 cmpl_errno = CMPL_ERRNO_TOO_LONG;
1432 strcpy(path_buf, dir_name);
1434 directory = opendir(dir_name);
1442 while((dirent_ptr = readdir(directory)) != NULL)
1444 int entry_len = strlen(dirent_ptr->d_name);
1445 buffer_size += entry_len + 1;
1448 if(path_buf_len + entry_len + 2 >= MAXPATHLEN)
1450 cmpl_errno = CMPL_ERRNO_TOO_LONG;
1451 closedir(directory);
1456 sent->name_buffer = g_new(gchar, buffer_size);
1457 sent->entries = g_new(CompletionDirEntry, entry_count);
1458 sent->entry_count = entry_count;
1460 buffer_ptr = sent->name_buffer;
1462 rewinddir(directory);
1464 for(i = 0; i < entry_count; i += 1)
1466 dirent_ptr = readdir(directory);
1471 closedir(directory);
1475 strcpy(buffer_ptr, dirent_ptr->d_name);
1476 sent->entries[i].entry_name = buffer_ptr;
1477 buffer_ptr += strlen(dirent_ptr->d_name);
1481 path_buf[path_buf_len] = '/';
1482 strcpy(path_buf + path_buf_len + 1, dirent_ptr->d_name);
1484 if(stat(path_buf, &ent_sbuf) >= 0 && S_ISDIR(ent_sbuf.st_mode))
1485 sent->entries[i].is_dir = 1;
1487 /* stat may fail, and we don't mind, since it could be a
1488 * dangling symlink. */
1489 sent->entries[i].is_dir = 0;
1492 qsort(sent->entries, sent->entry_count, sizeof(CompletionDirEntry), compare_cmpl_dir);
1494 closedir(directory);
1499 /* open a directory by absolute pathname */
1500 static CompletionDir*
1501 open_dir(gchar* dir_name, CompletionState* cmpl_state)
1504 CompletionDirSent *sent;
1507 if(stat(dir_name, &sbuf) < 0)
1513 cdsl = cmpl_state->directory_sent_storage;
1519 if(sent->inode == sbuf.st_ino &&
1520 sent->mtime == sbuf.st_mtime)
1521 return attach_dir(sent, dir_name, cmpl_state);
1526 sent = open_new_dir(dir_name, &sbuf);
1529 cmpl_state->directory_sent_storage =
1530 g_list_prepend(cmpl_state->directory_sent_storage, sent);
1532 return attach_dir(sent, dir_name, cmpl_state);
1538 static CompletionDir*
1539 attach_dir(CompletionDirSent* sent, gchar* dir_name, CompletionState *cmpl_state)
1541 CompletionDir* new_dir;
1543 new_dir = g_new(CompletionDir, 1);
1545 cmpl_state->directory_storage =
1546 g_list_prepend(cmpl_state->directory_storage, new_dir);
1548 new_dir->sent = sent;
1549 new_dir->fullname = g_strdup(dir_name);
1550 new_dir->fullname_len = strlen(dir_name);
1556 correct_dir_fullname(CompletionDir* cmpl_dir)
1558 gint length = strlen(cmpl_dir->fullname);
1561 if (strcmp(cmpl_dir->fullname + length - 2, "/.") == 0)
1562 cmpl_dir->fullname[length - 2] = 0;
1563 else if (strcmp(cmpl_dir->fullname + length - 3, "/./") == 0)
1564 cmpl_dir->fullname[length - 3] = 0;
1565 else if (strcmp(cmpl_dir->fullname + length - 3, "/..") == 0)
1569 strcpy(cmpl_dir->fullname, "/");
1570 cmpl_dir->fullname_len = 1;
1574 if(stat(cmpl_dir->fullname, &sbuf) < 0)
1580 cmpl_dir->fullname[length - 3] = 0;
1582 if(!correct_parent(cmpl_dir, &sbuf))
1585 else if (strcmp(cmpl_dir->fullname + length - 4, "/../") == 0)
1589 strcpy(cmpl_dir->fullname, "/");
1590 cmpl_dir->fullname_len = 1;
1594 if(stat(cmpl_dir->fullname, &sbuf) < 0)
1600 cmpl_dir->fullname[length - 4] = 0;
1602 if(!correct_parent(cmpl_dir, &sbuf))
1606 cmpl_dir->fullname_len = strlen(cmpl_dir->fullname);
1612 correct_parent(CompletionDir* cmpl_dir, struct stat *sbuf)
1619 last_slash = strrchr(cmpl_dir->fullname, '/');
1621 g_assert(last_slash);
1623 if(last_slash != cmpl_dir->fullname)
1631 if (stat(cmpl_dir->fullname, &parbuf) < 0)
1637 if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
1638 /* it wasn't a link */
1644 last_slash[0] = '/';
1646 /* it was a link, have to figure it out the hard way */
1648 new_name = find_parent_dir_fullname(cmpl_dir->fullname);
1653 g_free(cmpl_dir->fullname);
1655 cmpl_dir->fullname = new_name;
1661 find_parent_dir_fullname(gchar* dirname)
1663 gchar buffer[MAXPATHLEN];
1664 gchar buffer2[MAXPATHLEN];
1666 if(!getcwd(buffer, MAXPATHLEN))
1672 if(chdir(dirname) != 0 || chdir("..") != 0)
1678 if(!getcwd(buffer2, MAXPATHLEN))
1686 if(chdir(buffer) != 0)
1692 return g_strdup(buffer2);
1695 /**********************************************************************/
1696 /* Completion Operations */
1697 /**********************************************************************/
1699 static PossibleCompletion*
1700 attempt_homedir_completion(gchar* text_to_complete,
1701 CompletionState *cmpl_state)
1705 if (!cmpl_state->user_dir_name_buffer &&
1706 !get_pwdb(cmpl_state))
1708 length = strlen(text_to_complete) - 1;
1710 cmpl_state->user_completion_index += 1;
1712 while(cmpl_state->user_completion_index < cmpl_state->user_directories_len)
1714 index = first_diff_index(text_to_complete + 1,
1715 cmpl_state->user_directories
1716 [cmpl_state->user_completion_index].login);
1723 if(cmpl_state->last_valid_char < (index + 1))
1724 cmpl_state->last_valid_char = index + 1;
1725 cmpl_state->user_completion_index += 1;
1729 cmpl_state->the_completion.is_a_completion = 1;
1730 cmpl_state->the_completion.is_directory = 1;
1732 append_completion_text("~", cmpl_state);
1734 append_completion_text(cmpl_state->
1735 user_directories[cmpl_state->user_completion_index].login,
1738 return append_completion_text("/", cmpl_state);
1741 if(text_to_complete[1] ||
1742 cmpl_state->user_completion_index > cmpl_state->user_directories_len)
1744 cmpl_state->user_completion_index = -1;
1749 cmpl_state->user_completion_index += 1;
1750 cmpl_state->the_completion.is_a_completion = 1;
1751 cmpl_state->the_completion.is_directory = 1;
1753 return append_completion_text("~/", cmpl_state);
1757 /* returns the index (>= 0) of the first differing character,
1758 * PATTERN_MATCH if the completion matches */
1760 first_diff_index(gchar* pat, gchar* text)
1764 while(*pat && *text && *text == *pat)
1774 return PATTERN_MATCH;
1777 static PossibleCompletion*
1778 append_completion_text(gchar* text, CompletionState* cmpl_state)
1782 if(!cmpl_state->the_completion.text)
1785 len = strlen(text) + strlen(cmpl_state->the_completion.text) + 1;
1787 if(cmpl_state->the_completion.text_alloc > len)
1789 strcat(cmpl_state->the_completion.text, text);
1790 return &cmpl_state->the_completion;
1793 while(i < len) { i <<= 1; }
1795 cmpl_state->the_completion.text_alloc = i;
1797 cmpl_state->the_completion.text = (gchar*)g_realloc(cmpl_state->the_completion.text, i);
1799 if(!cmpl_state->the_completion.text)
1803 strcat(cmpl_state->the_completion.text, text);
1804 return &cmpl_state->the_completion;
1808 static CompletionDir*
1809 find_completion_dir(gchar* text_to_complete,
1810 gchar** remaining_text,
1811 CompletionState* cmpl_state)
1813 gchar* first_slash = strchr(text_to_complete, '/');
1814 CompletionDir* dir = cmpl_state->reference_dir;
1815 *remaining_text = text_to_complete;
1819 gint len = first_slash - *remaining_text;
1821 gint found_index = -1;
1823 gchar* pat_buf = g_new (gchar, len + 1);
1825 strncpy(pat_buf, *remaining_text, len);
1828 for(i = 0; i < dir->sent->entry_count; i += 1)
1830 if(dir->sent->entries[i].is_dir &&
1831 fnmatch(pat_buf, dir->sent->entries[i].entry_name,
1832 FNMATCH_FLAGS)!= FNM_NOMATCH)
1849 CompletionDir* next = open_relative_dir(dir->sent->entries[found_index].entry_name,
1858 next->cmpl_parent = dir;
1862 if(!correct_dir_fullname(dir))
1868 *remaining_text = first_slash + 1;
1869 first_slash = strchr(*remaining_text, '/');
1884 update_cmpl(PossibleCompletion* poss, CompletionState* cmpl_state)
1888 if(!poss || !cmpl_is_a_completion(poss))
1891 cmpl_len = strlen(cmpl_this_completion(poss));
1893 if(cmpl_state->updated_text_alloc < cmpl_len + 1)
1895 cmpl_state->updated_text =
1896 (gchar*)g_realloc(cmpl_state->updated_text,
1897 cmpl_state->updated_text_alloc);
1898 cmpl_state->updated_text_alloc = 2*cmpl_len;
1901 if(cmpl_state->updated_text_len < 0)
1903 strcpy(cmpl_state->updated_text, cmpl_this_completion(poss));
1904 cmpl_state->updated_text_len = cmpl_len;
1905 cmpl_state->re_complete = cmpl_is_directory(poss);
1907 else if(cmpl_state->updated_text_len == 0)
1909 cmpl_state->re_complete = FALSE;
1914 first_diff_index(cmpl_state->updated_text,
1915 cmpl_this_completion(poss));
1917 cmpl_state->re_complete = FALSE;
1919 if(first_diff == PATTERN_MATCH)
1922 if(first_diff > cmpl_state->updated_text_len)
1923 strcpy(cmpl_state->updated_text, cmpl_this_completion(poss));
1925 cmpl_state->updated_text_len = first_diff;
1926 cmpl_state->updated_text[first_diff] = 0;
1930 static PossibleCompletion*
1931 attempt_file_completion(CompletionState *cmpl_state)
1933 gchar *pat_buf, *first_slash;
1934 CompletionDir *dir = cmpl_state->active_completion_dir;
1936 dir->cmpl_index += 1;
1938 if(dir->cmpl_index == dir->sent->entry_count)
1940 if(dir->cmpl_parent == NULL)
1942 cmpl_state->active_completion_dir = NULL;
1948 cmpl_state->active_completion_dir = dir->cmpl_parent;
1950 return attempt_file_completion(cmpl_state);
1954 g_assert(dir->cmpl_text);
1956 first_slash = strchr(dir->cmpl_text, '/');
1960 gint len = first_slash - dir->cmpl_text;
1962 pat_buf = g_new (gchar, len + 1);
1963 strncpy(pat_buf, dir->cmpl_text, len);
1968 gint len = strlen(dir->cmpl_text);
1970 pat_buf = g_new (gchar, len + 2);
1971 strcpy(pat_buf, dir->cmpl_text);
1972 strcpy(pat_buf + len, "*");
1977 if(dir->sent->entries[dir->cmpl_index].is_dir)
1979 if(fnmatch(pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
1980 FNMATCH_FLAGS) != FNM_NOMATCH)
1982 CompletionDir* new_dir;
1984 new_dir = open_relative_dir(dir->sent->entries[dir->cmpl_index].entry_name,
1993 new_dir->cmpl_parent = dir;
1995 new_dir->cmpl_index = -1;
1996 new_dir->cmpl_text = first_slash + 1;
1998 cmpl_state->active_completion_dir = new_dir;
2001 return attempt_file_completion(cmpl_state);
2006 return attempt_file_completion(cmpl_state);
2012 return attempt_file_completion(cmpl_state);
2017 if(dir->cmpl_parent != NULL)
2019 append_completion_text(dir->fullname +
2020 strlen(cmpl_state->completion_dir->fullname) + 1,
2022 append_completion_text("/", cmpl_state);
2025 append_completion_text(dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
2027 cmpl_state->the_completion.is_a_completion =
2028 (fnmatch(pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
2029 FNMATCH_FLAGS) != FNM_NOMATCH);
2031 cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
2032 if(dir->sent->entries[dir->cmpl_index].is_dir)
2033 append_completion_text("/", cmpl_state);
2036 return &cmpl_state->the_completion;
2042 get_pwdb(CompletionState* cmpl_state)
2044 struct passwd *pwd_ptr;
2045 gchar* buf_ptr, *home_dir = NULL;
2046 gint len = 0, i, count = 0;
2048 if(cmpl_state->user_dir_name_buffer)
2052 while ((pwd_ptr = getpwent()) != NULL)
2054 len += strlen(pwd_ptr->pw_name);
2055 len += strlen(pwd_ptr->pw_dir);
2060 if (!cmpl_state->user_home_dir)
2062 /* the loser doesn't have $HOME set */
2065 pwd_ptr = getpwuid(getuid());
2071 home_dir = pwd_ptr->pw_dir;
2073 len += strlen(home_dir);
2079 cmpl_state->user_dir_name_buffer = g_new(gchar, len);
2080 cmpl_state->user_directories = g_new(CompletionUserDir, count);
2081 cmpl_state->user_directories_len = count;
2083 buf_ptr = cmpl_state->user_dir_name_buffer;
2085 if (!cmpl_state->user_home_dir)
2087 strcpy(buf_ptr, home_dir);
2088 cmpl_state->user_home_dir = buf_ptr;
2089 buf_ptr += strlen(buf_ptr);
2093 for(i = 0; i < count; i += 1)
2095 pwd_ptr = getpwent();
2102 strcpy(buf_ptr, pwd_ptr->pw_name);
2103 cmpl_state->user_directories[i].login = buf_ptr;
2104 buf_ptr += strlen(buf_ptr);
2106 strcpy(buf_ptr, pwd_ptr->pw_dir);
2107 cmpl_state->user_directories[i].homedir = buf_ptr;
2108 buf_ptr += strlen(buf_ptr);
2112 qsort(cmpl_state->user_directories,
2113 cmpl_state->user_directories_len,
2114 sizeof(CompletionUserDir),
2123 if(cmpl_state->user_dir_name_buffer)
2124 g_free(cmpl_state->user_dir_name_buffer);
2125 if(cmpl_state->user_directories)
2126 g_free(cmpl_state->user_directories);
2128 cmpl_state->user_dir_name_buffer = NULL;
2129 cmpl_state->user_directories = NULL;
2135 compare_user_dir(const void* a, const void* b)
2137 return strcmp((((CompletionUserDir*)a))->login,
2138 (((CompletionUserDir*)b))->login);
2142 compare_cmpl_dir(const void* a, const void* b)
2144 return strcmp((((CompletionDirEntry*)a))->entry_name,
2145 (((CompletionDirEntry*)b))->entry_name);
2149 cmpl_state_okay(CompletionState* cmpl_state)
2151 return cmpl_state && cmpl_state->reference_dir;
2155 cmpl_strerror(gint err)
2157 if(err == CMPL_ERRNO_TOO_LONG)
2158 return "Name too long";
2160 return g_strerror (err);