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,
311 (GtkArgSetFunc) NULL,
312 (GtkArgGetFunc) NULL,
315 file_selection_type = gtk_type_unique (gtk_window_get_type (), &filesel_info);
318 return file_selection_type;
322 gtk_file_selection_class_init (GtkFileSelectionClass *class)
324 GtkObjectClass *object_class;
326 object_class = (GtkObjectClass*) class;
328 parent_class = gtk_type_class (gtk_window_get_type ());
330 object_class->destroy = gtk_file_selection_destroy;
334 gtk_file_selection_init (GtkFileSelection *filesel)
337 GtkWidget *file_vbox;
338 GtkWidget *entry_vbox;
341 GtkWidget *list_hbox;
342 GtkWidget *action_area;
345 filesel->cmpl_state = cmpl_init_state ();
347 /* The dialog-sized vertical box */
348 filesel->main_vbox = gtk_vbox_new (FALSE, 10);
349 gtk_container_border_width (GTK_CONTAINER (filesel), 10);
350 gtk_container_add (GTK_CONTAINER (filesel), filesel->main_vbox);
351 gtk_widget_show (filesel->main_vbox);
353 /* The horizontal box containing the directory and file listboxes */
354 list_hbox = gtk_hbox_new (TRUE, 5);
355 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0);
356 gtk_widget_show (list_hbox);
359 /* The directories listbox */
360 dir_vbox = gtk_vbox_new (FALSE, 2);
361 gtk_box_pack_start (GTK_BOX (list_hbox), dir_vbox, TRUE, TRUE, 0);
362 gtk_widget_show (dir_vbox);
364 label = gtk_label_new ("Directories");
365 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
366 gtk_box_pack_start (GTK_BOX (dir_vbox), label, FALSE, FALSE, 0);
367 gtk_widget_show (label);
369 listbox = gtk_scrolled_window_new (NULL, NULL);
370 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (listbox),
371 GTK_POLICY_AUTOMATIC,
373 gtk_box_pack_start (GTK_BOX (dir_vbox), listbox, TRUE, TRUE, 0);
374 gtk_widget_set_usize (listbox, DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
375 gtk_widget_show (listbox);
377 filesel->dir_list = gtk_list_new ();
378 gtk_list_set_selection_mode (GTK_LIST (filesel->dir_list), GTK_SELECTION_BROWSE);
379 gtk_signal_connect (GTK_OBJECT (filesel->dir_list), "button_press_event",
380 (GtkSignalFunc) gtk_file_selection_dir_button, filesel);
381 key = gtk_signal_connect (GTK_OBJECT (filesel->dir_list),
383 (GtkSignalFunc) gtk_file_selection_dir_list_changed,
385 gtk_object_set_data (GTK_OBJECT (filesel->dir_list), list_changed_key, (gpointer) key);
386 gtk_container_add (GTK_CONTAINER (listbox), filesel->dir_list);
387 gtk_widget_show (filesel->dir_list);
390 /* The files listbox */
391 file_vbox = gtk_vbox_new (FALSE, 2);
392 gtk_box_pack_start (GTK_BOX (list_hbox), file_vbox, TRUE, TRUE, 0);
393 gtk_widget_show (file_vbox);
395 label = gtk_label_new ("Files");
396 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
397 gtk_box_pack_start (GTK_BOX (file_vbox), label, FALSE, FALSE, 0);
398 gtk_widget_show (label);
400 listbox = gtk_scrolled_window_new (NULL, NULL);
401 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (listbox),
402 GTK_POLICY_AUTOMATIC,
404 gtk_box_pack_start (GTK_BOX (file_vbox), listbox, TRUE, TRUE, 0);
405 gtk_widget_set_usize (listbox, FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
406 gtk_widget_show (listbox);
408 filesel->file_list = gtk_list_new ();
409 gtk_list_set_selection_mode (GTK_LIST (filesel->file_list), GTK_SELECTION_SINGLE);
410 gtk_signal_connect (GTK_OBJECT (filesel->file_list), "button_press_event",
411 (GtkSignalFunc) gtk_file_selection_file_button, filesel);
412 key = gtk_signal_connect (GTK_OBJECT (filesel->file_list),
414 (GtkSignalFunc) gtk_file_selection_file_list_changed,
416 gtk_object_set_data (GTK_OBJECT (filesel->file_list), list_changed_key, (gpointer) key);
417 gtk_container_add (GTK_CONTAINER (listbox), filesel->file_list);
418 gtk_widget_show (filesel->file_list);
421 /* The action area */
422 action_area = gtk_hbox_new (TRUE, 10);
423 gtk_box_pack_end (GTK_BOX (filesel->main_vbox), action_area, FALSE, FALSE, 0);
424 gtk_widget_show (action_area);
427 filesel->ok_button = gtk_button_new_with_label ("OK");
428 GTK_WIDGET_SET_FLAGS (filesel->ok_button, GTK_CAN_DEFAULT);
429 gtk_box_pack_start (GTK_BOX (action_area), filesel->ok_button, TRUE, TRUE, 0);
430 gtk_widget_grab_default (filesel->ok_button);
431 gtk_widget_show (filesel->ok_button);
433 /* The Cancel button */
434 filesel->cancel_button = gtk_button_new_with_label ("Cancel");
435 GTK_WIDGET_SET_FLAGS (filesel->cancel_button, GTK_CAN_DEFAULT);
436 gtk_box_pack_start (GTK_BOX (action_area), filesel->cancel_button, TRUE, TRUE, 0);
437 gtk_widget_show (filesel->cancel_button);
439 /* The Help button */
440 filesel->help_button = gtk_button_new_with_label ("Help");
441 GTK_WIDGET_SET_FLAGS (filesel->help_button, GTK_CAN_DEFAULT);
442 gtk_box_pack_start (GTK_BOX (action_area), filesel->help_button, TRUE, TRUE, 0);
443 gtk_widget_show (filesel->help_button);
446 /* The selection entry widget */
447 entry_vbox = gtk_vbox_new (FALSE, 2);
448 gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 0);
449 gtk_widget_show (entry_vbox);
451 filesel->selection_text = label = gtk_label_new ("");
452 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
453 gtk_box_pack_start (GTK_BOX (entry_vbox), label, FALSE, FALSE, 0);
454 gtk_widget_show (label);
456 filesel->selection_entry = gtk_entry_new ();
457 gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event",
458 (GtkSignalFunc) gtk_file_selection_key_press, filesel);
459 gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event",
460 (GtkSignalFunc) gtk_widget_grab_default,
461 GTK_OBJECT (filesel->ok_button));
462 gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "activate",
463 (GtkSignalFunc) gtk_button_clicked,
464 GTK_OBJECT (filesel->ok_button));
465 gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
466 gtk_widget_show (filesel->selection_entry);
468 if (!cmpl_state_okay (filesel->cmpl_state))
472 sprintf (err_buf, "Directory unreadable: %s", cmpl_strerror (cmpl_errno));
474 gtk_label_set (GTK_LABEL (filesel->selection_text), err_buf);
478 gtk_file_selection_populate (filesel, "", FALSE);
481 gtk_widget_grab_focus (filesel->selection_entry);
485 gtk_file_selection_new (const gchar *title)
487 GtkFileSelection *filesel;
489 filesel = gtk_type_new (gtk_file_selection_get_type ());
490 gtk_window_set_title (GTK_WINDOW (filesel), title);
492 return GTK_WIDGET (filesel);
496 gtk_file_selection_set_filename (GtkFileSelection *filesel,
497 const gchar *filename)
499 char buf[MAXPATHLEN];
500 const char *name, *last_slash;
502 g_return_if_fail (filesel != NULL);
503 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
504 g_return_if_fail (filename != NULL);
506 last_slash = strrchr (filename, '/');
515 gint len = MIN (MAXPATHLEN - 1, last_slash - filename + 1);
517 strncpy (buf, filename, len);
520 name = last_slash + 1;
523 gtk_file_selection_populate (filesel, buf, FALSE);
525 if (filesel->selection_entry)
526 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
530 gtk_file_selection_get_filename (GtkFileSelection *filesel)
532 static char nothing[2] = "";
536 g_return_val_if_fail (filesel != NULL, nothing);
537 g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
539 text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
542 filename = cmpl_completion_fullname (text, filesel->cmpl_state);
550 gtk_file_selection_destroy (GtkObject *object)
552 GtkFileSelection *filesel;
554 g_return_if_fail (object != NULL);
555 g_return_if_fail (GTK_IS_FILE_SELECTION (object));
557 filesel = GTK_FILE_SELECTION (object);
559 cmpl_free_state (filesel->cmpl_state);
561 if (GTK_OBJECT_CLASS (parent_class)->destroy)
562 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
566 gtk_file_selection_key_press (GtkWidget *widget,
570 GtkFileSelection *fs;
573 g_return_val_if_fail (widget != NULL, FALSE);
574 g_return_val_if_fail (event != NULL, FALSE);
576 if (event->keyval == GDK_Tab)
578 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
580 fs = GTK_FILE_SELECTION (user_data);
581 text = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
582 gtk_file_selection_populate (fs, text, TRUE);
591 gtk_file_selection_file_button (GtkWidget *widget,
592 GdkEventButton *event,
595 GtkFileSelection *fs;
596 GtkWidget *event_widget;
600 g_return_val_if_fail (widget != NULL, FALSE);
601 g_return_val_if_fail (event != NULL, FALSE);
603 fs = GTK_FILE_SELECTION (user_data);
604 g_return_val_if_fail (fs != NULL, FALSE);
605 g_return_val_if_fail (GTK_IS_FILE_SELECTION (fs), FALSE);
607 event_widget = gtk_get_event_widget ((GdkEvent*) event);
609 if (GTK_IS_LIST_ITEM (event_widget))
613 case GDK_BUTTON_PRESS:
614 filename = gtk_object_get_user_data (GTK_OBJECT (event_widget));
615 gtk_widget_grab_focus (fs->selection_entry);
616 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
620 case GDK_2BUTTON_PRESS:
621 gtk_button_clicked (GTK_BUTTON (fs->ok_button));
634 gtk_file_selection_file_list_changed (GtkList *list,
637 GtkFileSelection *fs;
639 g_return_if_fail (func_data != NULL);
640 g_return_if_fail (GTK_IS_FILE_SELECTION (func_data));
642 fs = GTK_FILE_SELECTION (func_data);
644 /* only act on an appropriate selection
646 if (list->selection && list->selection->data)
650 item = list->selection->data;
652 if (GTK_IS_LIST_ITEM (item))
656 filename = gtk_object_get_user_data (GTK_OBJECT (item));
657 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
663 gtk_file_selection_dir_button (GtkWidget *widget,
664 GdkEventButton *event,
667 GtkFileSelection *fs;
668 GtkWidget *event_widget;
672 g_return_val_if_fail (widget != NULL, FALSE);
673 g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
674 g_return_val_if_fail (event != NULL, FALSE);
676 fs = GTK_FILE_SELECTION (user_data);
677 g_return_val_if_fail (fs != NULL, FALSE);
678 g_return_val_if_fail (GTK_IS_FILE_SELECTION (fs), FALSE);
680 event_widget = gtk_get_event_widget ((GdkEvent*) event);
682 if (GTK_IS_LIST_ITEM (event_widget))
686 filename = gtk_object_get_user_data (GTK_OBJECT (event_widget));
688 key = (gint) gtk_object_get_data (GTK_OBJECT (widget), list_changed_key);
692 case GDK_BUTTON_PRESS:
694 gtk_signal_handler_block (GTK_OBJECT (widget), key);
695 gtk_widget_activate (GTK_WIDGET (event_widget));
696 gtk_signal_handler_unblock (GTK_OBJECT (widget), key);
698 gtk_widget_grab_focus (fs->selection_entry);
699 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
703 case GDK_2BUTTON_PRESS:
704 gtk_file_selection_populate (fs, filename, FALSE);
717 gtk_file_selection_dir_list_changed (GtkList *list,
720 GtkFileSelection *fs;
722 g_return_if_fail (func_data != NULL);
723 g_return_if_fail (GTK_IS_FILE_SELECTION (func_data));
725 fs = GTK_FILE_SELECTION (func_data);
727 /* only act on an appropriate selection
729 if (list->selection && list->selection->data)
733 item = list->selection->data;
735 if (GTK_IS_LIST_ITEM (item))
739 filename = gtk_object_get_user_data (GTK_OBJECT (item));
742 gtk_file_selection_populate (fs, filename, FALSE);
748 gtk_file_selection_populate (GtkFileSelection *fs,
752 CompletionState *cmpl_state;
753 PossibleCompletion* poss;
754 GList *dir_list = NULL;
755 GList *file_list = NULL;
758 gchar* rem_path = rel_path;
760 gint did_recurse = FALSE;
761 gint possible_count = 0;
762 gint selection_index = -1;
763 gint dir_changed_key;
765 g_return_if_fail (fs != NULL);
766 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
768 dir_changed_key = (gint) gtk_object_get_data (GTK_OBJECT (fs->dir_list), list_changed_key);
769 gtk_signal_handler_block (GTK_OBJECT (fs->dir_list), dir_changed_key);
771 cmpl_state = (CompletionState*) fs->cmpl_state;
772 poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
774 if (!cmpl_state_okay (cmpl_state))
776 /* Something went wrong. */
777 gtk_file_selection_abort (fs);
778 gtk_signal_handler_unblock (GTK_OBJECT (fs->dir_list), dir_changed_key);
782 g_assert (cmpl_state->reference_dir);
784 /* Set the dir_list and file_list to be GLists of strdup'd
785 * filenames, including ./ and ../ */
786 dir_list = g_list_prepend (dir_list, g_strdup("./"));
787 dir_list = g_list_prepend (dir_list, g_strdup("../"));
791 if (cmpl_is_a_completion (poss))
795 filename = g_strdup (cmpl_this_completion (poss));
797 if (cmpl_is_directory (poss))
799 if (strcmp (filename, "./") != 0 &&
800 strcmp (filename, "../") != 0)
801 dir_list = g_list_prepend (dir_list, filename);
804 file_list = g_list_prepend (file_list, filename);
807 poss = cmpl_next_completion (cmpl_state);
810 /* File lists are set. */
812 g_assert (cmpl_state->reference_dir);
816 /* User is trying to complete filenames, so advance the user's input
817 * string to the updated_text, which is the common leading substring
818 * of all possible completions, and if its a directory attempt
819 * attempt completions in it. */
821 if (cmpl_updated_text (cmpl_state)[0])
823 if (cmpl_updated_dir (cmpl_state))
825 gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
829 gtk_file_selection_populate (fs, dir_name, TRUE);
835 if (fs->selection_entry)
836 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
837 cmpl_updated_text (cmpl_state));
842 selection_index = cmpl_last_valid_char (cmpl_state) -
843 (strlen (rel_path) - strlen (rem_path));
844 if (fs->selection_entry)
845 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
850 if (fs->selection_entry)
851 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
856 GList *file_label_list = NULL;
857 GList *dir_label_list = NULL;
859 /* This reverses the lists. */
862 label = gtk_list_item_new_with_label (file_list->data);
863 gtk_object_set_user_data (GTK_OBJECT (label), file_list->data);
864 gtk_widget_show (label);
866 file_label_list = g_list_prepend (file_label_list, label);
867 file_list = file_list->next;
872 label = gtk_list_item_new_with_label (dir_list->data);
873 gtk_object_set_user_data (GTK_OBJECT (label), dir_list->data);
874 gtk_widget_show (label);
876 dir_label_list = g_list_prepend (dir_label_list, label);
877 dir_list = dir_list->next;
880 gtk_container_disable_resize (GTK_CONTAINER (fs));
882 if (fs->selection_entry)
883 gtk_entry_set_position (GTK_ENTRY (fs->selection_entry), selection_index);
885 if (fs->selection_entry)
887 sel_text = g_new (char, strlen (cmpl_reference_position (cmpl_state)) +
888 sizeof ("Selection: "));
889 strcpy (sel_text, "Selection: ");
890 strcat (sel_text, cmpl_reference_position (cmpl_state));
892 gtk_label_set (GTK_LABEL (fs->selection_text), sel_text);
898 gtk_container_foreach (GTK_CONTAINER (fs->dir_list),
899 gtk_file_selection_free_filename, NULL);
900 gtk_list_clear_items (GTK_LIST (fs->dir_list), 0, -1);
903 gtk_list_append_items (GTK_LIST (fs->dir_list), dir_label_list);
907 gtk_container_foreach (GTK_CONTAINER (fs->file_list),
908 gtk_file_selection_free_filename, NULL);
909 gtk_list_clear_items (GTK_LIST (fs->file_list), 0, -1);
912 gtk_list_append_items (GTK_LIST (fs->file_list), file_label_list);
915 gtk_container_enable_resize (GTK_CONTAINER (fs));
919 GList *dir_list0 = dir_list;
920 GList *file_list0 = file_list;
924 GList *tmp = dir_list;
925 dir_list = dir_list->next;
933 GList *tmp = file_list;
934 file_list = file_list->next;
940 g_list_free (dir_list0);
941 g_list_free (file_list0);
944 gtk_signal_handler_unblock (GTK_OBJECT (fs->dir_list), dir_changed_key);
948 gtk_file_selection_abort (GtkFileSelection *fs)
952 sprintf (err_buf, "Directory unreadable: %s", cmpl_strerror (cmpl_errno));
954 /* BEEP gdk_beep(); */
956 if (fs->selection_entry)
957 gtk_label_set (GTK_LABEL (fs->selection_text), err_buf);
961 gtk_file_selection_free_filename (GtkWidget *widget,
962 gpointer client_data)
964 g_return_if_fail (widget != NULL);
966 g_free (gtk_object_get_user_data (GTK_OBJECT (widget)));
967 gtk_object_set_user_data (GTK_OBJECT (widget), NULL);
972 /**********************************************************************/
973 /* External Interface */
974 /**********************************************************************/
976 /* The four completion state selectors
979 cmpl_updated_text (CompletionState* cmpl_state)
981 return cmpl_state->updated_text;
985 cmpl_updated_dir (CompletionState* cmpl_state)
987 return cmpl_state->re_complete;
991 cmpl_reference_position (CompletionState* cmpl_state)
993 return cmpl_state->reference_dir->fullname;
997 cmpl_last_valid_char (CompletionState* cmpl_state)
999 return cmpl_state->last_valid_char;
1003 cmpl_completion_fullname (gchar* text, CompletionState* cmpl_state)
1007 strcpy (cmpl_state->updated_text, text);
1009 else if (text[0] == '~')
1014 dir = open_user_dir (text, cmpl_state);
1018 /* spencer says just return ~something, so
1019 * for now just do it. */
1020 strcpy (cmpl_state->updated_text, text);
1025 strcpy (cmpl_state->updated_text, dir->fullname);
1027 slash = strchr (text, '/');
1030 strcat (cmpl_state->updated_text, slash);
1035 strcpy (cmpl_state->updated_text, cmpl_state->reference_dir->fullname);
1036 strcat (cmpl_state->updated_text, "/");
1037 strcat (cmpl_state->updated_text, text);
1040 return cmpl_state->updated_text;
1043 /* The three completion selectors
1046 cmpl_this_completion (PossibleCompletion* pc)
1052 cmpl_is_directory (PossibleCompletion* pc)
1054 return pc->is_directory;
1058 cmpl_is_a_completion (PossibleCompletion* pc)
1060 return pc->is_a_completion;
1063 /**********************************************************************/
1064 /* Construction, deletion */
1065 /**********************************************************************/
1067 static CompletionState*
1068 cmpl_init_state (void)
1070 gchar getcwd_buf[2*MAXPATHLEN];
1071 CompletionState *new_state;
1073 new_state = g_new (CompletionState, 1);
1075 if (!getcwd (getcwd_buf, MAXPATHLEN))
1081 new_state->reference_dir = NULL;
1082 new_state->completion_dir = NULL;
1083 new_state->active_completion_dir = NULL;
1085 if ((new_state->user_home_dir = getenv("HOME")) != NULL)
1087 /* if this fails, get_pwdb will fill it in. */
1088 new_state->user_home_dir = g_strdup(new_state->user_home_dir);
1091 new_state->directory_storage = NULL;
1092 new_state->directory_sent_storage = NULL;
1093 new_state->last_valid_char = 0;
1094 new_state->updated_text = g_new (gchar, MAXPATHLEN);
1095 new_state->updated_text_alloc = MAXPATHLEN;
1096 new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
1097 new_state->the_completion.text_alloc = MAXPATHLEN;
1098 new_state->user_dir_name_buffer = NULL;
1099 new_state->user_directories = NULL;
1101 new_state->reference_dir = open_dir (getcwd_buf, new_state);
1103 if (!new_state->reference_dir)
1110 cmpl_free_dir_list(GList* dp0)
1115 free_dir (dp->data);
1123 cmpl_free_dir_sent_list(GList* dp0)
1128 free_dir_sent (dp->data);
1136 cmpl_free_state (CompletionState* cmpl_state)
1138 cmpl_free_dir_list(cmpl_state->directory_storage);
1139 cmpl_free_dir_sent_list(cmpl_state->directory_sent_storage);
1141 if (cmpl_state->user_dir_name_buffer)
1142 g_free (cmpl_state->user_dir_name_buffer);
1143 if (cmpl_state->user_directories)
1144 g_free (cmpl_state->user_directories);
1145 if (cmpl_state->the_completion.text)
1146 g_free (cmpl_state->the_completion.text);
1147 if (cmpl_state->updated_text)
1148 g_free (cmpl_state->updated_text);
1150 g_free (cmpl_state);
1154 free_dir(CompletionDir* dir)
1156 g_free(dir->fullname);
1161 free_dir_sent(CompletionDirSent* sent)
1163 g_free(sent->name_buffer);
1164 g_free(sent->entries);
1169 prune_memory_usage(CompletionState *cmpl_state)
1171 GList* cdsl = cmpl_state->directory_sent_storage;
1172 GList* cdl = cmpl_state->directory_storage;
1176 for(; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
1180 cmpl_free_dir_sent_list(cdsl->next);
1184 cmpl_state->directory_storage = NULL;
1186 if (cdl->data == cmpl_state->reference_dir)
1187 cmpl_state->directory_storage = g_list_prepend(NULL, cdl->data);
1189 free_dir (cdl->data);
1196 /**********************************************************************/
1197 /* The main entrances. */
1198 /**********************************************************************/
1200 static PossibleCompletion*
1201 cmpl_completion_matches (gchar* text_to_complete,
1202 gchar** remaining_text,
1203 CompletionState* cmpl_state)
1206 PossibleCompletion *poss;
1208 prune_memory_usage(cmpl_state);
1210 g_assert(text_to_complete);
1212 cmpl_state->user_completion_index = -1;
1213 cmpl_state->last_completion_text = text_to_complete;
1214 cmpl_state->the_completion.text[0] = 0;
1215 cmpl_state->last_valid_char = 0;
1216 cmpl_state->updated_text_len = -1;
1217 cmpl_state->updated_text[0] = 0;
1218 cmpl_state->re_complete = FALSE;
1220 first_slash = strchr(text_to_complete, '/');
1222 if(text_to_complete[0] == '~' && !first_slash)
1224 /* Text starts with ~ and there is no slash, show all the
1225 * home directory completions.
1227 poss = attempt_homedir_completion(text_to_complete, cmpl_state);
1229 update_cmpl(poss, cmpl_state);
1234 cmpl_state->reference_dir =
1235 open_ref_dir(text_to_complete, remaining_text, cmpl_state);
1237 if(!cmpl_state->reference_dir)
1240 cmpl_state->completion_dir =
1241 find_completion_dir(*remaining_text, remaining_text, cmpl_state);
1243 cmpl_state->last_valid_char = *remaining_text - text_to_complete;
1245 if(!cmpl_state->completion_dir)
1248 cmpl_state->completion_dir->cmpl_index = -1;
1249 cmpl_state->completion_dir->cmpl_parent = NULL;
1250 cmpl_state->completion_dir->cmpl_text = *remaining_text;
1252 cmpl_state->active_completion_dir = cmpl_state->completion_dir;
1254 cmpl_state->reference_dir = cmpl_state->completion_dir;
1256 poss = attempt_file_completion(cmpl_state);
1258 update_cmpl(poss, cmpl_state);
1263 static PossibleCompletion*
1264 cmpl_next_completion (CompletionState* cmpl_state)
1266 PossibleCompletion* poss = NULL;
1268 cmpl_state->the_completion.text[0] = 0;
1270 if(cmpl_state->user_completion_index >= 0)
1271 poss = attempt_homedir_completion(cmpl_state->last_completion_text, cmpl_state);
1273 poss = attempt_file_completion(cmpl_state);
1275 update_cmpl(poss, cmpl_state);
1280 /**********************************************************************/
1281 /* Directory Operations */
1282 /**********************************************************************/
1284 /* Open the directory where completion will begin from, if possible. */
1285 static CompletionDir*
1286 open_ref_dir(gchar* text_to_complete,
1287 gchar** remaining_text,
1288 CompletionState* cmpl_state)
1291 CompletionDir *new_dir;
1293 first_slash = strchr(text_to_complete, '/');
1295 if (text_to_complete[0] == '/' || !cmpl_state->reference_dir)
1297 new_dir = open_dir("/", cmpl_state);
1300 *remaining_text = text_to_complete + 1;
1302 else if (text_to_complete[0] == '~')
1304 new_dir = open_user_dir(text_to_complete, cmpl_state);
1309 *remaining_text = first_slash + 1;
1311 *remaining_text = text_to_complete + strlen(text_to_complete);
1320 *remaining_text = text_to_complete;
1322 new_dir = open_dir(cmpl_state->reference_dir->fullname, cmpl_state);
1327 new_dir->cmpl_index = -1;
1328 new_dir->cmpl_parent = NULL;
1334 /* open a directory by user name */
1335 static CompletionDir*
1336 open_user_dir(gchar* text_to_complete,
1337 CompletionState *cmpl_state)
1342 g_assert(text_to_complete && text_to_complete[0] == '~');
1344 first_slash = strchr(text_to_complete, '/');
1347 cmp_len = first_slash - text_to_complete - 1;
1349 cmp_len = strlen(text_to_complete + 1);
1354 if (!cmpl_state->user_home_dir &&
1355 !get_pwdb(cmpl_state))
1357 return open_dir(cmpl_state->user_home_dir, cmpl_state);
1362 char* copy = g_new(char, cmp_len + 1);
1364 strncpy(copy, text_to_complete + 1, cmp_len);
1366 pwd = getpwnam(copy);
1374 return open_dir(pwd->pw_dir, cmpl_state);
1378 /* open a directory relative the the current relative directory */
1379 static CompletionDir*
1380 open_relative_dir(gchar* dir_name,
1382 CompletionState *cmpl_state)
1384 gchar path_buf[2*MAXPATHLEN];
1386 if(dir->fullname_len + strlen(dir_name) + 2 >= MAXPATHLEN)
1388 cmpl_errno = CMPL_ERRNO_TOO_LONG;
1392 strcpy(path_buf, dir->fullname);
1394 if(dir->fullname_len > 1)
1396 path_buf[dir->fullname_len] = '/';
1397 strcpy(path_buf + dir->fullname_len + 1, dir_name);
1401 strcpy(path_buf + dir->fullname_len, dir_name);
1404 return open_dir(path_buf, cmpl_state);
1407 /* after the cache lookup fails, really open a new directory */
1408 static CompletionDirSent*
1409 open_new_dir(gchar* dir_name, struct stat* sbuf)
1411 CompletionDirSent* sent;
1414 struct dirent *dirent_ptr;
1415 gint buffer_size = 0;
1416 gint entry_count = 0;
1418 struct stat ent_sbuf;
1419 char path_buf[MAXPATHLEN*2];
1422 sent = g_new(CompletionDirSent, 1);
1423 sent->mtime = sbuf->st_mtime;
1424 sent->inode = sbuf->st_ino;
1426 path_buf_len = strlen(dir_name);
1428 if (path_buf_len > MAXPATHLEN)
1430 cmpl_errno = CMPL_ERRNO_TOO_LONG;
1434 strcpy(path_buf, dir_name);
1436 directory = opendir(dir_name);
1444 while((dirent_ptr = readdir(directory)) != NULL)
1446 int entry_len = strlen(dirent_ptr->d_name);
1447 buffer_size += entry_len + 1;
1450 if(path_buf_len + entry_len + 2 >= MAXPATHLEN)
1452 cmpl_errno = CMPL_ERRNO_TOO_LONG;
1453 closedir(directory);
1458 sent->name_buffer = g_new(gchar, buffer_size);
1459 sent->entries = g_new(CompletionDirEntry, entry_count);
1460 sent->entry_count = entry_count;
1462 buffer_ptr = sent->name_buffer;
1464 rewinddir(directory);
1466 for(i = 0; i < entry_count; i += 1)
1468 dirent_ptr = readdir(directory);
1473 closedir(directory);
1477 strcpy(buffer_ptr, dirent_ptr->d_name);
1478 sent->entries[i].entry_name = buffer_ptr;
1479 buffer_ptr += strlen(dirent_ptr->d_name);
1483 path_buf[path_buf_len] = '/';
1484 strcpy(path_buf + path_buf_len + 1, dirent_ptr->d_name);
1486 if(stat(path_buf, &ent_sbuf) >= 0 && S_ISDIR(ent_sbuf.st_mode))
1487 sent->entries[i].is_dir = 1;
1489 /* stat may fail, and we don't mind, since it could be a
1490 * dangling symlink. */
1491 sent->entries[i].is_dir = 0;
1494 qsort(sent->entries, sent->entry_count, sizeof(CompletionDirEntry), compare_cmpl_dir);
1496 closedir(directory);
1501 /* open a directory by absolute pathname */
1502 static CompletionDir*
1503 open_dir(gchar* dir_name, CompletionState* cmpl_state)
1506 CompletionDirSent *sent;
1509 if(stat(dir_name, &sbuf) < 0)
1515 cdsl = cmpl_state->directory_sent_storage;
1521 if(sent->inode == sbuf.st_ino &&
1522 sent->mtime == sbuf.st_mtime)
1523 return attach_dir(sent, dir_name, cmpl_state);
1528 sent = open_new_dir(dir_name, &sbuf);
1531 cmpl_state->directory_sent_storage =
1532 g_list_prepend(cmpl_state->directory_sent_storage, sent);
1534 return attach_dir(sent, dir_name, cmpl_state);
1540 static CompletionDir*
1541 attach_dir(CompletionDirSent* sent, gchar* dir_name, CompletionState *cmpl_state)
1543 CompletionDir* new_dir;
1545 new_dir = g_new(CompletionDir, 1);
1547 cmpl_state->directory_storage =
1548 g_list_prepend(cmpl_state->directory_storage, new_dir);
1550 new_dir->sent = sent;
1551 new_dir->fullname = g_strdup(dir_name);
1552 new_dir->fullname_len = strlen(dir_name);
1558 correct_dir_fullname(CompletionDir* cmpl_dir)
1560 gint length = strlen(cmpl_dir->fullname);
1563 if (strcmp(cmpl_dir->fullname + length - 2, "/.") == 0)
1567 strcpy(cmpl_dir->fullname, "/");
1568 cmpl_dir->fullname_len = 1;
1571 cmpl_dir->fullname[length - 2] = 0;
1574 else if (strcmp(cmpl_dir->fullname + length - 3, "/./") == 0)
1575 cmpl_dir->fullname[length - 2] = 0;
1576 else if (strcmp(cmpl_dir->fullname + length - 3, "/..") == 0)
1580 strcpy(cmpl_dir->fullname, "/");
1581 cmpl_dir->fullname_len = 1;
1585 if(stat(cmpl_dir->fullname, &sbuf) < 0)
1591 cmpl_dir->fullname[length - 2] = 0;
1593 if(!correct_parent(cmpl_dir, &sbuf))
1596 else if (strcmp(cmpl_dir->fullname + length - 4, "/../") == 0)
1600 strcpy(cmpl_dir->fullname, "/");
1601 cmpl_dir->fullname_len = 1;
1605 if(stat(cmpl_dir->fullname, &sbuf) < 0)
1611 cmpl_dir->fullname[length - 3] = 0;
1613 if(!correct_parent(cmpl_dir, &sbuf))
1617 cmpl_dir->fullname_len = strlen(cmpl_dir->fullname);
1623 correct_parent(CompletionDir* cmpl_dir, struct stat *sbuf)
1630 last_slash = strrchr(cmpl_dir->fullname, '/');
1632 g_assert(last_slash);
1634 if(last_slash != cmpl_dir->fullname)
1635 { /* last_slash[0] = 0; */ }
1642 if (stat(cmpl_dir->fullname, &parbuf) < 0)
1648 if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
1649 /* it wasn't a link */
1655 last_slash[0] = '/'; */
1657 /* it was a link, have to figure it out the hard way */
1659 new_name = find_parent_dir_fullname(cmpl_dir->fullname);
1664 g_free(cmpl_dir->fullname);
1666 cmpl_dir->fullname = new_name;
1672 find_parent_dir_fullname(gchar* dirname)
1674 gchar buffer[MAXPATHLEN];
1675 gchar buffer2[MAXPATHLEN];
1677 if(!getcwd(buffer, MAXPATHLEN))
1683 if(chdir(dirname) != 0 || chdir("..") != 0)
1689 if(!getcwd(buffer2, MAXPATHLEN))
1697 if(chdir(buffer) != 0)
1703 return g_strdup(buffer2);
1706 /**********************************************************************/
1707 /* Completion Operations */
1708 /**********************************************************************/
1710 static PossibleCompletion*
1711 attempt_homedir_completion(gchar* text_to_complete,
1712 CompletionState *cmpl_state)
1716 if (!cmpl_state->user_dir_name_buffer &&
1717 !get_pwdb(cmpl_state))
1719 length = strlen(text_to_complete) - 1;
1721 cmpl_state->user_completion_index += 1;
1723 while(cmpl_state->user_completion_index < cmpl_state->user_directories_len)
1725 index = first_diff_index(text_to_complete + 1,
1726 cmpl_state->user_directories
1727 [cmpl_state->user_completion_index].login);
1734 if(cmpl_state->last_valid_char < (index + 1))
1735 cmpl_state->last_valid_char = index + 1;
1736 cmpl_state->user_completion_index += 1;
1740 cmpl_state->the_completion.is_a_completion = 1;
1741 cmpl_state->the_completion.is_directory = 1;
1743 append_completion_text("~", cmpl_state);
1745 append_completion_text(cmpl_state->
1746 user_directories[cmpl_state->user_completion_index].login,
1749 return append_completion_text("/", cmpl_state);
1752 if(text_to_complete[1] ||
1753 cmpl_state->user_completion_index > cmpl_state->user_directories_len)
1755 cmpl_state->user_completion_index = -1;
1760 cmpl_state->user_completion_index += 1;
1761 cmpl_state->the_completion.is_a_completion = 1;
1762 cmpl_state->the_completion.is_directory = 1;
1764 return append_completion_text("~/", cmpl_state);
1768 /* returns the index (>= 0) of the first differing character,
1769 * PATTERN_MATCH if the completion matches */
1771 first_diff_index(gchar* pat, gchar* text)
1775 while(*pat && *text && *text == *pat)
1785 return PATTERN_MATCH;
1788 static PossibleCompletion*
1789 append_completion_text(gchar* text, CompletionState* cmpl_state)
1793 if(!cmpl_state->the_completion.text)
1796 len = strlen(text) + strlen(cmpl_state->the_completion.text) + 1;
1798 if(cmpl_state->the_completion.text_alloc > len)
1800 strcat(cmpl_state->the_completion.text, text);
1801 return &cmpl_state->the_completion;
1804 while(i < len) { i <<= 1; }
1806 cmpl_state->the_completion.text_alloc = i;
1808 cmpl_state->the_completion.text = (gchar*)g_realloc(cmpl_state->the_completion.text, i);
1810 if(!cmpl_state->the_completion.text)
1814 strcat(cmpl_state->the_completion.text, text);
1815 return &cmpl_state->the_completion;
1819 static CompletionDir*
1820 find_completion_dir(gchar* text_to_complete,
1821 gchar** remaining_text,
1822 CompletionState* cmpl_state)
1824 gchar* first_slash = strchr(text_to_complete, '/');
1825 CompletionDir* dir = cmpl_state->reference_dir;
1826 *remaining_text = text_to_complete;
1830 gint len = first_slash - *remaining_text;
1832 gint found_index = -1;
1834 gchar* pat_buf = g_new (gchar, len + 1);
1836 strncpy(pat_buf, *remaining_text, len);
1839 for(i = 0; i < dir->sent->entry_count; i += 1)
1841 if(dir->sent->entries[i].is_dir &&
1842 fnmatch(pat_buf, dir->sent->entries[i].entry_name,
1843 FNMATCH_FLAGS)!= FNM_NOMATCH)
1860 CompletionDir* next = open_relative_dir(dir->sent->entries[found_index].entry_name,
1869 next->cmpl_parent = dir;
1873 if(!correct_dir_fullname(dir))
1879 *remaining_text = first_slash + 1;
1880 first_slash = strchr(*remaining_text, '/');
1895 update_cmpl(PossibleCompletion* poss, CompletionState* cmpl_state)
1899 if(!poss || !cmpl_is_a_completion(poss))
1902 cmpl_len = strlen(cmpl_this_completion(poss));
1904 if(cmpl_state->updated_text_alloc < cmpl_len + 1)
1906 cmpl_state->updated_text =
1907 (gchar*)g_realloc(cmpl_state->updated_text,
1908 cmpl_state->updated_text_alloc);
1909 cmpl_state->updated_text_alloc = 2*cmpl_len;
1912 if(cmpl_state->updated_text_len < 0)
1914 strcpy(cmpl_state->updated_text, cmpl_this_completion(poss));
1915 cmpl_state->updated_text_len = cmpl_len;
1916 cmpl_state->re_complete = cmpl_is_directory(poss);
1918 else if(cmpl_state->updated_text_len == 0)
1920 cmpl_state->re_complete = FALSE;
1925 first_diff_index(cmpl_state->updated_text,
1926 cmpl_this_completion(poss));
1928 cmpl_state->re_complete = FALSE;
1930 if(first_diff == PATTERN_MATCH)
1933 if(first_diff > cmpl_state->updated_text_len)
1934 strcpy(cmpl_state->updated_text, cmpl_this_completion(poss));
1936 cmpl_state->updated_text_len = first_diff;
1937 cmpl_state->updated_text[first_diff] = 0;
1941 static PossibleCompletion*
1942 attempt_file_completion(CompletionState *cmpl_state)
1944 gchar *pat_buf, *first_slash;
1945 CompletionDir *dir = cmpl_state->active_completion_dir;
1947 dir->cmpl_index += 1;
1949 if(dir->cmpl_index == dir->sent->entry_count)
1951 if(dir->cmpl_parent == NULL)
1953 cmpl_state->active_completion_dir = NULL;
1959 cmpl_state->active_completion_dir = dir->cmpl_parent;
1961 return attempt_file_completion(cmpl_state);
1965 g_assert(dir->cmpl_text);
1967 first_slash = strchr(dir->cmpl_text, '/');
1971 gint len = first_slash - dir->cmpl_text;
1973 pat_buf = g_new (gchar, len + 1);
1974 strncpy(pat_buf, dir->cmpl_text, len);
1979 gint len = strlen(dir->cmpl_text);
1981 pat_buf = g_new (gchar, len + 2);
1982 strcpy(pat_buf, dir->cmpl_text);
1983 strcpy(pat_buf + len, "*");
1988 if(dir->sent->entries[dir->cmpl_index].is_dir)
1990 if(fnmatch(pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
1991 FNMATCH_FLAGS) != FNM_NOMATCH)
1993 CompletionDir* new_dir;
1995 new_dir = open_relative_dir(dir->sent->entries[dir->cmpl_index].entry_name,
2004 new_dir->cmpl_parent = dir;
2006 new_dir->cmpl_index = -1;
2007 new_dir->cmpl_text = first_slash + 1;
2009 cmpl_state->active_completion_dir = new_dir;
2012 return attempt_file_completion(cmpl_state);
2017 return attempt_file_completion(cmpl_state);
2023 return attempt_file_completion(cmpl_state);
2028 if(dir->cmpl_parent != NULL)
2030 append_completion_text(dir->fullname +
2031 strlen(cmpl_state->completion_dir->fullname) + 1,
2033 append_completion_text("/", cmpl_state);
2036 append_completion_text(dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
2038 cmpl_state->the_completion.is_a_completion =
2039 (fnmatch(pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
2040 FNMATCH_FLAGS) != FNM_NOMATCH);
2042 cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
2043 if(dir->sent->entries[dir->cmpl_index].is_dir)
2044 append_completion_text("/", cmpl_state);
2047 return &cmpl_state->the_completion;
2053 get_pwdb(CompletionState* cmpl_state)
2055 struct passwd *pwd_ptr;
2056 gchar* buf_ptr, *home_dir = NULL;
2057 gint len = 0, i, count = 0;
2059 if(cmpl_state->user_dir_name_buffer)
2063 while ((pwd_ptr = getpwent()) != NULL)
2065 len += strlen(pwd_ptr->pw_name);
2066 len += strlen(pwd_ptr->pw_dir);
2071 if (!cmpl_state->user_home_dir)
2073 /* the loser doesn't have $HOME set */
2076 pwd_ptr = getpwuid(getuid());
2082 home_dir = pwd_ptr->pw_dir;
2084 len += strlen(home_dir);
2090 cmpl_state->user_dir_name_buffer = g_new(gchar, len);
2091 cmpl_state->user_directories = g_new(CompletionUserDir, count);
2092 cmpl_state->user_directories_len = count;
2094 buf_ptr = cmpl_state->user_dir_name_buffer;
2096 if (!cmpl_state->user_home_dir)
2098 strcpy(buf_ptr, home_dir);
2099 cmpl_state->user_home_dir = buf_ptr;
2100 buf_ptr += strlen(buf_ptr);
2104 for(i = 0; i < count; i += 1)
2106 pwd_ptr = getpwent();
2113 strcpy(buf_ptr, pwd_ptr->pw_name);
2114 cmpl_state->user_directories[i].login = buf_ptr;
2115 buf_ptr += strlen(buf_ptr);
2117 strcpy(buf_ptr, pwd_ptr->pw_dir);
2118 cmpl_state->user_directories[i].homedir = buf_ptr;
2119 buf_ptr += strlen(buf_ptr);
2123 qsort(cmpl_state->user_directories,
2124 cmpl_state->user_directories_len,
2125 sizeof(CompletionUserDir),
2134 if(cmpl_state->user_dir_name_buffer)
2135 g_free(cmpl_state->user_dir_name_buffer);
2136 if(cmpl_state->user_directories)
2137 g_free(cmpl_state->user_directories);
2139 cmpl_state->user_dir_name_buffer = NULL;
2140 cmpl_state->user_directories = NULL;
2146 compare_user_dir(const void* a, const void* b)
2148 return strcmp((((CompletionUserDir*)a))->login,
2149 (((CompletionUserDir*)b))->login);
2153 compare_cmpl_dir(const void* a, const void* b)
2155 return strcmp((((CompletionDirEntry*)a))->entry_name,
2156 (((CompletionDirEntry*)b))->entry_name);
2160 cmpl_state_okay(CompletionState* cmpl_state)
2162 return cmpl_state && cmpl_state->reference_dir;
2166 cmpl_strerror(gint err)
2168 if(err == CMPL_ERRNO_TOO_LONG)
2169 return "Name too long";
2171 return g_strerror (err);