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_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),
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);
1183 cmpl_state->directory_storage = NULL;
1185 if (cdl->data == cmpl_state->reference_dir)
1186 cmpl_state->directory_storage = g_list_prepend(NULL, cdl->data);
1188 free_dir (cdl->data);
1195 /**********************************************************************/
1196 /* The main entrances. */
1197 /**********************************************************************/
1199 static PossibleCompletion*
1200 cmpl_completion_matches (gchar* text_to_complete,
1201 gchar** remaining_text,
1202 CompletionState* cmpl_state)
1205 PossibleCompletion *poss;
1207 prune_memory_usage(cmpl_state);
1209 g_assert(text_to_complete);
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;
1219 first_slash = strchr(text_to_complete, '/');
1221 if(text_to_complete[0] == '~' && !first_slash)
1223 /* Text starts with ~ and there is no slash, show all the
1224 * home directory completions.
1226 poss = attempt_homedir_completion(text_to_complete, cmpl_state);
1228 update_cmpl(poss, cmpl_state);
1233 cmpl_state->reference_dir =
1234 open_ref_dir(text_to_complete, remaining_text, cmpl_state);
1236 if(!cmpl_state->reference_dir)
1239 cmpl_state->completion_dir =
1240 find_completion_dir(*remaining_text, remaining_text, cmpl_state);
1242 cmpl_state->last_valid_char = *remaining_text - text_to_complete;
1244 if(!cmpl_state->completion_dir)
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;
1251 cmpl_state->active_completion_dir = cmpl_state->completion_dir;
1253 cmpl_state->reference_dir = cmpl_state->completion_dir;
1255 poss = attempt_file_completion(cmpl_state);
1257 update_cmpl(poss, cmpl_state);
1262 static PossibleCompletion*
1263 cmpl_next_completion (CompletionState* cmpl_state)
1265 PossibleCompletion* poss = NULL;
1267 cmpl_state->the_completion.text[0] = 0;
1269 if(cmpl_state->user_completion_index >= 0)
1270 poss = attempt_homedir_completion(cmpl_state->last_completion_text, cmpl_state);
1272 poss = attempt_file_completion(cmpl_state);
1274 update_cmpl(poss, cmpl_state);
1279 /**********************************************************************/
1280 /* Directory Operations */
1281 /**********************************************************************/
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)
1290 CompletionDir *new_dir;
1292 first_slash = strchr(text_to_complete, '/');
1294 if (text_to_complete[0] == '/' || !cmpl_state->reference_dir)
1296 new_dir = open_dir("/", cmpl_state);
1299 *remaining_text = text_to_complete + 1;
1301 else if (text_to_complete[0] == '~')
1303 new_dir = open_user_dir(text_to_complete, cmpl_state);
1308 *remaining_text = first_slash + 1;
1310 *remaining_text = text_to_complete + strlen(text_to_complete);
1319 *remaining_text = text_to_complete;
1321 new_dir = open_dir(cmpl_state->reference_dir->fullname, cmpl_state);
1326 new_dir->cmpl_index = -1;
1327 new_dir->cmpl_parent = NULL;
1333 /* open a directory by user name */
1334 static CompletionDir*
1335 open_user_dir(gchar* text_to_complete,
1336 CompletionState *cmpl_state)
1341 g_assert(text_to_complete && text_to_complete[0] == '~');
1343 first_slash = strchr(text_to_complete, '/');
1346 cmp_len = first_slash - text_to_complete - 1;
1348 cmp_len = strlen(text_to_complete + 1);
1353 if (!cmpl_state->user_home_dir &&
1354 !get_pwdb(cmpl_state))
1356 return open_dir(cmpl_state->user_home_dir, cmpl_state);
1361 char* copy = g_new(char, cmp_len + 1);
1363 strncpy(copy, text_to_complete + 1, cmp_len);
1365 pwd = getpwnam(copy);
1373 return open_dir(pwd->pw_dir, cmpl_state);
1377 /* open a directory relative the the current relative directory */
1378 static CompletionDir*
1379 open_relative_dir(gchar* dir_name,
1381 CompletionState *cmpl_state)
1383 gchar path_buf[2*MAXPATHLEN];
1385 if(dir->fullname_len + strlen(dir_name) + 2 >= MAXPATHLEN)
1387 cmpl_errno = CMPL_ERRNO_TOO_LONG;
1391 strcpy(path_buf, dir->fullname);
1393 if(dir->fullname_len > 1)
1395 path_buf[dir->fullname_len] = '/';
1396 strcpy(path_buf + dir->fullname_len + 1, dir_name);
1400 strcpy(path_buf + dir->fullname_len, dir_name);
1403 return open_dir(path_buf, cmpl_state);
1406 /* after the cache lookup fails, really open a new directory */
1407 static CompletionDirSent*
1408 open_new_dir(gchar* dir_name, struct stat* sbuf)
1410 CompletionDirSent* sent;
1413 struct dirent *dirent_ptr;
1414 gint buffer_size = 0;
1415 gint entry_count = 0;
1417 struct stat ent_sbuf;
1418 char path_buf[MAXPATHLEN*2];
1421 sent = g_new(CompletionDirSent, 1);
1422 sent->mtime = sbuf->st_mtime;
1423 sent->inode = sbuf->st_ino;
1425 path_buf_len = strlen(dir_name);
1427 if (path_buf_len > MAXPATHLEN)
1429 cmpl_errno = CMPL_ERRNO_TOO_LONG;
1433 strcpy(path_buf, dir_name);
1435 directory = opendir(dir_name);
1443 while((dirent_ptr = readdir(directory)) != NULL)
1445 int entry_len = strlen(dirent_ptr->d_name);
1446 buffer_size += entry_len + 1;
1449 if(path_buf_len + entry_len + 2 >= MAXPATHLEN)
1451 cmpl_errno = CMPL_ERRNO_TOO_LONG;
1452 closedir(directory);
1457 sent->name_buffer = g_new(gchar, buffer_size);
1458 sent->entries = g_new(CompletionDirEntry, entry_count);
1459 sent->entry_count = entry_count;
1461 buffer_ptr = sent->name_buffer;
1463 rewinddir(directory);
1465 for(i = 0; i < entry_count; i += 1)
1467 dirent_ptr = readdir(directory);
1472 closedir(directory);
1476 strcpy(buffer_ptr, dirent_ptr->d_name);
1477 sent->entries[i].entry_name = buffer_ptr;
1478 buffer_ptr += strlen(dirent_ptr->d_name);
1482 path_buf[path_buf_len] = '/';
1483 strcpy(path_buf + path_buf_len + 1, dirent_ptr->d_name);
1485 if(stat(path_buf, &ent_sbuf) >= 0 && S_ISDIR(ent_sbuf.st_mode))
1486 sent->entries[i].is_dir = 1;
1488 /* stat may fail, and we don't mind, since it could be a
1489 * dangling symlink. */
1490 sent->entries[i].is_dir = 0;
1493 qsort(sent->entries, sent->entry_count, sizeof(CompletionDirEntry), compare_cmpl_dir);
1495 closedir(directory);
1500 /* open a directory by absolute pathname */
1501 static CompletionDir*
1502 open_dir(gchar* dir_name, CompletionState* cmpl_state)
1505 CompletionDirSent *sent;
1508 if(stat(dir_name, &sbuf) < 0)
1514 cdsl = cmpl_state->directory_sent_storage;
1520 if(sent->inode == sbuf.st_ino &&
1521 sent->mtime == sbuf.st_mtime)
1522 return attach_dir(sent, dir_name, cmpl_state);
1527 sent = open_new_dir(dir_name, &sbuf);
1530 cmpl_state->directory_sent_storage =
1531 g_list_prepend(cmpl_state->directory_sent_storage, sent);
1533 return attach_dir(sent, dir_name, cmpl_state);
1539 static CompletionDir*
1540 attach_dir(CompletionDirSent* sent, gchar* dir_name, CompletionState *cmpl_state)
1542 CompletionDir* new_dir;
1544 new_dir = g_new(CompletionDir, 1);
1546 cmpl_state->directory_storage =
1547 g_list_prepend(cmpl_state->directory_storage, new_dir);
1549 new_dir->sent = sent;
1550 new_dir->fullname = g_strdup(dir_name);
1551 new_dir->fullname_len = strlen(dir_name);
1557 correct_dir_fullname(CompletionDir* cmpl_dir)
1559 gint length = strlen(cmpl_dir->fullname);
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)
1570 strcpy(cmpl_dir->fullname, "/");
1571 cmpl_dir->fullname_len = 1;
1575 if(stat(cmpl_dir->fullname, &sbuf) < 0)
1581 cmpl_dir->fullname[length - 2] = 0;
1583 if(!correct_parent(cmpl_dir, &sbuf))
1586 else if (strcmp(cmpl_dir->fullname + length - 4, "/../") == 0)
1590 strcpy(cmpl_dir->fullname, "/");
1591 cmpl_dir->fullname_len = 1;
1595 if(stat(cmpl_dir->fullname, &sbuf) < 0)
1601 cmpl_dir->fullname[length - 3] = 0;
1603 if(!correct_parent(cmpl_dir, &sbuf))
1607 cmpl_dir->fullname_len = strlen(cmpl_dir->fullname);
1613 correct_parent(CompletionDir* cmpl_dir, struct stat *sbuf)
1620 last_slash = strrchr(cmpl_dir->fullname, '/');
1622 g_assert(last_slash);
1624 if(last_slash != cmpl_dir->fullname)
1625 { /* last_slash[0] = 0; */ }
1632 if (stat(cmpl_dir->fullname, &parbuf) < 0)
1638 if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
1639 /* it wasn't a link */
1645 last_slash[0] = '/'; */
1647 /* it was a link, have to figure it out the hard way */
1649 new_name = find_parent_dir_fullname(cmpl_dir->fullname);
1654 g_free(cmpl_dir->fullname);
1656 cmpl_dir->fullname = new_name;
1662 find_parent_dir_fullname(gchar* dirname)
1664 gchar buffer[MAXPATHLEN];
1665 gchar buffer2[MAXPATHLEN];
1667 if(!getcwd(buffer, MAXPATHLEN))
1673 if(chdir(dirname) != 0 || chdir("..") != 0)
1679 if(!getcwd(buffer2, MAXPATHLEN))
1687 if(chdir(buffer) != 0)
1693 return g_strdup(buffer2);
1696 /**********************************************************************/
1697 /* Completion Operations */
1698 /**********************************************************************/
1700 static PossibleCompletion*
1701 attempt_homedir_completion(gchar* text_to_complete,
1702 CompletionState *cmpl_state)
1706 if (!cmpl_state->user_dir_name_buffer &&
1707 !get_pwdb(cmpl_state))
1709 length = strlen(text_to_complete) - 1;
1711 cmpl_state->user_completion_index += 1;
1713 while(cmpl_state->user_completion_index < cmpl_state->user_directories_len)
1715 index = first_diff_index(text_to_complete + 1,
1716 cmpl_state->user_directories
1717 [cmpl_state->user_completion_index].login);
1724 if(cmpl_state->last_valid_char < (index + 1))
1725 cmpl_state->last_valid_char = index + 1;
1726 cmpl_state->user_completion_index += 1;
1730 cmpl_state->the_completion.is_a_completion = 1;
1731 cmpl_state->the_completion.is_directory = 1;
1733 append_completion_text("~", cmpl_state);
1735 append_completion_text(cmpl_state->
1736 user_directories[cmpl_state->user_completion_index].login,
1739 return append_completion_text("/", cmpl_state);
1742 if(text_to_complete[1] ||
1743 cmpl_state->user_completion_index > cmpl_state->user_directories_len)
1745 cmpl_state->user_completion_index = -1;
1750 cmpl_state->user_completion_index += 1;
1751 cmpl_state->the_completion.is_a_completion = 1;
1752 cmpl_state->the_completion.is_directory = 1;
1754 return append_completion_text("~/", cmpl_state);
1758 /* returns the index (>= 0) of the first differing character,
1759 * PATTERN_MATCH if the completion matches */
1761 first_diff_index(gchar* pat, gchar* text)
1765 while(*pat && *text && *text == *pat)
1775 return PATTERN_MATCH;
1778 static PossibleCompletion*
1779 append_completion_text(gchar* text, CompletionState* cmpl_state)
1783 if(!cmpl_state->the_completion.text)
1786 len = strlen(text) + strlen(cmpl_state->the_completion.text) + 1;
1788 if(cmpl_state->the_completion.text_alloc > len)
1790 strcat(cmpl_state->the_completion.text, text);
1791 return &cmpl_state->the_completion;
1794 while(i < len) { i <<= 1; }
1796 cmpl_state->the_completion.text_alloc = i;
1798 cmpl_state->the_completion.text = (gchar*)g_realloc(cmpl_state->the_completion.text, i);
1800 if(!cmpl_state->the_completion.text)
1804 strcat(cmpl_state->the_completion.text, text);
1805 return &cmpl_state->the_completion;
1809 static CompletionDir*
1810 find_completion_dir(gchar* text_to_complete,
1811 gchar** remaining_text,
1812 CompletionState* cmpl_state)
1814 gchar* first_slash = strchr(text_to_complete, '/');
1815 CompletionDir* dir = cmpl_state->reference_dir;
1816 *remaining_text = text_to_complete;
1820 gint len = first_slash - *remaining_text;
1822 gint found_index = -1;
1824 gchar* pat_buf = g_new (gchar, len + 1);
1826 strncpy(pat_buf, *remaining_text, len);
1829 for(i = 0; i < dir->sent->entry_count; i += 1)
1831 if(dir->sent->entries[i].is_dir &&
1832 fnmatch(pat_buf, dir->sent->entries[i].entry_name,
1833 FNMATCH_FLAGS)!= FNM_NOMATCH)
1850 CompletionDir* next = open_relative_dir(dir->sent->entries[found_index].entry_name,
1859 next->cmpl_parent = dir;
1863 if(!correct_dir_fullname(dir))
1869 *remaining_text = first_slash + 1;
1870 first_slash = strchr(*remaining_text, '/');
1885 update_cmpl(PossibleCompletion* poss, CompletionState* cmpl_state)
1889 if(!poss || !cmpl_is_a_completion(poss))
1892 cmpl_len = strlen(cmpl_this_completion(poss));
1894 if(cmpl_state->updated_text_alloc < cmpl_len + 1)
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;
1902 if(cmpl_state->updated_text_len < 0)
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);
1908 else if(cmpl_state->updated_text_len == 0)
1910 cmpl_state->re_complete = FALSE;
1915 first_diff_index(cmpl_state->updated_text,
1916 cmpl_this_completion(poss));
1918 cmpl_state->re_complete = FALSE;
1920 if(first_diff == PATTERN_MATCH)
1923 if(first_diff > cmpl_state->updated_text_len)
1924 strcpy(cmpl_state->updated_text, cmpl_this_completion(poss));
1926 cmpl_state->updated_text_len = first_diff;
1927 cmpl_state->updated_text[first_diff] = 0;
1931 static PossibleCompletion*
1932 attempt_file_completion(CompletionState *cmpl_state)
1934 gchar *pat_buf, *first_slash;
1935 CompletionDir *dir = cmpl_state->active_completion_dir;
1937 dir->cmpl_index += 1;
1939 if(dir->cmpl_index == dir->sent->entry_count)
1941 if(dir->cmpl_parent == NULL)
1943 cmpl_state->active_completion_dir = NULL;
1949 cmpl_state->active_completion_dir = dir->cmpl_parent;
1951 return attempt_file_completion(cmpl_state);
1955 g_assert(dir->cmpl_text);
1957 first_slash = strchr(dir->cmpl_text, '/');
1961 gint len = first_slash - dir->cmpl_text;
1963 pat_buf = g_new (gchar, len + 1);
1964 strncpy(pat_buf, dir->cmpl_text, len);
1969 gint len = strlen(dir->cmpl_text);
1971 pat_buf = g_new (gchar, len + 2);
1972 strcpy(pat_buf, dir->cmpl_text);
1973 strcpy(pat_buf + len, "*");
1978 if(dir->sent->entries[dir->cmpl_index].is_dir)
1980 if(fnmatch(pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
1981 FNMATCH_FLAGS) != FNM_NOMATCH)
1983 CompletionDir* new_dir;
1985 new_dir = open_relative_dir(dir->sent->entries[dir->cmpl_index].entry_name,
1994 new_dir->cmpl_parent = dir;
1996 new_dir->cmpl_index = -1;
1997 new_dir->cmpl_text = first_slash + 1;
1999 cmpl_state->active_completion_dir = new_dir;
2002 return attempt_file_completion(cmpl_state);
2007 return attempt_file_completion(cmpl_state);
2013 return attempt_file_completion(cmpl_state);
2018 if(dir->cmpl_parent != NULL)
2020 append_completion_text(dir->fullname +
2021 strlen(cmpl_state->completion_dir->fullname) + 1,
2023 append_completion_text("/", cmpl_state);
2026 append_completion_text(dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
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);
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);
2037 return &cmpl_state->the_completion;
2043 get_pwdb(CompletionState* cmpl_state)
2045 struct passwd *pwd_ptr;
2046 gchar* buf_ptr, *home_dir = NULL;
2047 gint len = 0, i, count = 0;
2049 if(cmpl_state->user_dir_name_buffer)
2053 while ((pwd_ptr = getpwent()) != NULL)
2055 len += strlen(pwd_ptr->pw_name);
2056 len += strlen(pwd_ptr->pw_dir);
2061 if (!cmpl_state->user_home_dir)
2063 /* the loser doesn't have $HOME set */
2066 pwd_ptr = getpwuid(getuid());
2072 home_dir = pwd_ptr->pw_dir;
2074 len += strlen(home_dir);
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;
2084 buf_ptr = cmpl_state->user_dir_name_buffer;
2086 if (!cmpl_state->user_home_dir)
2088 strcpy(buf_ptr, home_dir);
2089 cmpl_state->user_home_dir = buf_ptr;
2090 buf_ptr += strlen(buf_ptr);
2094 for(i = 0; i < count; i += 1)
2096 pwd_ptr = getpwent();
2103 strcpy(buf_ptr, pwd_ptr->pw_name);
2104 cmpl_state->user_directories[i].login = buf_ptr;
2105 buf_ptr += strlen(buf_ptr);
2107 strcpy(buf_ptr, pwd_ptr->pw_dir);
2108 cmpl_state->user_directories[i].homedir = buf_ptr;
2109 buf_ptr += strlen(buf_ptr);
2113 qsort(cmpl_state->user_directories,
2114 cmpl_state->user_directories_len,
2115 sizeof(CompletionUserDir),
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);
2129 cmpl_state->user_dir_name_buffer = NULL;
2130 cmpl_state->user_directories = NULL;
2136 compare_user_dir(const void* a, const void* b)
2138 return strcmp((((CompletionUserDir*)a))->login,
2139 (((CompletionUserDir*)b))->login);
2143 compare_cmpl_dir(const void* a, const void* b)
2145 return strcmp((((CompletionDirEntry*)a))->entry_name,
2146 (((CompletionDirEntry*)b))->entry_name);
2150 cmpl_state_okay(CompletionState* cmpl_state)
2152 return cmpl_state && cmpl_state->reference_dir;
2156 cmpl_strerror(gint err)
2158 if(err == CMPL_ERRNO_TOO_LONG)
2159 return "Name too long";
2161 return g_strerror (err);